aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/AntClassLoader.java1622
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/AntTypeDefinition.java389
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ArgumentProcessor.java72
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ArgumentProcessorRegistry.java171
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildEvent.java203
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildException.java153
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildListener.java110
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildLogger.java72
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ComponentHelper.java1101
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DefaultDefinitions.java76
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DefaultLogger.java380
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DemuxInputStream.java74
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DemuxOutputStream.java249
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Diagnostics.java715
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DirectoryScanner.java1900
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicAttribute.java38
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicAttributeNS.java41
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicConfigurator.java29
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicConfiguratorNS.java27
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicElement.java36
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicElementNS.java37
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicObjectAttribute.java41
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Evaluable.java29
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Executor.java48
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ExitException.java61
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ExitStatusException.java69
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ExtensionPoint.java60
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/FileScanner.java158
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/IntrospectionHelper.java1745
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Location.java179
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/MagicNames.java293
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Main.java1317
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/NoBannerLogger.java100
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PathTokenizer.java166
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Project.java2494
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ProjectComponent.java169
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ProjectHelper.java698
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ProjectHelperRepository.java337
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PropertyHelper.java1215
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/RuntimeConfigurable.java608
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/SubBuildListener.java58
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Target.java532
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Task.java481
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TaskAdapter.java182
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TaskConfigurationChecker.java111
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TaskContainer.java41
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TypeAdapter.java66
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/UnknownElement.java699
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/UnsupportedAttributeException.java49
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/UnsupportedElementException.java57
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/XmlLogger.java474
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/antlib.xml144
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/AttributeNamespace.java28
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/BaseIfAttribute.java86
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/EnableAttribute.java35
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/IfBlankAttribute.java39
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/IfSetAttribute.java39
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/IfTrueAttribute.java41
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/defaultManifest.mf4
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/dispatch/DispatchTask.java59
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/dispatch/DispatchUtils.java124
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/dispatch/Dispatchable.java31
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/BaseFilterReader.java199
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/BaseParamFilterReader.java74
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ChainableReader.java37
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ClassConstants.java165
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ConcatFilter.java218
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/EscapeUnicode.java123
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ExpandProperties.java142
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/FixCrLfFilter.java1004
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/HeadFilter.java222
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/LineContains.java244
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/LineContainsRegExp.java242
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/PrefixLines.java164
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ReplaceTokens.java379
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/SortFilter.java375
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StringInputStream.java49
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StripJavaComments.java145
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StripLineBreaks.java154
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StripLineComments.java237
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/SuffixLines.java174
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/TabsToSpaces.java155
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/TailFilter.java243
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/TokenFilter.java712
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/UniqFilter.java37
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/util/ChainReaderHelper.java288
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/util/JavaClassHelper.java70
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/AntXMLContext.java417
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/DefaultExecutor.java60
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/IgnoreDependenciesExecutor.java70
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/ProjectHelper2.java1238
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java1026
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/SingleCheckExecutor.java47
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/DefaultInputHandler.java120
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/GreedyInputHandler.java80
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/InputHandler.java41
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/InputRequest.java93
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/MultipleChoiceInputRequest.java58
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/PropertyFileInputHandler.java92
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/SecureInputHandler.java59
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/AntMain.java42
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/LaunchException.java38
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/Launcher.java412
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/Locator.java528
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/AnsiColorLogger.java249
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/BigProjectLogger.java194
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/CommonsLoggingListener.java332
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/Log4jListener.java177
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/MailLogger.java440
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/ProfileLogger.java112
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/SilentLogger.java62
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/SimpleBigProjectLogger.java46
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/TimestampedLogger.java54
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/defaults.properties58
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/loader/AntClassLoader2.java31
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/loader/AntClassLoader5.java65
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/GetProperty.java31
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/LocalProperties.java152
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/LocalPropertyStack.java161
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/NullReturn.java38
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/ParseNextProperty.java44
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/ParseProperties.java199
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/PropertyExpander.java51
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/ResolvePropertyMap.java151
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/package.html19
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java873
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AbstractJarSignerTask.java423
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Ant.java836
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AntStructure.java485
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Antlib.java184
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AntlibDefinition.java81
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Apt.java270
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AttributeNamespaceDef.java51
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AugmentReference.java96
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Available.java514
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BUnzip2.java112
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BZip2.java74
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Basename.java110
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BindTargets.java91
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BuildNumber.java201
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CVSPass.java172
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CallTarget.java255
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Checksum.java712
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Chmod.java261
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Classloader.java244
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CloseResources.java59
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CommandLauncherTask.java56
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Componentdef.java40
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Concat.java955
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ConditionTask.java130
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copy.java1111
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CopyPath.java214
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copydir.java163
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copyfile.java116
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Cvs.java41
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DefBase.java167
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DefaultExcludes.java115
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Definer.java639
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Delete.java836
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Deltree.java112
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DependSet.java299
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DiagnosticsTask.java41
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Dirname.java82
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Ear.java154
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Echo.java170
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/EchoXML.java137
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Exec.java281
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecTask.java726
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Execute.java734
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java331
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java784
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteStreamHandler.java68
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteWatchdog.java177
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Exit.java235
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Expand.java527
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Filter.java101
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java696
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/GUnzip.java97
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/GZip.java68
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/GenerateKey.java420
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Get.java883
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/HostInfo.java256
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ImportTask.java348
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Input.java259
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/JDBCTask.java525
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Jar.java1238
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Java.java965
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Javac.java1270
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Javadoc.java2624
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Jikes.java132
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/JikesOutputParser.java183
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/KeySubst.java191
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Length.java335
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LoadFile.java41
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LoadProperties.java248
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LoadResource.java236
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Local.java47
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LogOutputStream.java111
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LogStreamHandler.java70
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MacroDef.java851
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MacroInstance.java411
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MakeUrl.java298
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Manifest.java1183
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ManifestClassPath.java180
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ManifestException.java36
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java293
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MatchingTask.java446
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Mkdir.java114
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Move.java382
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Nice.java99
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Pack.java212
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Parallel.java489
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Patch.java215
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PathConvert.java511
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PreSetDef.java275
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ProcessDestroyer.java220
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ProjectHelperTask.java50
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Property.java726
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java145
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PumpStreamHandler.java298
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Recorder.java324
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/RecorderEntry.java367
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Redirector.java1022
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Rename.java96
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Replace.java955
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ResourceCount.java124
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Retry.java119
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Rmic.java853
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SQLExec.java1162
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SendEmail.java45
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sequential.java74
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SignJar.java645
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sleep.java194
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/StreamPumper.java252
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SubAnt.java642
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sync.java606
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Tar.java1016
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/TaskOutputStream.java96
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Taskdef.java51
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/TempFile.java163
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Touch.java381
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Transform.java30
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Truncate.java205
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Tstamp.java341
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Typedef.java46
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Unpack.java195
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Untar.java244
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/UpToDate.java277
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/VerifyJar.java207
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/WaitFor.java277
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/War.java233
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/WhichResource.java199
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison.java69
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison2.java33
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison3.java35
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison4.java42
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLogger.java31
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLoggerAware.java31
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java1689
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XmlProperty.java780
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Zip.java2274
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/AptCompilerAdapter.java187
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/AptExternalCompilerAdapter.java71
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapter.java52
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterExtension.java40
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterFactory.java202
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapter.java736
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Gcj.java160
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Javac12.java91
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Javac13.java70
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/JavacExternal.java92
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Jikes.java221
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Jvc.java116
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Kjc.java119
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Sj.java61
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/And.java50
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/AntVersion.java171
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Condition.java35
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ConditionBase.java281
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Contains.java76
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Equals.java148
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/FilesMatch.java101
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/HasFreeSpace.java101
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/HasMethod.java192
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Http.java117
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsFailure.java54
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsFalse.java55
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsFileSelected.java81
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsLastModified.java219
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsReachable.java207
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsReference.java89
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsSet.java51
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsSigned.java152
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsTrue.java55
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Matches.java119
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Not.java50
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Or.java50
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Os.java321
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ParserSupports.java150
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ResourceContains.java165
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ResourceExists.java56
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ResourcesMatch.java93
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Socket.java87
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/TypeFound.java90
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Xor.java49
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CVSEntry.java112
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogParser.java321
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogTask.java489
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogWriter.java114
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagDiff.java576
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagEntry.java109
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsUser.java93
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsVersion.java169
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/RCSFile.java69
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingOutputStream.java46
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingStreamHandler.java61
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/defaults.properties210
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/EmailAddress.java197
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/EmailTask.java634
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/Header.java61
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/Mailer.java277
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/Message.java203
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/MimeMailer.java343
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/PlainMailer.java177
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/UUMailer.java56
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/CommandLauncher.java213
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/CommandLauncherProxy.java54
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/Java13CommandLauncher.java66
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/MacCommandLauncher.java63
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/OS2CommandLauncher.java82
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/PerlScriptCommandLauncher.java90
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/ScriptCommandLauncher.java88
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/VmsCommandLauncher.java145
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/WinNTCommandLauncher.java78
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ANTLR.java438
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Cab.java357
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/EchoProperties.java543
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java513
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Native2Ascii.java328
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/NetRexxC.java1042
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java726
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/RenameExtensions.java146
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ReplaceRegExp.java533
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Rpm.java364
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/SchemaValidate.java529
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Script.java133
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java650
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XMLValidateTask.java764
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XSLTTraceSupport.java32
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Xalan2TraceSupport.java54
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheck.java208
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckin.java39
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckinDefault.java38
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckout.java35
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCreateTask.java335
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMReconfigure.java159
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/Continuus.java144
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckin.java343
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckout.java516
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCLock.java378
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkattr.java425
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkbl.java365
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkdir.java237
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkelem.java424
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklabel.java402
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklbtype.java440
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCRmtype.java373
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnCheckout.java141
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnlock.java260
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUpdate.java331
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/ClearCase.java242
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/AntAnalyzer.java144
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFile.java121
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileIterator.java33
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileUtils.java52
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java917
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/DirectoryIterator.java164
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/JarFileIterator.java89
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ClassCPInfo.java93
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantCPInfo.java63
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPool.java358
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPoolEntry.java242
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/DoubleCPInfo.java58
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FieldRefCPInfo.java130
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FloatCPInfo.java56
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/IntegerCPInfo.java56
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InterfaceMethodRefCPInfo.java137
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InvokeDynamicCPInfo.java85
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/LongCPInfo.java56
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodHandleCPInfo.java107
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodRefCPInfo.java133
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodTypeCPInfo.java82
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/NameAndTypeCPInfo.java112
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/StringCPInfo.java74
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/Utf8CPInfo.java67
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandDeploymentTool.java559
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandGenerateClient.java313
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DescriptorHandler.java390
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java61
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java630
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java953
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetDeploymentTool.java403
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbc.java1495
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbcTask.java321
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/InnerClassFilenameFilter.java54
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JbossDeploymentTool.java111
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JonasDeploymentTool.java835
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicDeploymentTool.java932
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicTOPLinkDeploymentTool.java113
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WebsphereDeploymentTool.java897
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatability.java57
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatibility.java57
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/DeweyDecimal.java54
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Extension.java690
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionAdapter.java215
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionResolver.java43
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionSet.java153
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionUtil.java215
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtraAttribute.java83
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibAvailableTask.java157
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibDisplayTask.java119
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibManifestTask.java296
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibResolveTask.java268
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibFileSet.java117
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibraryDisplayer.java150
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Specification.java605
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/AntResolver.java117
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/LocationResolver.java65
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/URLResolver.java133
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java632
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/image/Image.java421
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/AbstractHotDeploymentTool.java193
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/GenericHotDeploymentTool.java140
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/HotDeploymentTool.java61
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/JonasHotDeploymentTool.java252
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/ServerDeploy.java153
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/WebLogicHotDeploymentTool.java247
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJDoc.java226
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJTree.java416
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java579
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Gcjh.java89
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapter.java38
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapterFactory.java120
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Kaffeh.java94
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/SunJavah.java123
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTask.java684
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java139
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java183
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java458
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/Jasper41Mangler.java93
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspC.java709
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspMangler.java47
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspNameMangler.java155
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/WLJspc.java334
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/DefaultJspCompilerAdapter.java153
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JasperC.java182
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapter.java64
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapterFactory.java122
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java346
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java241
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java202
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java300
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Constants.java46
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/CustomJUnit4TestAdapterCache.java90
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java228
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java177
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FailureRecorder.java448
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java401
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestListener.java52
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestResult.java99
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.java193
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java65
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java2283
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirror.java190
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirrorImpl.java109
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java542
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java1297
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java179
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/OutErrSummaryJUnitResultFormatter.java36
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java317
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java213
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrash.java144
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestIgnored.java35
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestListenerWrapper.java63
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java141
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java366
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java329
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/DefaultNative2Ascii.java106
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/KaffeNative2Ascii.java88
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapter.java44
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.java116
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/SunNative2Ascii.java71
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTP.java2725
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPConfigurator.java99
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTask.java965
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskConfig.java28
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirror.java24
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirrorImpl.java1951
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/MimeMail.java43
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/RExecTask.java479
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/SetProxy.java283
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/TelnetTask.java397
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/Pvcs.java675
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/PvcsProject.java49
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java397
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDefBase.java132
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOS.java479
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckin.java101
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckout.java85
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCmd.java81
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSGet.java116
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSLabel.java91
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/package.html29
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/AntSoundPlayer.java251
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/SoundTask.java193
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashScreen.java181
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashTask.java297
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java272
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Directory.java196
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/LogListener.java30
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHBase.java236
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java519
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHSession.java333
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHUserInfo.java217
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java486
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java311
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java205
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java331
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessageBySftp.java277
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java71
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java114
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java577
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/AbstractAccessTask.java110
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chgrp.java84
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chown.java84
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java600
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSS.java784
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSADD.java122
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKIN.java114
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKOUT.java165
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCP.java69
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCREATE.java91
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSConstants.java127
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSGET.java172
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSHISTORY.java199
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSLABEL.java108
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/windows/Attrib.java196
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/DefaultRmicAdapter.java493
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/ForkingSunRmic.java90
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/KaffeRmic.java104
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapter.java67
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapterFactory.java135
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/SunRmic.java114
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/WLRmic.java136
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/XNewRmic.java52
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/AbstractFileSet.java922
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/AntFilterReader.java178
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ArchiveFileSet.java596
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ArchiveScanner.java363
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Assertions.java359
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Commandline.java689
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/CommandlineJava.java699
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Comparison.java95
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/DTDLocation.java33
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/DataType.java367
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Description.java116
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/DirSet.java115
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/EnumeratedAttribute.java153
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Environment.java177
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FileList.java219
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FileSet.java94
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FilterChain.java418
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FilterSet.java654
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FilterSetCollection.java88
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FlexInteger.java54
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/LogLevel.java87
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Mapper.java322
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Parameter.java82
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Parameterizable.java31
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Path.java775
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/PatternSet.java541
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Permissions.java356
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/PropertySet.java577
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Quantifier.java146
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/RedirectorElement.java630
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Reference.java134
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/RegularExpression.java141
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Resource.java439
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ResourceCollection.java50
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ResourceFactory.java38
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ResourceLocation.java106
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Substitution.java77
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/TarFileSet.java269
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/TarScanner.java87
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/TimeComparison.java122
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/XMLCatalog.java1128
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ZipFileSet.java117
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ZipScanner.java99
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/conditions/antlib.xml93
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/defaults.properties101
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/mappers/CutDirsMapper.java76
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/mappers/FilterMapper.java87
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/AbstractScriptComponent.java158
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptCondition.java68
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptFilter.java183
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptMapper.java91
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptSelector.java223
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/depend/ClassfileSet.java183
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/depend/DependScanner.java225
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Arc.java123
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/BasicShape.java55
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/ColorMapper.java102
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Draw.java102
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/DrawOperation.java40
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Ellipse.java87
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/ImageOperation.java72
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Rectangle.java119
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Rotate.java118
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Scale.java181
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Text.java128
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/TransformOperation.java38
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resolver/ApacheCatalog.java121
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resolver/ApacheCatalogResolver.java174
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resolver/package.html35
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AbstractClasspathResource.java265
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AbstractResourceCollectionWrapper.java209
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AllButFirst.java60
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AllButLast.java53
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Appendable.java35
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ArchiveResource.java293
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Archives.java194
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BCFileSet.java73
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BZip2Resource.java85
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BaseResourceCollectionContainer.java268
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BaseResourceCollectionWrapper.java56
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/CompressedResource.java60
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ContentTransformingResource.java177
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Difference.java63
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FailFast.java135
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FileProvider.java36
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FileResource.java392
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FileResourceIterator.java149
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Files.java503
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/First.java48
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/GZipResource.java75
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ImmutableResourceException.java46
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Intersect.java64
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/JavaConstantResource.java70
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/JavaResource.java141
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Last.java73
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/LazyResourceCollectionWrapper.java176
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/LogOutputResource.java68
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/MappedResource.java116
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/MappedResourceCollection.java267
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/MultiRootFileSet.java237
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/PropertyResource.java206
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ResourceDecorator.java270
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ResourceList.java239
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Resources.java274
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Restrict.java156
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/SizeLimitCollection.java71
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Sort.java98
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/StringResource.java261
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/TarResource.java198
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Tokens.java137
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Touchable.java32
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/URLProvider.java35
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/URLResource.java454
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Union.java156
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ZipResource.java226
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Content.java70
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Date.java45
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/DelegatedResourceComparator.java125
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Exists.java43
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/FileSystem.java60
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Name.java38
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/ResourceComparator.java81
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Reverse.java91
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Size.java39
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Type.java44
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/And.java58
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Compare.java149
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Date.java162
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Exists.java37
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/InstanceOf.java130
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Majority.java84
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Name.java151
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/None.java59
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Not.java66
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Or.java58
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelector.java35
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelectorContainer.java128
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Size.java73
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Type.java110
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/AbstractSelectorContainer.java353
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/AndSelector.java74
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/BaseExtendSelector.java87
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/BaseSelector.java113
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/BaseSelectorContainer.java343
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ContainsRegexpSelector.java219
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ContainsSelector.java221
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DateSelector.java260
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DependSelector.java78
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DepthSelector.java185
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DifferentSelector.java114
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ExtendFileSelector.java39
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ExtendSelector.java201
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/FileSelector.java48
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/FilenameSelector.java195
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/MajoritySelector.java103
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/MappingSelector.java165
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/NoneSelector.java75
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/NotSelector.java73
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/OrSelector.java75
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/PresentSelector.java198
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ReadableSelector.java49
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectSelector.java231
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectorContainer.java187
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectorScanner.java49
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectorUtils.java695
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SignedSelector.java59
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SizeSelector.java279
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/TokenizedPath.java222
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/TokenizedPattern.java177
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/TypeSelector.java135
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/WritableSelector.java46
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Algorithm.java48
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Cache.java72
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ChecksumAlgorithm.java151
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java208
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/EqualComparator.java58
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/HashvalueAlgorithm.java80
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java971
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java236
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/spi/Provider.java63
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/spi/Service.java116
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Base64Converter.java124
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ChainedMapper.java60
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ClasspathUtils.java463
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/CollectionUtils.java278
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/CompositeMapper.java50
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java136
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ConcatResourceInputStream.java147
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ContainerMapper.java119
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DOMElementWriter.java640
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DOMUtils.java163
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DateUtils.java301
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DeweyDecimal.java237
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FileNameMapper.java60
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FileTokenizer.java48
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FileUtils.java1722
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FirstMatchMapper.java45
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FlatFileNameMapper.java54
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/GlobPatternMapper.java203
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/IdentityMapper.java52
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/IdentityStack.java130
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/JAXPUtils.java260
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/JavaEnvUtils.java573
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/KeepAliveInputStream.java66
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/KeepAliveOutputStream.java83
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LayoutPreservingProperties.java775
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LazyFileOutputStream.java162
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LazyHashtable.java120
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java161
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LineOrientedOutputStream.java158
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LineOrientedOutputStreamRedirector.java68
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LineTokenizer.java115
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LinkedHashtable.java132
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LoaderUtils.java140
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/MergingMapper.java67
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/OutputStreamFunneler.java176
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/PackageNameMapper.java49
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ProcessUtil.java65
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/PropertyOutputStream.java69
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ProxySetup.java115
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ReaderInputStream.java205
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ReflectUtil.java212
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ReflectWrapper.java99
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/RegexpPatternMapper.java159
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ResourceUtils.java860
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/RetryHandler.java74
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Retryable.java38
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptFixBSFPath.java147
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunner.java27
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java360
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunnerCreator.java133
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunnerHelper.java206
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/SourceFileScanner.java173
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/SplitClassLoader.java73
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/StringTokenizer.java154
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/StringUtils.java273
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/SymbolicLinkUtils.java296
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/TaskLogger.java79
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/TeeOutputStream.java95
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/TimeoutObserver.java37
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Tokenizer.java44
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/UUEncoder.java148
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/UnPackageNameMapper.java48
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/UnicodeUtil.java46
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/VectorSet.java242
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Watchdog.java128
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/WeakishReference.java91
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/WorkerAnt.java172
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/XMLFragment.java155
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/XmlConstants.java65
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/AbstractAnalyzer.java286
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/DependencyAnalyzer.java130
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/bcel/AncestorAnalyzer.java144
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/bcel/DependencyVisitor.java192
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/bcel/FullAnalyzer.java136
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/facade/FacadeTaskHelper.java165
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/facade/ImplementationSpecificArgument.java61
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/java15/ProxyDiagnostics.java108
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/JavaxScriptRunner.java163
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/NoExitSecurityManager.java51
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/ScriptRunner.java177
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/WeakishReference12.java45
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaOroMatcher.java170
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaOroRegexp.java98
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpMatcher.java161
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpRegexp.java90
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpMatcher.java170
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpRegexp.java105
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/Regexp.java50
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpFactory.java85
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpMatcher.java110
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpMatcherFactory.java114
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpUtil.java109
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/version.txt2
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/BZip2Constants.java109
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/BlockSort.java1081
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/CBZip2InputStream.java1062
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/CBZip2OutputStream.java1580
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/CRC.java141
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/mail/ErrorInQuitException.java43
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/mail/MailMessage.java526
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/mail/SmtpResponseReader.java106
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarArchiveSparseEntry.java63
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarBuffer.java463
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarConstants.java293
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarEntry.java1149
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarInputStream.java660
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarOutputStream.java657
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarUtils.java564
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/AbstractUnicodeExtraField.java183
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/AsiExtraField.java352
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/CentralDirectoryParsingZipExtraField.java40
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ExtraFieldUtils.java314
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/FallbackZipEncoding.java94
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/GeneralPurposeBit.java194
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/JarMarker.java108
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/NioZipEncoding.java122
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Simple8BitZipEncoding.java274
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnicodeCommentExtraField.java70
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnicodePathExtraField.java67
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnixStat.java76
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnparseableExtraFieldData.java115
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnrecognizedExtraField.java154
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnsupportedZipFeatureException.java89
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Zip64ExtendedInformationExtraField.java322
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Zip64Mode.java47
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Zip64RequiredException.java49
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipConstants.java59
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEightByteInteger.java229
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEncoding.java84
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEncodingHelper.java252
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEntry.java786
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipExtraField.java85
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipFile.java1048
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipLong.java201
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipOutputStream.java1674
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipShort.java166
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipUtil.java252
873 files changed, 222974 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/AntClassLoader.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/AntClassLoader.java
new file mode 100644
index 00000000..607ada71
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/AntClassLoader.java
@@ -0,0 +1,1622 @@
+/*
+ * 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.tools.ant;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.CodeSource;
+import java.security.ProtectionDomain;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.jar.Attributes;
+import java.util.jar.Attributes.Name;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import org.apache.tools.ant.launch.Locator;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.CollectionUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.LoaderUtils;
+import org.apache.tools.ant.util.ReflectUtil;
+import org.apache.tools.ant.util.VectorSet;
+import org.apache.tools.zip.ZipLong;
+
+/**
+ * Used to load classes within ant with a different classpath from
+ * that used to start ant. Note that it is possible to force a class
+ * into this loader even when that class is on the system classpath by
+ * using the forceLoadClass method. Any subsequent classes loaded by that
+ * class will then use this loader rather than the system class loader.
+ *
+ * <p>
+ * Note that this classloader has a feature to allow loading
+ * in reverse order and for "isolation".
+ * Due to the fact that a number of
+ * methods in java.lang.ClassLoader are final (at least
+ * in java 1.4 getResources) this means that the
+ * class has to fake the given parent.
+ * </p>
+ *
+ */
+public class AntClassLoader extends ClassLoader implements SubBuildListener {
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * An enumeration of all resources of a given name found within the
+ * classpath of this class loader. This enumeration is used by the
+ * ClassLoader.findResources method, which is in
+ * turn used by the ClassLoader.getResources method.
+ *
+ * @see AntClassLoader#findResources(String)
+ * @see java.lang.ClassLoader#getResources(String)
+ */
+ private class ResourceEnumeration implements Enumeration<URL> {
+ /**
+ * The name of the resource being searched for.
+ */
+ private final String resourceName;
+
+ /**
+ * The index of the next classpath element to search.
+ */
+ private int pathElementsIndex;
+
+ /**
+ * The URL of the next resource to return in the enumeration. If this
+ * field is <code>null</code> then the enumeration has been completed,
+ * i.e., there are no more elements to return.
+ */
+ private URL nextResource;
+
+ /**
+ * Constructs a new enumeration of resources of the given name found
+ * within this class loader's classpath.
+ *
+ * @param name the name of the resource to search for.
+ */
+ ResourceEnumeration(final String name) {
+ this.resourceName = name;
+ this.pathElementsIndex = 0;
+ findNextResource();
+ }
+
+ /**
+ * Indicates whether there are more elements in the enumeration to
+ * return.
+ *
+ * @return <code>true</code> if there are more elements in the
+ * enumeration; <code>false</code> otherwise.
+ */
+ public boolean hasMoreElements() {
+ return (this.nextResource != null);
+ }
+
+ /**
+ * Returns the next resource in the enumeration.
+ *
+ * @return the next resource in the enumeration
+ */
+ public URL nextElement() {
+ final URL ret = this.nextResource;
+ if (ret == null) {
+ throw new NoSuchElementException();
+ }
+ findNextResource();
+ return ret;
+ }
+
+ /**
+ * Locates the next resource of the correct name in the classpath and
+ * sets <code>nextResource</code> to the URL of that resource. If no
+ * more resources can be found, <code>nextResource</code> is set to
+ * <code>null</code>.
+ */
+ private void findNextResource() {
+ URL url = null;
+ while ((pathElementsIndex < pathComponents.size()) && (url == null)) {
+ try {
+ final File pathComponent = pathComponents.elementAt(pathElementsIndex);
+ url = getResourceURL(pathComponent, this.resourceName);
+ pathElementsIndex++;
+ } catch (final BuildException e) {
+ // ignore path elements which are not valid relative to the
+ // project
+ }
+ }
+ this.nextResource = url;
+ }
+ }
+
+ /**
+ * The size of buffers to be used in this classloader.
+ */
+ private static final int BUFFER_SIZE = 8192;
+
+ /**
+ * Number of array elements in a test array of strings
+ */
+ private static final int NUMBER_OF_STRINGS = 256;
+
+ /**
+ * The components of the classpath that the classloader searches
+ * for classes.
+ */
+ private final Vector<File> pathComponents = new VectorSet<File>();
+
+ /**
+ * The project to which this class loader belongs.
+ */
+ private Project project;
+
+ /**
+ * Indicates whether the parent class loader should be
+ * consulted before trying to load with this class loader.
+ */
+ private boolean parentFirst = true;
+
+ /**
+ * These are the package roots that are to be loaded by the parent class
+ * loader regardless of whether the parent class loader is being searched
+ * first or not.
+ */
+ private final Vector<String> systemPackages = new Vector<String>();
+
+ /**
+ * These are the package roots that are to be loaded by this class loader
+ * regardless of whether the parent class loader is being searched first
+ * or not.
+ */
+ private final Vector<String> loaderPackages = new Vector<String>();
+
+ /**
+ * Whether or not this classloader will ignore the base
+ * classloader if it can't find a class.
+ *
+ * @see #setIsolated(boolean)
+ */
+ private boolean ignoreBase = false;
+
+ /**
+ * The parent class loader, if one is given or can be determined.
+ */
+ private ClassLoader parent = null;
+
+ /**
+ * A hashtable of zip files opened by the classloader (File to JarFile).
+ */
+ private Hashtable<File, JarFile> jarFiles = new Hashtable<File, JarFile>();
+
+ /** Static map of jar file/time to manifest class-path entries */
+ private static Map<String, String> pathMap =
+ Collections.synchronizedMap(new HashMap<String, String>());
+
+ /**
+ * The context loader saved when setting the thread's current
+ * context loader.
+ */
+ private ClassLoader savedContextLoader = null;
+
+ /**
+ * Whether or not the context loader is currently saved.
+ */
+ private boolean isContextLoaderSaved = false;
+
+ /**
+ * Create an Ant ClassLoader for a given project, with
+ * a parent classloader and an initial classpath.
+ * @since Ant 1.7.
+ * @param parent the parent for this classloader.
+ * @param project The project to which this classloader is to
+ * belong.
+ * @param classpath The classpath to use to load classes.
+ */
+ public AntClassLoader(final ClassLoader parent, final Project project, final Path classpath) {
+ setParent(parent);
+ setClassPath(classpath);
+ setProject(project);
+ }
+
+ /**
+ * Create an Ant Class Loader
+ */
+ public AntClassLoader() {
+ setParent(null);
+ }
+
+ /**
+ * Creates a classloader for the given project using the classpath given.
+ *
+ * @param project The project to which this classloader is to belong.
+ * Must not be <code>null</code>.
+ * @param classpath The classpath to use to load the classes. This
+ * is combined with the system classpath in a manner
+ * determined by the value of ${build.sysclasspath}.
+ * May be <code>null</code>, in which case no path
+ * elements are set up to start with.
+ */
+ public AntClassLoader(final Project project, final Path classpath) {
+ setParent(null);
+ setProject(project);
+ setClassPath(classpath);
+ }
+
+ /**
+ * Creates a classloader for the given project using the classpath given.
+ *
+ * @param parent The parent classloader to which unsatisfied loading
+ * attempts are delegated. May be <code>null</code>,
+ * in which case the classloader which loaded this
+ * class is used as the parent.
+ * @param project The project to which this classloader is to belong.
+ * Must not be <code>null</code>.
+ * @param classpath the classpath to use to load the classes.
+ * May be <code>null</code>, in which case no path
+ * elements are set up to start with.
+ * @param parentFirst If <code>true</code>, indicates that the parent
+ * classloader should be consulted before trying to
+ * load the a class through this loader.
+ */
+ public AntClassLoader(
+ final ClassLoader parent, final Project project, final Path classpath, final boolean parentFirst) {
+ this(project, classpath);
+ if (parent != null) {
+ setParent(parent);
+ }
+ setParentFirst(parentFirst);
+ addJavaLibraries();
+ }
+
+ /**
+ * Creates a classloader for the given project using the classpath given.
+ *
+ * @param project The project to which this classloader is to belong.
+ * Must not be <code>null</code>.
+ * @param classpath The classpath to use to load the classes. May be
+ * <code>null</code>, in which case no path
+ * elements are set up to start with.
+ * @param parentFirst If <code>true</code>, indicates that the parent
+ * classloader should be consulted before trying to
+ * load the a class through this loader.
+ */
+ public AntClassLoader(final Project project, final Path classpath, final boolean parentFirst) {
+ this(null, project, classpath, parentFirst);
+ }
+
+ /**
+ * Creates an empty class loader. The classloader should be configured
+ * with path elements to specify where the loader is to look for
+ * classes.
+ *
+ * @param parent The parent classloader to which unsatisfied loading
+ * attempts are delegated. May be <code>null</code>,
+ * in which case the classloader which loaded this
+ * class is used as the parent.
+ * @param parentFirst If <code>true</code>, indicates that the parent
+ * classloader should be consulted before trying to
+ * load the a class through this loader.
+ */
+ public AntClassLoader(final ClassLoader parent, final boolean parentFirst) {
+ setParent(parent);
+ project = null;
+ this.parentFirst = parentFirst;
+ }
+
+ /**
+ * Set the project associated with this class loader
+ *
+ * @param project the project instance
+ */
+ public void setProject(final Project project) {
+ this.project = project;
+ if (project != null) {
+ project.addBuildListener(this);
+ }
+ }
+
+ /**
+ * Set the classpath to search for classes to load. This should not be
+ * changed once the classloader starts to server classes
+ *
+ * @param classpath the search classpath consisting of directories and
+ * jar/zip files.
+ */
+ public void setClassPath(final Path classpath) {
+ pathComponents.removeAllElements();
+ if (classpath != null) {
+ final Path actualClasspath = classpath.concatSystemClasspath("ignore");
+ final String[] pathElements = actualClasspath.list();
+ for (int i = 0; i < pathElements.length; ++i) {
+ try {
+ addPathElement(pathElements[i]);
+ } catch (final BuildException e) {
+ // ignore path elements which are invalid
+ // relative to the project
+ }
+ }
+ }
+ }
+
+ /**
+ * Set the parent for this class loader. This is the class loader to which
+ * this class loader will delegate to load classes
+ *
+ * @param parent the parent class loader.
+ */
+ public void setParent(final ClassLoader parent) {
+ this.parent = parent == null ? AntClassLoader.class.getClassLoader() : parent;
+ }
+
+ /**
+ * Control whether class lookup is delegated to the parent loader first
+ * or after this loader. Use with extreme caution. Setting this to
+ * false violates the class loader hierarchy and can lead to Linkage errors
+ *
+ * @param parentFirst if true, delegate initial class search to the parent
+ * classloader.
+ */
+ public void setParentFirst(final boolean parentFirst) {
+ this.parentFirst = parentFirst;
+ }
+
+ /**
+ * Logs a message through the project object if one has been provided.
+ *
+ * @param message The message to log.
+ * Should not be <code>null</code>.
+ *
+ * @param priority The logging priority of the message.
+ */
+ protected void log(final String message, final int priority) {
+ if (project != null) {
+ project.log(message, priority);
+ }
+ }
+
+ /**
+ * Sets the current thread's context loader to this classloader, storing
+ * the current loader value for later resetting.
+ */
+ public void setThreadContextLoader() {
+ if (isContextLoaderSaved) {
+ throw new BuildException("Context loader has not been reset");
+ }
+ if (LoaderUtils.isContextLoaderAvailable()) {
+ savedContextLoader = LoaderUtils.getContextClassLoader();
+ ClassLoader loader = this;
+ if (project != null && "only".equals(project.getProperty("build.sysclasspath"))) {
+ loader = this.getClass().getClassLoader();
+ }
+ LoaderUtils.setContextClassLoader(loader);
+ isContextLoaderSaved = true;
+ }
+ }
+
+ /**
+ * Resets the current thread's context loader to its original value.
+ */
+ public void resetThreadContextLoader() {
+ if (LoaderUtils.isContextLoaderAvailable() && isContextLoaderSaved) {
+ LoaderUtils.setContextClassLoader(savedContextLoader);
+ savedContextLoader = null;
+ isContextLoaderSaved = false;
+ }
+ }
+
+
+ /**
+ * Adds an element to the classpath to be searched.
+ *
+ * @param pathElement The path element to add. Must not be
+ * <code>null</code>.
+ *
+ * @exception BuildException if the given path element cannot be resolved
+ * against the project.
+ */
+ public void addPathElement(final String pathElement) throws BuildException {
+ final File pathComponent = project != null ? project.resolveFile(pathElement) : new File(
+ pathElement);
+ try {
+ addPathFile(pathComponent);
+ } catch (final IOException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Add a path component.
+ * This simply adds the file, unlike addPathElement
+ * it does not open jar files and load files from
+ * their CLASSPATH entry in the manifest file.
+ * @param file the jar file or directory to add.
+ */
+ public void addPathComponent(final File file) {
+ if (pathComponents.contains(file)) {
+ return;
+ }
+ pathComponents.addElement(file);
+ }
+
+ /**
+ * Add a file to the path.
+ * Reads the manifest, if available, and adds any additional class path jars
+ * specified in the manifest.
+ *
+ * @param pathComponent the file which is to be added to the path for
+ * this class loader
+ *
+ * @throws IOException if data needed from the file cannot be read.
+ */
+ protected void addPathFile(final File pathComponent) throws IOException {
+ if (!pathComponents.contains(pathComponent)) {
+ pathComponents.addElement(pathComponent);
+ }
+ if (pathComponent.isDirectory()) {
+ return;
+ }
+
+ final String absPathPlusTimeAndLength = pathComponent.getAbsolutePath()
+ + pathComponent.lastModified() + "-" + pathComponent.length();
+ String classpath = pathMap.get(absPathPlusTimeAndLength);
+ if (classpath == null) {
+ JarFile jarFile = null;
+ try {
+ jarFile = new JarFile(pathComponent);
+ final Manifest manifest = jarFile.getManifest();
+ if (manifest == null) {
+ return;
+ }
+ classpath = manifest.getMainAttributes()
+ .getValue(Attributes.Name.CLASS_PATH);
+ } finally {
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ }
+ if (classpath == null) {
+ classpath = "";
+ }
+ pathMap.put(absPathPlusTimeAndLength, classpath);
+ }
+
+ if (!"".equals(classpath)) {
+ final URL baseURL = FILE_UTILS.getFileURL(pathComponent);
+ final StringTokenizer st = new StringTokenizer(classpath);
+ while (st.hasMoreTokens()) {
+ final String classpathElement = st.nextToken();
+ final URL libraryURL = new URL(baseURL, classpathElement);
+ if (!libraryURL.getProtocol().equals("file")) {
+ log("Skipping jar library " + classpathElement
+ + " since only relative URLs are supported by this" + " loader",
+ Project.MSG_VERBOSE);
+ continue;
+ }
+ final String decodedPath = Locator.decodeUri(libraryURL.getFile());
+ final File libraryFile = new File(decodedPath);
+ if (libraryFile.exists() && !isInPath(libraryFile)) {
+ addPathFile(libraryFile);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the classpath this classloader will consult.
+ *
+ * @return the classpath used for this classloader, with elements
+ * separated by the path separator for the system.
+ */
+ public String getClasspath() {
+ final StringBuilder sb = new StringBuilder();
+ boolean firstPass = true;
+ final Enumeration<File> componentEnum = pathComponents.elements();
+ while (componentEnum.hasMoreElements()) {
+ if (!firstPass) {
+ sb.append(System.getProperty("path.separator"));
+ } else {
+ firstPass = false;
+ }
+ sb.append(componentEnum.nextElement().getAbsolutePath());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Sets whether this classloader should run in isolated mode. In
+ * isolated mode, classes not found on the given classpath will
+ * not be referred to the parent class loader but will cause a
+ * ClassNotFoundException.
+ *
+ * @param isolated Whether or not this classloader should run in
+ * isolated mode.
+ */
+ public synchronized void setIsolated(final boolean isolated) {
+ ignoreBase = isolated;
+ }
+
+ /**
+ * Forces initialization of a class in a JDK 1.1 compatible, albeit hacky
+ * way.
+ *
+ * @param theClass The class to initialize.
+ * Must not be <code>null</code>.
+ *
+ * @deprecated since 1.6.x.
+ * Use Class.forName with initialize=true instead.
+ */
+ @Deprecated
+ public static void initializeClass(final Class<?> theClass) {
+ // ***HACK*** We ask the VM to create an instance
+ // by voluntarily providing illegal arguments to force
+ // the VM to run the class' static initializer, while
+ // at the same time not running a valid constructor.
+
+ final Constructor<?>[] cons = theClass.getDeclaredConstructors();
+ //At least one constructor is guaranteed to be there, but check anyway.
+ if (cons != null) {
+ if (cons.length > 0 && cons[0] != null) {
+ final String[] strs = new String[NUMBER_OF_STRINGS];
+ try {
+ cons[0].newInstance((Object[]) strs);
+ // Expecting an exception to be thrown by this call:
+ // IllegalArgumentException: wrong number of Arguments
+ } catch (final Exception e) {
+ // Ignore - we are interested only in the side
+ // effect - that of getting the static initializers
+ // invoked. As we do not want to call a valid
+ // constructor to get this side effect, an
+ // attempt is made to call a hopefully
+ // invalid constructor - come on, nobody
+ // would have a constructor that takes in
+ // 256 String arguments ;-)
+ // (In fact, they can't - according to JVM spec
+ // section 4.10, the number of method parameters is limited
+ // to 255 by the definition of a method descriptor.
+ // Constructors count as methods here.)
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds a package root to the list of packages which must be loaded on the
+ * parent loader.
+ *
+ * All subpackages are also included.
+ *
+ * @param packageRoot The root of all packages to be included.
+ * Should not be <code>null</code>.
+ */
+ public void addSystemPackageRoot(final String packageRoot) {
+ systemPackages.addElement(packageRoot + (packageRoot.endsWith(".") ? "" : "."));
+ }
+
+ /**
+ * Adds a package root to the list of packages which must be loaded using
+ * this loader.
+ *
+ * All subpackages are also included.
+ *
+ * @param packageRoot The root of all packages to be included.
+ * Should not be <code>null</code>.
+ */
+ public void addLoaderPackageRoot(final String packageRoot) {
+ loaderPackages.addElement(packageRoot + (packageRoot.endsWith(".") ? "" : "."));
+ }
+
+ /**
+ * Loads a class through this class loader even if that class is available
+ * on the parent classpath.
+ *
+ * This ensures that any classes which are loaded by the returned class
+ * will use this classloader.
+ *
+ * @param classname The name of the class to be loaded.
+ * Must not be <code>null</code>.
+ *
+ * @return the required Class object
+ *
+ * @exception ClassNotFoundException if the requested class does not exist
+ * on this loader's classpath.
+ */
+ public Class<?> forceLoadClass(final String classname) throws ClassNotFoundException {
+ log("force loading " + classname, Project.MSG_DEBUG);
+
+ Class<?> theClass = findLoadedClass(classname);
+
+ if (theClass == null) {
+ theClass = findClass(classname);
+ }
+ return theClass;
+ }
+
+ /**
+ * Loads a class through this class loader but defer to the parent class
+ * loader.
+ *
+ * This ensures that instances of the returned class will be compatible
+ * with instances which have already been loaded on the parent
+ * loader.
+ *
+ * @param classname The name of the class to be loaded.
+ * Must not be <code>null</code>.
+ *
+ * @return the required Class object
+ *
+ * @exception ClassNotFoundException if the requested class does not exist
+ * on this loader's classpath.
+ */
+ public Class<?> forceLoadSystemClass(final String classname) throws ClassNotFoundException {
+ log("force system loading " + classname, Project.MSG_DEBUG);
+
+ Class<?> theClass = findLoadedClass(classname);
+
+ if (theClass == null) {
+ theClass = findBaseClass(classname);
+ }
+ return theClass;
+ }
+
+ /**
+ * Returns a stream to read the requested resource name.
+ *
+ * @param name The name of the resource for which a stream is required.
+ * Must not be <code>null</code>.
+ *
+ * @return a stream to the required resource or <code>null</code> if the
+ * resource cannot be found on the loader's classpath.
+ */
+ @Override
+ public InputStream getResourceAsStream(final String name) {
+ InputStream resourceStream = null;
+ if (isParentFirst(name)) {
+ resourceStream = loadBaseResource(name);
+ }
+ if (resourceStream != null) {
+ log("ResourceStream for " + name
+ + " loaded from parent loader", Project.MSG_DEBUG);
+ } else {
+ resourceStream = loadResource(name);
+ if (resourceStream != null) {
+ log("ResourceStream for " + name
+ + " loaded from ant loader", Project.MSG_DEBUG);
+ }
+ }
+ if (resourceStream == null && !isParentFirst(name)) {
+ if (ignoreBase) {
+ resourceStream = getRootLoader() == null
+ ? null
+ : getRootLoader().getResourceAsStream(name);
+ } else {
+ resourceStream = loadBaseResource(name);
+ }
+ if (resourceStream != null) {
+ log("ResourceStream for " + name + " loaded from parent loader",
+ Project.MSG_DEBUG);
+ }
+ }
+ if (resourceStream == null) {
+ log("Couldn't load ResourceStream for " + name, Project.MSG_DEBUG);
+ }
+ return resourceStream;
+ }
+
+ /**
+ * Returns a stream to read the requested resource name from this loader.
+ *
+ * @param name The name of the resource for which a stream is required.
+ * Must not be <code>null</code>.
+ *
+ * @return a stream to the required resource or <code>null</code> if
+ * the resource cannot be found on the loader's classpath.
+ */
+ private InputStream loadResource(final String name) {
+ // we need to search the components of the path to see if we can
+ // find the class we want.
+ InputStream stream = null;
+
+ final Enumeration<File> e = pathComponents.elements();
+ while (e.hasMoreElements() && stream == null) {
+ final File pathComponent = e.nextElement();
+ stream = getResourceStream(pathComponent, name);
+ }
+ return stream;
+ }
+
+ /**
+ * Finds a system resource (which should be loaded from the parent
+ * classloader).
+ *
+ * @param name The name of the system resource to load.
+ * Must not be <code>null</code>.
+ *
+ * @return a stream to the named resource, or <code>null</code> if
+ * the resource cannot be found.
+ */
+ private InputStream loadBaseResource(final String name) {
+ return parent == null ? super.getResourceAsStream(name) : parent.getResourceAsStream(name);
+ }
+
+ /**
+ * Returns an inputstream to a given resource in the given file which may
+ * either be a directory or a zip file.
+ *
+ * @param file the file (directory or jar) in which to search for the
+ * resource. Must not be <code>null</code>.
+ * @param resourceName The name of the resource for which a stream is
+ * required. Must not be <code>null</code>.
+ *
+ * @return a stream to the required resource or <code>null</code> if
+ * the resource cannot be found in the given file.
+ */
+ private InputStream getResourceStream(final File file, final String resourceName) {
+ try {
+ JarFile jarFile = jarFiles.get(file);
+ if (jarFile == null && file.isDirectory()) {
+ final File resource = new File(file, resourceName);
+ if (resource.exists()) {
+ return new FileInputStream(resource);
+ }
+ } else {
+ if (jarFile == null) {
+ if (file.exists()) {
+ jarFile = new JarFile(file);
+ jarFiles.put(file, jarFile);
+ } else {
+ return null;
+ }
+ //to eliminate a race condition, retrieve the entry
+ //that is in the hash table under that filename
+ jarFile = jarFiles.get(file);
+ }
+ final JarEntry entry = jarFile.getJarEntry(resourceName);
+ if (entry != null) {
+ return jarFile.getInputStream(entry);
+ }
+ }
+ } catch (final Exception e) {
+ log("Ignoring Exception " + e.getClass().getName() + ": " + e.getMessage()
+ + " reading resource " + resourceName + " from " + file, Project.MSG_VERBOSE);
+ }
+ return null;
+ }
+
+ /**
+ * Tests whether or not the parent classloader should be checked for a
+ * resource before this one. If the resource matches both the "use parent
+ * classloader first" and the "use this classloader first" lists, the latter
+ * takes priority.
+ *
+ * @param resourceName
+ * The name of the resource to check. Must not be
+ * <code>null</code>.
+ *
+ * @return whether or not the parent classloader should be checked for a
+ * resource before this one is.
+ */
+ private boolean isParentFirst(final String resourceName) {
+ // default to the global setting and then see
+ // if this class belongs to a package which has been
+ // designated to use a specific loader first
+ // (this one or the parent one)
+
+ // TODO - shouldn't this always return false in isolated mode?
+
+ boolean useParentFirst = parentFirst;
+
+ for (final Enumeration<String> e = systemPackages.elements(); e.hasMoreElements();) {
+ final String packageName = e.nextElement();
+ if (resourceName.startsWith(packageName)) {
+ useParentFirst = true;
+ break;
+ }
+ }
+ for (final Enumeration<String> e = loaderPackages.elements(); e.hasMoreElements();) {
+ final String packageName = e.nextElement();
+ if (resourceName.startsWith(packageName)) {
+ useParentFirst = false;
+ break;
+ }
+ }
+ return useParentFirst;
+ }
+
+ /**
+ * Used for isolated resource seaching.
+ * @return the root classloader of AntClassLoader.
+ */
+ private ClassLoader getRootLoader() {
+ ClassLoader ret = getClass().getClassLoader();
+ while (ret != null && ret.getParent() != null) {
+ ret = ret.getParent();
+ }
+ return ret;
+ }
+
+ /**
+ * Finds the resource with the given name. A resource is
+ * some data (images, audio, text, etc) that can be accessed by class
+ * code in a way that is independent of the location of the code.
+ *
+ * @param name The name of the resource for which a stream is required.
+ * Must not be <code>null</code>.
+ *
+ * @return a URL for reading the resource, or <code>null</code> if the
+ * resource could not be found or the caller doesn't have
+ * adequate privileges to get the resource.
+ */
+ @Override
+ public URL getResource(final String name) {
+ // we need to search the components of the path to see if
+ // we can find the class we want.
+ URL url = null;
+ if (isParentFirst(name)) {
+ url = parent == null ? super.getResource(name) : parent.getResource(name);
+ }
+ if (url != null) {
+ log("Resource " + name + " loaded from parent loader", Project.MSG_DEBUG);
+ } else {
+ // try and load from this loader if the parent either didn't find
+ // it or wasn't consulted.
+ final Enumeration<File> e = pathComponents.elements();
+ while (e.hasMoreElements() && url == null) {
+ final File pathComponent = e.nextElement();
+ url = getResourceURL(pathComponent, name);
+ if (url != null) {
+ log("Resource " + name + " loaded from ant loader", Project.MSG_DEBUG);
+ }
+ }
+ }
+ if (url == null && !isParentFirst(name)) {
+ // this loader was first but it didn't find it - try the parent
+ if (ignoreBase) {
+ url = getRootLoader() == null ? null : getRootLoader().getResource(name);
+ } else {
+ url = parent == null ? super.getResource(name) : parent.getResource(name);
+ }
+ if (url != null) {
+ log("Resource " + name + " loaded from parent loader", Project.MSG_DEBUG);
+ }
+ }
+ if (url == null) {
+ log("Couldn't load Resource " + name, Project.MSG_DEBUG);
+ }
+ return url;
+ }
+
+ /**
+ * Finds all the resources with the given name. A resource is some
+ * data (images, audio, text, etc) that can be accessed by class
+ * code in a way that is independent of the location of the code.
+ *
+ * <p>Would override getResources if that wasn't final in Java
+ * 1.4.</p>
+ *
+ * @param name name of the resource
+ * @return possible URLs as enumeration
+ * @throws IOException
+ * @see {@link #findResources(String, boolean)}
+ * @since Ant 1.8.0
+ */
+ public Enumeration<URL> getNamedResources(final String name)
+ throws IOException {
+ return findResources(name, false);
+ }
+
+ /**
+ * Returns an enumeration of URLs representing all the resources with the
+ * given name by searching the class loader's classpath.
+ *
+ * @param name The resource name to search for.
+ * Must not be <code>null</code>.
+ * @return an enumeration of URLs for the resources
+ * @exception IOException if I/O errors occurs (can't happen)
+ */
+ @Override
+ protected Enumeration<URL> findResources(final String name) throws IOException {
+ return findResources(name, true);
+ }
+
+ /**
+ * Returns an enumeration of URLs representing all the resources with the
+ * given name by searching the class loader's classpath.
+ *
+ * @param name The resource name to search for.
+ * Must not be <code>null</code>.
+ * @param parentHasBeenSearched whether ClassLoader.this.parent
+ * has been searched - will be true if the method is (indirectly)
+ * called from ClassLoader.getResources
+ * @return an enumeration of URLs for the resources
+ * @exception IOException if I/O errors occurs (can't happen)
+ */
+ protected Enumeration<URL> findResources(final String name,
+ final boolean parentHasBeenSearched)
+ throws IOException {
+ final Enumeration<URL> mine = new ResourceEnumeration(name);
+ Enumeration<URL> base;
+ if (parent != null && (!parentHasBeenSearched || parent != getParent())) {
+ // Delegate to the parent:
+ base = parent.getResources(name);
+ // Note: could cause overlaps in case
+ // ClassLoader.this.parent has matches and
+ // parentHasBeenSearched is true
+ } else {
+ // ClassLoader.this.parent is already delegated to for example from
+ // ClassLoader.getResources, no need:
+ base = new CollectionUtils.EmptyEnumeration<URL>();
+ }
+ if (isParentFirst(name)) {
+ // Normal case.
+ return CollectionUtils.append(base, mine);
+ }
+ if (ignoreBase) {
+ return getRootLoader() == null ? mine : CollectionUtils.append(mine, getRootLoader()
+ .getResources(name));
+ }
+ // parent last:
+ return CollectionUtils.append(mine, base);
+ }
+
+ /**
+ * Returns the URL of a given resource in the given file which may
+ * either be a directory or a zip file.
+ *
+ * @param file The file (directory or jar) in which to search for
+ * the resource. Must not be <code>null</code>.
+ * @param resourceName The name of the resource for which a stream
+ * is required. Must not be <code>null</code>.
+ *
+ * @return a stream to the required resource or <code>null</code> if the
+ * resource cannot be found in the given file object.
+ */
+ protected URL getResourceURL(final File file, final String resourceName) {
+ try {
+ JarFile jarFile = jarFiles.get(file);
+ if (jarFile == null && file.isDirectory()) {
+ final File resource = new File(file, resourceName);
+
+ if (resource.exists()) {
+ try {
+ return FILE_UTILS.getFileURL(resource);
+ } catch (final MalformedURLException ex) {
+ return null;
+ }
+ }
+ } else {
+ if (jarFile == null) {
+ if (file.exists()) {
+ if (!isZip(file)) {
+ final String msg = "CLASSPATH element " + file
+ + " is not a JAR.";
+ log(msg, Project.MSG_WARN);
+ System.err.println(msg);
+ return null;
+ }
+ jarFile = new JarFile(file);
+ jarFiles.put(file, jarFile);
+ } else {
+ return null;
+ }
+ // potential race-condition
+ jarFile = jarFiles.get(file);
+ }
+ final JarEntry entry = jarFile.getJarEntry(resourceName);
+ if (entry != null) {
+ try {
+ return new URL("jar:" + FILE_UTILS.getFileURL(file) + "!/" + entry);
+ } catch (final MalformedURLException ex) {
+ return null;
+ }
+ }
+ }
+ } catch (final Exception e) {
+ final String msg = "Unable to obtain resource from " + file + ": ";
+ log(msg + e, Project.MSG_WARN);
+ System.err.println(msg);
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Loads a class with this class loader.
+ *
+ * This class attempts to load the class in an order determined by whether
+ * or not the class matches the system/loader package lists, with the
+ * loader package list taking priority. If the classloader is in isolated
+ * mode, failure to load the class in this loader will result in a
+ * ClassNotFoundException.
+ *
+ * @param classname The name of the class to be loaded.
+ * Must not be <code>null</code>.
+ * @param resolve <code>true</code> if all classes upon which this class
+ * depends are to be loaded.
+ *
+ * @return the required Class object
+ *
+ * @exception ClassNotFoundException if the requested class does not exist
+ * on the system classpath (when not in isolated mode) or this loader's
+ * classpath.
+ */
+ @Override
+ protected synchronized Class<?> loadClass(final String classname, final boolean resolve)
+ throws ClassNotFoundException {
+ // 'sync' is needed - otherwise 2 threads can load the same class
+ // twice, resulting in LinkageError: duplicated class definition.
+ // findLoadedClass avoids that, but without sync it won't work.
+
+ Class<?> theClass = findLoadedClass(classname);
+ if (theClass != null) {
+ return theClass;
+ }
+ if (isParentFirst(classname)) {
+ try {
+ theClass = findBaseClass(classname);
+ log("Class " + classname + " loaded from parent loader " + "(parentFirst)",
+ Project.MSG_DEBUG);
+ } catch (final ClassNotFoundException cnfe) {
+ theClass = findClass(classname);
+ log("Class " + classname + " loaded from ant loader " + "(parentFirst)",
+ Project.MSG_DEBUG);
+ }
+ } else {
+ try {
+ theClass = findClass(classname);
+ log("Class " + classname + " loaded from ant loader", Project.MSG_DEBUG);
+ } catch (final ClassNotFoundException cnfe) {
+ if (ignoreBase) {
+ throw cnfe;
+ }
+ theClass = findBaseClass(classname);
+ log("Class " + classname + " loaded from parent loader", Project.MSG_DEBUG);
+ }
+ }
+ if (resolve) {
+ resolveClass(theClass);
+ }
+ return theClass;
+ }
+
+ /**
+ * Converts the class dot notation to a filesystem equivalent for
+ * searching purposes.
+ *
+ * @param classname The class name in dot format (eg java.lang.Integer).
+ * Must not be <code>null</code>.
+ *
+ * @return the classname in filesystem format (eg java/lang/Integer.class)
+ */
+ private String getClassFilename(final String classname) {
+ return classname.replace('.', '/') + ".class";
+ }
+
+ /**
+ * Define a class given its bytes
+ *
+ * @param container the container from which the class data has been read
+ * may be a directory or a jar/zip file.
+ *
+ * @param classData the bytecode data for the class
+ * @param classname the name of the class
+ *
+ * @return the Class instance created from the given data
+ *
+ * @throws IOException if the class data cannot be read.
+ */
+ protected Class<?> defineClassFromData(final File container, final byte[] classData, final String classname)
+ throws IOException {
+ definePackage(container, classname);
+ final ProtectionDomain currentPd = Project.class.getProtectionDomain();
+ final String classResource = getClassFilename(classname);
+ final CodeSource src = new CodeSource(FILE_UTILS.getFileURL(container),
+ getCertificates(container,
+ classResource));
+ final ProtectionDomain classesPd =
+ new ProtectionDomain(src, currentPd.getPermissions(),
+ this,
+ currentPd.getPrincipals());
+ return defineClass(classname, classData, 0, classData.length,
+ classesPd);
+ }
+
+ /**
+ * Define the package information associated with a class.
+ *
+ * @param container the file containing the class definition.
+ * @param className the class name of for which the package information
+ * is to be determined.
+ *
+ * @exception IOException if the package information cannot be read from the
+ * container.
+ */
+ protected void definePackage(final File container, final String className) throws IOException {
+ final int classIndex = className.lastIndexOf('.');
+ if (classIndex == -1) {
+ return;
+ }
+ final String packageName = className.substring(0, classIndex);
+ if (getPackage(packageName) != null) {
+ // already defined
+ return;
+ }
+ // define the package now
+ final Manifest manifest = getJarManifest(container);
+
+ if (manifest == null) {
+ definePackage(packageName, null, null, null, null, null, null, null);
+ } else {
+ definePackage(container, packageName, manifest);
+ }
+ }
+
+ /**
+ * Get the manifest from the given jar, if it is indeed a jar and it has a
+ * manifest
+ *
+ * @param container the File from which a manifest is required.
+ *
+ * @return the jar's manifest or null is the container is not a jar or it
+ * has no manifest.
+ *
+ * @exception IOException if the manifest cannot be read.
+ */
+ private Manifest getJarManifest(final File container) throws IOException {
+ if (container.isDirectory()) {
+ return null;
+ }
+ final JarFile jarFile = jarFiles.get(container);
+ if (jarFile == null) {
+ return null;
+ }
+ return jarFile.getManifest();
+ }
+
+ /**
+ * Get the certificates for a given jar entry, if it is indeed a jar.
+ *
+ * @param container the File from which to read the entry
+ * @param entry the entry of which the certificates are requested
+ *
+ * @return the entry's certificates or null is the container is
+ * not a jar or it has no certificates.
+ *
+ * @exception IOException if the manifest cannot be read.
+ */
+ private Certificate[] getCertificates(final File container, final String entry)
+ throws IOException {
+ if (container.isDirectory()) {
+ return null;
+ }
+ final JarFile jarFile = jarFiles.get(container);
+ if (jarFile == null) {
+ return null;
+ }
+ final JarEntry ent = jarFile.getJarEntry(entry);
+ return ent == null ? null : ent.getCertificates();
+ }
+
+ /**
+ * Define the package information when the class comes from a
+ * jar with a manifest
+ *
+ * @param container the jar file containing the manifest
+ * @param packageName the name of the package being defined.
+ * @param manifest the jar's manifest
+ */
+ protected void definePackage(final File container, final String packageName, final Manifest manifest) {
+ final String sectionName = packageName.replace('.', '/') + "/";
+
+ String specificationTitle = null;
+ String specificationVendor = null;
+ String specificationVersion = null;
+ String implementationTitle = null;
+ String implementationVendor = null;
+ String implementationVersion = null;
+ String sealedString = null;
+ URL sealBase = null;
+
+ final Attributes sectionAttributes = manifest.getAttributes(sectionName);
+ if (sectionAttributes != null) {
+ specificationTitle = sectionAttributes.getValue(Name.SPECIFICATION_TITLE);
+ specificationVendor = sectionAttributes.getValue(Name.SPECIFICATION_VENDOR);
+ specificationVersion = sectionAttributes.getValue(Name.SPECIFICATION_VERSION);
+ implementationTitle = sectionAttributes.getValue(Name.IMPLEMENTATION_TITLE);
+ implementationVendor = sectionAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
+ implementationVersion = sectionAttributes.getValue(Name.IMPLEMENTATION_VERSION);
+ sealedString = sectionAttributes.getValue(Name.SEALED);
+ }
+ final Attributes mainAttributes = manifest.getMainAttributes();
+ if (mainAttributes != null) {
+ if (specificationTitle == null) {
+ specificationTitle = mainAttributes.getValue(Name.SPECIFICATION_TITLE);
+ }
+ if (specificationVendor == null) {
+ specificationVendor = mainAttributes.getValue(Name.SPECIFICATION_VENDOR);
+ }
+ if (specificationVersion == null) {
+ specificationVersion = mainAttributes.getValue(Name.SPECIFICATION_VERSION);
+ }
+ if (implementationTitle == null) {
+ implementationTitle = mainAttributes.getValue(Name.IMPLEMENTATION_TITLE);
+ }
+ if (implementationVendor == null) {
+ implementationVendor = mainAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
+ }
+ if (implementationVersion == null) {
+ implementationVersion = mainAttributes.getValue(Name.IMPLEMENTATION_VERSION);
+ }
+ if (sealedString == null) {
+ sealedString = mainAttributes.getValue(Name.SEALED);
+ }
+ }
+ if (sealedString != null && sealedString.equalsIgnoreCase("true")) {
+ try {
+ sealBase = new URL(FileUtils.getFileUtils().toURI(container.getAbsolutePath()));
+ } catch (final MalformedURLException e) {
+ // ignore
+ }
+ }
+ definePackage(packageName, specificationTitle, specificationVersion, specificationVendor,
+ implementationTitle, implementationVersion, implementationVendor, sealBase);
+ }
+
+ /**
+ * Reads a class definition from a stream.
+ *
+ * @param stream The stream from which the class is to be read.
+ * Must not be <code>null</code>.
+ * @param classname The name of the class in the stream.
+ * Must not be <code>null</code>.
+ * @param container the file or directory containing the class.
+ *
+ * @return the Class object read from the stream.
+ *
+ * @exception IOException if there is a problem reading the class from the
+ * stream.
+ * @exception SecurityException if there is a security problem while
+ * reading the class from the stream.
+ */
+ private Class<?> getClassFromStream(final InputStream stream, final String classname, final File container)
+ throws IOException, SecurityException {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int bytesRead = -1;
+ final byte[] buffer = new byte[BUFFER_SIZE];
+
+ while ((bytesRead = stream.read(buffer, 0, BUFFER_SIZE)) != -1) {
+ baos.write(buffer, 0, bytesRead);
+ }
+ final byte[] classData = baos.toByteArray();
+ return defineClassFromData(container, classData, classname);
+ }
+
+ /**
+ * Searches for and load a class on the classpath of this class loader.
+ *
+ * @param name The name of the class to be loaded. Must not be
+ * <code>null</code>.
+ *
+ * @return the required Class object
+ *
+ * @exception ClassNotFoundException if the requested class does not exist
+ * on this loader's classpath.
+ */
+ @Override
+ public Class<?> findClass(final String name) throws ClassNotFoundException {
+ log("Finding class " + name, Project.MSG_DEBUG);
+ return findClassInComponents(name);
+ }
+
+ /**
+ * Indicate if the given file is in this loader's path
+ *
+ * @param component the file which is to be checked
+ *
+ * @return true if the file is in the class path
+ */
+ protected boolean isInPath(final File component) {
+ return pathComponents.contains(component);
+ }
+
+ /**
+ * Finds a class on the given classpath.
+ *
+ * @param name The name of the class to be loaded. Must not be
+ * <code>null</code>.
+ *
+ * @return the required Class object
+ *
+ * @exception ClassNotFoundException if the requested class does not exist
+ * on this loader's classpath.
+ */
+ private Class<?> findClassInComponents(final String name)
+ throws ClassNotFoundException {
+ // we need to search the components of the path to see if
+ // we can find the class we want.
+ final String classFilename = getClassFilename(name);
+ final Enumeration<File> e = pathComponents.elements();
+ while (e.hasMoreElements()) {
+ final File pathComponent = e.nextElement();
+ InputStream stream = null;
+ try {
+ stream = getResourceStream(pathComponent, classFilename);
+ if (stream != null) {
+ log("Loaded from " + pathComponent + " "
+ + classFilename, Project.MSG_DEBUG);
+ return getClassFromStream(stream, name, pathComponent);
+ }
+ } catch (final SecurityException se) {
+ throw se;
+ } catch (final IOException ioe) {
+ // ioe.printStackTrace();
+ log("Exception reading component " + pathComponent + " (reason: "
+ + ioe.getMessage() + ")", Project.MSG_VERBOSE);
+ } finally {
+ FileUtils.close(stream);
+ }
+ }
+ throw new ClassNotFoundException(name);
+ }
+
+ /**
+ * Finds a system class (which should be loaded from the same classloader
+ * as the Ant core).
+ *
+ * For JDK 1.1 compatibility, this uses the findSystemClass method if
+ * no parent classloader has been specified.
+ *
+ * @param name The name of the class to be loaded.
+ * Must not be <code>null</code>.
+ *
+ * @return the required Class object
+ *
+ * @exception ClassNotFoundException if the requested class does not exist
+ * on this loader's classpath.
+ */
+ private Class<?> findBaseClass(final String name) throws ClassNotFoundException {
+ return parent == null ? findSystemClass(name) : parent.loadClass(name);
+ }
+
+ /**
+ * Cleans up any resources held by this classloader. Any open archive
+ * files are closed.
+ */
+ public synchronized void cleanup() {
+ for (final Enumeration<JarFile> e = jarFiles.elements(); e.hasMoreElements();) {
+ final JarFile jarFile = e.nextElement();
+ try {
+ jarFile.close();
+ } catch (final IOException ioe) {
+ // ignore
+ }
+ }
+ jarFiles = new Hashtable<File, JarFile>();
+ if (project != null) {
+ project.removeBuildListener(this);
+ }
+ project = null;
+ }
+
+ /**
+ * Gets the parent as has been specified in the constructor or via
+ * setParent.
+ *
+ * @return classloader
+ * @since Ant 1.8.0
+ */
+ public ClassLoader getConfiguredParent() {
+ return parent;
+ }
+
+ /**
+ * Empty implementation to satisfy the BuildListener interface.
+ *
+ * @param event the buildStarted event
+ */
+ public void buildStarted(final BuildEvent event) {
+ // Not significant for the class loader.
+ }
+
+ /**
+ * Cleans up any resources held by this classloader at the end
+ * of a build.
+ *
+ * @param event the buildFinished event
+ */
+ public void buildFinished(final BuildEvent event) {
+ cleanup();
+ }
+
+ /**
+ * Cleans up any resources held by this classloader at the end of
+ * a subbuild if it has been created for the subbuild's project
+ * instance.
+ *
+ * @param event the buildFinished event
+ *
+ * @since Ant 1.6.2
+ */
+ public void subBuildFinished(final BuildEvent event) {
+ if (event.getProject() == project) {
+ cleanup();
+ }
+ }
+
+ /**
+ * Empty implementation to satisfy the BuildListener interface.
+ *
+ * @param event the buildStarted event
+ *
+ * @since Ant 1.6.2
+ */
+ public void subBuildStarted(final BuildEvent event) {
+ // Not significant for the class loader.
+ }
+
+ /**
+ * Empty implementation to satisfy the BuildListener interface.
+ *
+ * @param event the targetStarted event
+ */
+ public void targetStarted(final BuildEvent event) {
+ // Not significant for the class loader.
+ }
+
+ /**
+ * Empty implementation to satisfy the BuildListener interface.
+ *
+ * @param event the targetFinished event
+ */
+ public void targetFinished(final BuildEvent event) {
+ // Not significant for the class loader.
+ }
+
+ /**
+ * Empty implementation to satisfy the BuildListener interface.
+ *
+ * @param event the taskStarted event
+ */
+ public void taskStarted(final BuildEvent event) {
+ // Not significant for the class loader.
+ }
+
+ /**
+ * Empty implementation to satisfy the BuildListener interface.
+ *
+ * @param event the taskFinished event
+ */
+ public void taskFinished(final BuildEvent event) {
+ // Not significant for the class loader.
+ }
+
+ /**
+ * Empty implementation to satisfy the BuildListener interface.
+ *
+ * @param event the messageLogged event
+ */
+ public void messageLogged(final BuildEvent event) {
+ // Not significant for the class loader.
+ }
+
+ /**
+ * add any libraries that come with different java versions
+ * here
+ */
+ public void addJavaLibraries() {
+ final Vector<String> packages = JavaEnvUtils.getJrePackages();
+ final Enumeration<String> e = packages.elements();
+ while (e.hasMoreElements()) {
+ final String packageName = e.nextElement();
+ addSystemPackageRoot(packageName);
+ }
+ }
+
+ /**
+ * Returns a <code>String</code> representing this loader.
+ * @return the path that this classloader has.
+ */
+ @Override
+ public String toString() {
+ return "AntClassLoader[" + getClasspath() + "]";
+ }
+
+ private static Class<?> subClassToLoad = null;
+ private static final Class<?>[] CONSTRUCTOR_ARGS = new Class[] {
+ ClassLoader.class, Project.class, Path.class, Boolean.TYPE
+ };
+
+ static {
+ if (JavaEnvUtils.isAtLeastJavaVersion(JavaEnvUtils.JAVA_1_5)) {
+ try {
+ subClassToLoad =
+ Class.forName("org.apache.tools.ant.loader.AntClassLoader5");
+ } catch (final ClassNotFoundException e) {
+ // this is Java5 but the installation is lacking our subclass
+ }
+ }
+ }
+
+ /**
+ * Factory method
+ */
+ public static AntClassLoader newAntClassLoader(final ClassLoader parent,
+ final Project project,
+ final Path path,
+ final boolean parentFirst) {
+ if (subClassToLoad != null) {
+ return (AntClassLoader)
+ ReflectUtil.newInstance(subClassToLoad,
+ CONSTRUCTOR_ARGS,
+ new Object[] {
+ parent, project, path,
+ Boolean.valueOf(parentFirst)
+ });
+ }
+ return new AntClassLoader(parent, project, path, parentFirst);
+ }
+
+ private static final ZipLong EOCD_SIG = new ZipLong(0X06054B50L);
+ private static final ZipLong SINGLE_SEGMENT_SPLIT_MARKER =
+ new ZipLong(0X30304B50L);
+
+ private static boolean isZip(final File file) throws IOException {
+ final byte[] sig = new byte[4];
+ if (readFully(file, sig)) {
+ final ZipLong start = new ZipLong(sig);
+ return ZipLong.LFH_SIG.equals(start) // normal file
+ || EOCD_SIG.equals(start) // empty zip
+ || ZipLong.DD_SIG.equals(start) // split zip
+ || SINGLE_SEGMENT_SPLIT_MARKER.equals(start);
+ }
+ return false;
+ }
+
+ private static boolean readFully(final File f, final byte[] b) throws IOException {
+ final FileInputStream fis = new FileInputStream(f);
+ try {
+ final int len = b.length;
+ int count = 0, x = 0;
+ while (count != len) {
+ x = fis.read(b, count, len - count);
+ if (x == -1) {
+ break;
+ }
+ count += x;
+ }
+ return count == len;
+ } finally {
+ fis.close();
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/AntTypeDefinition.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/AntTypeDefinition.java
new file mode 100644
index 00000000..104820f1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/AntTypeDefinition.java
@@ -0,0 +1,389 @@
+/*
+ * 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.tools.ant;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+
+/**
+ * This class contains all the information
+ * on a particular ant type,
+ * the classname, adapter and the class
+ * it should be assignable from.
+ * This type replaces the task/datatype split
+ * of pre ant 1.6.
+ *
+ */
+public class AntTypeDefinition {
+ private String name;
+ private Class<?> clazz;
+ private Class<?> adapterClass;
+ private Class<?> adaptToClass;
+ private String className;
+ private ClassLoader classLoader;
+ private boolean restrict = false;
+
+ /**
+ * Set the restrict attribute.
+ * @param restrict the value to set.
+ */
+ public void setRestrict(boolean restrict) {
+ this.restrict = restrict;
+ }
+
+ /**
+ * Get the restrict attribute.
+ * @return the restrict attribute.
+ */
+ public boolean isRestrict() {
+ return restrict;
+ }
+
+ /**
+ * Set the definition's name.
+ * @param name the name of the definition.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Return the definition's name.
+ * @return the name of the definition.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the class of the definition.
+ * As a side-effect may set the classloader and classname.
+ * @param clazz the class of this definition.
+ */
+ public void setClass(Class<?> clazz) {
+ this.clazz = clazz;
+ if (clazz == null) {
+ return;
+ }
+ this.classLoader = (classLoader == null)
+ ? clazz.getClassLoader() : classLoader;
+ this.className = (className == null) ? clazz.getName() : className;
+ }
+
+ /**
+ * Set the classname of the definition.
+ * @param className the classname of this definition.
+ */
+ public void setClassName(String className) {
+ this.className = className;
+ }
+
+ /**
+ * Get the classname of the definition.
+ * @return the name of the class of this definition.
+ */
+ public String getClassName() {
+ return className;
+ }
+
+ /**
+ * Set the adapter class for this definition.
+ * This class is used to adapt the definitions class if
+ * required.
+ * @param adapterClass the adapterClass.
+ */
+ public void setAdapterClass(Class<?> adapterClass) {
+ this.adapterClass = adapterClass;
+ }
+
+ /**
+ * Set the assignable class for this definition.
+ * @param adaptToClass the assignable class.
+ */
+
+ public void setAdaptToClass(Class<?> adaptToClass) {
+ this.adaptToClass = adaptToClass;
+ }
+
+ /**
+ * Set the classloader to use to create an instance
+ * of the definition.
+ * @param classLoader the ClassLoader.
+ */
+ public void setClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * Get the classloader for this definition.
+ * @return the classloader for this definition.
+ */
+ public ClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ /**
+ * Get the exposed class for this
+ * definition. This will be a proxy class
+ * (adapted class) if there is an adapter
+ * class and the definition class is not
+ * assignable from the assignable class.
+ * @param project the current project.
+ * @return the exposed class - may return null if unable to load the class
+ */
+ public Class<?> getExposedClass(Project project) {
+ if (adaptToClass != null) {
+ Class<?> z = getTypeClass(project);
+ if (z == null || adaptToClass.isAssignableFrom(z)) {
+ return z;
+ }
+ }
+ return (adapterClass == null) ? getTypeClass(project) : adapterClass;
+ }
+
+ /**
+ * Get the definition class.
+ * @param project the current project.
+ * @return the type of the definition.
+ */
+ public Class<?> getTypeClass(Project project) {
+ try {
+ return innerGetTypeClass();
+ } catch (NoClassDefFoundError ncdfe) {
+ project.log("Could not load a dependent class ("
+ + ncdfe.getMessage() + ") for type "
+ + name, Project.MSG_DEBUG);
+ } catch (ClassNotFoundException cnfe) {
+ project.log("Could not load class (" + className
+ + ") for type " + name, Project.MSG_DEBUG);
+ }
+ return null;
+ }
+
+ /**
+ * Try and load a class, with no attempt to catch any fault.
+ * @return the class that implements this component
+ * @throws ClassNotFoundException if the class cannot be found.
+ * @throws NoClassDefFoundError if the there is an error
+ * finding the class.
+ */
+ public Class<?> innerGetTypeClass() throws ClassNotFoundException {
+ if (clazz != null) {
+ return clazz;
+ }
+ if (classLoader == null) {
+ clazz = Class.forName(className);
+ } else {
+ clazz = classLoader.loadClass(className);
+ }
+ return clazz;
+ }
+
+ /**
+ * Create an instance of the definition.
+ * The instance may be wrapped in a proxy class.
+ * @param project the current project.
+ * @return the created object.
+ */
+ public Object create(Project project) {
+ return icreate(project);
+ }
+
+ /**
+ * Create a component object based on
+ * its definition.
+ * @return the component as an <code>Object</code>.
+ */
+ private Object icreate(Project project) {
+ Class<?> c = getTypeClass(project);
+ if (c == null) {
+ return null;
+ }
+ Object o = createAndSet(project, c);
+ if (o == null || adapterClass == null) {
+ return o;
+ }
+ if (adaptToClass != null) {
+ if (adaptToClass.isAssignableFrom(o.getClass())) {
+ return o;
+ }
+ }
+ TypeAdapter adapterObject = (TypeAdapter) createAndSet(
+ project, adapterClass);
+ if (adapterObject == null) {
+ return null;
+ }
+ adapterObject.setProxy(o);
+ return adapterObject;
+ }
+
+ /**
+ * Checks if the attributes are correct.
+ * <ul>
+ * <li>if the class can be created.</li>
+ * <li>if an adapter class can be created</li>
+ * <li>if the type is assignable from adapter</li>
+ * <li>if the type can be used with the adapter class</li>
+ * </ul>
+ * @param project the current project.
+ */
+ public void checkClass(Project project) {
+ if (clazz == null) {
+ clazz = getTypeClass(project);
+ if (clazz == null) {
+ throw new BuildException(
+ "Unable to create class for " + getName());
+ }
+ }
+ // check adapter
+ if (adapterClass != null && (adaptToClass == null
+ || !adaptToClass.isAssignableFrom(clazz))) {
+ TypeAdapter adapter = (TypeAdapter) createAndSet(
+ project, adapterClass);
+ if (adapter == null) {
+ throw new BuildException("Unable to create adapter object");
+ }
+ adapter.checkProxyClass(clazz);
+ }
+ }
+
+ /**
+ * Get the constructor of the definition
+ * and invoke it.
+ * @return the instantiated <code>Object</code>.
+ */
+ private Object createAndSet(Project project, Class<?> c) {
+ try {
+ Object o = innerCreateAndSet(c, project);
+ return o;
+ } catch (InvocationTargetException ex) {
+ Throwable t = ex.getTargetException();
+ throw new BuildException(
+ "Could not create type " + name + " due to " + t, t);
+ } catch (NoClassDefFoundError ncdfe) {
+ String msg = "Type " + name + ": A class needed by class "
+ + c + " cannot be found: " + ncdfe.getMessage();
+ throw new BuildException(msg, ncdfe);
+ } catch (NoSuchMethodException nsme) {
+ throw new BuildException("Could not create type " + name
+ + " as the class " + c + " has no compatible constructor");
+ } catch (InstantiationException nsme) {
+ throw new BuildException("Could not create type "
+ + name + " as the class " + c + " is abstract");
+ } catch (IllegalAccessException e) {
+ throw new BuildException("Could not create type "
+ + name + " as the constructor " + c + " is not accessible");
+ } catch (Throwable t) {
+ throw new BuildException(
+ "Could not create type " + name + " due to " + t, t);
+ }
+ }
+
+ /**
+ * Inner implementation of the {@link #createAndSet(Project, Class)} logic, with no
+ * exception catching.
+ * @param <T> return type of the method
+ * @param newclass class to create
+ * @param project the project to use
+ * @return a newly constructed and bound instance.
+ * @throws NoSuchMethodException no good constructor.
+ * @throws InstantiationException cannot initialize the object.
+ * @throws IllegalAccessException cannot access the object.
+ * @throws InvocationTargetException error in invocation.
+ */
+ public <T> T innerCreateAndSet(Class<T> newclass, Project project)
+ throws NoSuchMethodException,
+ InstantiationException,
+ IllegalAccessException,
+ InvocationTargetException {
+ Constructor<T> ctor;
+ boolean noArg = false;
+ // DataType can have a "no arg" constructor or take a single
+ // Project argument.
+ try {
+ ctor = newclass.getConstructor(new Class[0]);
+ noArg = true;
+ } catch (NoSuchMethodException nse) {
+ //can throw the same exception, if there is no this(Project) ctor.
+ ctor = newclass.getConstructor(new Class[] {Project.class});
+ noArg = false;
+ }
+ //now we instantiate
+ T o = ctor.newInstance(
+ ((noArg) ? new Object[0] : new Object[] {project}));
+
+ //set up project references.
+ project.setProjectReference(o);
+ return o;
+ }
+
+ /**
+ * Equality method for this definition (assumes the names are the same).
+ *
+ * @param other another definition.
+ * @param project the project the definition.
+ * @return true if the definitions are the same.
+ */
+ public boolean sameDefinition(AntTypeDefinition other, Project project) {
+ return (other != null && other.getClass() == getClass()
+ && other.getTypeClass(project).equals(getTypeClass(project))
+ && other.getExposedClass(project).equals(getExposedClass(project))
+ && other.restrict == restrict
+ && other.adapterClass == adapterClass
+ && other.adaptToClass == adaptToClass);
+ }
+
+ /**
+ * Similar definition;
+ * used to compare two definitions defined twice with the same
+ * name and the same types.
+ * The classloader may be different but have the same
+ * path so #sameDefinition cannot
+ * be used.
+ * @param other the definition to compare to.
+ * @param project the current project.
+ * @return true if the definitions are the same.
+ */
+ public boolean similarDefinition(AntTypeDefinition other, Project project) {
+ if (other == null
+ || getClass() != other.getClass()
+ || !getClassName().equals(other.getClassName())
+ || !extractClassname(adapterClass).equals(
+ extractClassname(other.adapterClass))
+ || !extractClassname(adaptToClass).equals(
+ extractClassname(other.adaptToClass))
+ || restrict != other.restrict) {
+ return false;
+ }
+ // all the names are the same: check if the class path of the loader
+ // is the same
+ ClassLoader oldLoader = other.getClassLoader();
+ ClassLoader newLoader = getClassLoader();
+ return oldLoader == newLoader
+ || (oldLoader instanceof AntClassLoader
+ && newLoader instanceof AntClassLoader
+ && ((AntClassLoader) oldLoader).getClasspath()
+ .equals(((AntClassLoader) newLoader).getClasspath()));
+ }
+
+ private String extractClassname(Class<?> c) {
+ return (c == null) ? "<null>" : c.getClass().getName();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ArgumentProcessor.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ArgumentProcessor.java
new file mode 100644
index 00000000..07812f2f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ArgumentProcessor.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.tools.ant;
+
+import java.io.PrintStream;
+import java.util.List;
+
+/**
+ * Processor of arguments of the command line.
+ * <p>
+ * Arguments supported by third party code should not conflict with Ant core
+ * ones. It is then recommended to chose specific 'enough' argument name,
+ * avoiding for instance one letter arguments. By the way, if there any
+ * conflict, Ant will take precedence.
+ *
+ * @since 1.9
+ */
+public interface ArgumentProcessor {
+
+ /**
+ * Read the arguments from the command line at the specified position
+ * <p>
+ * If the argument is not supported, returns -1. Else, the position of the
+ * first argument not supported.
+ */
+ int readArguments(String[] args, int pos);
+
+ /**
+ * If some arguments matched with {@link #readArguments(String[], int)},
+ * this method is called after all arguments were parsed. Returns
+ * <code>true</code> if Ant should stop there, ie the build file not parsed
+ * and the project should not be executed.
+ */
+ boolean handleArg(List<String> args);
+
+ /**
+ * If some arguments matched with {@link #readArguments(String[], int)},
+ * this method is called just before the project being configured
+ */
+ void prepareConfigure(Project project, List<String> args);
+
+ /**
+ * Handle the arguments with {@link #readArguments(String[], int)}, just
+ * after the project being configured. Returns <code>true</code> if Ant
+ * should stop there, ie the build file not parsed and the project should
+ * not be executed.
+ */
+ boolean handleArg(Project project, List<String> arg);
+
+ /**
+ * Print the usage of the supported arguments
+ *
+ * @see org.apache.tools.ant.Main#printUsage()
+ */
+ void printUsage(PrintStream writer);
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ArgumentProcessorRegistry.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ArgumentProcessorRegistry.java
new file mode 100644
index 00000000..bdb7c0a8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ArgumentProcessorRegistry.java
@@ -0,0 +1,171 @@
+/*
+ * 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.tools.ant;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.tools.ant.util.LoaderUtils;
+
+/**
+ * The global registry for {@link ArgumentProcessor}s.
+ * <p>
+ * An {@link ArgumentProcessor} implementation can be registered via the system
+ * property <code>org.apache.tools.ant.ArgumentProcessor</code>, or via a JDK1.3
+ * 'service', by putting the fully qualified name of the implementation into the
+ * file <code>META-INF/services/org.apache.tools.ant.ArgumentProcessor</code>
+ * <p>
+ * Use the system property <code>ant.argument-processor.debug</code> to enable
+ * the print of debug log.
+ *
+ * @since 1.9
+ */
+public class ArgumentProcessorRegistry {
+
+ private static final String DEBUG_ARGUMENT_PROCESSOR_REPOSITORY = "ant.argument-processor-repo.debug";
+
+ // The message log level is not accessible here because everything
+ // is instanciated statically
+ private static final boolean DEBUG = "true".equals(System.getProperty(DEBUG_ARGUMENT_PROCESSOR_REPOSITORY));
+
+ private static final String SERVICE_ID = "META-INF/services/org.apache.tools.ant.ArgumentProcessor";
+
+ private static ArgumentProcessorRegistry instance = new ArgumentProcessorRegistry();
+
+ private List<ArgumentProcessor> processors = new ArrayList<ArgumentProcessor>();
+
+ public static ArgumentProcessorRegistry getInstance() {
+ return instance;
+ }
+
+ private ArgumentProcessorRegistry() {
+ collectArgumentProcessors();
+ }
+
+ public List<ArgumentProcessor> getProcessors() {
+ return processors;
+ }
+
+ private void collectArgumentProcessors() {
+ try {
+ ClassLoader classLoader = LoaderUtils.getContextClassLoader();
+ if (classLoader != null) {
+ Enumeration<URL> resources = classLoader.getResources(SERVICE_ID);
+ while (resources.hasMoreElements()) {
+ URL resource = resources.nextElement();
+ URLConnection conn = resource.openConnection();
+ conn.setUseCaches(false);
+ ArgumentProcessor processor = getProcessorByService(conn.getInputStream());
+ registerArgumentProcessor(processor);
+ }
+ }
+
+ InputStream systemResource = ClassLoader.getSystemResourceAsStream(SERVICE_ID);
+ if (systemResource != null) {
+ ArgumentProcessor processor = getProcessorByService(systemResource);
+ registerArgumentProcessor(processor);
+ }
+ } catch (Exception e) {
+ System.err.println("Unable to load ArgumentProcessor from service "
+ + SERVICE_ID + " (" + e.getClass().getName() + ": "
+ + e.getMessage() + ")");
+ if (DEBUG) {
+ e.printStackTrace(System.err);
+ }
+ }
+ }
+
+ public void registerArgumentProcessor(String helperClassName)
+ throws BuildException {
+ registerArgumentProcessor(getProcessor(helperClassName));
+ }
+
+ public void registerArgumentProcessor(
+ Class< ? extends ArgumentProcessor> helperClass)
+ throws BuildException {
+ registerArgumentProcessor(getProcessor(helperClass));
+ }
+
+ private ArgumentProcessor getProcessor(String helperClassName) {
+ try {
+ @SuppressWarnings("unchecked")
+ Class< ? extends ArgumentProcessor> cl = (Class< ? extends ArgumentProcessor>) Class.forName(helperClassName);
+ return getProcessor(cl);
+ } catch (ClassNotFoundException e) {
+ throw new BuildException("Argument processor class "
+ + helperClassName + " was not found", e);
+ }
+ }
+
+ private ArgumentProcessor getProcessor(
+ Class< ? extends ArgumentProcessor> processorClass) {
+ ArgumentProcessor processor;
+ try {
+ processor = processorClass.getConstructor().newInstance();
+ } catch (Exception e) {
+ throw new BuildException("The argument processor class"
+ + processorClass.getClass().getName()
+ + " could not be instanciated with a default constructor",
+ e);
+ }
+ return processor;
+ }
+
+ public void registerArgumentProcessor(ArgumentProcessor processor) {
+ if (processor == null) {
+ return;
+ }
+ processors.add(processor);
+ if (DEBUG) {
+ System.out.println("Argument processor "
+ + processor.getClass().getName() + " registered.");
+ }
+ }
+
+ private ArgumentProcessor getProcessorByService(InputStream is)
+ throws IOException {
+ InputStreamReader isr = null;
+ try {
+ try {
+ isr = new InputStreamReader(is, "UTF-8");
+ } catch (java.io.UnsupportedEncodingException e) {
+ isr = new InputStreamReader(is);
+ }
+ BufferedReader rd = new BufferedReader(isr);
+ String processorClassName = rd.readLine();
+ if (processorClassName != null && !"".equals(processorClassName)) {
+ return getProcessor(processorClassName);
+ }
+ } finally {
+ try {
+ isr.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildEvent.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildEvent.java
new file mode 100644
index 00000000..623ad1e6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildEvent.java
@@ -0,0 +1,203 @@
+/*
+ * 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.tools.ant;
+
+import java.util.EventObject;
+
+/**
+ * Class representing an event occurring during a build. An
+ * event is built by specifying either a project, a task or a target.
+ * A project level event will only have a project reference;
+ * a target level event will have project and target references;
+ * a task level event will have project, target and task references.
+ *
+ */
+public class BuildEvent extends EventObject {
+
+ private static final long serialVersionUID = 4538050075952288486L;
+
+ /** Project which emitted the event. */
+ private final Project project;
+ /** Target which emitted the event, if specified. */
+ private final Target target;
+ /** Task which emitted the event, if specified. */
+ private final Task task;
+ /**
+ * Message associated with the event. This is only used for
+ * "messageLogged" events.
+ */
+ private String message;
+ /**
+ * The priority of the message, for "messageLogged" events.
+ */
+ private int priority = Project.MSG_VERBOSE;
+ /**
+ * The exception associated with this event, if any.
+ * This is only used for "messageLogged", "taskFinished", "targetFinished",
+ * and "buildFinished" events.
+ */
+ private Throwable exception;
+
+ /**
+ * Construct a BuildEvent for a project level event.
+ *
+ * @param project the project that emitted the event.
+ * Should not be <code>null</code>.
+ */
+ public BuildEvent(Project project) {
+ super(project);
+ this.project = project;
+ this.target = null;
+ this.task = null;
+ }
+
+ /**
+ * Construct a BuildEvent for a target level event.
+ * The project associated with the event is derived
+ * from the given target.
+ *
+ * @param target the target that emitted the event.
+ * Must not be <code>null</code>.
+ */
+ public BuildEvent(Target target) {
+ super(target);
+ this.project = target.getProject();
+ this.target = target;
+ this.task = null;
+ }
+
+ /**
+ * Construct a BuildEvent for a task level event.
+ * The project and target associated with the event
+ * are derived from the given task.
+ *
+ * @param task the task that emitted the event.
+ * Must not be <code>null</code>.
+ */
+ public BuildEvent(Task task) {
+ super(task);
+ this.project = task.getProject();
+ this.target = task.getOwningTarget();
+ this.task = task;
+ }
+
+ /**
+ * Sets the message and priority associated with this event.
+ * This is used for "messageLogged" events.
+ *
+ * @param message the message to be associated with this event.
+ * Should not be <code>null</code>.
+ * @param priority the priority to be associated with this event,
+ * as defined in the {@link Project Project} class.
+ *
+ * @see BuildListener#messageLogged(BuildEvent)
+ */
+ public void setMessage(String message, int priority) {
+ this.message = message;
+ this.priority = priority;
+ }
+
+ /**
+ * Sets the exception associated with this event. This is used
+ * for "messageLogged", "taskFinished", "targetFinished", and "buildFinished"
+ * events.
+ *
+ * @param exception The exception to be associated with this event.
+ * May be <code>null</code>.
+ *
+ * @see BuildListener#messageLogged(BuildEvent)
+ * @see BuildListener#taskFinished(BuildEvent)
+ * @see BuildListener#targetFinished(BuildEvent)
+ * @see BuildListener#buildFinished(BuildEvent)
+ */
+ public void setException(Throwable exception) {
+ this.exception = exception;
+ }
+
+ /**
+ * Returns the project that fired this event.
+ *
+ * @return the project that fired this event
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * Returns the target that fired this event.
+ *
+ * @return the project that fired this event, or <code>null</code>
+ * if this event is a project level event.
+ */
+ public Target getTarget() {
+ return target;
+ }
+
+ /**
+ * Returns the task that fired this event.
+ *
+ * @return the task that fired this event, or <code>null</code>
+ * if this event is a project or target level event.
+ */
+ public Task getTask() {
+ return task;
+ }
+
+ /**
+ * Returns the logging message. This field will only be set
+ * for "messageLogged" events.
+ *
+ * @return the message associated with this event, or <code>null</code>
+ * if no message has been set.
+ *
+ * @see BuildListener#messageLogged(BuildEvent)
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Returns the priority of the logging message. This field will only
+ * be set for "messageLogged" events. The meaning of this priority
+ * is as specified by the constants in the {@link Project Project} class.
+ *
+ * @return the priority associated with this event.
+ *
+ * @see BuildListener#messageLogged(BuildEvent)
+ */
+ public int getPriority() {
+ return priority;
+ }
+
+ /**
+ * Returns the exception that was thrown, if any. This field will only
+ * be set for "messageLogged", "taskFinished", "targetFinished", and "buildFinished"
+ * events.
+ *
+ * @return the exception associated with this exception, or
+ * <code>null</code> if no exception has been set.
+ *
+ * @see BuildListener#messageLogged(BuildEvent)
+ * @see BuildListener#taskFinished(BuildEvent)
+ * @see BuildListener#targetFinished(BuildEvent)
+ * @see BuildListener#buildFinished(BuildEvent)
+ */
+ public Throwable getException() {
+ return exception;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildException.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildException.java
new file mode 100644
index 00000000..34c16051
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildException.java
@@ -0,0 +1,153 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Signals an error condition during a build
+ */
+public class BuildException extends RuntimeException {
+
+ private static final long serialVersionUID = -5419014565354664240L;
+
+ /** Location in the build file where the exception occurred */
+ private Location location = Location.UNKNOWN_LOCATION;
+
+ /**
+ * Constructs a build exception with no descriptive information.
+ */
+ public BuildException() {
+ super();
+ }
+
+ /**
+ * Constructs an exception with the given descriptive message.
+ *
+ * @param message A description of or information about the exception.
+ * Should not be <code>null</code>.
+ */
+ public BuildException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs an exception with the given message and exception as
+ * a root cause.
+ *
+ * @param message A description of or information about the exception.
+ * Should not be <code>null</code> unless a cause is specified.
+ * @param cause The exception that might have caused this one.
+ * May be <code>null</code>.
+ */
+ public BuildException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructs an exception with the given message and exception as
+ * a root cause and a location in a file.
+ *
+ * @param msg A description of or information about the exception.
+ * Should not be <code>null</code> unless a cause is specified.
+ * @param cause The exception that might have caused this one.
+ * May be <code>null</code>.
+ * @param location The location in the project file where the error
+ * occurred. Must not be <code>null</code>.
+ */
+ public BuildException(String msg, Throwable cause, Location location) {
+ this(msg, cause);
+ this.location = location;
+ }
+
+ /**
+ * Constructs an exception with the given exception as a root cause.
+ *
+ * @param cause The exception that might have caused this one.
+ * Should not be <code>null</code>.
+ */
+ public BuildException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructs an exception with the given descriptive message and a
+ * location in a file.
+ *
+ * @param message A description of or information about the exception.
+ * Should not be <code>null</code>.
+ * @param location The location in the project file where the error
+ * occurred. Must not be <code>null</code>.
+ */
+ public BuildException(String message, Location location) {
+ super(message);
+ this.location = location;
+ }
+
+ /**
+ * Constructs an exception with the given exception as
+ * a root cause and a location in a file.
+ *
+ * @param cause The exception that might have caused this one.
+ * Should not be <code>null</code>.
+ * @param location The location in the project file where the error
+ * occurred. Must not be <code>null</code>.
+ */
+ public BuildException(Throwable cause, Location location) {
+ this(cause);
+ this.location = location;
+ }
+
+ /**
+ * Returns the nested exception, if any.
+ *
+ * @return the nested exception, or <code>null</code> if no
+ * exception is associated with this one
+ * @deprecated Use {@link #getCause} instead.
+ */
+ public Throwable getException() {
+ return getCause();
+ }
+
+ /**
+ * Returns the location of the error and the error message.
+ *
+ * @return the location of the error and the error message
+ */
+ public String toString() {
+ return location.toString() + getMessage();
+ }
+
+ /**
+ * Sets the file location where the error occurred.
+ *
+ * @param location The file location where the error occurred.
+ * Must not be <code>null</code>.
+ */
+ public void setLocation(Location location) {
+ this.location = location;
+ }
+
+ /**
+ * Returns the file location where the error occurred.
+ *
+ * @return the file location where the error occurred.
+ */
+ public Location getLocation() {
+ return location;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildListener.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildListener.java
new file mode 100644
index 00000000..ed6731cf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildListener.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.tools.ant;
+
+import java.util.EventListener;
+
+/**
+ * Instances of classes that implement this interface can register
+ * to be notified when things happened during a build.
+ *
+ * @see BuildEvent
+ * @see Project#addBuildListener(BuildListener)
+ *
+ */
+public interface BuildListener extends EventListener {
+
+ /**
+ * Signals that a build has started. This event
+ * is fired before any targets have started.
+ *
+ * <p>This event is fired before the project instance is fully
+ * configured. In particular no properties have been set and the
+ * project may not know its name or default target, yet.</p>
+ *
+ * @param event An event with any relevant extra information.
+ * Must not be <code>null</code>.
+ */
+ void buildStarted(BuildEvent event);
+
+ /**
+ * Signals that the last target has finished. This event
+ * will still be fired if an error occurred during the build.
+ *
+ * @param event An event with any relevant extra information.
+ * Must not be <code>null</code>.
+ *
+ * @see BuildEvent#getException()
+ */
+ void buildFinished(BuildEvent event);
+
+ /**
+ * Signals that a target is starting.
+ *
+ * @param event An event with any relevant extra information.
+ * Must not be <code>null</code>.
+ *
+ * @see BuildEvent#getTarget()
+ */
+ void targetStarted(BuildEvent event);
+
+ /**
+ * Signals that a target has finished. This event will
+ * still be fired if an error occurred during the build.
+ *
+ * @param event An event with any relevant extra information.
+ * Must not be <code>null</code>.
+ *
+ * @see BuildEvent#getException()
+ */
+ void targetFinished(BuildEvent event);
+
+ /**
+ * Signals that a task is starting.
+ *
+ * @param event An event with any relevant extra information.
+ * Must not be <code>null</code>.
+ *
+ * @see BuildEvent#getTask()
+ */
+ void taskStarted(BuildEvent event);
+
+ /**
+ * Signals that a task has finished. This event will still
+ * be fired if an error occurred during the build.
+ *
+ * @param event An event with any relevant extra information.
+ * Must not be <code>null</code>.
+ *
+ * @see BuildEvent#getException()
+ */
+ void taskFinished(BuildEvent event);
+
+ /**
+ * Signals a message logging event.
+ *
+ * @param event An event with any relevant extra information.
+ * Must not be <code>null</code>.
+ *
+ * @see BuildEvent#getMessage()
+ * @see BuildEvent#getException()
+ * @see BuildEvent#getPriority()
+ */
+ void messageLogged(BuildEvent event);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildLogger.java
new file mode 100644
index 00000000..249d8f51
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/BuildLogger.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.tools.ant;
+
+import java.io.PrintStream;
+
+/**
+ * Interface used by Ant to log the build output.
+ *
+ * A build logger is a build listener which has the 'right' to send output to
+ * the ant log, which is usually <code>System.out</code> unless redirected by
+ * the <code>-logfile</code> option.
+ *
+ */
+public interface BuildLogger extends BuildListener {
+
+ /**
+ * Sets the highest level of message this logger should respond to.
+ *
+ * Only messages with a message level lower than or equal to the
+ * given level should be written to the log.
+ * <P>
+ * Constants for the message levels are in the
+ * {@link Project Project} class. The order of the levels, from least
+ * to most verbose, is <code>MSG_ERR</code>, <code>MSG_WARN</code>,
+ * <code>MSG_INFO</code>, <code>MSG_VERBOSE</code>,
+ * <code>MSG_DEBUG</code>.
+ *
+ * @param level the logging level for the logger.
+ */
+ void setMessageOutputLevel(int level);
+
+ /**
+ * Sets the output stream to which this logger is to send its output.
+ *
+ * @param output The output stream for the logger.
+ * Must not be <code>null</code>.
+ */
+ void setOutputPrintStream(PrintStream output);
+
+ /**
+ * Sets this logger to produce emacs (and other editor) friendly output.
+ *
+ * @param emacsMode <code>true</code> if output is to be unadorned so that
+ * emacs and other editors can parse files names, etc.
+ */
+ void setEmacsMode(boolean emacsMode);
+
+ /**
+ * Sets the output stream to which this logger is to send error messages.
+ *
+ * @param err The error stream for the logger.
+ * Must not be <code>null</code>.
+ */
+ void setErrorPrintStream(PrintStream err);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ComponentHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ComponentHelper.java
new file mode 100644
index 00000000..eceedeef
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ComponentHelper.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.tools.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Stack;
+
+import org.apache.tools.ant.launch.Launcher;
+import org.apache.tools.ant.taskdefs.Definer;
+import org.apache.tools.ant.taskdefs.Typedef;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Component creation and configuration.
+ *
+ * The class is based around handing component
+ * definitions in an AntTypeTable.
+ *
+ * The old task/type methods have been kept
+ * for backward compatibly.
+ * Project will just delegate its calls to this class.
+ *
+ * A very simple hook mechanism is provided that allows users to plug
+ * in custom code. It is also possible to replace the default behavior
+ * ( for example in an app embedding ant )
+ *
+ * @since Ant1.6
+ */
+public class ComponentHelper {
+ /** Map of component name to lists of restricted definitions */
+ private Map<String, List<AntTypeDefinition>> restrictedDefinitions = new HashMap<String, List<AntTypeDefinition>>();
+
+ /** Map from component name to anttypedefinition */
+ private final Hashtable<String, AntTypeDefinition> antTypeTable = new Hashtable<String, AntTypeDefinition>();
+
+ /** Map of tasks generated from antTypeTable */
+ private final Hashtable<String, Class<?>> taskClassDefinitions = new Hashtable<String, Class<?>>();
+
+ /** flag to rebuild taskClassDefinitions */
+ private boolean rebuildTaskClassDefinitions = true;
+
+ /** Map of types generated from antTypeTable */
+ private final Hashtable<String, Class<?>> typeClassDefinitions = new Hashtable<String, Class<?>>();
+
+ /** flag to rebuild typeClassDefinitions */
+ private boolean rebuildTypeClassDefinitions = true;
+
+ /** Set of namespaces that have been checked for antlibs */
+ private final HashSet<String> checkedNamespaces = new HashSet<String>();
+
+ /**
+ * Stack of antlib contexts used to resolve definitions while
+ * processing antlib
+ */
+ private Stack<String> antLibStack = new Stack<String>();
+
+ /** current antlib uri */
+ private String antLibCurrentUri = null;
+
+ /**
+ * this does not appear to be used anywhere in the Ant codebase
+ * even via its accessors
+ */
+ private ComponentHelper next;
+
+ /**
+ * Project that owns a component helper
+ */
+ private Project project;
+
+ /**
+ * Error string when the file taskdefs/defaults.properties cannot be found
+ */
+ private static final String ERROR_NO_TASK_LIST_LOAD = "Can't load default task list";
+
+ /**
+ * Error string when the typedefs/defaults.properties cannot be found
+ */
+ private static final String ERROR_NO_TYPE_LIST_LOAD = "Can't load default type list";
+
+ /**
+ * reference under which we register ourselves with a project -{@value}
+ */
+ public static final String COMPONENT_HELPER_REFERENCE = "ant.ComponentHelper";
+
+ /**
+ * string used to control build.syspath policy {@value}
+ */
+ private static final String BUILD_SYSCLASSPATH_ONLY = "only";
+
+ /**
+ * special name of ant's property task -{@value}. There is some
+ * contrived work here to enable this early.
+ */
+ private static final String ANT_PROPERTY_TASK = "property";
+
+ // {tasks, types}
+ private static Properties[] defaultDefinitions = new Properties[2];
+
+ /**
+ * Get the project.
+ * @return the project owner of this helper.
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * Find a project component for a specific project, creating
+ * it if it does not exist.
+ * @param project the project.
+ * @return the project component for a specific project.
+ */
+ public static ComponentHelper getComponentHelper(Project project) {
+ if (project == null) {
+ return null;
+ }
+ // Singleton for now, it may change ( per/classloader )
+ ComponentHelper ph = (ComponentHelper) project.getReference(COMPONENT_HELPER_REFERENCE);
+ if (ph != null) {
+ return ph;
+ }
+ ph = new ComponentHelper();
+ ph.setProject(project);
+
+ project.addReference(COMPONENT_HELPER_REFERENCE, ph);
+ return ph;
+ }
+
+ /**
+ * Creates a new ComponentHelper instance.
+ */
+ protected ComponentHelper() {
+ }
+
+ /**
+ * Set the next chained component helper.
+ *
+ * @param next the next chained component helper.
+ */
+ public void setNext(ComponentHelper next) {
+ this.next = next;
+ }
+
+ /**
+ * Get the next chained component helper.
+ *
+ * @return the next chained component helper.
+ */
+ public ComponentHelper getNext() {
+ return next;
+ }
+
+ /**
+ * Sets the project for this component helper.
+ *
+ * @param project the project for this helper.
+ */
+ public void setProject(Project project) {
+ this.project = project;
+// antTypeTable = new Hashtable<String, AntTypeDefinition>(project);
+ }
+
+ /**
+ * @return A copy of the CheckedNamespace.
+ */
+ private synchronized Set<String> getCheckedNamespace() {
+ @SuppressWarnings("unchecked")
+ final Set<String> result = (Set<String>) checkedNamespaces.clone();
+ return result;
+ }
+
+ /**
+ * @return A deep copy of the restrictredDefinition
+ */
+ private Map<String, List<AntTypeDefinition>> getRestrictedDefinition() {
+ final Map<String, List<AntTypeDefinition>> result = new HashMap<String, List<AntTypeDefinition>>();
+ synchronized (restrictedDefinitions) {
+ for (Map.Entry<String, List<AntTypeDefinition>> entry : restrictedDefinitions.entrySet()) {
+ List<AntTypeDefinition> entryVal = entry.getValue();
+ synchronized (entryVal) {
+ //copy the entryVal
+ entryVal = new ArrayList<AntTypeDefinition> (entryVal);
+ }
+ result.put(entry.getKey(), entryVal);
+ }
+ }
+ return result;
+ }
+
+
+ /**
+ * Used with creating child projects. Each child
+ * project inherits the component definitions
+ * from its parent.
+ * @param helper the component helper of the parent project.
+ */
+ public void initSubProject(ComponentHelper helper) {
+ // add the types of the parent project
+ @SuppressWarnings("unchecked")
+ final Hashtable<String, AntTypeDefinition> typeTable = (Hashtable<String, AntTypeDefinition>) helper.antTypeTable.clone();
+ synchronized (antTypeTable) {
+ for (AntTypeDefinition def : typeTable.values()) {
+ antTypeTable.put(def.getName(), def);
+ }
+ }
+ // add the parsed namespaces of the parent project
+ Set<String> inheritedCheckedNamespace = helper.getCheckedNamespace();
+ synchronized (this) {
+ checkedNamespaces.addAll(inheritedCheckedNamespace);
+ }
+ Map<String, List<AntTypeDefinition>> inheritedRestrictedDef = helper.getRestrictedDefinition();
+ synchronized (restrictedDefinitions) {
+ restrictedDefinitions.putAll(inheritedRestrictedDef);
+ }
+ }
+
+ /**
+ * Factory method to create the components.
+ *
+ * This should be called by UnknownElement.
+ *
+ * @param ue The Unknown Element creating this component.
+ * @param ns Namespace URI. Also available as ue.getNamespace().
+ * @param componentType The component type,
+ * Also available as ue.getComponentName().
+ * @return the created component.
+ * @throws BuildException if an error occurs.
+ */
+ public Object createComponent(UnknownElement ue, String ns, String componentType)
+ throws BuildException {
+ Object component = createComponent(componentType);
+ if (component instanceof Task) {
+ Task task = (Task) component;
+ task.setLocation(ue.getLocation());
+ task.setTaskType(componentType);
+ task.setTaskName(ue.getTaskName());
+ task.setOwningTarget(ue.getOwningTarget());
+ task.init();
+ }
+ return component;
+ }
+
+ /**
+ * Create an object for a component.
+ *
+ * @param componentName the name of the component, if
+ * the component is in a namespace, the
+ * name is prefixed with the namespace uri and ":".
+ * @return the class if found or null if not.
+ */
+ public Object createComponent(String componentName) {
+ AntTypeDefinition def = getDefinition(componentName);
+ return def == null ? null : def.create(project);
+ }
+
+ /**
+ * Return the class of the component name.
+ *
+ * @param componentName the name of the component, if
+ * the component is in a namespace, the
+ * name is prefixed with the namespace uri and ":".
+ * @return the class if found or null if not.
+ */
+ public Class<?> getComponentClass(String componentName) {
+ AntTypeDefinition def = getDefinition(componentName);
+ return def == null ? null : def.getExposedClass(project);
+ }
+
+ /**
+ * Return the antTypeDefinition for a componentName.
+ * @param componentName the name of the component.
+ * @return the ant definition or null if not present.
+ */
+ public AntTypeDefinition getDefinition(String componentName) {
+ checkNamespace(componentName);
+ return antTypeTable.get(componentName);
+ }
+
+ /**
+ * This method is initialization code implementing the original ant component
+ * loading from /org/apache/tools/ant/taskdefs/default.properties
+ * and /org/apache/tools/ant/types/default.properties.
+ */
+ public void initDefaultDefinitions() {
+ initTasks();
+ initTypes();
+ new DefaultDefinitions(this).execute();
+ }
+
+ /**
+ * Adds a new task definition to the project.
+ * Attempting to override an existing definition with an
+ * equivalent one (i.e. with the same classname) results in
+ * a verbose log message. Attempting to override an existing definition
+ * with a different one results in a warning log message.
+ *
+ * @param taskName The name of the task to add.
+ * Must not be <code>null</code>.
+ * @param taskClass The full name of the class implementing the task.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the class is unsuitable for being an Ant
+ * task. An error level message is logged before
+ * this exception is thrown.
+ *
+ * @see #checkTaskClass(Class)
+ */
+ public void addTaskDefinition(String taskName, Class<?> taskClass) {
+ checkTaskClass(taskClass);
+ AntTypeDefinition def = new AntTypeDefinition();
+ def.setName(taskName);
+ def.setClassLoader(taskClass.getClassLoader());
+ def.setClass(taskClass);
+ def.setAdapterClass(TaskAdapter.class);
+ def.setClassName(taskClass.getName());
+ def.setAdaptToClass(Task.class);
+ updateDataTypeDefinition(def);
+ }
+
+ /**
+ * Checks whether or not a class is suitable for serving as Ant task.
+ * Ant task implementation classes must be public, concrete, and have
+ * a no-arg constructor.
+ *
+ * @param taskClass The class to be checked.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the class is unsuitable for being an Ant
+ * task. An error level message is logged before
+ * this exception is thrown.
+ */
+ public void checkTaskClass(final Class<?> taskClass) throws BuildException {
+ if (!Modifier.isPublic(taskClass.getModifiers())) {
+ final String message = taskClass + " is not public";
+ project.log(message, Project.MSG_ERR);
+ throw new BuildException(message);
+ }
+ if (Modifier.isAbstract(taskClass.getModifiers())) {
+ final String message = taskClass + " is abstract";
+ project.log(message, Project.MSG_ERR);
+ throw new BuildException(message);
+ }
+ try {
+ taskClass.getConstructor((Class[]) null);
+ // don't have to check for public, since
+ // getConstructor finds public constructors only.
+ } catch (NoSuchMethodException e) {
+ final String message = "No public no-arg constructor in " + taskClass;
+ project.log(message, Project.MSG_ERR);
+ throw new BuildException(message);
+ }
+ if (!Task.class.isAssignableFrom(taskClass)) {
+ TaskAdapter.checkTaskClass(taskClass, project);
+ }
+ }
+
+ /**
+ * Returns the current task definition hashtable. The returned hashtable is
+ * "live" and so should not be modified. Also, the returned table may be
+ * modified asynchronously.
+ *
+ * @return a map of from task name to implementing class
+ * (String to Class).
+ */
+ public Hashtable<String, Class<?>> getTaskDefinitions() {
+ synchronized (taskClassDefinitions) {
+ synchronized (antTypeTable) {
+ if (rebuildTaskClassDefinitions) {
+ taskClassDefinitions.clear();
+ for (Map.Entry<String, AntTypeDefinition> e : antTypeTable.entrySet()) {
+ final Class<?> clazz = e.getValue().getExposedClass(project);
+ if (clazz == null) {
+ continue;
+ }
+ if (Task.class.isAssignableFrom(clazz)) {
+ taskClassDefinitions.put(e.getKey(), e.getValue().getTypeClass(project));
+ }
+ }
+ rebuildTaskClassDefinitions = false;
+ }
+ }
+ }
+ return taskClassDefinitions;
+ }
+
+ /**
+ * Returns the current type definition hashtable. The returned hashtable is
+ * "live" and so should not be modified.
+ *
+ * @return a map of from type name to implementing class
+ * (String to Class).
+ */
+ public Hashtable<String, Class<?>> getDataTypeDefinitions() {
+ synchronized (typeClassDefinitions) {
+ synchronized (antTypeTable) {
+ if (rebuildTypeClassDefinitions) {
+ typeClassDefinitions.clear();
+ for (Map.Entry<String, AntTypeDefinition> e : antTypeTable.entrySet()) {
+ final Class<?> clazz = e.getValue().getExposedClass(project);
+ if (clazz == null) {
+ continue;
+ }
+ if (!Task.class.isAssignableFrom(clazz)) {
+ typeClassDefinitions.put(e.getKey(), e.getValue().getTypeClass(project));
+ }
+ }
+ rebuildTypeClassDefinitions = false;
+ }
+ }
+ }
+ return typeClassDefinitions;
+ }
+
+ /**
+ * This returns a list of restricted definitions for a name.
+ * The returned List is "live" and so should not be modified.
+ * Also, the returned list may be modified asynchronously.
+ * Any access must be guarded with a lock on the list itself.
+ *
+ * @param componentName the name to use.
+ * @return the list of restricted definitions for a particular name.
+ */
+ public List<AntTypeDefinition> getRestrictedDefinitions(String componentName) {
+ synchronized (restrictedDefinitions) {
+ return restrictedDefinitions.get(componentName);
+ }
+ }
+
+ /**
+ * Adds a new datatype definition.
+ * Attempting to override an existing definition with an
+ * equivalent one (i.e. with the same classname) results in
+ * a verbose log message. Attempting to override an existing definition
+ * with a different one results in a warning log message, but the
+ * definition is changed.
+ *
+ * @param typeName The name of the datatype.
+ * Must not be <code>null</code>.
+ * @param typeClass The full name of the class implementing the datatype.
+ * Must not be <code>null</code>.
+ */
+ public void addDataTypeDefinition(String typeName, Class<?> typeClass) {
+ final AntTypeDefinition def = new AntTypeDefinition();
+ def.setName(typeName);
+ def.setClass(typeClass);
+ updateDataTypeDefinition(def);
+ project.log(" +User datatype: " + typeName + " " + typeClass.getName(),
+ Project.MSG_DEBUG);
+ }
+
+ /**
+ * Describe <code>addDataTypeDefinition</code> method here.
+ *
+ * @param def an <code>AntTypeDefinition</code> value.
+ */
+ public void addDataTypeDefinition(AntTypeDefinition def) {
+ if (!def.isRestrict()) {
+ updateDataTypeDefinition(def);
+ } else {
+ updateRestrictedDefinition(def);
+ }
+ }
+
+ /**
+ * Returns the current datatype definition hashtable. The returned
+ * hashtable is "live" and so should not be modified.
+ *
+ * @return a map of from datatype name to datatype definition
+ * (String to {@link AntTypeDefinition}).
+ */
+ public Hashtable<String, AntTypeDefinition> getAntTypeTable() {
+ return antTypeTable;
+ }
+
+ /**
+ * Creates a new instance of a task.
+ *
+ * Called from Project.createTask(), which can be called by tasks.
+ *
+ * @param taskType The name of the task to create an instance of.
+ * Must not be <code>null</code>.
+ *
+ * @return an instance of the specified task, or <code>null</code> if
+ * the task name is not recognised.
+ *
+ * @exception BuildException if the task name is recognised but task
+ * creation fails.
+ */
+ public Task createTask(String taskType) throws BuildException {
+ Task task = createNewTask(taskType);
+ if (task == null && taskType.equals(ANT_PROPERTY_TASK)) {
+ // quick fix for Ant.java use of property before
+ // initializing the project
+ addTaskDefinition(ANT_PROPERTY_TASK, org.apache.tools.ant.taskdefs.Property.class);
+ task = createNewTask(taskType);
+ }
+ return task;
+ }
+
+ /**
+ * Creates a new instance of a task.
+ * @since ant1.6
+ * @param taskType The name of the task to create an instance of.
+ * Must not be <code>null</code>.
+ *
+ * @return an instance of the specified task, or <code>null</code> if
+ * the task name is not recognised.
+ *
+ * @exception BuildException if the task name is recognised but task
+ * creation fails.
+ */
+ private Task createNewTask(String taskType) throws BuildException {
+ Class<?> c = getComponentClass(taskType);
+ if (c == null || !(Task.class.isAssignableFrom(c))) {
+ return null;
+ }
+ Object obj = createComponent(taskType);
+ if (obj == null) {
+ return null;
+ }
+ if (!(obj instanceof Task)) {
+ throw new BuildException("Expected a Task from '" + taskType
+ + "' but got an instance of " + obj.getClass().getName() + " instead");
+ }
+ Task task = (Task) obj;
+ task.setTaskType(taskType);
+
+ // set default value, can be changed by the user
+ task.setTaskName(taskType);
+
+ project.log(" +Task: " + taskType, Project.MSG_DEBUG);
+ return task;
+ }
+
+ /**
+ * Creates a new instance of a data type.
+ *
+ * @param typeName The name of the data type to create an instance of.
+ * Must not be <code>null</code>.
+ *
+ * @return an instance of the specified data type, or <code>null</code> if
+ * the data type name is not recognised.
+ *
+ * @exception BuildException if the data type name is recognised but
+ * instance creation fails.
+ */
+ public Object createDataType(String typeName) throws BuildException {
+ return createComponent(typeName);
+ }
+
+ /**
+ * Returns a description of the type of the given element.
+ * <p>
+ * This is useful for logging purposes.
+ *
+ * @param element The element to describe.
+ * Must not be <code>null</code>.
+ *
+ * @return a description of the element type.
+ *
+ * @since Ant 1.6
+ */
+ public String getElementName(Object element) {
+ return getElementName(element, false);
+ }
+
+ /**
+ * Returns a description of the type of the given element.
+ * <p>
+ * This is useful for logging purposes.
+ *
+ * @param o The element to describe.
+ * Must not be <code>null</code>.
+ * @param brief whether to use a brief description.
+ * @return a description of the element type.
+ *
+ * @since Ant 1.7
+ */
+ public String getElementName(Object o, boolean brief) {
+ // PR: I do not know what to do if the object class
+ // has multiple defines
+ // but this is for logging only...
+ Class<?> elementClass = o.getClass();
+ String elementClassname = elementClass.getName();
+ synchronized (antTypeTable) {
+ for (AntTypeDefinition def : antTypeTable.values()) {
+ if (elementClassname.equals(def.getClassName())
+ && (elementClass == def.getExposedClass(project))) {
+ String name = def.getName();
+ return brief ? name : "The <" + name + "> type";
+ }
+ }
+ }
+ return getUnmappedElementName(o.getClass(), brief);
+ }
+
+ /**
+ * Convenient way to get some element name even when you may not have a
+ * Project context.
+ * @param p The optional Project instance.
+ * @param o The element to describe.
+ * Must not be <code>null</code>.
+ * @param brief whether to use a brief description.
+ * @return a description of the element type.
+ * @since Ant 1.7
+ */
+ public static String getElementName(Project p, Object o, boolean brief) {
+ if (p == null) {
+ p = Project.getProject(o);
+ }
+ return p == null ? getUnmappedElementName(o.getClass(), brief) : getComponentHelper(p)
+ .getElementName(o, brief);
+ }
+
+ private static String getUnmappedElementName(Class<?> c, boolean brief) {
+ if (brief) {
+ String name = c.getName();
+ return name.substring(name.lastIndexOf('.') + 1);
+ }
+ return c.toString();
+ }
+
+ /**
+ * Check if definition is a valid definition--it may be a
+ * definition of an optional task that does not exist.
+ * @param def the definition to test.
+ * @return true if exposed type of definition is present.
+ */
+ private boolean validDefinition(AntTypeDefinition def) {
+ return !(def.getTypeClass(project) == null || def.getExposedClass(project) == null);
+ }
+
+ /**
+ * Check if two definitions are the same.
+ * @param def the new definition.
+ * @param old the old definition.
+ * @return true if the two definitions are the same.
+ */
+ private boolean sameDefinition(AntTypeDefinition def, AntTypeDefinition old) {
+ boolean defValid = validDefinition(def);
+ boolean sameValidity = (defValid == validDefinition(old));
+ //must have same validity; then if they are valid they must also be the same:
+ return sameValidity && (!defValid || def.sameDefinition(old, project));
+ }
+
+ /**
+ * update the restricted definition table with a new or
+ * modified definition.
+ */
+ private void updateRestrictedDefinition(AntTypeDefinition def) {
+ String name = def.getName();
+ List<AntTypeDefinition> list = null;
+ synchronized (restrictedDefinitions) {
+ list = restrictedDefinitions.get(name);
+ if (list == null) {
+ list = new ArrayList<AntTypeDefinition>();
+ restrictedDefinitions.put(name, list);
+ }
+ }
+ // Check if the classname is already present and remove it
+ // if it is
+ synchronized (list) {
+ for (Iterator<AntTypeDefinition> i = list.iterator(); i.hasNext();) {
+ AntTypeDefinition current = i.next();
+ if (current.getClassName().equals(def.getClassName())) {
+ i.remove();
+ break;
+ }
+ }
+ list.add(def);
+ }
+ }
+
+ /**
+ * Update the component definition table with a new or
+ * modified definition.
+ * @param def the definition to update or insert.
+ */
+ private void updateDataTypeDefinition(AntTypeDefinition def) {
+ String name = def.getName();
+ synchronized (antTypeTable) {
+ rebuildTaskClassDefinitions = true;
+ rebuildTypeClassDefinitions = true;
+ final AntTypeDefinition old = antTypeTable.get(name);
+ if (old != null) {
+ if (sameDefinition(def, old)) {
+ return;
+ }
+ Class<?> oldClass = old.getExposedClass(project);
+ boolean isTask = oldClass != null && Task.class.isAssignableFrom(oldClass);
+ project.log("Trying to override old definition of "
+ + (isTask ? "task " : "datatype ") + name, (def.similarDefinition(old,
+ project)) ? Project.MSG_VERBOSE : Project.MSG_WARN);
+ }
+ project.log(" +Datatype " + name + " " + def.getClassName(), Project.MSG_DEBUG);
+ antTypeTable.put(name, def);
+ }
+ }
+
+ /**
+ * Called at the start of processing an antlib.
+ * @param uri the uri that is associated with this antlib.
+ */
+ public void enterAntLib(String uri) {
+ antLibCurrentUri = uri;
+ antLibStack.push(uri);
+ }
+
+ /**
+ * @return the current antlib uri.
+ */
+ public String getCurrentAntlibUri() {
+ return antLibCurrentUri;
+ }
+
+ /**
+ * Called at the end of processing an antlib.
+ */
+ public void exitAntLib() {
+ antLibStack.pop();
+ antLibCurrentUri = (antLibStack.size() == 0) ? null : (String) antLibStack.peek();
+ }
+
+ /**
+ * Load ant's tasks.
+ */
+ private void initTasks() {
+ ClassLoader classLoader = getClassLoader(null);
+ Properties props = getDefaultDefinitions(false);
+ Enumeration<?> e = props.propertyNames();
+ while (e.hasMoreElements()) {
+ String name = (String) e.nextElement();
+ String className = props.getProperty(name);
+ AntTypeDefinition def = new AntTypeDefinition();
+ def.setName(name);
+ def.setClassName(className);
+ def.setClassLoader(classLoader);
+ def.setAdaptToClass(Task.class);
+ def.setAdapterClass(TaskAdapter.class);
+ antTypeTable.put(name, def);
+ }
+ }
+
+ private ClassLoader getClassLoader(ClassLoader classLoader) {
+ String buildSysclasspath = project.getProperty(MagicNames.BUILD_SYSCLASSPATH);
+ if (project.getCoreLoader() != null
+ && !(BUILD_SYSCLASSPATH_ONLY.equals(buildSysclasspath))) {
+ classLoader = project.getCoreLoader();
+ }
+ return classLoader;
+ }
+
+ /**
+ * Load default task or type definitions - just the names,
+ * no class loading.
+ * Caches results between calls to reduce overhead.
+ * @param type true for typedefs, false for taskdefs
+ * @return a mapping from definition names to class names
+ * @throws BuildException if there was some problem loading
+ * or parsing the definitions list
+ */
+ private static synchronized Properties getDefaultDefinitions(boolean type)
+ throws BuildException {
+ int idx = type ? 1 : 0;
+ if (defaultDefinitions[idx] == null) {
+ String resource = type ? MagicNames.TYPEDEFS_PROPERTIES_RESOURCE
+ : MagicNames.TASKDEF_PROPERTIES_RESOURCE;
+ String errorString = type ? ERROR_NO_TYPE_LIST_LOAD : ERROR_NO_TASK_LIST_LOAD;
+ InputStream in = null;
+ try {
+ in = ComponentHelper.class.getResourceAsStream(resource);
+ if (in == null) {
+ throw new BuildException(errorString);
+ }
+ Properties p = new Properties();
+ p.load(in);
+ defaultDefinitions[idx] = p;
+ } catch (IOException e) {
+ throw new BuildException(errorString, e);
+ } finally {
+ FileUtils.close(in);
+ }
+ }
+ return defaultDefinitions[idx];
+ }
+
+ /**
+ * Load ant's datatypes.
+ */
+ private void initTypes() {
+ ClassLoader classLoader = getClassLoader(null);
+ Properties props = getDefaultDefinitions(true);
+ Enumeration<?> e = props.propertyNames();
+ while (e.hasMoreElements()) {
+ String name = (String) e.nextElement();
+ String className = props.getProperty(name);
+ AntTypeDefinition def = new AntTypeDefinition();
+ def.setName(name);
+ def.setClassName(className);
+ def.setClassLoader(classLoader);
+ antTypeTable.put(name, def);
+ }
+ }
+
+ /**
+ * Called for each component name, check if the
+ * associated URI has been examined for antlibs.
+ * @param componentName the name of the component, which should include a URI
+ * prefix if it is in a namespace
+ */
+ private synchronized void checkNamespace(String componentName) {
+ String uri = ProjectHelper.extractUriFromComponentName(componentName);
+ if ("".equals(uri)) {
+ uri = ProjectHelper.ANT_CORE_URI;
+ }
+ if (!uri.startsWith(ProjectHelper.ANTLIB_URI)) {
+ return; // namespace that does not contain antlib
+ }
+ if (checkedNamespaces.contains(uri)) {
+ return; // Already processed
+ }
+ checkedNamespaces.add(uri);
+
+ if (antTypeTable.size() == 0) {
+ // Project instance doesn't know the tasks and types
+ // defined in defaults.properties, likely created by the
+ // user - without those definitions it cannot parse antlib
+ // files as taskdef, typedef and friends are unknown
+ initDefaultDefinitions();
+ }
+ Typedef definer = new Typedef();
+ definer.setProject(project);
+ definer.init();
+ definer.setURI(uri);
+ //there to stop error messages being "null"
+ definer.setTaskName(uri);
+ //if this is left out, bad things happen. like all build files break
+ //on the first element encountered.
+ definer.setResource(Definer.makeResourceFromURI(uri));
+ // a fishing expedition :- ignore errors if antlib not present
+ definer.setOnError(new Typedef.OnError(Typedef.OnError.POLICY_IGNORE));
+ definer.execute();
+ }
+
+ /**
+ * Handler called to do decent diagnosis on instantiation failure.
+ * @param componentName component name.
+ * @param type component type, used in error messages
+ * @return a string containing as much diagnostics info as possible.
+ */
+ public String diagnoseCreationFailure(String componentName, String type) {
+ StringWriter errorText = new StringWriter();
+ PrintWriter out = new PrintWriter(errorText);
+ out.println("Problem: failed to create " + type + " " + componentName);
+ //class of problem
+ boolean lowlevel = false;
+ boolean jars = false;
+ boolean definitions = false;
+ boolean antTask;
+ String home = System.getProperty(Launcher.USER_HOMEDIR);
+ File libDir = new File(home, Launcher.USER_LIBDIR);
+ String antHomeLib;
+ boolean probablyIDE = false;
+ String anthome = System.getProperty(MagicNames.ANT_HOME);
+ if (anthome != null) {
+ File antHomeLibDir = new File(anthome, "lib");
+ antHomeLib = antHomeLibDir.getAbsolutePath();
+ } else {
+ //running under an IDE that doesn't set ANT_HOME
+ probablyIDE = true;
+ antHomeLib = "ANT_HOME" + File.separatorChar + "lib";
+ }
+ StringBuffer dirListingText = new StringBuffer();
+ final String tab = " -";
+ dirListingText.append(tab);
+ dirListingText.append(antHomeLib);
+ dirListingText.append('\n');
+ if (probablyIDE) {
+ dirListingText.append(tab);
+ dirListingText.append("the IDE Ant configuration dialogs");
+ } else {
+ dirListingText.append(tab);
+ dirListingText.append(libDir);
+ dirListingText.append('\n');
+ dirListingText.append(tab);
+ dirListingText.append("a directory added on the command line with the -lib argument");
+ }
+ String dirListing = dirListingText.toString();
+
+ //look up the name
+ AntTypeDefinition def = getDefinition(componentName);
+ if (def == null) {
+ //not a known type
+ printUnknownDefinition(out, componentName, dirListing);
+ definitions = true;
+ } else {
+ //we are defined, so it is an instantiation problem
+ final String classname = def.getClassName();
+ antTask = classname.startsWith("org.apache.tools.ant.");
+ boolean optional = classname.startsWith("org.apache.tools.ant.taskdefs.optional");
+ optional |= classname.startsWith("org.apache.tools.ant.types.optional");
+
+ //start with instantiating the class.
+ Class<?> clazz = null;
+ try {
+ clazz = def.innerGetTypeClass();
+ } catch (ClassNotFoundException e) {
+ jars = true;
+ if (!optional) {
+ definitions = true;
+ }
+ printClassNotFound(out, classname, optional, dirListing);
+ } catch (NoClassDefFoundError ncdfe) {
+ jars = true;
+ printNotLoadDependentClass(out, optional, ncdfe, dirListing);
+ }
+ //here we successfully loaded the class or failed.
+ if (clazz != null) {
+ //success: proceed with more steps
+ try {
+ def.innerCreateAndSet(clazz, project);
+ //hey, there is nothing wrong with us
+ out.println("The component could be instantiated.");
+ } catch (NoSuchMethodException e) {
+ lowlevel = true;
+ out.println("Cause: The class " + classname
+ + " has no compatible constructor.");
+
+ } catch (InstantiationException e) {
+ lowlevel = true;
+ out.println("Cause: The class " + classname
+ + " is abstract and cannot be instantiated.");
+ } catch (IllegalAccessException e) {
+ lowlevel = true;
+ out.println("Cause: The constructor for " + classname
+ + " is private and cannot be invoked.");
+ } catch (InvocationTargetException ex) {
+ lowlevel = true;
+ Throwable t = ex.getTargetException();
+ out.println("Cause: The constructor threw the exception");
+ out.println(t.toString());
+ t.printStackTrace(out);
+ } catch (NoClassDefFoundError ncdfe) {
+ jars = true;
+ out.println("Cause: A class needed by class " + classname
+ + " cannot be found: ");
+ out.println(" " + ncdfe.getMessage());
+ out.println("Action: Determine what extra JAR files are"
+ + " needed, and place them in:");
+ out.println(dirListing);
+ }
+ }
+ out.println();
+ out.println("Do not panic, this is a common problem.");
+ if (definitions) {
+ out.println("It may just be a typographical error in the build file "
+ + "or the task/type declaration.");
+ }
+ if (jars) {
+ out.println("The commonest cause is a missing JAR.");
+ }
+ if (lowlevel) {
+ out.println("This is quite a low level problem, which may need "
+ + "consultation with the author of the task.");
+ if (antTask) {
+ out.println("This may be the Ant team. Please file a "
+ + "defect or contact the developer team.");
+ } else {
+ out.println("This does not appear to be a task bundled with Ant.");
+ out.println("Please take it up with the supplier of the third-party " + type
+ + ".");
+ out.println("If you have written it yourself, you probably have a bug to fix.");
+ }
+ } else {
+ out.println();
+ out.println("This is not a bug; it is a configuration problem");
+ }
+ }
+ out.flush();
+ out.close();
+ return errorText.toString();
+ }
+
+ /**
+ * Print unknown definition.forking
+ */
+ private void printUnknownDefinition(PrintWriter out, String componentName, String dirListing) {
+ boolean isAntlib = componentName.startsWith(MagicNames.ANTLIB_PREFIX);
+ String uri = ProjectHelper.extractUriFromComponentName(componentName);
+ out.println("Cause: The name is undefined.");
+ out.println("Action: Check the spelling.");
+ out.println("Action: Check that any custom tasks/types have been declared.");
+ out.println("Action: Check that any <presetdef>/<macrodef>"
+ + " declarations have taken place.");
+ if (uri.length() > 0) {
+ final List<AntTypeDefinition> matches = findTypeMatches(uri);
+ if (matches.size() > 0) {
+ out.println();
+ out.println("The definitions in the namespace " + uri + " are:");
+ for (AntTypeDefinition def : matches) {
+ String local = ProjectHelper.extractNameFromComponentName(def.getName());
+ out.println(" " + local);
+ }
+ } else {
+ out.println("No types or tasks have been defined in this namespace yet");
+ if (isAntlib) {
+ out.println();
+ out.println("This appears to be an antlib declaration. ");
+ out.println("Action: Check that the implementing library exists in one of:");
+ out.println(dirListing);
+ }
+ }
+ }
+ }
+
+ /**
+ * Print class not found.
+ */
+ private void printClassNotFound(PrintWriter out, String classname, boolean optional,
+ String dirListing) {
+ out.println("Cause: the class " + classname + " was not found.");
+ if (optional) {
+ out.println(" This looks like one of Ant's optional components.");
+ out.println("Action: Check that the appropriate optional JAR exists in");
+ out.println(dirListing);
+ } else {
+ out.println("Action: Check that the component has been correctly declared");
+ out.println(" and that the implementing JAR is in one of:");
+ out.println(dirListing);
+ }
+ }
+
+ /**
+ * Print could not load dependent class.
+ */
+ private void printNotLoadDependentClass(PrintWriter out, boolean optional,
+ NoClassDefFoundError ncdfe, String dirListing) {
+ out.println("Cause: Could not load a dependent class "
+ + ncdfe.getMessage());
+ if (optional) {
+ out.println(" It is not enough to have Ant's optional JARs");
+ out.println(" you need the JAR files that the" + " optional tasks depend upon.");
+ out.println(" Ant's optional task dependencies are" + " listed in the manual.");
+ } else {
+ out.println(" This class may be in a separate JAR" + " that is not installed.");
+ }
+ out.println("Action: Determine what extra JAR files are"
+ + " needed, and place them in one of:");
+ out.println(dirListing);
+ }
+
+ /**
+ * Create a list of all definitions that match a prefix, usually the URI
+ * of a library
+ * @param prefix prefix to match off
+ * @return the (possibly empty) list of definitions
+ */
+ private List<AntTypeDefinition> findTypeMatches(String prefix) {
+ final List<AntTypeDefinition> result = new ArrayList<AntTypeDefinition>();
+ synchronized (antTypeTable) {
+ for (AntTypeDefinition def : antTypeTable.values()) {
+ if (def.getName().startsWith(prefix)) {
+ result.add(def);
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DefaultDefinitions.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DefaultDefinitions.java
new file mode 100644
index 00000000..062018d0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DefaultDefinitions.java
@@ -0,0 +1,76 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Default definitions.
+ * @since Ant 1.9.1
+ */
+public final class DefaultDefinitions {
+ private static final String IF_NAMESPACE = "ant:if";
+ private static final String UNLESS_NAMESPACE = "ant:unless";
+ private static final String OATA = "org.apache.tools.ant.";
+
+ private final ComponentHelper componentHelper;
+
+ /**
+ * Create a default definitions object.
+ * @param componentHelper the componenthelper to initialize.
+ */
+ public DefaultDefinitions(ComponentHelper componentHelper) {
+ this.componentHelper = componentHelper;
+ }
+
+ /**
+ * Register the definitions.
+ */
+ public void execute() {
+ attributeNamespaceDef(IF_NAMESPACE);
+ attributeNamespaceDef(UNLESS_NAMESPACE);
+
+ ifUnlessDef("true", "IfTrueAttribute");
+ ifUnlessDef("set", "IfSetAttribute");
+ ifUnlessDef("blank", "IfBlankAttribute");
+ }
+
+ private void attributeNamespaceDef(String ns) {
+ AntTypeDefinition def = new AntTypeDefinition();
+ def.setName(ProjectHelper.nsToComponentName(ns));
+ def.setClassName(OATA + "attribute.AttributeNamespace");
+ def.setClassLoader(getClass().getClassLoader());
+ def.setRestrict(true);
+ componentHelper.addDataTypeDefinition(def);
+ }
+
+ private void ifUnlessDef(String name, String base) {
+ String classname = OATA + "attribute." + base;
+ componentDef(IF_NAMESPACE, name, classname);
+ componentDef(UNLESS_NAMESPACE, name, classname + "$Unless");
+ }
+
+ private void componentDef(String ns, String name, String classname) {
+ AntTypeDefinition def = new AntTypeDefinition();
+ String n = ProjectHelper.genComponentName(ns, name);
+ def.setName(ProjectHelper.genComponentName(ns, name));
+ def.setClassName(classname);
+ def.setClassLoader(getClass().getClassLoader());
+ def.setRestrict(true);
+ componentHelper.addDataTypeDefinition(def);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DefaultLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DefaultLogger.java
new file mode 100644
index 00000000..dbc60486
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DefaultLogger.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.tools.ant;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.StringReader;
+import java.text.DateFormat;
+import java.util.Date;
+
+import org.apache.tools.ant.util.DateUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Writes build events to a PrintStream. Currently, it
+ * only writes which targets are being executed, and
+ * any messages that get logged.
+ *
+ */
+public class DefaultLogger implements BuildLogger {
+ /**
+ * Size of left-hand column for right-justified task name.
+ * @see #messageLogged(BuildEvent)
+ */
+ public static final int LEFT_COLUMN_SIZE = 12;
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ /** PrintStream to write non-error messages to */
+ protected PrintStream out;
+
+ /** PrintStream to write error messages to */
+ protected PrintStream err;
+
+ /** Lowest level of message to write out */
+ protected int msgOutputLevel = Project.MSG_ERR;
+
+ /** Time of the start of the build */
+ private long startTime = System.currentTimeMillis();
+
+ // CheckStyle:ConstantNameCheck OFF - bc
+ /** Line separator */
+ protected static final String lSep = StringUtils.LINE_SEP;
+ // CheckStyle:ConstantNameCheck ON
+
+ /** Whether or not to use emacs-style output */
+ protected boolean emacsMode = false;
+ // CheckStyle:VisibilityModifier ON
+
+
+ /**
+ * Sole constructor.
+ */
+ public DefaultLogger() {
+ }
+
+ /**
+ * Sets the highest level of message this logger should respond to.
+ *
+ * Only messages with a message level lower than or equal to the
+ * given level should be written to the log.
+ * <p>
+ * Constants for the message levels are in the
+ * {@link Project Project} class. The order of the levels, from least
+ * to most verbose, is <code>MSG_ERR</code>, <code>MSG_WARN</code>,
+ * <code>MSG_INFO</code>, <code>MSG_VERBOSE</code>,
+ * <code>MSG_DEBUG</code>.
+ * <p>
+ * The default message level for DefaultLogger is Project.MSG_ERR.
+ *
+ * @param level the logging level for the logger.
+ */
+ public void setMessageOutputLevel(int level) {
+ this.msgOutputLevel = level;
+ }
+
+ /**
+ * Sets the output stream to which this logger is to send its output.
+ *
+ * @param output The output stream for the logger.
+ * Must not be <code>null</code>.
+ */
+ public void setOutputPrintStream(PrintStream output) {
+ this.out = new PrintStream(output, true);
+ }
+
+ /**
+ * Sets the output stream to which this logger is to send error messages.
+ *
+ * @param err The error stream for the logger.
+ * Must not be <code>null</code>.
+ */
+ public void setErrorPrintStream(PrintStream err) {
+ this.err = new PrintStream(err, true);
+ }
+
+ /**
+ * Sets this logger to produce emacs (and other editor) friendly output.
+ *
+ * @param emacsMode <code>true</code> if output is to be unadorned so that
+ * emacs and other editors can parse files names, etc.
+ */
+ public void setEmacsMode(boolean emacsMode) {
+ this.emacsMode = emacsMode;
+ }
+
+ /**
+ * Responds to a build being started by just remembering the current time.
+ *
+ * @param event Ignored.
+ */
+ public void buildStarted(BuildEvent event) {
+ startTime = System.currentTimeMillis();
+ }
+
+ static void throwableMessage(StringBuffer m, Throwable error, boolean verbose) {
+ while (error instanceof BuildException) { // #43398
+ Throwable cause = error.getCause();
+ if (cause == null) {
+ break;
+ }
+ String msg1 = error.toString();
+ String msg2 = cause.toString();
+ if (msg1.endsWith(msg2)) {
+ m.append(msg1.substring(0, msg1.length() - msg2.length()));
+ error = cause;
+ } else {
+ break;
+ }
+ }
+ if (verbose || !(error instanceof BuildException)) {
+ m.append(StringUtils.getStackTrace(error));
+ } else {
+ m.append(error).append(lSep);
+ }
+ }
+
+ /**
+ * Prints whether the build succeeded or failed,
+ * any errors the occurred during the build, and
+ * how long the build took.
+ *
+ * @param event An event with any relevant extra information.
+ * Must not be <code>null</code>.
+ */
+ public void buildFinished(BuildEvent event) {
+ Throwable error = event.getException();
+ StringBuffer message = new StringBuffer();
+ if (error == null) {
+ message.append(StringUtils.LINE_SEP);
+ message.append(getBuildSuccessfulMessage());
+ } else {
+ message.append(StringUtils.LINE_SEP);
+ message.append(getBuildFailedMessage());
+ message.append(StringUtils.LINE_SEP);
+ throwableMessage(message, error, Project.MSG_VERBOSE <= msgOutputLevel);
+ }
+ message.append(StringUtils.LINE_SEP);
+ message.append("Total time: ");
+ message.append(formatTime(System.currentTimeMillis() - startTime));
+
+ String msg = message.toString();
+ if (error == null) {
+ printMessage(msg, out, Project.MSG_VERBOSE);
+ } else {
+ printMessage(msg, err, Project.MSG_ERR);
+ }
+ log(msg);
+ }
+
+ /**
+ * This is an override point: the message that indicates whether a build failed.
+ * Subclasses can change/enhance the message.
+ * @return The classic "BUILD FAILED"
+ */
+ protected String getBuildFailedMessage() {
+ return "BUILD FAILED";
+ }
+
+ /**
+ * This is an override point: the message that indicates that a build succeeded.
+ * Subclasses can change/enhance the message.
+ * @return The classic "BUILD SUCCESSFUL"
+ */
+ protected String getBuildSuccessfulMessage() {
+ return "BUILD SUCCESSFUL";
+ }
+
+ /**
+ * Logs a message to say that the target has started if this
+ * logger allows information-level messages.
+ *
+ * @param event An event with any relevant extra information.
+ * Must not be <code>null</code>.
+ */
+ public void targetStarted(BuildEvent event) {
+ if (Project.MSG_INFO <= msgOutputLevel
+ && !event.getTarget().getName().equals("")) {
+ String msg = StringUtils.LINE_SEP
+ + event.getTarget().getName() + ":";
+ printMessage(msg, out, event.getPriority());
+ log(msg);
+ }
+ }
+
+ /**
+ * No-op implementation.
+ *
+ * @param event Ignored.
+ */
+ public void targetFinished(BuildEvent event) {
+ }
+
+ /**
+ * No-op implementation.
+ *
+ * @param event Ignored.
+ */
+ public void taskStarted(BuildEvent event) {
+ }
+
+ /**
+ * No-op implementation.
+ *
+ * @param event Ignored.
+ */
+ public void taskFinished(BuildEvent event) {
+ }
+
+ /**
+ * Logs a message, if the priority is suitable.
+ * In non-emacs mode, task level messages are prefixed by the
+ * task name which is right-justified.
+ *
+ * @param event A BuildEvent containing message information.
+ * Must not be <code>null</code>.
+ */
+ public void messageLogged(BuildEvent event) {
+ int priority = event.getPriority();
+ // Filter out messages based on priority
+ if (priority <= msgOutputLevel) {
+
+ StringBuffer message = new StringBuffer();
+ if (event.getTask() != null && !emacsMode) {
+ // Print out the name of the task if we're in one
+ String name = event.getTask().getTaskName();
+ String label = "[" + name + "] ";
+ int size = LEFT_COLUMN_SIZE - label.length();
+ StringBuffer tmp = new StringBuffer();
+ for (int i = 0; i < size; i++) {
+ tmp.append(" ");
+ }
+ tmp.append(label);
+ label = tmp.toString();
+
+ BufferedReader r = null;
+ try {
+ r = new BufferedReader(
+ new StringReader(event.getMessage()));
+ String line = r.readLine();
+ boolean first = true;
+ do {
+ if (first) {
+ if (line == null) {
+ message.append(label);
+ break;
+ }
+ } else {
+ message.append(StringUtils.LINE_SEP);
+ }
+ first = false;
+ message.append(label).append(line);
+ line = r.readLine();
+ } while (line != null);
+ } catch (IOException e) {
+ // shouldn't be possible
+ message.append(label).append(event.getMessage());
+ } finally {
+ if (r != null) {
+ FileUtils.close(r);
+ }
+ }
+
+ } else {
+ //emacs mode or there is no task
+ message.append(event.getMessage());
+ }
+ Throwable ex = event.getException();
+ if (Project.MSG_DEBUG <= msgOutputLevel && ex != null) {
+ message.append(StringUtils.getStackTrace(ex));
+ }
+
+ String msg = message.toString();
+ if (priority != Project.MSG_ERR) {
+ printMessage(msg, out, priority);
+ } else {
+ printMessage(msg, err, priority);
+ }
+ log(msg);
+ }
+ }
+
+ /**
+ * Convenience method to format a specified length of time.
+ *
+ * @param millis Length of time to format, in milliseconds.
+ *
+ * @return the time as a formatted string.
+ *
+ * @see DateUtils#formatElapsedTime(long)
+ */
+ protected static String formatTime(final long millis) {
+ return DateUtils.formatElapsedTime(millis);
+ }
+
+ /**
+ * Prints a message to a PrintStream.
+ *
+ * @param message The message to print.
+ * Should not be <code>null</code>.
+ * @param stream A PrintStream to print the message to.
+ * Must not be <code>null</code>.
+ * @param priority The priority of the message.
+ * (Ignored in this implementation.)
+ */
+ protected void printMessage(final String message,
+ final PrintStream stream,
+ final int priority) {
+ stream.println(message);
+ }
+
+ /**
+ * Empty implementation which allows subclasses to receive the
+ * same output that is generated here.
+ *
+ * @param message Message being logged. Should not be <code>null</code>.
+ */
+ protected void log(String message) {
+ }
+
+ /**
+ * Get the current time.
+ * @return the current time as a formatted string.
+ * @since Ant1.7.1
+ */
+ protected String getTimestamp() {
+ Date date = new Date(System.currentTimeMillis());
+ DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
+ String finishTime = formatter.format(date);
+ return finishTime;
+ }
+
+ /**
+ * Get the project name or null
+ * @param event the event
+ * @return the project that raised this event
+ * @since Ant1.7.1
+ */
+ protected String extractProjectName(BuildEvent event) {
+ Project project = event.getProject();
+ return (project != null) ? project.getName() : null;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DemuxInputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DemuxInputStream.java
new file mode 100644
index 00000000..ea263ca7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DemuxInputStream.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.tools.ant;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ *
+ * Passes input requests to the project object for demuxing into
+ * individual tasks and threads.
+ *
+ * @since Ant 1.6
+ */
+public class DemuxInputStream extends InputStream {
+
+ private static final int MASK_8BIT = 0xFF;
+ /**
+ * The project to from which to get input.
+ */
+ private Project project;
+
+ /**
+ * Create a DemuxInputStream for the given project
+ *
+ * @param project the project instance
+ */
+ public DemuxInputStream(Project project) {
+ this.project = project;
+ }
+
+ /**
+ * Read a byte from the project's demuxed input.
+ * @return the next byte
+ * @throws IOException on error
+ */
+ public int read() throws IOException {
+ byte[] buffer = new byte[1];
+ if (project.demuxInput(buffer, 0, 1) == -1) {
+ return -1;
+ }
+ return buffer[0] & MASK_8BIT;
+ }
+
+
+ /**
+ * Read bytes from the project's demuxed input.
+ * @param buffer an array of bytes to read into
+ * @param offset the offset in the array of bytes
+ * @param length the number of bytes in the array
+ * @return the number of bytes read
+ * @throws IOException on error
+ */
+ public int read(byte[] buffer, int offset, int length) throws IOException {
+ return project.demuxInput(buffer, offset, length);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DemuxOutputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DemuxOutputStream.java
new file mode 100644
index 00000000..bd399132
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DemuxOutputStream.java
@@ -0,0 +1,249 @@
+/*
+ * 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.tools.ant;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.WeakHashMap;
+
+
+/**
+ * Logs content written by a thread and forwards the buffers onto the
+ * project object which will forward the content to the appropriate
+ * task.
+ *
+ * @since 1.4
+ */
+public class DemuxOutputStream extends OutputStream {
+
+ /**
+ * A data class to store information about a buffer. Such information
+ * is stored on a per-thread basis.
+ */
+ private static class BufferInfo {
+ /**
+ * The per-thread output stream.
+ */
+ private ByteArrayOutputStream buffer;
+
+ /**
+ * Indicates we have just seen a carriage return. It may be part of
+ * a crlf pair or a single cr invoking processBuffer twice.
+ */
+ private boolean crSeen = false;
+ }
+
+ /** Maximum buffer size. */
+ private static final int MAX_SIZE = 1024;
+
+ /** Initial buffer size. */
+ private static final int INITIAL_SIZE = 132;
+
+ /** Carriage return */
+ private static final int CR = 0x0d;
+
+ /** Linefeed */
+ private static final int LF = 0x0a;
+
+ /** Mapping from thread to buffer (Thread to BufferInfo). */
+ private WeakHashMap<Thread, BufferInfo> buffers = new WeakHashMap<Thread, BufferInfo>();
+
+ /**
+ * The project to send output to.
+ */
+ private Project project;
+
+ /**
+ * Whether or not this stream represents an error stream.
+ */
+ private boolean isErrorStream;
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param project The project instance for which output is being
+ * demultiplexed. Must not be <code>null</code>.
+ * @param isErrorStream <code>true</code> if this is the error string,
+ * otherwise a normal output stream. This is
+ * passed to the project so it knows
+ * which stream it is receiving.
+ */
+ public DemuxOutputStream(Project project, boolean isErrorStream) {
+ this.project = project;
+ this.isErrorStream = isErrorStream;
+ }
+
+ /**
+ * Returns the buffer associated with the current thread.
+ *
+ * @return a BufferInfo for the current thread to write data to
+ */
+ private BufferInfo getBufferInfo() {
+ Thread current = Thread.currentThread();
+ BufferInfo bufferInfo = (BufferInfo) buffers.get(current);
+ if (bufferInfo == null) {
+ bufferInfo = new BufferInfo();
+ bufferInfo.buffer = new ByteArrayOutputStream(INITIAL_SIZE);
+ bufferInfo.crSeen = false;
+ buffers.put(current, bufferInfo);
+ }
+ return bufferInfo;
+ }
+
+ /**
+ * Resets the buffer for the current thread.
+ */
+ private void resetBufferInfo() {
+ Thread current = Thread.currentThread();
+ BufferInfo bufferInfo = (BufferInfo) buffers.get(current);
+ try {
+ bufferInfo.buffer.close();
+ } catch (IOException e) {
+ // Shouldn't happen
+ }
+ bufferInfo.buffer = new ByteArrayOutputStream();
+ bufferInfo.crSeen = false;
+ }
+
+ /**
+ * Removes the buffer for the current thread.
+ */
+ private void removeBuffer() {
+ Thread current = Thread.currentThread();
+ buffers.remove (current);
+ }
+
+ /**
+ * Writes the data to the buffer and flushes the buffer if a line
+ * separator is detected or if the buffer has reached its maximum size.
+ *
+ * @param cc data to log (byte).
+ * @exception IOException if the data cannot be written to the stream
+ */
+ public void write(int cc) throws IOException {
+ final byte c = (byte) cc;
+
+ BufferInfo bufferInfo = getBufferInfo();
+
+ if (c == '\n') {
+ // LF is always end of line (i.e. CRLF or single LF)
+ bufferInfo.buffer.write(cc);
+ processBuffer(bufferInfo.buffer);
+ } else {
+ if (bufferInfo.crSeen) {
+ // CR without LF - send buffer then add char
+ processBuffer(bufferInfo.buffer);
+ }
+ // add into buffer
+ bufferInfo.buffer.write(cc);
+ }
+ bufferInfo.crSeen = (c == '\r');
+ if (!bufferInfo.crSeen && bufferInfo.buffer.size() > MAX_SIZE) {
+ processBuffer(bufferInfo.buffer);
+ }
+ }
+
+ /**
+ * Converts the buffer to a string and sends it to the project.
+ *
+ * @param buffer the ByteArrayOutputStream used to collect the output
+ * until a line separator is seen.
+ *
+ * @see Project#demuxOutput(String,boolean)
+ */
+ protected void processBuffer(ByteArrayOutputStream buffer) {
+ String output = buffer.toString();
+ project.demuxOutput(output, isErrorStream);
+ resetBufferInfo();
+ }
+
+ /**
+ * Converts the buffer to a string and sends it to the project.
+ *
+ * @param buffer the ByteArrayOutputStream used to collect the output
+ * until a line separator is seen.
+ *
+ * @see Project#demuxOutput(String,boolean)
+ */
+ protected void processFlush(ByteArrayOutputStream buffer) {
+ String output = buffer.toString();
+ project.demuxFlush(output, isErrorStream);
+ resetBufferInfo();
+ }
+
+ /**
+ * Equivalent to flushing the stream.
+ *
+ * @exception IOException if there is a problem closing the stream.
+ *
+ * @see #flush
+ */
+ public void close() throws IOException {
+ flush();
+ removeBuffer();
+ }
+
+ /**
+ * Writes all remaining data in the buffer associated
+ * with the current thread to the project.
+ *
+ * @exception IOException if there is a problem flushing the stream.
+ */
+ public void flush() throws IOException {
+ BufferInfo bufferInfo = getBufferInfo();
+ if (bufferInfo.buffer.size() > 0) {
+ processFlush(bufferInfo.buffer);
+ }
+ }
+
+ /**
+ * Write a block of characters to the output stream
+ *
+ * @param b the array containing the data
+ * @param off the offset into the array where data starts
+ * @param len the length of block
+ *
+ * @throws IOException if the data cannot be written into the stream.
+ */
+ public void write(byte[] b, int off, int len) throws IOException {
+ // find the line breaks and pass other chars through in blocks
+ int offset = off;
+ int blockStartOffset = offset;
+ int remaining = len;
+ BufferInfo bufferInfo = getBufferInfo();
+ while (remaining > 0) {
+ while (remaining > 0 && b[offset] != LF && b[offset] != CR) {
+ offset++;
+ remaining--;
+ }
+ // either end of buffer or a line separator char
+ int blockLength = offset - blockStartOffset;
+ if (blockLength > 0) {
+ bufferInfo.buffer.write(b, blockStartOffset, blockLength);
+ }
+ while (remaining > 0 && (b[offset] == LF || b[offset] == CR)) {
+ write(b[offset]);
+ offset++;
+ remaining--;
+ }
+ blockStartOffset = offset;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Diagnostics.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Diagnostics.java
new file mode 100644
index 00000000..6389f6ee
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Diagnostics.java
@@ -0,0 +1,715 @@
+/*
+ * 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.tools.ant;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.Calendar;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.TimeZone;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+
+import org.apache.tools.ant.launch.Launcher;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.ProxySetup;
+import org.xml.sax.XMLReader;
+
+/**
+ * A little diagnostic helper that output some information that may help
+ * in support. It should quickly give correct information about the
+ * jar existing in ant.home/lib and the jar versions...
+ *
+ * @since Ant 1.5
+ */
+public final class Diagnostics {
+
+ /** the version number for java 1.5 returned from JavaEnvUtils */
+ private static final int JAVA_1_5_NUMBER = 15;
+
+ /**
+ * value for which a difference between clock and temp file time triggers
+ * a warning.
+ * {@value}
+ */
+ private static final int BIG_DRIFT_LIMIT = 10000;
+
+ /**
+ * How big a test file to write.
+ * {@value}
+ */
+ private static final int TEST_FILE_SIZE = 32;
+ private static final int KILOBYTE = 1024;
+ private static final int SECONDS_PER_MILLISECOND = 1000;
+ private static final int SECONDS_PER_MINUTE = 60;
+ private static final int MINUTES_PER_HOUR = 60;
+
+ /**
+ * The error text when a security manager blocks access to a property.
+ * {@value}
+ */
+ protected static final String ERROR_PROPERTY_ACCESS_BLOCKED
+ = "Access to this property blocked by a security manager";
+
+ /** utility class */
+ private Diagnostics() {
+ // hidden constructor
+ }
+
+ /**
+ * Doesn't do anything.
+ * @deprecated Obsolete since Ant 1.8.2
+ * @return <tt>true</tt>
+ */
+ public static boolean isOptionalAvailable() {
+ return true;
+ }
+
+ /**
+ * Doesn't do anything.
+ * @deprecated Obsolete since Ant 1.8.2
+ */
+ public static void validateVersion() throws BuildException {
+ }
+
+ /**
+ * return the list of jar files existing in ANT_HOME/lib
+ * and that must have been picked up by Ant script.
+ * @return the list of jar files existing in ant.home/lib or
+ * <tt>null</tt> if an error occurs.
+ */
+ public static File[] listLibraries() {
+ String home = System.getProperty(MagicNames.ANT_HOME);
+ if (home == null) {
+ return null;
+ }
+ File libDir = new File(home, "lib");
+ return listJarFiles(libDir);
+
+ }
+
+ /**
+ * get a list of all JAR files in a directory
+ * @param libDir directory
+ * @return array of files (or null for no such directory)
+ */
+ private static File[] listJarFiles(File libDir) {
+ FilenameFilter filter = new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.endsWith(".jar");
+ }
+ };
+ File[] files = libDir.listFiles(filter);
+ return files;
+ }
+
+ /**
+ * main entry point for command line
+ * @param args command line arguments.
+ */
+ public static void main(String[] args) {
+ doReport(System.out);
+ }
+
+ /**
+ * Helper method to get the implementation version.
+ * @param clazz the class to get the information from.
+ * @return null if there is no package or implementation version.
+ * '?.?' for JDK 1.0 or 1.1.
+ */
+ private static String getImplementationVersion(Class<?> clazz) {
+ return clazz.getPackage().getImplementationVersion();
+ }
+
+ /**
+ * Helper method to get the location.
+ * @param clazz the class to get the information from.
+ * @since Ant 1.8.0
+ */
+ private static URL getClassLocation(Class<?> clazz) {
+ if (clazz.getProtectionDomain().getCodeSource() == null) {
+ return null;
+ }
+ return clazz.getProtectionDomain().getCodeSource().getLocation();
+ }
+
+ /**
+ * what parser are we using.
+ * @return the classname of the parser
+ */
+ private static String getXMLParserName() {
+ SAXParser saxParser = getSAXParser();
+ if (saxParser == null) {
+ return "Could not create an XML Parser";
+ }
+ // check to what is in the classname
+ String saxParserName = saxParser.getClass().getName();
+ return saxParserName;
+ }
+
+ /**
+ * what parser are we using.
+ * @return the classname of the parser
+ */
+ private static String getXSLTProcessorName() {
+ Transformer transformer = getXSLTProcessor();
+ if (transformer == null) {
+ return "Could not create an XSLT Processor";
+ }
+ // check to what is in the classname
+ String processorName = transformer.getClass().getName();
+ return processorName;
+ }
+
+ /**
+ * Create a JAXP SAXParser
+ * @return parser or null for trouble
+ */
+ private static SAXParser getSAXParser() {
+ SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+ if (saxParserFactory == null) {
+ return null;
+ }
+ SAXParser saxParser = null;
+ try {
+ saxParser = saxParserFactory.newSAXParser();
+ } catch (Exception e) {
+ // ignore
+ ignoreThrowable(e);
+ }
+ return saxParser;
+ }
+
+ /**
+ * Create a JAXP XSLT Transformer
+ * @return parser or null for trouble
+ */
+ private static Transformer getXSLTProcessor() {
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ if (transformerFactory == null) {
+ return null;
+ }
+ Transformer transformer = null;
+ try {
+ transformer = transformerFactory.newTransformer();
+ } catch (Exception e) {
+ // ignore
+ ignoreThrowable(e);
+ }
+ return transformer;
+ }
+
+ /**
+ * get the location of the parser
+ * @return path or null for trouble in tracking it down
+ */
+ private static String getXMLParserLocation() {
+ SAXParser saxParser = getSAXParser();
+ if (saxParser == null) {
+ return null;
+ }
+ URL location = getClassLocation(saxParser.getClass());
+ return location != null ? location.toString() : null;
+ }
+
+ private static String getNamespaceParserName() {
+ try {
+ XMLReader reader = JAXPUtils.getNamespaceXMLReader();
+ return reader.getClass().getName();
+ } catch (BuildException e) {
+ //ignore
+ ignoreThrowable(e);
+ return null;
+ }
+ }
+
+ private static String getNamespaceParserLocation() {
+ try {
+ XMLReader reader = JAXPUtils.getNamespaceXMLReader();
+ URL location = getClassLocation(reader.getClass());
+ return location != null ? location.toString() : null;
+ } catch (BuildException e) {
+ //ignore
+ ignoreThrowable(e);
+ return null;
+ }
+ }
+
+ /**
+ * get the location of the parser
+ * @return path or null for trouble in tracking it down
+ */
+ private static String getXSLTProcessorLocation() {
+ Transformer transformer = getXSLTProcessor();
+ if (transformer == null) {
+ return null;
+ }
+ URL location = getClassLocation(transformer.getClass());
+ return location != null ? location.toString() : null;
+ }
+
+ /**
+ * ignore exceptions. This is to allow future
+ * implementations to log at a verbose level
+ * @param thrown
+ */
+ private static void ignoreThrowable(Throwable thrown) {
+ }
+
+
+ /**
+ * Print a report to the given stream.
+ * @param out the stream to print the report to.
+ */
+ public static void doReport(PrintStream out) {
+ doReport(out, Project.MSG_INFO);
+ }
+
+ /**
+ * Print a report to the given stream.
+ * @param out the stream to print the report to.
+ * @param logLevel denotes the level of detail requested as one of
+ * Project's MSG_* constants.
+ */
+ public static void doReport(PrintStream out, int logLevel) {
+ out.println("------- Ant diagnostics report -------");
+ out.println(Main.getAntVersion());
+ header(out, "Implementation Version");
+
+ out.println("core tasks : " + getImplementationVersion(Main.class)
+ + " in " + getClassLocation(Main.class));
+
+ header(out, "ANT PROPERTIES");
+ doReportAntProperties(out);
+
+ header(out, "ANT_HOME/lib jar listing");
+ doReportAntHomeLibraries(out);
+
+ header(out, "USER_HOME/.ant/lib jar listing");
+ doReportUserHomeLibraries(out);
+
+ header(out, "Tasks availability");
+ doReportTasksAvailability(out);
+
+ header(out, "org.apache.env.Which diagnostics");
+ doReportWhich(out);
+
+ header(out, "XML Parser information");
+ doReportParserInfo(out);
+
+ header(out, "XSLT Processor information");
+ doReportXSLTProcessorInfo(out);
+
+ header(out, "System properties");
+ doReportSystemProperties(out);
+
+ header(out, "Temp dir");
+ doReportTempDir(out);
+
+ header(out, "Locale information");
+ doReportLocale(out);
+
+ header(out, "Proxy information");
+ doReportProxy(out);
+
+ out.println();
+ }
+
+ private static void header(PrintStream out, String section) {
+ out.println();
+ out.println("-------------------------------------------");
+ out.print(" ");
+ out.println(section);
+ out.println("-------------------------------------------");
+ }
+
+ /**
+ * Report a listing of system properties existing in the current vm.
+ * @param out the stream to print the properties to.
+ */
+ private static void doReportSystemProperties(PrintStream out) {
+ Properties sysprops = null;
+ try {
+ sysprops = System.getProperties();
+ } catch (SecurityException e) {
+ ignoreThrowable(e);
+ out.println("Access to System.getProperties() blocked " + "by a security manager");
+ return;
+ }
+ for (Enumeration<?> keys = sysprops.propertyNames();
+ keys.hasMoreElements();) {
+ String key = (String) keys.nextElement();
+ String value = getProperty(key);
+ out.println(key + " : " + value);
+ }
+ }
+
+ /**
+ * Get the value of a system property. If a security manager
+ * blocks access to a property it fills the result in with an error
+ * @param key
+ * @return the system property's value or error text
+ * @see #ERROR_PROPERTY_ACCESS_BLOCKED
+ */
+ private static String getProperty(String key) {
+ String value;
+ try {
+ value = System.getProperty(key);
+ } catch (SecurityException e) {
+ value = ERROR_PROPERTY_ACCESS_BLOCKED;
+ }
+ return value;
+ }
+
+ /**
+ * Report the content of ANT_HOME/lib directory
+ * @param out the stream to print the content to
+ */
+ private static void doReportAntProperties(PrintStream out) {
+ Project p = new Project();
+ p.initProperties();
+ out.println(MagicNames.ANT_VERSION + ": " + p.getProperty(MagicNames.ANT_VERSION));
+ out.println(MagicNames.ANT_JAVA_VERSION + ": "
+ + p.getProperty(MagicNames.ANT_JAVA_VERSION));
+ out.println("Is this the Apache Harmony VM? "
+ + (JavaEnvUtils.isApacheHarmony() ? "yes" : "no"));
+ out.println("Is this the Kaffe VM? "
+ + (JavaEnvUtils.isKaffe() ? "yes" : "no"));
+ out.println("Is this gij/gcj? "
+ + (JavaEnvUtils.isGij() ? "yes" : "no"));
+ out.println(MagicNames.ANT_LIB + ": " + p.getProperty(MagicNames.ANT_LIB));
+ out.println(MagicNames.ANT_HOME + ": " + p.getProperty(MagicNames.ANT_HOME));
+ }
+
+ /**
+ * Report the content of ANT_HOME/lib directory
+ * @param out the stream to print the content to
+ */
+ private static void doReportAntHomeLibraries(PrintStream out) {
+ out.println(MagicNames.ANT_HOME + ": " + System.getProperty(MagicNames.ANT_HOME));
+ File[] libs = listLibraries();
+ printLibraries(libs, out);
+ }
+
+ /**
+ * Report the content of ~/.ant/lib directory
+ *
+ * @param out the stream to print the content to
+ */
+ private static void doReportUserHomeLibraries(PrintStream out) {
+ String home = System.getProperty(Launcher.USER_HOMEDIR);
+ out.println("user.home: " + home);
+ File libDir = new File(home, Launcher.USER_LIBDIR);
+ File[] libs = listJarFiles(libDir);
+ printLibraries(libs, out);
+ }
+
+ /**
+ * list the libraries
+ * @param libs array of libraries (can be null)
+ * @param out output stream
+ */
+ private static void printLibraries(File[] libs, PrintStream out) {
+ if (libs == null) {
+ out.println("No such directory.");
+ return;
+ }
+ for (int i = 0; i < libs.length; i++) {
+ out.println(libs[i].getName() + " (" + libs[i].length() + " bytes)");
+ }
+ }
+
+
+ /**
+ * Call org.apache.env.Which if available
+ * @param out the stream to print the content to.
+ */
+ private static void doReportWhich(PrintStream out) {
+ Throwable error = null;
+ try {
+ Class<?> which = Class.forName("org.apache.env.Which");
+ Method method = which.getMethod(
+ "main", new Class[] {String[].class});
+ method.invoke(null, new Object[]{new String[]{}});
+ } catch (ClassNotFoundException e) {
+ out.println("Not available.");
+ out.println("Download it at http://xml.apache.org/commons/");
+ } catch (InvocationTargetException e) {
+ error = e.getTargetException() == null ? e : e.getTargetException();
+ } catch (Throwable e) {
+ error = e;
+ }
+ // report error if something weird happens...this is diagnostic.
+ if (error != null) {
+ out.println("Error while running org.apache.env.Which");
+ error.printStackTrace();
+ }
+ }
+
+ /**
+ * Create a report about non-available tasks that are defined in the
+ * mapping but could not be found via lookup. It might generally happen
+ * because Ant requires multiple libraries to compile and one of them
+ * was missing when compiling Ant.
+ * @param out the stream to print the tasks report to
+ * <tt>null</tt> for a missing stream (ie mapping).
+ */
+ private static void doReportTasksAvailability(PrintStream out) {
+ InputStream is = Main.class.getResourceAsStream(
+ MagicNames.TASKDEF_PROPERTIES_RESOURCE);
+ if (is == null) {
+ out.println("None available");
+ } else {
+ Properties props = new Properties();
+ try {
+ props.load(is);
+ for (Enumeration<?> keys = props.keys(); keys.hasMoreElements();) {
+ String key = (String) keys.nextElement();
+ String classname = props.getProperty(key);
+ try {
+ Class.forName(classname);
+ props.remove(key);
+ } catch (ClassNotFoundException e) {
+ out.println(key + " : Not Available "
+ + "(the implementation class is not present)");
+ } catch (NoClassDefFoundError e) {
+ String pkg = e.getMessage().replace('/', '.');
+ out.println(key + " : Missing dependency " + pkg);
+ } catch (LinkageError e) {
+ out.println(key + " : Initialization error");
+ }
+ }
+ if (props.size() == 0) {
+ out.println("All defined tasks are available");
+ } else {
+ out.println("A task being missing/unavailable should only "
+ + "matter if you are trying to use it");
+ }
+ } catch (IOException e) {
+ out.println(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * tell the user about the XML parser
+ * @param out
+ */
+ private static void doReportParserInfo(PrintStream out) {
+ String parserName = getXMLParserName();
+ String parserLocation = getXMLParserLocation();
+ printParserInfo(out, "XML Parser", parserName, parserLocation);
+ printParserInfo(out, "Namespace-aware parser", getNamespaceParserName(),
+ getNamespaceParserLocation());
+ }
+
+ /**
+ * tell the user about the XSLT processor
+ * @param out
+ */
+ private static void doReportXSLTProcessorInfo(PrintStream out) {
+ String processorName = getXSLTProcessorName();
+ String processorLocation = getXSLTProcessorLocation();
+ printParserInfo(out, "XSLT Processor", processorName, processorLocation);
+ }
+
+ private static void printParserInfo(PrintStream out, String parserType, String parserName,
+ String parserLocation) {
+ if (parserName == null) {
+ parserName = "unknown";
+ }
+ if (parserLocation == null) {
+ parserLocation = "unknown";
+ }
+ out.println(parserType + " : " + parserName);
+ out.println(parserType + " Location: " + parserLocation);
+ }
+
+ /**
+ * try and create a temp file in our temp dir; this
+ * checks that it has space and access.
+ * We also do some clock reporting.
+ * @param out
+ */
+ private static void doReportTempDir(PrintStream out) {
+ String tempdir = System.getProperty("java.io.tmpdir");
+ if (tempdir == null) {
+ out.println("Warning: java.io.tmpdir is undefined");
+ return;
+ }
+ out.println("Temp dir is " + tempdir);
+ File tempDirectory = new File(tempdir);
+ if (!tempDirectory.exists()) {
+ out.println("Warning, java.io.tmpdir directory does not exist: " + tempdir);
+ return;
+ }
+ //create the file
+ long now = System.currentTimeMillis();
+ File tempFile = null;
+ FileOutputStream fileout = null;
+ FileInputStream filein = null;
+ try {
+ tempFile = File.createTempFile("diag", "txt", tempDirectory);
+ //do some writing to it
+ fileout = new FileOutputStream(tempFile);
+ byte[] buffer = new byte[KILOBYTE];
+ for (int i = 0; i < TEST_FILE_SIZE; i++) {
+ fileout.write(buffer);
+ }
+ fileout.close();
+ fileout = null;
+
+ // read to make sure the file has been written completely
+ Thread.sleep(1000);
+ filein = new FileInputStream(tempFile);
+ int total = 0;
+ int read = 0;
+ while ((read = filein.read(buffer, 0, KILOBYTE)) > 0) {
+ total += read;
+ }
+ filein.close();
+ filein = null;
+
+ long filetime = tempFile.lastModified();
+ long drift = filetime - now;
+ tempFile.delete();
+
+ out.print("Temp dir is writeable");
+ if (total != TEST_FILE_SIZE * KILOBYTE) {
+ out.println(", but seems to be full. Wrote "
+ + (TEST_FILE_SIZE * KILOBYTE)
+ + "but could only read " + total + " bytes.");
+ } else {
+ out.println();
+ }
+
+ out.println("Temp dir alignment with system clock is " + drift + " ms");
+ if (Math.abs(drift) > BIG_DRIFT_LIMIT) {
+ out.println("Warning: big clock drift -maybe a network filesystem");
+ }
+ } catch (IOException e) {
+ ignoreThrowable(e);
+ out.println("Failed to create a temporary file in the temp dir " + tempdir);
+ out.println("File " + tempFile + " could not be created/written to");
+ } catch (InterruptedException e) {
+ ignoreThrowable(e);
+ out.println("Failed to check whether tempdir is writable");
+ } finally {
+ FileUtils.close(fileout);
+ FileUtils.close(filein);
+ if (tempFile != null && tempFile.exists()) {
+ tempFile.delete();
+ }
+ }
+ }
+
+ /**
+ * Report locale information
+ * @param out stream to print to
+ */
+ private static void doReportLocale(PrintStream out) {
+ //calendar stuff.
+ Calendar cal = Calendar.getInstance();
+ TimeZone tz = cal.getTimeZone();
+ out.println("Timezone "
+ + tz.getDisplayName()
+ + " offset="
+ + tz.getOffset(cal.get(Calendar.ERA), cal.get(Calendar.YEAR), cal
+ .get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), cal
+ .get(Calendar.DAY_OF_WEEK), ((cal.get(Calendar.HOUR_OF_DAY)
+ * MINUTES_PER_HOUR + cal.get(Calendar.MINUTE))
+ * SECONDS_PER_MINUTE + cal.get(Calendar.SECOND))
+ * SECONDS_PER_MILLISECOND + cal.get(Calendar.MILLISECOND)));
+ }
+
+ /**
+ * print a property name="value" pair if the property is set;
+ * print nothing if it is null
+ * @param out stream to print on
+ * @param key property name
+ */
+ private static void printProperty(PrintStream out, String key) {
+ String value = getProperty(key);
+ if (value != null) {
+ out.print(key);
+ out.print(" = ");
+ out.print('"');
+ out.print(value);
+ out.println('"');
+ }
+ }
+
+ /**
+ * Report proxy information
+ *
+ * @param out stream to print to
+ * @since Ant1.7
+ */
+ private static void doReportProxy(PrintStream out) {
+ printProperty(out, ProxySetup.HTTP_PROXY_HOST);
+ printProperty(out, ProxySetup.HTTP_PROXY_PORT);
+ printProperty(out, ProxySetup.HTTP_PROXY_USERNAME);
+ printProperty(out, ProxySetup.HTTP_PROXY_PASSWORD);
+ printProperty(out, ProxySetup.HTTP_NON_PROXY_HOSTS);
+ printProperty(out, ProxySetup.HTTPS_PROXY_HOST);
+ printProperty(out, ProxySetup.HTTPS_PROXY_PORT);
+ printProperty(out, ProxySetup.HTTPS_NON_PROXY_HOSTS);
+ printProperty(out, ProxySetup.FTP_PROXY_HOST);
+ printProperty(out, ProxySetup.FTP_PROXY_PORT);
+ printProperty(out, ProxySetup.FTP_NON_PROXY_HOSTS);
+ printProperty(out, ProxySetup.SOCKS_PROXY_HOST);
+ printProperty(out, ProxySetup.SOCKS_PROXY_PORT);
+ printProperty(out, ProxySetup.SOCKS_PROXY_USERNAME);
+ printProperty(out, ProxySetup.SOCKS_PROXY_PASSWORD);
+
+ if (JavaEnvUtils.getJavaVersionNumber() < JAVA_1_5_NUMBER) {
+ return;
+ }
+ printProperty(out, ProxySetup.USE_SYSTEM_PROXIES);
+ final String proxyDiagClassname = "org.apache.tools.ant.util.java15.ProxyDiagnostics";
+ try {
+ Class<?> proxyDiagClass = Class.forName(proxyDiagClassname);
+ Object instance = proxyDiagClass.newInstance();
+ out.println("Java1.5+ proxy settings:");
+ out.println(instance.toString());
+ } catch (ClassNotFoundException e) {
+ //not included, do nothing
+ } catch (IllegalAccessException e) {
+ //not included, do nothing
+ } catch (InstantiationException e) {
+ //not included, do nothing
+ } catch (NoClassDefFoundError e) {
+ // not included, to nothing
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DirectoryScanner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DirectoryScanner.java
new file mode 100644
index 00000000..709779a7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DirectoryScanner.java
@@ -0,0 +1,1900 @@
+/*
+ * 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.tools.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.SelectorScanner;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+import org.apache.tools.ant.types.selectors.TokenizedPath;
+import org.apache.tools.ant.types.selectors.TokenizedPattern;
+import org.apache.tools.ant.util.CollectionUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.SymbolicLinkUtils;
+import org.apache.tools.ant.util.VectorSet;
+
+/**
+ * Class for scanning a directory for files/directories which match certain
+ * criteria.
+ * <p>
+ * These criteria consist of selectors and patterns which have been specified.
+ * With the selectors you can select which files you want to have included.
+ * Files which are not selected are excluded. With patterns you can include
+ * or exclude files based on their filename.
+ * <p>
+ * The idea is simple. A given directory is recursively scanned for all files
+ * and directories. Each file/directory is matched against a set of selectors,
+ * including special support for matching against filenames with include and
+ * and exclude patterns. Only files/directories which match at least one
+ * pattern of the include pattern list or other file selector, and don't match
+ * any pattern of the exclude pattern list or fail to match against a required
+ * selector will be placed in the list of files/directories found.
+ * <p>
+ * When no list of include patterns is supplied, "**" will be used, which
+ * means that everything will be matched. When no list of exclude patterns is
+ * supplied, an empty list is used, such that nothing will be excluded. When
+ * no selectors are supplied, none are applied.
+ * <p>
+ * The filename pattern matching is done as follows:
+ * The name to be matched is split up in path segments. A path segment is the
+ * name of a directory or file, which is bounded by
+ * <code>File.separator</code> ('/' under UNIX, '\' under Windows).
+ * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
+ * "def","ghi" and "xyz.java".
+ * The same is done for the pattern against which should be matched.
+ * <p>
+ * The segments of the name and the pattern are then matched against each
+ * other. When '**' is used for a path segment in the pattern, it matches
+ * zero or more path segments of the name.
+ * <p>
+ * There is a special case regarding the use of <code>File.separator</code>s
+ * at the beginning of the pattern and the string to match:<br>
+ * When a pattern starts with a <code>File.separator</code>, the string
+ * to match must also start with a <code>File.separator</code>.
+ * When a pattern does not start with a <code>File.separator</code>, the
+ * string to match may not start with a <code>File.separator</code>.
+ * When one of these rules is not obeyed, the string will not
+ * match.
+ * <p>
+ * When a name path segment is matched against a pattern path segment, the
+ * following special characters can be used:<br>
+ * '*' matches zero or more characters<br>
+ * '?' matches one character.
+ * <p>
+ * Examples:
+ * <p>
+ * "**\*.class" matches all .class files/dirs in a directory tree.
+ * <p>
+ * "test\a??.java" matches all files/dirs which start with an 'a', then two
+ * more characters and then ".java", in a directory called test.
+ * <p>
+ * "**" matches everything in a directory tree.
+ * <p>
+ * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where
+ * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
+ * <p>
+ * Case sensitivity may be turned off if necessary. By default, it is
+ * turned on.
+ * <p>
+ * Example of usage:
+ * <pre>
+ * String[] includes = {"**\\*.class"};
+ * String[] excludes = {"modules\\*\\**"};
+ * ds.setIncludes(includes);
+ * ds.setExcludes(excludes);
+ * ds.setBasedir(new File("test"));
+ * ds.setCaseSensitive(true);
+ * ds.scan();
+ *
+ * System.out.println("FILES:");
+ * String[] files = ds.getIncludedFiles();
+ * for (int i = 0; i &lt; files.length; i++) {
+ * System.out.println(files[i]);
+ * }
+ * </pre>
+ * This will scan a directory called test for .class files, but excludes all
+ * files in all proper subdirectories of a directory called "modules".
+ *
+ */
+public class DirectoryScanner
+ implements FileScanner, SelectorScanner, ResourceFactory {
+
+ /** Is OpenVMS the operating system we're running on? */
+ private static final boolean ON_VMS = Os.isFamily("openvms");
+
+ /**
+ * Patterns which should be excluded by default.
+ *
+ * <p>Note that you can now add patterns to the list of default
+ * excludes. Added patterns will not become part of this array
+ * that has only been kept around for backwards compatibility
+ * reasons.</p>
+ *
+ * @deprecated since 1.6.x.
+ * Use the {@link #getDefaultExcludes getDefaultExcludes}
+ * method instead.
+ */
+ @Deprecated
+ protected static final String[] DEFAULTEXCLUDES = {
+ // Miscellaneous typical temporary files
+ SelectorUtils.DEEP_TREE_MATCH + "/*~",
+ SelectorUtils.DEEP_TREE_MATCH + "/#*#",
+ SelectorUtils.DEEP_TREE_MATCH + "/.#*",
+ SelectorUtils.DEEP_TREE_MATCH + "/%*%",
+ SelectorUtils.DEEP_TREE_MATCH + "/._*",
+
+ // CVS
+ SelectorUtils.DEEP_TREE_MATCH + "/CVS",
+ SelectorUtils.DEEP_TREE_MATCH + "/CVS/" + SelectorUtils.DEEP_TREE_MATCH,
+ SelectorUtils.DEEP_TREE_MATCH + "/.cvsignore",
+
+ // SCCS
+ SelectorUtils.DEEP_TREE_MATCH + "/SCCS",
+ SelectorUtils.DEEP_TREE_MATCH + "/SCCS/" + SelectorUtils.DEEP_TREE_MATCH,
+
+ // Visual SourceSafe
+ SelectorUtils.DEEP_TREE_MATCH + "/vssver.scc",
+
+ // Subversion
+ SelectorUtils.DEEP_TREE_MATCH + "/.svn",
+ SelectorUtils.DEEP_TREE_MATCH + "/.svn/" + SelectorUtils.DEEP_TREE_MATCH,
+
+ // Git
+ SelectorUtils.DEEP_TREE_MATCH + "/.git",
+ SelectorUtils.DEEP_TREE_MATCH + "/.git/" + SelectorUtils.DEEP_TREE_MATCH,
+ SelectorUtils.DEEP_TREE_MATCH + "/.gitattributes",
+ SelectorUtils.DEEP_TREE_MATCH + "/.gitignore",
+ SelectorUtils.DEEP_TREE_MATCH + "/.gitmodules",
+
+ // Mercurial
+ SelectorUtils.DEEP_TREE_MATCH + "/.hg",
+ SelectorUtils.DEEP_TREE_MATCH + "/.hg/" + SelectorUtils.DEEP_TREE_MATCH,
+ SelectorUtils.DEEP_TREE_MATCH + "/.hgignore",
+ SelectorUtils.DEEP_TREE_MATCH + "/.hgsub",
+ SelectorUtils.DEEP_TREE_MATCH + "/.hgsubstate",
+ SelectorUtils.DEEP_TREE_MATCH + "/.hgtags",
+
+ // Bazaar
+ SelectorUtils.DEEP_TREE_MATCH + "/.bzr",
+ SelectorUtils.DEEP_TREE_MATCH + "/.bzr/" + SelectorUtils.DEEP_TREE_MATCH,
+ SelectorUtils.DEEP_TREE_MATCH + "/.bzrignore",
+
+ // Mac
+ SelectorUtils.DEEP_TREE_MATCH + "/.DS_Store"
+ };
+
+ /**
+ * default value for {@link #maxLevelsOfSymlinks maxLevelsOfSymlinks}
+ * @since Ant 1.8.0
+ */
+ public static final int MAX_LEVELS_OF_SYMLINKS = 5;
+ /**
+ * The end of the exception message if something that should be
+ * there doesn't exist.
+ */
+ public static final String DOES_NOT_EXIST_POSTFIX = " does not exist.";
+
+ /** Helper. */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** Helper. */
+ private static final SymbolicLinkUtils SYMLINK_UTILS =
+ SymbolicLinkUtils.getSymbolicLinkUtils();
+
+ /**
+ * Patterns which should be excluded by default.
+ *
+ * @see #addDefaultExcludes()
+ */
+ private static final Set<String> defaultExcludes = new HashSet<String>();
+ static {
+ resetDefaultExcludes();
+ }
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /** The base directory to be scanned. */
+ protected File basedir;
+
+ /** The patterns for the files to be included. */
+ protected String[] includes;
+
+ /** The patterns for the files to be excluded. */
+ protected String[] excludes;
+
+ /** Selectors that will filter which files are in our candidate list. */
+ protected FileSelector[] selectors = null;
+
+ /**
+ * The files which matched at least one include and no excludes
+ * and were selected.
+ */
+ protected Vector<String> filesIncluded;
+
+ /** The files which did not match any includes or selectors. */
+ protected Vector<String> filesNotIncluded;
+
+ /**
+ * The files which matched at least one include and at least
+ * one exclude.
+ */
+ protected Vector<String> filesExcluded;
+
+ /**
+ * The directories which matched at least one include and no excludes
+ * and were selected.
+ */
+ protected Vector<String> dirsIncluded;
+
+ /** The directories which were found and did not match any includes. */
+ protected Vector<String> dirsNotIncluded;
+
+ /**
+ * The directories which matched at least one include and at least one
+ * exclude.
+ */
+ protected Vector<String> dirsExcluded;
+
+ /**
+ * The files which matched at least one include and no excludes and
+ * which a selector discarded.
+ */
+ protected Vector<String> filesDeselected;
+
+ /**
+ * The directories which matched at least one include and no excludes
+ * but which a selector discarded.
+ */
+ protected Vector<String> dirsDeselected;
+
+ /** Whether or not our results were built by a slow scan. */
+ protected boolean haveSlowResults = false;
+
+ /**
+ * Whether or not the file system should be treated as a case sensitive
+ * one.
+ */
+ protected boolean isCaseSensitive = true;
+
+ /**
+ * Whether a missing base directory is an error.
+ * @since Ant 1.7.1
+ */
+ protected boolean errorOnMissingDir = true;
+
+ /**
+ * Whether or not symbolic links should be followed.
+ *
+ * @since Ant 1.5
+ */
+ private boolean followSymlinks = true;
+
+ /** Whether or not everything tested so far has been included. */
+ protected boolean everythingIncluded = true;
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * List of all scanned directories.
+ *
+ * @since Ant 1.6
+ */
+ private final Set<String> scannedDirs = new HashSet<String>();
+
+ /**
+ * Map of all include patterns that are full file names and don't
+ * contain any wildcards.
+ *
+ * <p>Maps pattern string to TokenizedPath.</p>
+ *
+ * <p>If this instance is not case sensitive, the file names get
+ * turned to upper case.</p>
+ *
+ * <p>Gets lazily initialized on the first invocation of
+ * isIncluded or isExcluded and cleared at the end of the scan
+ * method (cleared in clearCaches, actually).</p>
+ *
+ * @since Ant 1.8.0
+ */
+ private final Map<String, TokenizedPath> includeNonPatterns = new HashMap<String, TokenizedPath>();
+
+ /**
+ * Map of all exclude patterns that are full file names and don't
+ * contain any wildcards.
+ *
+ * <p>Maps pattern string to TokenizedPath.</p>
+ *
+ * <p>If this instance is not case sensitive, the file names get
+ * turned to upper case.</p>
+ *
+ * <p>Gets lazily initialized on the first invocation of
+ * isIncluded or isExcluded and cleared at the end of the scan
+ * method (cleared in clearCaches, actually).</p>
+ *
+ * @since Ant 1.8.0
+ */
+ private final Map<String, TokenizedPath> excludeNonPatterns = new HashMap<String, TokenizedPath>();
+
+ /**
+ * Array of all include patterns that contain wildcards.
+ *
+ * <p>Gets lazily initialized on the first invocation of
+ * isIncluded or isExcluded and cleared at the end of the scan
+ * method (cleared in clearCaches, actually).</p>
+ */
+ private TokenizedPattern[] includePatterns;
+
+ /**
+ * Array of all exclude patterns that contain wildcards.
+ *
+ * <p>Gets lazily initialized on the first invocation of
+ * isIncluded or isExcluded and cleared at the end of the scan
+ * method (cleared in clearCaches, actually).</p>
+ */
+ private TokenizedPattern[] excludePatterns;
+
+ /**
+ * Have the non-pattern sets and pattern arrays for in- and
+ * excludes been initialized?
+ *
+ * @since Ant 1.6.3
+ */
+ private boolean areNonPatternSetsReady = false;
+
+ /**
+ * Scanning flag.
+ *
+ * @since Ant 1.6.3
+ */
+ private boolean scanning = false;
+
+ /**
+ * Scanning lock.
+ *
+ * @since Ant 1.6.3
+ */
+ private final Object scanLock = new Object();
+
+ /**
+ * Slow scanning flag.
+ *
+ * @since Ant 1.6.3
+ */
+ private boolean slowScanning = false;
+
+ /**
+ * Slow scanning lock.
+ *
+ * @since Ant 1.6.3
+ */
+ private final Object slowScanLock = new Object();
+
+ /**
+ * Exception thrown during scan.
+ *
+ * @since Ant 1.6.3
+ */
+ private IllegalStateException illegal = null;
+
+ /**
+ * The maximum number of times a symbolic link may be followed
+ * during a scan.
+ *
+ * @since Ant 1.8.0
+ */
+ private int maxLevelsOfSymlinks = MAX_LEVELS_OF_SYMLINKS;
+
+
+ /**
+ * Absolute paths of all symlinks that haven't been followed but
+ * would have been if followsymlinks had been true or
+ * maxLevelsOfSymlinks had been higher.
+ *
+ * @since Ant 1.8.0
+ */
+ private final Set<String> notFollowedSymlinks = new HashSet<String>();
+
+ /**
+ * Sole constructor.
+ */
+ public DirectoryScanner() {
+ }
+
+ /**
+ * Test whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ * <p>
+ * This is not a general purpose test and should only be used if you
+ * can live with false positives. For example, <code>pattern=**\a</code>
+ * and <code>str=b</code> will yield <code>true</code>.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * <code>null</code>.
+ * @param str The path to match, as a String. Must not be
+ * <code>null</code>.
+ *
+ * @return whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ */
+ protected static boolean matchPatternStart(final String pattern, final String str) {
+ return SelectorUtils.matchPatternStart(pattern, str);
+ }
+
+ /**
+ * Test whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ * <p>
+ * This is not a general purpose test and should only be used if you
+ * can live with false positives. For example, <code>pattern=**\a</code>
+ * and <code>str=b</code> will yield <code>true</code>.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * <code>null</code>.
+ * @param str The path to match, as a String. Must not be
+ * <code>null</code>.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ *
+ * @return whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ */
+ protected static boolean matchPatternStart(final String pattern, final String str,
+ final boolean isCaseSensitive) {
+ return SelectorUtils.matchPatternStart(pattern, str, isCaseSensitive);
+ }
+
+ /**
+ * Test whether or not a given path matches a given pattern.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * <code>null</code>.
+ * @param str The path to match, as a String. Must not be
+ * <code>null</code>.
+ *
+ * @return <code>true</code> if the pattern matches against the string,
+ * or <code>false</code> otherwise.
+ */
+ protected static boolean matchPath(final String pattern, final String str) {
+ return SelectorUtils.matchPath(pattern, str);
+ }
+
+ /**
+ * Test whether or not a given path matches a given pattern.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * <code>null</code>.
+ * @param str The path to match, as a String. Must not be
+ * <code>null</code>.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ *
+ * @return <code>true</code> if the pattern matches against the string,
+ * or <code>false</code> otherwise.
+ */
+ protected static boolean matchPath(final String pattern, final String str,
+ final boolean isCaseSensitive) {
+ return SelectorUtils.matchPath(pattern, str, isCaseSensitive);
+ }
+
+ /**
+ * Test whether or not a string matches against a pattern.
+ * The pattern may contain two special characters:<br>
+ * '*' means zero or more characters<br>
+ * '?' means one and only one character
+ *
+ * @param pattern The pattern to match against.
+ * Must not be <code>null</code>.
+ * @param str The string which must be matched against the pattern.
+ * Must not be <code>null</code>.
+ *
+ * @return <code>true</code> if the string matches against the pattern,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean match(final String pattern, final String str) {
+ return SelectorUtils.match(pattern, str);
+ }
+
+ /**
+ * Test whether or not a string matches against a pattern.
+ * The pattern may contain two special characters:<br>
+ * '*' means zero or more characters<br>
+ * '?' means one and only one character
+ *
+ * @param pattern The pattern to match against.
+ * Must not be <code>null</code>.
+ * @param str The string which must be matched against the pattern.
+ * Must not be <code>null</code>.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ *
+ *
+ * @return <code>true</code> if the string matches against the pattern,
+ * or <code>false</code> otherwise.
+ */
+ protected static boolean match(final String pattern, final String str,
+ final boolean isCaseSensitive) {
+ return SelectorUtils.match(pattern, str, isCaseSensitive);
+ }
+
+
+ /**
+ * Get the list of patterns that should be excluded by default.
+ *
+ * @return An array of <code>String</code> based on the current
+ * contents of the <code>defaultExcludes</code>
+ * <code>Set</code>.
+ *
+ * @since Ant 1.6
+ */
+ public static String[] getDefaultExcludes() {
+ synchronized (defaultExcludes) {
+ return defaultExcludes.toArray(new String[defaultExcludes
+ .size()]);
+ }
+ }
+
+ /**
+ * Add a pattern to the default excludes unless it is already a
+ * default exclude.
+ *
+ * @param s A string to add as an exclude pattern.
+ * @return <code>true</code> if the string was added;
+ * <code>false</code> if it already existed.
+ *
+ * @since Ant 1.6
+ */
+ public static boolean addDefaultExclude(final String s) {
+ synchronized (defaultExcludes) {
+ return defaultExcludes.add(s);
+ }
+ }
+
+ /**
+ * Remove a string if it is a default exclude.
+ *
+ * @param s The string to attempt to remove.
+ * @return <code>true</code> if <code>s</code> was a default
+ * exclude (and thus was removed);
+ * <code>false</code> if <code>s</code> was not
+ * in the default excludes list to begin with.
+ *
+ * @since Ant 1.6
+ */
+ public static boolean removeDefaultExclude(final String s) {
+ synchronized (defaultExcludes) {
+ return defaultExcludes.remove(s);
+ }
+ }
+
+ /**
+ * Go back to the hardwired default exclude patterns.
+ *
+ * @since Ant 1.6
+ */
+ public static void resetDefaultExcludes() {
+ synchronized (defaultExcludes) {
+ defaultExcludes.clear();
+ for (int i = 0; i < DEFAULTEXCLUDES.length; i++) {
+ defaultExcludes.add(DEFAULTEXCLUDES[i]);
+ }
+ }
+ }
+
+ /**
+ * Set the base directory to be scanned. This is the directory which is
+ * scanned recursively. All '/' and '\' characters are replaced by
+ * <code>File.separatorChar</code>, so the separator used need not match
+ * <code>File.separatorChar</code>.
+ *
+ * @param basedir The base directory to scan.
+ */
+ public void setBasedir(final String basedir) {
+ setBasedir(basedir == null ? (File) null
+ : new File(basedir.replace('/', File.separatorChar).replace(
+ '\\', File.separatorChar)));
+ }
+
+ /**
+ * Set the base directory to be scanned. This is the directory which is
+ * scanned recursively.
+ *
+ * @param basedir The base directory for scanning.
+ */
+ public synchronized void setBasedir(final File basedir) {
+ this.basedir = basedir;
+ }
+
+ /**
+ * Return the base directory to be scanned.
+ * This is the directory which is scanned recursively.
+ *
+ * @return the base directory to be scanned.
+ */
+ public synchronized File getBasedir() {
+ return basedir;
+ }
+
+ /**
+ * Find out whether include exclude patterns are matched in a
+ * case sensitive way.
+ * @return whether or not the scanning is case sensitive.
+ * @since Ant 1.6
+ */
+ public synchronized boolean isCaseSensitive() {
+ return isCaseSensitive;
+ }
+
+ /**
+ * Set whether or not include and exclude patterns are matched
+ * in a case sensitive way.
+ *
+ * @param isCaseSensitive whether or not the file system should be
+ * regarded as a case sensitive one.
+ */
+ public synchronized void setCaseSensitive(final boolean isCaseSensitive) {
+ this.isCaseSensitive = isCaseSensitive;
+ }
+
+ /**
+ * Sets whether or not a missing base directory is an error
+ *
+ * @param errorOnMissingDir whether or not a missing base directory
+ * is an error
+ * @since Ant 1.7.1
+ */
+ public void setErrorOnMissingDir(final boolean errorOnMissingDir) {
+ this.errorOnMissingDir = errorOnMissingDir;
+ }
+
+ /**
+ * Get whether or not a DirectoryScanner follows symbolic links.
+ *
+ * @return flag indicating whether symbolic links should be followed.
+ *
+ * @since Ant 1.6
+ */
+ public synchronized boolean isFollowSymlinks() {
+ return followSymlinks;
+ }
+
+ /**
+ * Set whether or not symbolic links should be followed.
+ *
+ * @param followSymlinks whether or not symbolic links should be followed.
+ */
+ public synchronized void setFollowSymlinks(final boolean followSymlinks) {
+ this.followSymlinks = followSymlinks;
+ }
+
+ /**
+ * The maximum number of times a symbolic link may be followed
+ * during a scan.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setMaxLevelsOfSymlinks(final int max) {
+ maxLevelsOfSymlinks = max;
+ }
+
+ /**
+ * Set the list of include patterns to use. All '/' and '\' characters
+ * are replaced by <code>File.separatorChar</code>, so the separator used
+ * need not match <code>File.separatorChar</code>.
+ * <p>
+ * When a pattern ends with a '/' or '\', "**" is appended.
+ *
+ * @param includes A list of include patterns.
+ * May be <code>null</code>, indicating that all files
+ * should be included. If a non-<code>null</code>
+ * list is given, all elements must be
+ * non-<code>null</code>.
+ */
+ public synchronized void setIncludes(final String[] includes) {
+ if (includes == null) {
+ this.includes = null;
+ } else {
+ this.includes = new String[includes.length];
+ for (int i = 0; i < includes.length; i++) {
+ this.includes[i] = normalizePattern(includes[i]);
+ }
+ }
+ }
+
+ /**
+ * Set the list of exclude patterns to use. All '/' and '\' characters
+ * are replaced by <code>File.separatorChar</code>, so the separator used
+ * need not match <code>File.separatorChar</code>.
+ * <p>
+ * When a pattern ends with a '/' or '\', "**" is appended.
+ *
+ * @param excludes A list of exclude patterns.
+ * May be <code>null</code>, indicating that no files
+ * should be excluded. If a non-<code>null</code> list is
+ * given, all elements must be non-<code>null</code>.
+ */
+ public synchronized void setExcludes(final String[] excludes) {
+ if (excludes == null) {
+ this.excludes = null;
+ } else {
+ this.excludes = new String[excludes.length];
+ for (int i = 0; i < excludes.length; i++) {
+ this.excludes[i] = normalizePattern(excludes[i]);
+ }
+ }
+ }
+
+ /**
+ * Add to the list of exclude patterns to use. All '/' and '\'
+ * characters are replaced by <code>File.separatorChar</code>, so
+ * the separator used need not match <code>File.separatorChar</code>.
+ * <p>
+ * When a pattern ends with a '/' or '\', "**" is appended.
+ *
+ * @param excludes A list of exclude patterns.
+ * May be <code>null</code>, in which case the
+ * exclude patterns don't get changed at all.
+ *
+ * @since Ant 1.6.3
+ */
+ public synchronized void addExcludes(final String[] excludes) {
+ if (excludes != null && excludes.length > 0) {
+ if (this.excludes != null && this.excludes.length > 0) {
+ final String[] tmp = new String[excludes.length
+ + this.excludes.length];
+ System.arraycopy(this.excludes, 0, tmp, 0,
+ this.excludes.length);
+ for (int i = 0; i < excludes.length; i++) {
+ tmp[this.excludes.length + i] =
+ normalizePattern(excludes[i]);
+ }
+ this.excludes = tmp;
+ } else {
+ setExcludes(excludes);
+ }
+ }
+ }
+
+ /**
+ * All '/' and '\' characters are replaced by
+ * <code>File.separatorChar</code>, so the separator used need not
+ * match <code>File.separatorChar</code>.
+ *
+ * <p> When a pattern ends with a '/' or '\', "**" is appended.
+ *
+ * @since Ant 1.6.3
+ */
+ private static String normalizePattern(final String p) {
+ String pattern = p.replace('/', File.separatorChar)
+ .replace('\\', File.separatorChar);
+ if (pattern.endsWith(File.separator)) {
+ pattern += SelectorUtils.DEEP_TREE_MATCH;
+ }
+ return pattern;
+ }
+
+ /**
+ * Set the selectors that will select the filelist.
+ *
+ * @param selectors specifies the selectors to be invoked on a scan.
+ */
+ public synchronized void setSelectors(final FileSelector[] selectors) {
+ this.selectors = selectors;
+ }
+
+ /**
+ * Return whether or not the scanner has included all the files or
+ * directories it has come across so far.
+ *
+ * @return <code>true</code> if all files and directories which have
+ * been found so far have been included.
+ */
+ public synchronized boolean isEverythingIncluded() {
+ return everythingIncluded;
+ }
+
+ /**
+ * Scan for files which match at least one include pattern and don't match
+ * any exclude patterns. If there are selectors then the files must pass
+ * muster there, as well. Scans under basedir, if set; otherwise the
+ * include patterns without leading wildcards specify the absolute paths of
+ * the files that may be included.
+ *
+ * @exception IllegalStateException if the base directory was set
+ * incorrectly (i.e. if it doesn't exist or isn't a directory).
+ */
+ public void scan() throws IllegalStateException {
+ synchronized (scanLock) {
+ if (scanning) {
+ while (scanning) {
+ try {
+ scanLock.wait();
+ } catch (final InterruptedException e) {
+ continue;
+ }
+ }
+ if (illegal != null) {
+ throw illegal;
+ }
+ return;
+ }
+ scanning = true;
+ }
+ final File savedBase = basedir;
+ try {
+ synchronized (this) {
+ illegal = null;
+ clearResults();
+
+ // set in/excludes to reasonable defaults if needed:
+ final boolean nullIncludes = (includes == null);
+ includes = nullIncludes
+ ? new String[] {SelectorUtils.DEEP_TREE_MATCH} : includes;
+ final boolean nullExcludes = (excludes == null);
+ excludes = nullExcludes ? new String[0] : excludes;
+
+ if (basedir != null && !followSymlinks
+ && SYMLINK_UTILS.isSymbolicLink(basedir)) {
+ notFollowedSymlinks.add(basedir.getAbsolutePath());
+ basedir = null;
+ }
+
+ if (basedir == null) {
+ // if no basedir and no includes, nothing to do:
+ if (nullIncludes) {
+ return;
+ }
+ } else {
+ if (!basedir.exists()) {
+ if (errorOnMissingDir) {
+ illegal = new IllegalStateException("basedir "
+ + basedir
+ + DOES_NOT_EXIST_POSTFIX);
+ } else {
+ // Nothing to do - basedir does not exist
+ return;
+ }
+ } else if (!basedir.isDirectory()) {
+ illegal = new IllegalStateException("basedir "
+ + basedir
+ + " is not a"
+ + " directory.");
+ }
+ if (illegal != null) {
+ throw illegal;
+ }
+ }
+ if (isIncluded(TokenizedPath.EMPTY_PATH)) {
+ if (!isExcluded(TokenizedPath.EMPTY_PATH)) {
+ if (isSelected("", basedir)) {
+ dirsIncluded.addElement("");
+ } else {
+ dirsDeselected.addElement("");
+ }
+ } else {
+ dirsExcluded.addElement("");
+ }
+ } else {
+ dirsNotIncluded.addElement("");
+ }
+ checkIncludePatterns();
+ clearCaches();
+ includes = nullIncludes ? null : includes;
+ excludes = nullExcludes ? null : excludes;
+ }
+ } catch (final IOException ex) {
+ throw new BuildException(ex);
+ } finally {
+ basedir = savedBase;
+ synchronized (scanLock) {
+ scanning = false;
+ scanLock.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * This routine is actually checking all the include patterns in
+ * order to avoid scanning everything under base dir.
+ * @since Ant 1.6
+ */
+ private void checkIncludePatterns() {
+ ensureNonPatternSetsReady();
+ final Map<TokenizedPath, String> newroots = new HashMap<TokenizedPath, String>();
+
+ // put in the newroots map the include patterns without
+ // wildcard tokens
+ for (int i = 0; i < includePatterns.length; i++) {
+ final String pattern = includePatterns[i].toString();
+ if (!shouldSkipPattern(pattern)) {
+ newroots.put(includePatterns[i].rtrimWildcardTokens(),
+ pattern);
+ }
+ }
+ for (final Map.Entry<String, TokenizedPath> entry : includeNonPatterns.entrySet()) {
+ final String pattern = entry.getKey();
+ if (!shouldSkipPattern(pattern)) {
+ newroots.put(entry.getValue(), pattern);
+ }
+ }
+
+ if (newroots.containsKey(TokenizedPath.EMPTY_PATH)
+ && basedir != null) {
+ // we are going to scan everything anyway
+ scandir(basedir, "", true);
+ } else {
+ File canonBase = null;
+ if (basedir != null) {
+ try {
+ canonBase = basedir.getCanonicalFile();
+ } catch (final IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+ // only scan directories that can include matched files or
+ // directories
+ for (final Map.Entry<TokenizedPath, String> entry : newroots.entrySet()) {
+ TokenizedPath currentPath = entry.getKey();
+ String currentelement = currentPath.toString();
+ if (basedir == null
+ && !FileUtils.isAbsolutePath(currentelement)) {
+ continue;
+ }
+ File myfile = new File(basedir, currentelement);
+
+ if (myfile.exists()) {
+ // may be on a case insensitive file system. We want
+ // the results to show what's really on the disk, so
+ // we need to double check.
+ try {
+ final String path = (basedir == null)
+ ? myfile.getCanonicalPath()
+ : FILE_UTILS.removeLeadingPath(canonBase,
+ myfile.getCanonicalFile());
+ if (!path.equals(currentelement) || ON_VMS) {
+ myfile = currentPath.findFile(basedir, true);
+ if (myfile != null && basedir != null) {
+ currentelement = FILE_UTILS.removeLeadingPath(
+ basedir, myfile);
+ if (!currentPath.toString()
+ .equals(currentelement)) {
+ currentPath =
+ new TokenizedPath(currentelement);
+ }
+ }
+ }
+ } catch (final IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+
+ if ((myfile == null || !myfile.exists()) && !isCaseSensitive()) {
+ final File f = currentPath.findFile(basedir, false);
+ if (f != null && f.exists()) {
+ // adapt currentelement to the case we've
+ // actually found
+ currentelement = (basedir == null)
+ ? f.getAbsolutePath()
+ : FILE_UTILS.removeLeadingPath(basedir, f);
+ myfile = f;
+ currentPath = new TokenizedPath(currentelement);
+ }
+ }
+
+ if (myfile != null && myfile.exists()) {
+ if (!followSymlinks && currentPath.isSymlink(basedir)) {
+ accountForNotFollowedSymlink(currentPath, myfile);
+ continue;
+ }
+ if (myfile.isDirectory()) {
+ if (isIncluded(currentPath)
+ && currentelement.length() > 0) {
+ accountForIncludedDir(currentPath, myfile, true);
+ } else {
+ scandir(myfile, currentPath, true);
+ }
+ } else if (myfile.isFile()) {
+ final String originalpattern = entry.getValue();
+ final boolean included = isCaseSensitive()
+ ? originalpattern.equals(currentelement)
+ : originalpattern.equalsIgnoreCase(currentelement);
+ if (included) {
+ accountForIncludedFile(currentPath, myfile);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * true if the pattern specifies a relative path without basedir
+ * or an absolute path not inside basedir.
+ *
+ * @since Ant 1.8.0
+ */
+ private boolean shouldSkipPattern(final String pattern) {
+ if (FileUtils.isAbsolutePath(pattern)) {
+ //skip abs. paths not under basedir, if set:
+ if (basedir != null
+ && !SelectorUtils.matchPatternStart(pattern,
+ basedir.getAbsolutePath(),
+ isCaseSensitive())) {
+ return true;
+ }
+ } else if (basedir == null) {
+ //skip non-abs. paths if basedir == null:
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Clear the result caches for a scan.
+ */
+ protected synchronized void clearResults() {
+ filesIncluded = new VectorSet<String>();
+ filesNotIncluded = new VectorSet<String>();
+ filesExcluded = new VectorSet<String>();
+ filesDeselected = new VectorSet<String>();
+ dirsIncluded = new VectorSet<String>();
+ dirsNotIncluded = new VectorSet<String>();
+ dirsExcluded = new VectorSet<String>();
+ dirsDeselected = new VectorSet<String>();
+ everythingIncluded = (basedir != null);
+ scannedDirs.clear();
+ notFollowedSymlinks.clear();
+ }
+
+ /**
+ * Top level invocation for a slow scan. A slow scan builds up a full
+ * list of excluded/included files/directories, whereas a fast scan
+ * will only have full results for included files, as it ignores
+ * directories which can't possibly hold any included files/directories.
+ * <p>
+ * Returns immediately if a slow scan has already been completed.
+ */
+ protected void slowScan() {
+ synchronized (slowScanLock) {
+ if (haveSlowResults) {
+ return;
+ }
+ if (slowScanning) {
+ while (slowScanning) {
+ try {
+ slowScanLock.wait();
+ } catch (final InterruptedException e) {
+ // Empty
+ }
+ }
+ return;
+ }
+ slowScanning = true;
+ }
+ try {
+ synchronized (this) {
+
+ // set in/excludes to reasonable defaults if needed:
+ final boolean nullIncludes = (includes == null);
+ includes = nullIncludes
+ ? new String[] {SelectorUtils.DEEP_TREE_MATCH} : includes;
+ final boolean nullExcludes = (excludes == null);
+ excludes = nullExcludes ? new String[0] : excludes;
+
+ final String[] excl = new String[dirsExcluded.size()];
+ dirsExcluded.copyInto(excl);
+
+ final String[] notIncl = new String[dirsNotIncluded.size()];
+ dirsNotIncluded.copyInto(notIncl);
+
+ ensureNonPatternSetsReady();
+
+ processSlowScan(excl);
+ processSlowScan(notIncl);
+ clearCaches();
+ includes = nullIncludes ? null : includes;
+ excludes = nullExcludes ? null : excludes;
+ }
+ } finally {
+ synchronized (slowScanLock) {
+ haveSlowResults = true;
+ slowScanning = false;
+ slowScanLock.notifyAll();
+ }
+ }
+ }
+
+ private void processSlowScan(final String[] arr) {
+ for (int i = 0; i < arr.length; i++) {
+ final TokenizedPath path = new TokenizedPath(arr[i]);
+ if (!couldHoldIncluded(path) || contentsExcluded(path)) {
+ scandir(new File(basedir, arr[i]), path, false);
+ }
+ }
+ }
+
+ /**
+ * Scan the given directory for files and directories. Found files and
+ * directories are placed in their respective collections, based on the
+ * matching of includes, excludes, and the selectors. When a directory
+ * is found, it is scanned recursively.
+ *
+ * @param dir The directory to scan. Must not be <code>null</code>.
+ * @param vpath The path relative to the base directory (needed to
+ * prevent problems with an absolute path when using
+ * dir). Must not be <code>null</code>.
+ * @param fast Whether or not this call is part of a fast scan.
+ *
+ * @see #filesIncluded
+ * @see #filesNotIncluded
+ * @see #filesExcluded
+ * @see #dirsIncluded
+ * @see #dirsNotIncluded
+ * @see #dirsExcluded
+ * @see #slowScan
+ */
+ protected void scandir(final File dir, final String vpath, final boolean fast) {
+ scandir(dir, new TokenizedPath(vpath), fast);
+ }
+
+ /**
+ * Scan the given directory for files and directories. Found files and
+ * directories are placed in their respective collections, based on the
+ * matching of includes, excludes, and the selectors. When a directory
+ * is found, it is scanned recursively.
+ *
+ * @param dir The directory to scan. Must not be <code>null</code>.
+ * @param path The path relative to the base directory (needed to
+ * prevent problems with an absolute path when using
+ * dir). Must not be <code>null</code>.
+ * @param fast Whether or not this call is part of a fast scan.
+ *
+ * @see #filesIncluded
+ * @see #filesNotIncluded
+ * @see #filesExcluded
+ * @see #dirsIncluded
+ * @see #dirsNotIncluded
+ * @see #dirsExcluded
+ * @see #slowScan
+ */
+ private void scandir(final File dir, final TokenizedPath path, final boolean fast) {
+ if (dir == null) {
+ throw new BuildException("dir must not be null.");
+ }
+ final String[] newfiles = dir.list();
+ if (newfiles == null) {
+ if (!dir.exists()) {
+ throw new BuildException(dir + DOES_NOT_EXIST_POSTFIX);
+ } else if (!dir.isDirectory()) {
+ throw new BuildException(dir + " is not a directory.");
+ } else {
+ throw new BuildException("IO error scanning directory '"
+ + dir.getAbsolutePath() + "'");
+ }
+ }
+ scandir(dir, path, fast, newfiles, new LinkedList<String>());
+ }
+
+ private void scandir(final File dir, final TokenizedPath path, final boolean fast,
+ String[] newfiles, final LinkedList<String> directoryNamesFollowed) {
+ String vpath = path.toString();
+ if (vpath.length() > 0 && !vpath.endsWith(File.separator)) {
+ vpath += File.separator;
+ }
+
+ // avoid double scanning of directories, can only happen in fast mode
+ if (fast && hasBeenScanned(vpath)) {
+ return;
+ }
+ if (!followSymlinks) {
+ final ArrayList<String> noLinks = new ArrayList<String>();
+ for (int i = 0; i < newfiles.length; i++) {
+ try {
+ if (SYMLINK_UTILS.isSymbolicLink(dir, newfiles[i])) {
+ final String name = vpath + newfiles[i];
+ final File file = new File(dir, newfiles[i]);
+ if (file.isDirectory()) {
+ dirsExcluded.addElement(name);
+ } else if (file.isFile()) {
+ filesExcluded.addElement(name);
+ }
+ accountForNotFollowedSymlink(name, file);
+ } else {
+ noLinks.add(newfiles[i]);
+ }
+ } catch (final IOException ioe) {
+ final String msg = "IOException caught while checking "
+ + "for links, couldn't get canonical path!";
+ // will be caught and redirected to Ant's logging system
+ System.err.println(msg);
+ noLinks.add(newfiles[i]);
+ }
+ }
+ newfiles = (noLinks.toArray(new String[noLinks.size()]));
+ } else {
+ directoryNamesFollowed.addFirst(dir.getName());
+ }
+
+ for (int i = 0; i < newfiles.length; i++) {
+ final String name = vpath + newfiles[i];
+ final TokenizedPath newPath = new TokenizedPath(path, newfiles[i]);
+ final File file = new File(dir, newfiles[i]);
+ final String[] children = file.list();
+ if (children == null || (children.length == 0 && file.isFile())) {
+ if (isIncluded(newPath)) {
+ accountForIncludedFile(newPath, file);
+ } else {
+ everythingIncluded = false;
+ filesNotIncluded.addElement(name);
+ }
+ } else if (file.isDirectory()) { // dir
+
+ if (followSymlinks
+ && causesIllegalSymlinkLoop(newfiles[i], dir,
+ directoryNamesFollowed)) {
+ // will be caught and redirected to Ant's logging system
+ System.err.println("skipping symbolic link "
+ + file.getAbsolutePath()
+ + " -- too many levels of symbolic"
+ + " links.");
+ notFollowedSymlinks.add(file.getAbsolutePath());
+ continue;
+ }
+
+ if (isIncluded(newPath)) {
+ accountForIncludedDir(newPath, file, fast, children,
+ directoryNamesFollowed);
+ } else {
+ everythingIncluded = false;
+ dirsNotIncluded.addElement(name);
+ if (fast && couldHoldIncluded(newPath)
+ && !contentsExcluded(newPath)) {
+ scandir(file, newPath, fast, children,
+ directoryNamesFollowed);
+ }
+ }
+ if (!fast) {
+ scandir(file, newPath, fast, children, directoryNamesFollowed);
+ }
+ }
+ }
+
+ if (followSymlinks) {
+ directoryNamesFollowed.removeFirst();
+ }
+ }
+
+ /**
+ * Process included file.
+ * @param name path of the file relative to the directory of the FileSet.
+ * @param file included File.
+ */
+ private void accountForIncludedFile(final TokenizedPath name, final File file) {
+ processIncluded(name, file, filesIncluded, filesExcluded,
+ filesDeselected);
+ }
+
+ /**
+ * Process included directory.
+ * @param name path of the directory relative to the directory of
+ * the FileSet.
+ * @param file directory as File.
+ * @param fast whether to perform fast scans.
+ */
+ private void accountForIncludedDir(final TokenizedPath name, final File file,
+ final boolean fast) {
+ processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected);
+ if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) {
+ scandir(file, name, fast);
+ }
+ }
+
+ private void accountForIncludedDir(final TokenizedPath name,
+ final File file, final boolean fast,
+ final String[] children,
+ final LinkedList<String> directoryNamesFollowed) {
+ processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected);
+ if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) {
+ scandir(file, name, fast, children, directoryNamesFollowed);
+ }
+ }
+
+ private void accountForNotFollowedSymlink(final String name, final File file) {
+ accountForNotFollowedSymlink(new TokenizedPath(name), file);
+ }
+
+ private void accountForNotFollowedSymlink(final TokenizedPath name, final File file) {
+ if (!isExcluded(name) &&
+ (isIncluded(name)
+ || (file.isDirectory() && couldHoldIncluded(name)
+ && !contentsExcluded(name)))) {
+ notFollowedSymlinks.add(file.getAbsolutePath());
+ }
+ }
+
+ private void processIncluded(final TokenizedPath path,
+ final File file, final Vector<String> inc, final Vector<String> exc,
+ final Vector<String> des) {
+ final String name = path.toString();
+ if (inc.contains(name) || exc.contains(name) || des.contains(name)) {
+ return;
+ }
+
+ boolean included = false;
+ if (isExcluded(path)) {
+ exc.add(name);
+ } else if (isSelected(name, file)) {
+ included = true;
+ inc.add(name);
+ } else {
+ des.add(name);
+ }
+ everythingIncluded &= included;
+ }
+
+ /**
+ * Test whether or not a name matches against at least one include
+ * pattern.
+ *
+ * @param name The name to match. Must not be <code>null</code>.
+ * @return <code>true</code> when the name matches against at least one
+ * include pattern, or <code>false</code> otherwise.
+ */
+ protected boolean isIncluded(final String name) {
+ return isIncluded(new TokenizedPath(name));
+ }
+
+ /**
+ * Test whether or not a name matches against at least one include
+ * pattern.
+ *
+ * @param name The name to match. Must not be <code>null</code>.
+ * @return <code>true</code> when the name matches against at least one
+ * include pattern, or <code>false</code> otherwise.
+ */
+ private boolean isIncluded(final TokenizedPath path) {
+ ensureNonPatternSetsReady();
+
+ if (isCaseSensitive()
+ ? includeNonPatterns.containsKey(path.toString())
+ : includeNonPatterns.containsKey(path.toString().toUpperCase())) {
+ return true;
+ }
+ for (int i = 0; i < includePatterns.length; i++) {
+ if (includePatterns[i].matchPath(path, isCaseSensitive())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test whether or not a name matches the start of at least one include
+ * pattern.
+ *
+ * @param name The name to match. Must not be <code>null</code>.
+ * @return <code>true</code> when the name matches against the start of at
+ * least one include pattern, or <code>false</code> otherwise.
+ */
+ protected boolean couldHoldIncluded(final String name) {
+ return couldHoldIncluded(new TokenizedPath(name));
+ }
+
+ /**
+ * Test whether or not a name matches the start of at least one include
+ * pattern.
+ *
+ * @param tokenizedName The name to match. Must not be <code>null</code>.
+ * @return <code>true</code> when the name matches against the start of at
+ * least one include pattern, or <code>false</code> otherwise.
+ */
+ private boolean couldHoldIncluded(final TokenizedPath tokenizedName) {
+ for (int i = 0; i < includePatterns.length; i++) {
+ if (couldHoldIncluded(tokenizedName, includePatterns[i])) {
+ return true;
+ }
+ }
+ for (final Iterator<TokenizedPath> iter = includeNonPatterns.values().iterator();
+ iter.hasNext();) {
+ if (couldHoldIncluded(tokenizedName,
+ iter.next().toPattern())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test whether or not a name matches the start of the given
+ * include pattern.
+ *
+ * @param tokenizedName The name to match. Must not be <code>null</code>.
+ * @return <code>true</code> when the name matches against the start of the
+ * include pattern, or <code>false</code> otherwise.
+ */
+ private boolean couldHoldIncluded(final TokenizedPath tokenizedName,
+ final TokenizedPattern tokenizedInclude) {
+ return tokenizedInclude.matchStartOf(tokenizedName, isCaseSensitive())
+ && isMorePowerfulThanExcludes(tokenizedName.toString())
+ && isDeeper(tokenizedInclude, tokenizedName);
+ }
+
+ /**
+ * Verify that a pattern specifies files deeper
+ * than the level of the specified file.
+ * @param pattern the pattern to check.
+ * @param name the name to check.
+ * @return whether the pattern is deeper than the name.
+ * @since Ant 1.6.3
+ */
+ private boolean isDeeper(final TokenizedPattern pattern, final TokenizedPath name) {
+ return pattern.containsPattern(SelectorUtils.DEEP_TREE_MATCH)
+ || pattern.depth() > name.depth();
+ }
+
+ /**
+ * Find out whether one particular include pattern is more powerful
+ * than all the excludes.
+ * Note: the power comparison is based on the length of the include pattern
+ * and of the exclude patterns without the wildcards.
+ * Ideally the comparison should be done based on the depth
+ * of the match; that is to say how many file separators have been matched
+ * before the first ** or the end of the pattern.
+ *
+ * IMPORTANT : this function should return false "with care".
+ *
+ * @param name the relative path to test.
+ * @return true if there is no exclude pattern more powerful than
+ * this include pattern.
+ * @since Ant 1.6
+ */
+ private boolean isMorePowerfulThanExcludes(final String name) {
+ final String soughtexclude =
+ name + File.separatorChar + SelectorUtils.DEEP_TREE_MATCH;
+ for (int counter = 0; counter < excludePatterns.length; counter++) {
+ if (excludePatterns[counter].toString().equals(soughtexclude)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Test whether all contents of the specified directory must be excluded.
+ * @param path the path to check.
+ * @return whether all the specified directory's contents are excluded.
+ */
+ /* package */ boolean contentsExcluded(final TokenizedPath path) {
+ for (int i = 0; i < excludePatterns.length; i++) {
+ if (excludePatterns[i].endsWith(SelectorUtils.DEEP_TREE_MATCH)
+ && excludePatterns[i].withoutLastToken()
+ .matchPath(path, isCaseSensitive())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test whether or not a name matches against at least one exclude
+ * pattern.
+ *
+ * @param name The name to match. Must not be <code>null</code>.
+ * @return <code>true</code> when the name matches against at least one
+ * exclude pattern, or <code>false</code> otherwise.
+ */
+ protected boolean isExcluded(final String name) {
+ return isExcluded(new TokenizedPath(name));
+ }
+
+ /**
+ * Test whether or not a name matches against at least one exclude
+ * pattern.
+ *
+ * @param name The name to match. Must not be <code>null</code>.
+ * @return <code>true</code> when the name matches against at least one
+ * exclude pattern, or <code>false</code> otherwise.
+ */
+ private boolean isExcluded(final TokenizedPath name) {
+ ensureNonPatternSetsReady();
+
+ if (isCaseSensitive()
+ ? excludeNonPatterns.containsKey(name.toString())
+ : excludeNonPatterns.containsKey(name.toString().toUpperCase())) {
+ return true;
+ }
+ for (int i = 0; i < excludePatterns.length; i++) {
+ if (excludePatterns[i].matchPath(name, isCaseSensitive())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test whether a file should be selected.
+ *
+ * @param name the filename to check for selecting.
+ * @param file the java.io.File object for this filename.
+ * @return <code>false</code> when the selectors says that the file
+ * should not be selected, <code>true</code> otherwise.
+ */
+ protected boolean isSelected(final String name, final File file) {
+ if (selectors != null) {
+ for (int i = 0; i < selectors.length; i++) {
+ if (!selectors[i].isSelected(basedir, name, file)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the names of the files which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the files which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ */
+ public String[] getIncludedFiles() {
+ String[] files;
+ synchronized (this) {
+ if (filesIncluded == null) {
+ throw new IllegalStateException("Must call scan() first");
+ }
+ files = new String[filesIncluded.size()];
+ filesIncluded.copyInto(files);
+ }
+ Arrays.sort(files);
+ return files;
+ }
+
+ /**
+ * Return the count of included files.
+ * @return <code>int</code>.
+ * @since Ant 1.6.3
+ */
+ public synchronized int getIncludedFilesCount() {
+ if (filesIncluded == null) {
+ throw new IllegalStateException("Must call scan() first");
+ }
+ return filesIncluded.size();
+ }
+
+ /**
+ * Return the names of the files which matched none of the include
+ * patterns. The names are relative to the base directory. This involves
+ * performing a slow scan if one has not already been completed.
+ *
+ * @return the names of the files which matched none of the include
+ * patterns.
+ *
+ * @see #slowScan
+ */
+ public synchronized String[] getNotIncludedFiles() {
+ slowScan();
+ final String[] files = new String[filesNotIncluded.size()];
+ filesNotIncluded.copyInto(files);
+ return files;
+ }
+
+ /**
+ * Return the names of the files which matched at least one of the
+ * include patterns and at least one of the exclude patterns.
+ * The names are relative to the base directory. This involves
+ * performing a slow scan if one has not already been completed.
+ *
+ * @return the names of the files which matched at least one of the
+ * include patterns and at least one of the exclude patterns.
+ *
+ * @see #slowScan
+ */
+ public synchronized String[] getExcludedFiles() {
+ slowScan();
+ final String[] files = new String[filesExcluded.size()];
+ filesExcluded.copyInto(files);
+ return files;
+ }
+
+ /**
+ * <p>Return the names of the files which were selected out and
+ * therefore not ultimately included.</p>
+ *
+ * <p>The names are relative to the base directory. This involves
+ * performing a slow scan if one has not already been completed.</p>
+ *
+ * @return the names of the files which were deselected.
+ *
+ * @see #slowScan
+ */
+ public synchronized String[] getDeselectedFiles() {
+ slowScan();
+ final String[] files = new String[filesDeselected.size()];
+ filesDeselected.copyInto(files);
+ return files;
+ }
+
+ /**
+ * Return the names of the directories which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the directories which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ */
+ public String[] getIncludedDirectories() {
+ String[] directories;
+ synchronized (this) {
+ if (dirsIncluded == null) {
+ throw new IllegalStateException("Must call scan() first");
+ }
+ directories = new String[dirsIncluded.size()];
+ dirsIncluded.copyInto(directories);
+ }
+ Arrays.sort(directories);
+ return directories;
+ }
+
+ /**
+ * Return the count of included directories.
+ * @return <code>int</code>.
+ * @since Ant 1.6.3
+ */
+ public synchronized int getIncludedDirsCount() {
+ if (dirsIncluded == null) {
+ throw new IllegalStateException("Must call scan() first");
+ }
+ return dirsIncluded.size();
+ }
+
+ /**
+ * Return the names of the directories which matched none of the include
+ * patterns. The names are relative to the base directory. This involves
+ * performing a slow scan if one has not already been completed.
+ *
+ * @return the names of the directories which matched none of the include
+ * patterns.
+ *
+ * @see #slowScan
+ */
+ public synchronized String[] getNotIncludedDirectories() {
+ slowScan();
+ final String[] directories = new String[dirsNotIncluded.size()];
+ dirsNotIncluded.copyInto(directories);
+ return directories;
+ }
+
+ /**
+ * Return the names of the directories which matched at least one of the
+ * include patterns and at least one of the exclude patterns.
+ * The names are relative to the base directory. This involves
+ * performing a slow scan if one has not already been completed.
+ *
+ * @return the names of the directories which matched at least one of the
+ * include patterns and at least one of the exclude patterns.
+ *
+ * @see #slowScan
+ */
+ public synchronized String[] getExcludedDirectories() {
+ slowScan();
+ final String[] directories = new String[dirsExcluded.size()];
+ dirsExcluded.copyInto(directories);
+ return directories;
+ }
+
+ /**
+ * <p>Return the names of the directories which were selected out and
+ * therefore not ultimately included.</p>
+ *
+ * <p>The names are relative to the base directory. This involves
+ * performing a slow scan if one has not already been completed.</p>
+ *
+ * @return the names of the directories which were deselected.
+ *
+ * @see #slowScan
+ */
+ public synchronized String[] getDeselectedDirectories() {
+ slowScan();
+ final String[] directories = new String[dirsDeselected.size()];
+ dirsDeselected.copyInto(directories);
+ return directories;
+ }
+
+ /**
+ * Absolute paths of all symbolic links that haven't been followed
+ * but would have been followed had followsymlinks been true or
+ * maxLevelsOfSymlinks been bigger.
+ *
+ * @return sorted array of not followed symlinks
+ * @since Ant 1.8.0
+ * @see #notFollowedSymlinks
+ */
+ public synchronized String[] getNotFollowedSymlinks() {
+ String[] links;
+ synchronized (this) {
+ links = notFollowedSymlinks
+ .toArray(new String[notFollowedSymlinks.size()]);
+ }
+ Arrays.sort(links);
+ return links;
+ }
+
+ /**
+ * Add default exclusions to the current exclusions set.
+ */
+ public synchronized void addDefaultExcludes() {
+ final int excludesLength = excludes == null ? 0 : excludes.length;
+ String[] newExcludes;
+ final String[] defaultExcludesTemp = getDefaultExcludes();
+ newExcludes = new String[excludesLength + defaultExcludesTemp.length];
+ if (excludesLength > 0) {
+ System.arraycopy(excludes, 0, newExcludes, 0, excludesLength);
+ }
+ for (int i = 0; i < defaultExcludesTemp.length; i++) {
+ newExcludes[i + excludesLength] =
+ defaultExcludesTemp[i].replace('/', File.separatorChar)
+ .replace('\\', File.separatorChar);
+ }
+ excludes = newExcludes;
+ }
+
+ /**
+ * Get the named resource.
+ * @param name path name of the file relative to the dir attribute.
+ *
+ * @return the resource with the given name.
+ * @since Ant 1.5.2
+ */
+ public synchronized Resource getResource(final String name) {
+ return new FileResource(basedir, name);
+ }
+
+ /**
+ * Has the directory with the given path relative to the base
+ * directory already been scanned?
+ *
+ * <p>Registers the given directory as scanned as a side effect.</p>
+ *
+ * @since Ant 1.6
+ */
+ private boolean hasBeenScanned(final String vpath) {
+ return !scannedDirs.add(vpath);
+ }
+
+ /**
+ * This method is of interest for testing purposes. The returned
+ * Set is live and should not be modified.
+ * @return the Set of relative directory names that have been scanned.
+ */
+ /* package-private */ Set<String> getScannedDirs() {
+ return scannedDirs;
+ }
+
+ /**
+ * Clear internal caches.
+ *
+ * @since Ant 1.6
+ */
+ private synchronized void clearCaches() {
+ includeNonPatterns.clear();
+ excludeNonPatterns.clear();
+ includePatterns = null;
+ excludePatterns = null;
+ areNonPatternSetsReady = false;
+ }
+
+ /**
+ * Ensure that the in|exclude &quot;patterns&quot;
+ * have been properly divided up.
+ *
+ * @since Ant 1.6.3
+ */
+ /* package */ synchronized void ensureNonPatternSetsReady() {
+ if (!areNonPatternSetsReady) {
+ includePatterns = fillNonPatternSet(includeNonPatterns, includes);
+ excludePatterns = fillNonPatternSet(excludeNonPatterns, excludes);
+ areNonPatternSetsReady = true;
+ }
+ }
+
+ /**
+ * Add all patterns that are not real patterns (do not contain
+ * wildcards) to the set and returns the real patterns.
+ *
+ * @param map Map to populate.
+ * @param patterns String[] of patterns.
+ * @since Ant 1.8.0
+ */
+ private TokenizedPattern[] fillNonPatternSet(final Map<String, TokenizedPath> map, final String[] patterns) {
+ final ArrayList<TokenizedPattern> al = new ArrayList<TokenizedPattern>(patterns.length);
+ for (int i = 0; i < patterns.length; i++) {
+ if (!SelectorUtils.hasWildcards(patterns[i])) {
+ final String s = isCaseSensitive()
+ ? patterns[i] : patterns[i].toUpperCase();
+ map.put(s, new TokenizedPath(s));
+ } else {
+ al.add(new TokenizedPattern(patterns[i]));
+ }
+ }
+ return al.toArray(new TokenizedPattern[al.size()]);
+ }
+
+ /**
+ * Would following the given directory cause a loop of symbolic
+ * links deeper than allowed?
+ *
+ * <p>Can only happen if the given directory has been seen at
+ * least more often than allowed during the current scan and it is
+ * a symbolic link and enough other occurrences of the same name
+ * higher up are symbolic links that point to the same place.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ private boolean causesIllegalSymlinkLoop(final String dirName, final File parent,
+ final LinkedList<String> directoryNamesFollowed) {
+ try {
+ if (directoryNamesFollowed.size() >= maxLevelsOfSymlinks
+ && CollectionUtils.frequency(directoryNamesFollowed, dirName)
+ >= maxLevelsOfSymlinks
+ && SYMLINK_UTILS.isSymbolicLink(parent, dirName)) {
+
+ final ArrayList<String> files = new ArrayList<String>();
+ File f = FILE_UTILS.resolveFile(parent, dirName);
+ final String target = f.getCanonicalPath();
+ files.add(target);
+
+ String relPath = "";
+ for (final String dir : directoryNamesFollowed) {
+ relPath += "../";
+ if (dirName.equals(dir)) {
+ f = FILE_UTILS.resolveFile(parent, relPath + dir);
+ files.add(f.getCanonicalPath());
+ if (files.size() > maxLevelsOfSymlinks
+ && CollectionUtils.frequency(files, target)
+ > maxLevelsOfSymlinks) {
+ return true;
+ }
+ }
+ }
+
+ }
+ return false;
+ } catch (final IOException ex) {
+ throw new BuildException("Caught error while checking for"
+ + " symbolic links", ex);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicAttribute.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicAttribute.java
new file mode 100644
index 00000000..445c33ee
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicAttribute.java
@@ -0,0 +1,38 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Enables a task to control unknown attributes
+ *
+ * @since Ant 1.5
+ */
+public interface DynamicAttribute {
+
+ /**
+ * Set a named attribute to the given value
+ *
+ * @param name the name of the attribute
+ * @param value the new value of the attribute
+ * @throws BuildException when any error occurs
+ */
+ void setDynamicAttribute(String name, String value)
+ throws BuildException;
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicAttributeNS.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicAttributeNS.java
new file mode 100644
index 00000000..7d6e84e5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicAttributeNS.java
@@ -0,0 +1,41 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Enables a task to control unknown attributes.
+ *
+ * @since Ant 1.7
+ */
+public interface DynamicAttributeNS {
+
+ /**
+ * Set a named attribute to the given value
+ *
+ * @param uri The namespace uri for this attribute, "" is
+ * used if there is no namespace uri.
+ * @param localName The localname of this attribute.
+ * @param qName The qualified name for this attribute
+ * @param value The value of this attribute.
+ * @throws BuildException when any error occurs
+ */
+ void setDynamicAttribute(
+ String uri, String localName, String qName, String value)
+ throws BuildException;
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicConfigurator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicConfigurator.java
new file mode 100644
index 00000000..e48062be
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicConfigurator.java
@@ -0,0 +1,29 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Enables a task to control unknown attributes and elements.
+ *
+ * @since Ant 1.5
+ */
+public interface DynamicConfigurator
+ extends DynamicAttribute, DynamicElement {
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicConfiguratorNS.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicConfiguratorNS.java
new file mode 100644
index 00000000..40b3d656
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicConfiguratorNS.java
@@ -0,0 +1,27 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Enables a task to control unknown attributes and elements.
+ *
+ * @since Ant 1.7
+ */
+public interface DynamicConfiguratorNS
+ extends DynamicAttributeNS, DynamicElementNS {
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicElement.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicElement.java
new file mode 100644
index 00000000..b9caf0c4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicElement.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.tools.ant;
+
+/**
+ * Enables a task to control unknown elements.
+ *
+ * @since Ant 1.5
+ */
+public interface DynamicElement {
+
+ /**
+ * Create an element with the given name
+ *
+ * @param name the element name
+ * @throws BuildException when any error occurs
+ * @return the element created
+ */
+ Object createDynamicElement(String name) throws BuildException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicElementNS.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicElementNS.java
new file mode 100644
index 00000000..57bfa530
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicElementNS.java
@@ -0,0 +1,37 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Enables a task to control unknown elements.
+ *
+ * @since Ant 1.7
+ */
+public interface DynamicElementNS {
+ /**
+ * Create an element with the given name
+ *
+ * @param uri The namespace uri for this attribute.
+ * @param localName The localname of this attribute.
+ * @param qName The qualified name for this element.
+ * @throws BuildException when any error occurs
+ * @return the element created for this element.
+ */
+ Object createDynamicElement(
+ String uri, String localName, String qName) throws BuildException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicObjectAttribute.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicObjectAttribute.java
new file mode 100644
index 00000000..9a9aca90
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DynamicObjectAttribute.java
@@ -0,0 +1,41 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Enables a task to control unknown attributes.
+ * Same as {@link DynamicAttribute} but authorize arbitrary Object as value
+ * rather than String
+ *
+ * @see DynamicAttribute
+ * @since Ant 1.9
+ */
+public interface DynamicObjectAttribute {
+
+ /**
+ * Set a named attribute to the given value
+ *
+ * @param name the name of the attribute
+ * @param value the new value of the attribute
+ * @throws BuildException when any error occurs
+ */
+ void setDynamicAttribute(String name, Object value)
+ throws BuildException;
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Evaluable.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Evaluable.java
new file mode 100644
index 00000000..47f09c73
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Evaluable.java
@@ -0,0 +1,29 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Kind of task attribute that can be evaluated before being assigned
+ *
+ * @see RuntimeConfigurable
+ */
+public interface Evaluable {
+
+ Object eval();
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Executor.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Executor.java
new file mode 100644
index 00000000..9aff1487
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Executor.java
@@ -0,0 +1,48 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Target executor abstraction.
+ * @since Ant 1.6.3
+ */
+public interface Executor {
+
+ /**
+ * Execute the specified Targets for the specified Project.
+ * @param project the Ant Project.
+ * @param targetNames String[] of Target names as specified on the command line.
+ * @throws BuildException on error
+ */
+ void executeTargets(Project project, String[] targetNames)
+ throws BuildException;
+
+ /**
+ * Get the appropriate subproject Executor instance.
+ *
+ * This allows the top executor to control what type of executor is used to execute
+ * subprojects via &lt;ant&gt;/&lt;antcall&gt;/&lt;subant&gt; and task that extend these.
+ * All bundled Executors return a SingleCheckExecutor (running a merged set of
+ * depended targets for all targets called) to run sub-builds.
+ *
+ * @return an Executor instance.
+ */
+ Executor getSubProjectExecutor();
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ExitException.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ExitException.java
new file mode 100644
index 00000000..11e1bc81
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ExitException.java
@@ -0,0 +1,61 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Used to report exit status of classes which call System.exit().
+ *
+ * @see org.apache.tools.ant.util.optional.NoExitSecurityManager
+ * @see org.apache.tools.ant.types.Permissions
+ *
+ */
+public class ExitException extends SecurityException {
+
+ private static final long serialVersionUID = 2772487854280543363L;
+
+ /** Status code */
+ private int status;
+
+ /**
+ * Constructs an exit exception.
+ * @param status the status code returned via System.exit()
+ */
+ public ExitException(int status) {
+ super("ExitException: status " + status);
+ this.status = status;
+ }
+
+ /**
+ * Constructs an exit exception.
+ * @param msg the message to be displayed.
+ * @param status the status code returned via System.exit()
+ */
+ public ExitException(String msg, int status) {
+ super(msg);
+ this.status = status;
+ }
+
+ /**
+ * The status code returned by System.exit()
+ *
+ * @return the status code returned by System.exit()
+ */
+ public int getStatus() {
+ return status;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ExitStatusException.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ExitStatusException.java
new file mode 100644
index 00000000..1eb5127b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ExitStatusException.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.tools.ant;
+
+/**
+ * BuildException + exit status.
+ *
+ * @since Ant 1.7
+ */
+public class ExitStatusException extends BuildException {
+
+ private static final long serialVersionUID = 7760846806886585968L;
+
+ /** Status code */
+ private int status;
+
+ /**
+ * Constructs an <code>ExitStatusException</code>.
+ * @param status the associated status code
+ */
+ public ExitStatusException(int status) {
+ super();
+ this.status = status;
+ }
+
+ /**
+ * Constructs an <code>ExitStatusException</code>.
+ * @param msg the associated message
+ * @param status the associated status code
+ */
+ public ExitStatusException(String msg, int status) {
+ super(msg);
+ this.status = status;
+ }
+
+ /**
+ * Construct an exit status exception with location information too
+ * @param message error message
+ * @param status exit status
+ * @param location exit location
+ */
+ public ExitStatusException(String message, int status, Location location) {
+ super(message, location);
+ this.status = status;
+ }
+
+ /**
+ * Get the status code.
+ * @return <code>int</code>
+ */
+ public int getStatus() {
+ return status;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ExtensionPoint.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ExtensionPoint.java
new file mode 100644
index 00000000..32c8c55e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ExtensionPoint.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.tools.ant;
+
+/**
+ * An extension point build files can provide as a place where other
+ * build files can add new dependencies.
+ *
+ * @since Ant 1.8.0
+ */
+public class ExtensionPoint extends Target {
+
+ public ExtensionPoint() {
+ }
+
+ /**
+ * Cloning constructor.
+ * @param other the Target to clone.
+ */
+ public ExtensionPoint(Target other) {
+ //Should we have a clone constructor taking an ExtensionPoint as parameter?
+ super(other);
+ }
+
+
+ private static final String NO_CHILDREN_ALLOWED
+ = "you must not nest child elements into an extension-point";
+
+ /**
+ * Throws an exception.
+ */
+ @Override
+ public final void addTask(Task task) {
+ throw new BuildException(NO_CHILDREN_ALLOWED);
+ }
+
+ /**
+ * Throws an exception.
+ */
+ @Override
+ public final void addDataType(RuntimeConfigurable r) {
+ throw new BuildException(NO_CHILDREN_ALLOWED);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/FileScanner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/FileScanner.java
new file mode 100644
index 00000000..a7cb9dea
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/FileScanner.java
@@ -0,0 +1,158 @@
+/*
+ * 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.tools.ant;
+
+import java.io.File;
+
+/**
+ * An interface used to describe the actions required of any type of
+ * directory scanner.
+ *
+ */
+public interface FileScanner {
+ /**
+ * Adds default exclusions to the current exclusions set.
+ */
+ void addDefaultExcludes();
+
+ /**
+ * Returns the base directory to be scanned.
+ * This is the directory which is scanned recursively.
+ *
+ * @return the base directory to be scanned
+ */
+ File getBasedir();
+
+ /**
+ * Returns the names of the directories which matched at least one of the
+ * include patterns and at least one of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the directories which matched at least one of the
+ * include patterns and at least one of the exclude patterns.
+ */
+ String[] getExcludedDirectories();
+
+ /**
+ * Returns the names of the files which matched at least one of the
+ * include patterns and at least one of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the files which matched at least one of the
+ * include patterns and at least one of the exclude patterns.
+ *
+ */
+ String[] getExcludedFiles();
+
+ /**
+ * Returns the names of the directories which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the directories which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ */
+ String[] getIncludedDirectories();
+
+ /**
+ * Returns the names of the files which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the files which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ */
+ String[] getIncludedFiles();
+
+ /**
+ * Returns the names of the directories which matched none of the include
+ * patterns. The names are relative to the base directory.
+ *
+ * @return the names of the directories which matched none of the include
+ * patterns.
+ */
+ String[] getNotIncludedDirectories();
+
+ /**
+ * Returns the names of the files which matched none of the include
+ * patterns. The names are relative to the base directory.
+ *
+ * @return the names of the files which matched none of the include
+ * patterns.
+ */
+ String[] getNotIncludedFiles();
+
+ /**
+ * Scans the base directory for files which match at least one include
+ * pattern and don't match any exclude patterns.
+ *
+ * @exception IllegalStateException if the base directory was set
+ * incorrectly (i.e. if it is <code>null</code>, doesn't exist,
+ * or isn't a directory).
+ */
+ void scan() throws IllegalStateException;
+
+ /**
+ * Sets the base directory to be scanned. This is the directory which is
+ * scanned recursively. All '/' and '\' characters should be replaced by
+ * <code>File.separatorChar</code>, so the separator used need not match
+ * <code>File.separatorChar</code>.
+ *
+ * @param basedir The base directory to scan.
+ * Must not be <code>null</code>.
+ */
+ void setBasedir(String basedir);
+
+ /**
+ * Sets the base directory to be scanned. This is the directory which is
+ * scanned recursively.
+ *
+ * @param basedir The base directory for scanning.
+ * Should not be <code>null</code>.
+ */
+ void setBasedir(File basedir);
+
+ /**
+ * Sets the list of exclude patterns to use.
+ *
+ * @param excludes A list of exclude patterns.
+ * May be <code>null</code>, indicating that no files
+ * should be excluded. If a non-<code>null</code> list is
+ * given, all elements must be non-<code>null</code>.
+ */
+ void setExcludes(String[] excludes);
+
+ /**
+ * Sets the list of include patterns to use.
+ *
+ * @param includes A list of include patterns.
+ * May be <code>null</code>, indicating that all files
+ * should be included. If a non-<code>null</code>
+ * list is given, all elements must be
+ * non-<code>null</code>.
+ */
+ void setIncludes(String[] includes);
+
+ /**
+ * Sets whether or not the file system should be regarded as case sensitive.
+ *
+ * @param isCaseSensitive whether or not the file system should be
+ * regarded as a case sensitive one
+ */
+ void setCaseSensitive(boolean isCaseSensitive);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/IntrospectionHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/IntrospectionHelper.java
new file mode 100644
index 00000000..30086563
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/IntrospectionHelper.java
@@ -0,0 +1,1745 @@
+/*
+ * 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.tools.ant;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.tools.ant.taskdefs.PreSetDef;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Helper class that collects the methods a task or nested element
+ * holds to set attributes, create nested elements or hold PCDATA
+ * elements.
+ *
+ * It contains hashtables containing classes that use introspection
+ * to handle all the invocation of the project-component specific methods.
+ *
+ * This class is somewhat complex, as it implements the O/X mapping between
+ * Ant XML and Java class instances. This is not the best place for someone new
+ * to Ant to start contributing to the codebase, as a change here can break the
+ * entire system in interesting ways. Always run a full test of Ant before checking
+ * in/submitting changes to this file.
+ *
+ * The class is final and has a private constructor.
+ * To get an instance for a specific (class,project) combination,
+ * use {@link #getHelper(Project,Class)}.
+ * This may return an existing version, or a new one
+ * ...do not make any assumptions about its uniqueness, or its validity after the Project
+ * instance has finished its build.
+ *
+ */
+public final class IntrospectionHelper {
+
+ /**
+ * Helper instances we've already created (Class.getName() to IntrospectionHelper).
+ */
+ private static final Map<String, IntrospectionHelper> HELPERS = new Hashtable<String, IntrospectionHelper>();
+
+ /**
+ * Map from primitive types to wrapper classes for use in
+ * createAttributeSetter (Class to Class). Note that char
+ * and boolean are in here even though they get special treatment
+ * - this way we only need to test for the wrapper class.
+ */
+ private static final Map<Class<?>, Class<?>> PRIMITIVE_TYPE_MAP = new HashMap<Class<?>, Class<?>>(8);
+
+ // Set up PRIMITIVE_TYPE_MAP
+ static {
+ final Class<?>[] primitives = {Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE,
+ Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE};
+ final Class<?>[] wrappers = {Boolean.class, Byte.class, Character.class, Short.class,
+ Integer.class, Long.class, Float.class, Double.class};
+ for (int i = 0; i < primitives.length; i++) {
+ PRIMITIVE_TYPE_MAP.put (primitives[i], wrappers[i]);
+ }
+ }
+
+ private static final int MAX_REPORT_NESTED_TEXT = 20;
+ private static final String ELLIPSIS = "...";
+
+ /**
+ * Map from attribute names to attribute types
+ * (String to Class).
+ */
+ private final Hashtable<String, Class<?>> attributeTypes = new Hashtable<String, Class<?>>();
+
+ /**
+ * Map from attribute names to attribute setter methods
+ * (String to AttributeSetter).
+ */
+ private final Hashtable<String, AttributeSetter> attributeSetters = new Hashtable<String, AttributeSetter>();
+
+ /**
+ * Map from attribute names to nested types
+ * (String to Class).
+ */
+ private final Hashtable<String, Class<?>> nestedTypes = new Hashtable<String, Class<?>>();
+
+ /**
+ * Map from attribute names to methods to create nested types
+ * (String to NestedCreator).
+ */
+ private final Hashtable<String, NestedCreator> nestedCreators = new Hashtable<String, NestedCreator>();
+
+ /**
+ * Vector of methods matching add[Configured](Class) pattern.
+ */
+ private final List<Method> addTypeMethods = new ArrayList<Method>();
+
+ /**
+ * The method to invoke to add PCDATA.
+ */
+ private final Method addText;
+
+ /**
+ * The class introspected by this instance.
+ */
+ private final Class<?> bean;
+
+ /**
+ * Sole constructor, which is private to ensure that all
+ * IntrospectionHelpers are created via {@link #getHelper(Class) getHelper}.
+ * Introspects the given class for bean-like methods.
+ * Each method is examined in turn, and the following rules are applied:
+ * <p>
+ * <ul>
+ * <li>If the method is <code>Task.setLocation(Location)</code>,
+ * <code>Task.setTaskType(String)</code>
+ * or <code>TaskContainer.addTask(Task)</code>, it is ignored. These
+ * methods are handled differently elsewhere.
+ * <li><code>void addText(String)</code> is recognised as the method for
+ * adding PCDATA to a bean.
+ * <li><code>void setFoo(Bar)</code> is recognised as a method for
+ * setting the value of attribute <code>foo</code>, so long as
+ * <code>Bar</code> is non-void and is not an array type.
+ * As of Ant 1.8, a Resource or FileProvider parameter overrides a java.io.File parameter;
+ * in practice the only effect of this is to allow objects rendered from
+ * the 1.8 PropertyHelper implementation to be used as Resource parameters,
+ * since Resources set from Strings are resolved as project-relative files
+ * to preserve backward compatibility. Beyond this, non-String
+ * parameter types always overload String parameter types; these are
+ * the only guarantees made in terms of priority.
+ * <li><code>Foo createBar()</code> is recognised as a method for
+ * creating a nested element called <code>bar</code> of type
+ * <code>Foo</code>, so long as <code>Foo</code> is not a primitive or
+ * array type.
+ * <li><code>void addConfiguredFoo(Bar)</code> is recognised as a
+ * method for storing a pre-configured element called
+ * <code>foo</code> and of type <code>Bar</code>, so long as
+ * <code>Bar</code> is not an array, primitive or String type.
+ * <code>Bar</code> must have an accessible constructor taking no
+ * arguments.
+ * <li><code>void addFoo(Bar)</code> is recognised as a method for storing
+ * an element called <code>foo</code> and of type <code>Bar</code>, so
+ * long as <code>Bar</code> is not an array, primitive or String type.
+ * <code>Bar</code> must have an accessible constructor taking no
+ * arguments. This is distinct from the 'addConfigured' idiom in that
+ * the nested element is added to the parent immediately after it is
+ * constructed; in practice this means that <code>addFoo(Bar)</code> should
+ * do little or nothing with its argument besides storing it for later use.
+ * </ul>
+ * Note that only one method is retained to create/set/addConfigured/add
+ * any element or attribute.
+ *
+ * @param bean The bean type to introspect.
+ * Must not be <code>null</code>.
+ *
+ * @see #getHelper(Class)
+ */
+ private IntrospectionHelper(final Class<?> bean) {
+ this.bean = bean;
+ final Method[] methods = bean.getMethods();
+ Method addTextMethod = null;
+ for (int i = 0; i < methods.length; i++) {
+ final Method m = methods[i];
+ final String name = m.getName();
+ final Class<?> returnType = m.getReturnType();
+ final Class<?>[] args = m.getParameterTypes();
+
+ // check of add[Configured](Class) pattern
+ if (args.length == 1 && java.lang.Void.TYPE.equals(returnType)
+ && ("add".equals(name) || "addConfigured".equals(name))) {
+ insertAddTypeMethod(m);
+ continue;
+ }
+ // not really user settable properties on tasks/project components
+ if (org.apache.tools.ant.ProjectComponent.class.isAssignableFrom(bean)
+ && args.length == 1 && isHiddenSetMethod(name, args[0])) {
+ continue;
+ }
+ // hide addTask for TaskContainers
+ if (isContainer() && args.length == 1 && "addTask".equals(name)
+ && org.apache.tools.ant.Task.class.equals(args[0])) {
+ continue;
+ }
+ if ("addText".equals(name) && java.lang.Void.TYPE.equals(returnType)
+ && args.length == 1 && java.lang.String.class.equals(args[0])) {
+ addTextMethod = methods[i];
+ } else if (name.startsWith("set") && java.lang.Void.TYPE.equals(returnType)
+ && args.length == 1 && !args[0].isArray()) {
+ final String propName = getPropertyName(name, "set");
+ AttributeSetter as = attributeSetters.get(propName);
+ if (as != null) {
+ if (java.lang.String.class.equals(args[0])) {
+ /*
+ Ignore method m, as there is an overloaded
+ form of this method that takes in a
+ non-string argument, which gains higher
+ priority.
+ */
+ continue;
+ }
+ if (java.io.File.class.equals(args[0])) {
+ // Ant Resources/FileProviders override java.io.File
+ if (Resource.class.equals(as.type) || FileProvider.class.equals(as.type)) {
+ continue;
+ }
+ }
+ /*
+ In cases other than those just explicitly covered,
+ we just override that with the new one.
+ This mechanism does not guarantee any specific order
+ in which the methods will be selected: so any code
+ that depends on the order in which "set" methods have
+ been defined, is not guaranteed to be selected in any
+ particular order.
+ */
+ }
+ as = createAttributeSetter(m, args[0], propName);
+ if (as != null) {
+ attributeTypes.put(propName, args[0]);
+ attributeSetters.put(propName, as);
+ }
+ } else if (name.startsWith("create") && !returnType.isArray()
+ && !returnType.isPrimitive() && args.length == 0) {
+
+ final String propName = getPropertyName(name, "create");
+ // Check if a create of this property is already present
+ // add takes preference over create for CB purposes
+ if (nestedCreators.get(propName) == null) {
+ nestedTypes.put(propName, returnType);
+ nestedCreators.put(propName, new CreateNestedCreator(m));
+ }
+ } else if (name.startsWith("addConfigured")
+ && java.lang.Void.TYPE.equals(returnType) && args.length == 1
+ && !java.lang.String.class.equals(args[0])
+ && !args[0].isArray() && !args[0].isPrimitive()) {
+ try {
+ Constructor<?> constructor = null;
+ try {
+ constructor = args[0].getConstructor();
+ } catch (final NoSuchMethodException ex) {
+ constructor = args[0].getConstructor(Project.class);
+ }
+ final String propName = getPropertyName(name, "addConfigured");
+ nestedTypes.put(propName, args[0]);
+ nestedCreators.put(propName, new AddNestedCreator(m,
+ constructor, AddNestedCreator.ADD_CONFIGURED));
+ } catch (final NoSuchMethodException nse) {
+ // ignore
+ }
+ } else if (name.startsWith("add")
+ && java.lang.Void.TYPE.equals(returnType) && args.length == 1
+ && !java.lang.String.class.equals(args[0])
+ && !args[0].isArray() && !args[0].isPrimitive()) {
+ try {
+ Constructor<?> constructor = null;
+ try {
+ constructor = args[0].getConstructor();
+ } catch (final NoSuchMethodException ex) {
+ constructor = args[0].getConstructor(Project.class);
+ }
+ final String propName = getPropertyName(name, "add");
+ if (nestedTypes.get(propName) != null) {
+ /*
+ * Ignore this method as there is an addConfigured
+ * form of this method that has a higher
+ * priority
+ */
+ continue;
+ }
+ nestedTypes.put(propName, args[0]);
+ nestedCreators.put(propName, new AddNestedCreator(m,
+ constructor, AddNestedCreator.ADD));
+ } catch (final NoSuchMethodException nse) {
+ // ignore
+ }
+ }
+ }
+ addText = addTextMethod;
+ }
+
+ /**
+ * Certain set methods are part of the Ant core interface to tasks and
+ * therefore not to be considered for introspection
+ *
+ * @param name the name of the set method
+ * @param type the type of the set method's parameter
+ * @return true if the given set method is to be hidden.
+ */
+ private boolean isHiddenSetMethod(final String name, final Class<?> type) {
+ if ("setLocation".equals(name) && org.apache.tools.ant.Location.class.equals(type)) {
+ return true;
+ }
+ if ("setTaskType".equals(name) && java.lang.String.class.equals(type)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns a helper for the given class, either from the cache
+ * or by creating a new instance.
+ *
+ * @param c The class for which a helper is required.
+ * Must not be <code>null</code>.
+ *
+ * @return a helper for the specified class
+ */
+ public static synchronized IntrospectionHelper getHelper(final Class<?> c) {
+ return getHelper(null, c);
+ }
+
+ /**
+ * Returns a helper for the given class, either from the cache
+ * or by creating a new instance.
+ *
+ * The method will make sure the helper will be cleaned up at the end of
+ * the project, and only one instance will be created for each class.
+ *
+ * @param p the project instance. Can be null, in which case the helper is not cached.
+ * @param c The class for which a helper is required.
+ * Must not be <code>null</code>.
+ *
+ * @return a helper for the specified class
+ */
+ public static synchronized IntrospectionHelper getHelper(final Project p, final Class<?> c) {
+ IntrospectionHelper ih = HELPERS.get(c.getName());
+ // If a helper cannot be found, or if the helper is for another
+ // classloader, create a new IH
+ if (ih == null || ih.bean != c) {
+ ih = new IntrospectionHelper(c);
+ if (p != null) {
+ // #30162: do *not* cache this if there is no project, as we
+ // cannot guarantee that the cache will be cleared.
+ HELPERS.put(c.getName(), ih);
+ }
+ }
+ return ih;
+ }
+
+ /**
+ * Sets the named attribute in the given element, which is part of the
+ * given project.
+ *
+ * @param p The project containing the element. This is used when files
+ * need to be resolved. Must not be <code>null</code>.
+ * @param element The element to set the attribute in. Must not be
+ * <code>null</code>.
+ * @param attributeName The name of the attribute to set. Must not be
+ * <code>null</code>.
+ * @param value The value to set the attribute to. This may be interpreted
+ * or converted to the necessary type if the setter method
+ * doesn't accept an object of the supplied type.
+ *
+ * @exception BuildException if the introspected class doesn't support
+ * the given attribute, or if the setting
+ * method fails.
+ */
+ public void setAttribute(final Project p, final Object element, final String attributeName,
+ final Object value) throws BuildException {
+ final AttributeSetter as = attributeSetters.get(
+ attributeName.toLowerCase(Locale.ENGLISH));
+ if (as == null && value != null) {
+ if (element instanceof DynamicAttributeNS) {
+ final DynamicAttributeNS dc = (DynamicAttributeNS) element;
+ final String uriPlusPrefix = ProjectHelper.extractUriFromComponentName(attributeName);
+ final String uri = ProjectHelper.extractUriFromComponentName(uriPlusPrefix);
+ final String localName = ProjectHelper.extractNameFromComponentName(attributeName);
+ final String qName = "".equals(uri) ? localName : uri + ":" + localName;
+ dc.setDynamicAttribute(uri, localName, qName, value.toString());
+ return;
+ }
+ if (element instanceof DynamicObjectAttribute) {
+ final DynamicObjectAttribute dc = (DynamicObjectAttribute) element;
+ dc.setDynamicAttribute(attributeName.toLowerCase(Locale.ENGLISH), value);
+ return;
+ }
+ if (element instanceof DynamicAttribute) {
+ final DynamicAttribute dc = (DynamicAttribute) element;
+ dc.setDynamicAttribute(attributeName.toLowerCase(Locale.ENGLISH), value.toString());
+ return;
+ }
+ if (attributeName.indexOf(':') >= 0) {
+ return; // Ignore attribute from unknown uri's
+ }
+ final String msg = getElementName(p, element)
+ + " doesn't support the \"" + attributeName + "\" attribute.";
+ throw new UnsupportedAttributeException(msg, attributeName);
+ }
+ try {
+ as.setObject(p, element, value);
+ } catch (final IllegalAccessException ie) {
+ // impossible as getMethods should only return public methods
+ throw new BuildException(ie);
+ } catch (final InvocationTargetException ite) {
+ throw extractBuildException(ite);
+ }
+ }
+
+ /**
+ * Sets the named attribute in the given element, which is part of the
+ * given project.
+ *
+ * @param p The project containing the element. This is used when files
+ * need to be resolved. Must not be <code>null</code>.
+ * @param element The element to set the attribute in. Must not be
+ * <code>null</code>.
+ * @param attributeName The name of the attribute to set. Must not be
+ * <code>null</code>.
+ * @param value The value to set the attribute to. This may be interpreted
+ * or converted to the necessary type if the setter method
+ * doesn't just take a string. Must not be <code>null</code>.
+ *
+ * @exception BuildException if the introspected class doesn't support
+ * the given attribute, or if the setting
+ * method fails.
+ */
+ public void setAttribute(final Project p, final Object element, final String attributeName,
+ final String value) throws BuildException {
+ setAttribute(p, element, attributeName, (Object) value);
+ }
+
+ /**
+ * Adds PCDATA to an element, using the element's
+ * <code>void addText(String)</code> method, if it has one. If no
+ * such method is present, a BuildException is thrown if the
+ * given text contains non-whitespace.
+ *
+ * @param project The project which the element is part of.
+ * Must not be <code>null</code>.
+ * @param element The element to add the text to.
+ * Must not be <code>null</code>.
+ * @param text The text to add.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if non-whitespace text is provided and no
+ * method is available to handle it, or if
+ * the handling method fails.
+ */
+ public void addText(final Project project, final Object element, String text)
+ throws BuildException {
+ if (addText == null) {
+ text = text.trim();
+ // Element doesn't handle text content
+ if (text.length() == 0) {
+ // Only whitespace - ignore
+ return;
+ }
+ // Not whitespace - fail
+ throw new BuildException(project.getElementName(element)
+ + " doesn't support nested text data (\"" + condenseText(text) + "\").");
+ }
+ try {
+ addText.invoke(element, new Object[] {text});
+ } catch (final IllegalAccessException ie) {
+ // impossible as getMethods should only return public methods
+ throw new BuildException(ie);
+ } catch (final InvocationTargetException ite) {
+ throw extractBuildException(ite);
+ }
+ }
+
+ /**
+ * part of the error message created by {@link #throwNotSupported
+ * throwNotSupported}.
+ * @since Ant 1.8.0
+ */
+ protected static final String NOT_SUPPORTED_CHILD_PREFIX =
+ " doesn't support the nested \"";
+
+ /**
+ * part of the error message created by {@link #throwNotSupported
+ * throwNotSupported}.
+ * @since Ant 1.8.0
+ */
+ protected static final String NOT_SUPPORTED_CHILD_POSTFIX = "\" element.";
+
+ /**
+ * Utility method to throw a NotSupported exception
+ *
+ * @param project the Project instance.
+ * @param parent the object which doesn't support a requested element
+ * @param elementName the name of the Element which is trying to be created.
+ */
+ public void throwNotSupported(final Project project, final Object parent, final String elementName) {
+ final String msg = project.getElementName(parent)
+ + NOT_SUPPORTED_CHILD_PREFIX + elementName
+ + NOT_SUPPORTED_CHILD_POSTFIX;
+ throw new UnsupportedElementException(msg, elementName);
+ }
+
+ /**
+ * Get the specific NestedCreator for a given project/parent/element combination
+ * @param project ant project
+ * @param parentUri URI of the parent.
+ * @param parent the parent class
+ * @param elementName element to work with. This can contain
+ * a URI,localname tuple of of the form uri:localname
+ * @param child the bit of XML to work with
+ * @return a nested creator that can handle the child elements.
+ * @throws BuildException if the parent does not support child elements of that name
+ */
+ private NestedCreator getNestedCreator(
+ final Project project, String parentUri, final Object parent,
+ final String elementName, final UnknownElement child) throws BuildException {
+
+ String uri = ProjectHelper.extractUriFromComponentName(elementName);
+ final String name = ProjectHelper.extractNameFromComponentName(elementName);
+
+ if (uri.equals(ProjectHelper.ANT_CORE_URI)) {
+ uri = "";
+ }
+ if (parentUri.equals(ProjectHelper.ANT_CORE_URI)) {
+ parentUri = "";
+ }
+ NestedCreator nc = null;
+ if (uri.equals(parentUri) || uri.length() == 0) {
+ nc = nestedCreators.get(name.toLowerCase(Locale.ENGLISH));
+ }
+ if (nc == null) {
+ nc = createAddTypeCreator(project, parent, elementName);
+ }
+ if (nc == null &&
+ (parent instanceof DynamicElementNS
+ || parent instanceof DynamicElement)
+ ) {
+ final String qName = child == null ? name : child.getQName();
+ final Object nestedElement =
+ createDynamicElement(parent,
+ child == null ? "" : child.getNamespace(),
+ name, qName);
+ if (nestedElement != null) {
+ nc = new NestedCreator(null) {
+ @Override
+ Object create(final Project project, final Object parent, final Object ignore) {
+ return nestedElement;
+ }
+ };
+ }
+ }
+ if (nc == null) {
+ throwNotSupported(project, parent, elementName);
+ }
+ return nc;
+ }
+
+ /**
+ * Invokes the "correct" createDynamicElement method on parent in
+ * order to obtain a child element by name.
+ *
+ * @since Ant 1.8.0.
+ */
+ private Object createDynamicElement(final Object parent, final String ns,
+ final String localName, final String qName) {
+ Object nestedElement = null;
+ if (parent instanceof DynamicElementNS) {
+ final DynamicElementNS dc = (DynamicElementNS) parent;
+ nestedElement = dc.createDynamicElement(ns, localName, qName);
+ }
+ if (nestedElement == null && parent instanceof DynamicElement) {
+ final DynamicElement dc = (DynamicElement) parent;
+ nestedElement =
+ dc.createDynamicElement(localName.toLowerCase(Locale.ENGLISH));
+ }
+ return nestedElement;
+ }
+
+ /**
+ * Creates a named nested element. Depending on the results of the
+ * initial introspection, either a method in the given parent instance
+ * or a simple no-arg constructor is used to create an instance of the
+ * specified element type.
+ *
+ * @param project Project to which the parent object belongs.
+ * Must not be <code>null</code>. If the resulting
+ * object is an instance of ProjectComponent, its
+ * Project reference is set to this parameter value.
+ * @param parent Parent object used to create the instance.
+ * Must not be <code>null</code>.
+ * @param elementName Name of the element to create an instance of.
+ * Must not be <code>null</code>.
+ *
+ * @return an instance of the specified element type
+ * @deprecated since 1.6.x.
+ * This is not a namespace aware method.
+ *
+ * @exception BuildException if no method is available to create the
+ * element instance, or if the creating method fails.
+ */
+ @Deprecated
+ public Object createElement(final Project project, final Object parent, final String elementName)
+ throws BuildException {
+ final NestedCreator nc = getNestedCreator(project, "", parent, elementName, null);
+ try {
+ final Object nestedElement = nc.create(project, parent, null);
+ if (project != null) {
+ project.setProjectReference(nestedElement);
+ }
+ return nestedElement;
+ } catch (final IllegalAccessException ie) {
+ // impossible as getMethods should only return public methods
+ throw new BuildException(ie);
+ } catch (final InstantiationException ine) {
+ // impossible as getMethods should only return public methods
+ throw new BuildException(ine);
+ } catch (final InvocationTargetException ite) {
+ throw extractBuildException(ite);
+ }
+ }
+
+ /**
+ * returns an object that creates and stores an object
+ * for an element of a parent.
+ *
+ * @param project Project to which the parent object belongs.
+ * @param parentUri The namespace uri of the parent object.
+ * @param parent Parent object used to create the creator object to
+ * create and store and instance of a subelement.
+ * @param elementName Name of the element to create an instance of.
+ * @param ue The unknown element associated with the element.
+ * @return a creator object to create and store the element instance.
+ */
+ public Creator getElementCreator(
+ final Project project, final String parentUri, final Object parent, final String elementName, final UnknownElement ue) {
+ final NestedCreator nc = getNestedCreator(project, parentUri, parent, elementName, ue);
+ return new Creator(project, parent, nc);
+ }
+
+ /**
+ * Indicates whether the introspected class is a dynamic one,
+ * supporting arbitrary nested elements and/or attributes.
+ *
+ * @return <div><code>true</code> if the introspected class is dynamic;
+ * <code>false</code> otherwise.</div>
+ * @since Ant 1.6.3
+ *
+ * @see DynamicElement
+ * @see DynamicElementNS
+ */
+ public boolean isDynamic() {
+ return DynamicElement.class.isAssignableFrom(bean)
+ || DynamicElementNS.class.isAssignableFrom(bean);
+ }
+
+ /**
+ * Indicates whether the introspected class is a task container,
+ * supporting arbitrary nested tasks/types.
+ *
+ * @return <code>true</code> if the introspected class is a container;
+ * <code>false</code> otherwise.
+ * @since Ant 1.6.3
+ *
+ * @see TaskContainer
+ */
+ public boolean isContainer() {
+ return TaskContainer.class.isAssignableFrom(bean);
+ }
+
+ /**
+ * Indicates if this element supports a nested element of the
+ * given name.
+ *
+ * @param elementName the name of the nested element being checked
+ *
+ * @return true if the given nested element is supported
+ */
+ public boolean supportsNestedElement(final String elementName) {
+ return supportsNestedElement("", elementName);
+ }
+
+ /**
+ * Indicate if this element supports a nested element of the
+ * given name.
+ *
+ * <p>Note that this method will always return true if the
+ * introspected class is {@link #isDynamic dynamic} or contains a
+ * method named "add" with void return type and a single argument.
+ * To ge a more thorough answer, use the four-arg version of this
+ * method instead.</p>
+ *
+ * @param parentUri the uri of the parent
+ * @param elementName the name of the nested element being checked
+ *
+ * @return true if the given nested element is supported
+ */
+ public boolean supportsNestedElement(final String parentUri, final String elementName) {
+ if (isDynamic() || addTypeMethods.size() > 0) {
+ return true;
+ }
+ return supportsReflectElement(parentUri, elementName);
+ }
+
+ /**
+ * Indicate if this element supports a nested element of the
+ * given name.
+ *
+ * <p>Note that this method will always return true if the
+ * introspected class is {@link #isDynamic dynamic}, so be
+ * prepared to catch an exception about unsupported children when
+ * calling {@link #getElementCreator getElementCreator}.</p>
+ *
+ * @param parentUri the uri of the parent
+ * @param elementName the name of the nested element being checked
+ * @param project currently executing project instance
+ * @param parent the parent element
+ *
+ * @return true if the given nested element is supported
+ * @since Ant 1.8.0.
+ */
+ public boolean supportsNestedElement(final String parentUri, final String elementName,
+ final Project project, final Object parent) {
+ if (addTypeMethods.size() > 0
+ && createAddTypeCreator(project, parent, elementName) != null) {
+ return true;
+ }
+ return isDynamic() || supportsReflectElement(parentUri, elementName);
+ }
+
+ /**
+ * Check if this element supports a nested element from reflection.
+ *
+ * @param parentUri the uri of the parent
+ * @param elementName the name of the nested element being checked
+ *
+ * @return true if the given nested element is supported
+ * @since Ant 1.8.0
+ */
+ public boolean supportsReflectElement(
+ String parentUri, final String elementName) {
+ final String name = ProjectHelper.extractNameFromComponentName(elementName);
+ if (!nestedCreators.containsKey(name.toLowerCase(Locale.ENGLISH))) {
+ return false;
+ }
+ String uri = ProjectHelper.extractUriFromComponentName(elementName);
+ if (uri.equals(ProjectHelper.ANT_CORE_URI)) {
+ uri = "";
+ }
+ if ("".equals(uri)) {
+ return true;
+ }
+ if (parentUri.equals(ProjectHelper.ANT_CORE_URI)) {
+ parentUri = "";
+ }
+ return uri.equals(parentUri);
+ }
+
+ /**
+ * Stores a named nested element using a storage method determined
+ * by the initial introspection. If no appropriate storage method
+ * is available, this method returns immediately.
+ *
+ * @param project Ignored in this implementation.
+ * May be <code>null</code>.
+ *
+ * @param parent Parent instance to store the child in.
+ * Must not be <code>null</code>.
+ *
+ * @param child Child instance to store in the parent.
+ * Should not be <code>null</code>.
+ *
+ * @param elementName Name of the child element to store.
+ * May be <code>null</code>, in which case
+ * this method returns immediately.
+ *
+ * @exception BuildException if the storage method fails.
+ */
+ public void storeElement(final Project project, final Object parent, final Object child,
+ final String elementName) throws BuildException {
+ if (elementName == null) {
+ return;
+ }
+ final NestedCreator ns = nestedCreators.get(elementName.toLowerCase(Locale.ENGLISH));
+ if (ns == null) {
+ return;
+ }
+ try {
+ ns.store(parent, child);
+ } catch (final IllegalAccessException ie) {
+ // impossible as getMethods should only return public methods
+ throw new BuildException(ie);
+ } catch (final InstantiationException ine) {
+ // impossible as getMethods should only return public methods
+ throw new BuildException(ine);
+ } catch (final InvocationTargetException ite) {
+ throw extractBuildException(ite);
+ }
+ }
+
+ /**
+ * Helper method to extract the inner fault from an {@link InvocationTargetException}, and turn
+ * it into a BuildException. If it is already a BuildException, it is type cast and returned; if
+ * not a new BuildException is created containing the child as nested text.
+ * @param ite
+ * @return the nested exception
+ */
+ private static BuildException extractBuildException(final InvocationTargetException ite) {
+ final Throwable t = ite.getTargetException();
+ if (t instanceof BuildException) {
+ return (BuildException) t;
+ }
+ return new BuildException(t);
+ }
+
+ /**
+ * Returns the type of a named nested element.
+ *
+ * @param elementName The name of the element to find the type of.
+ * Must not be <code>null</code>.
+ *
+ * @return the type of the nested element with the specified name.
+ * This will never be <code>null</code>.
+ *
+ * @exception BuildException if the introspected class does not
+ * support the named nested element.
+ */
+ public Class<?> getElementType(final String elementName) throws BuildException {
+ final Class<?> nt = nestedTypes.get(elementName);
+ if (nt == null) {
+ throw new UnsupportedElementException("Class "
+ + bean.getName() + " doesn't support the nested \""
+ + elementName + "\" element.", elementName);
+ }
+ return nt;
+ }
+
+ /**
+ * Returns the type of a named attribute.
+ *
+ * @param attributeName The name of the attribute to find the type of.
+ * Must not be <code>null</code>.
+ *
+ * @return the type of the attribute with the specified name.
+ * This will never be <code>null</code>.
+ *
+ * @exception BuildException if the introspected class does not
+ * support the named attribute.
+ */
+ public Class<?> getAttributeType(final String attributeName) throws BuildException {
+ final Class<?> at = attributeTypes.get(attributeName);
+ if (at == null) {
+ throw new UnsupportedAttributeException("Class "
+ + bean.getName() + " doesn't support the \""
+ + attributeName + "\" attribute.", attributeName);
+ }
+ return at;
+ }
+
+ /**
+ * Returns the addText method when the introspected
+ * class supports nested text.
+ *
+ * @return the method on this introspected class that adds nested text.
+ * Cannot be <code>null</code>.
+ * @throws BuildException if the introspected class does not
+ * support the nested text.
+ * @since Ant 1.6.3
+ */
+ public Method getAddTextMethod() throws BuildException {
+ if (!supportsCharacters()) {
+ throw new BuildException("Class " + bean.getName()
+ + " doesn't support nested text data.");
+ }
+ return addText;
+ }
+
+ /**
+ * Returns the adder or creator method of a named nested element.
+ *
+ * @param elementName The name of the attribute to find the setter
+ * method of. Must not be <code>null</code>.
+ * @return the method on this introspected class that adds or creates this
+ * nested element. Can be <code>null</code> when the introspected
+ * class is a dynamic configurator!
+ * @throws BuildException if the introspected class does not
+ * support the named nested element.
+ * @since Ant 1.6.3
+ */
+ public Method getElementMethod(final String elementName) throws BuildException {
+ final Object creator = nestedCreators.get(elementName);
+ if (creator == null) {
+ throw new UnsupportedElementException("Class "
+ + bean.getName() + " doesn't support the nested \""
+ + elementName + "\" element.", elementName);
+ }
+ return ((NestedCreator) creator).method;
+ }
+
+ /**
+ * Returns the setter method of a named attribute.
+ *
+ * @param attributeName The name of the attribute to find the setter
+ * method of. Must not be <code>null</code>.
+ * @return the method on this introspected class that sets this attribute.
+ * This will never be <code>null</code>.
+ * @throws BuildException if the introspected class does not
+ * support the named attribute.
+ * @since Ant 1.6.3
+ */
+ public Method getAttributeMethod(final String attributeName) throws BuildException {
+ final Object setter = attributeSetters.get(attributeName);
+ if (setter == null) {
+ throw new UnsupportedAttributeException("Class "
+ + bean.getName() + " doesn't support the \""
+ + attributeName + "\" attribute.", attributeName);
+ }
+ return ((AttributeSetter) setter).method;
+ }
+
+ /**
+ * Returns whether or not the introspected class supports PCDATA.
+ *
+ * @return whether or not the introspected class supports PCDATA.
+ */
+ public boolean supportsCharacters() {
+ return addText != null;
+ }
+
+ /**
+ * Returns an enumeration of the names of the attributes supported by the introspected class.
+ *
+ * @return an enumeration of the names of the attributes supported by the introspected class.
+ * @see #getAttributeMap
+ */
+ public Enumeration<String> getAttributes() {
+ return attributeSetters.keys();
+ }
+
+ /**
+ * Returns a read-only map of attributes supported by the introspected class.
+ *
+ * @return an attribute name to attribute <code>Class</code>
+ * unmodifiable map. Can be empty, but never <code>null</code>.
+ * @since Ant 1.6.3
+ */
+ public Map<String, Class<?>> getAttributeMap() {
+ return attributeTypes.isEmpty()
+ ? Collections.<String, Class<?>> emptyMap() : Collections.unmodifiableMap(attributeTypes);
+ }
+
+ /**
+ * Returns an enumeration of the names of the nested elements supported
+ * by the introspected class.
+ *
+ * @return an enumeration of the names of the nested elements supported
+ * by the introspected class.
+ * @see #getNestedElementMap
+ */
+ public Enumeration<String> getNestedElements() {
+ return nestedTypes.keys();
+ }
+
+ /**
+ * Returns a read-only map of nested elements supported
+ * by the introspected class.
+ *
+ * @return a nested-element name to nested-element <code>Class</code>
+ * unmodifiable map. Can be empty, but never <code>null</code>.
+ * @since Ant 1.6.3
+ */
+ public Map<String, Class<?>> getNestedElementMap() {
+ return nestedTypes.isEmpty()
+ ? Collections.<String, Class<?>> emptyMap() : Collections.unmodifiableMap(nestedTypes);
+ }
+
+ /**
+ * Returns a read-only list of extension points supported
+ * by the introspected class.
+ * <p>
+ * A task/type or nested element with void methods named <code>add()</code>
+ * or <code>addConfigured()</code>, taking a single class or interface
+ * argument, supports extensions point. This method returns the list of
+ * all these <em>void add[Configured](type)</em> methods.
+ *
+ * @return a list of void, single argument add() or addConfigured()
+ * <code>Method</code>s of all supported extension points.
+ * These methods are sorted such that if the argument type of a
+ * method derives from another type also an argument of a method
+ * of this list, the method with the most derived argument will
+ * always appear first. Can be empty, but never <code>null</code>.
+ * @since Ant 1.6.3
+ */
+ public List<Method> getExtensionPoints() {
+ return addTypeMethods.isEmpty()
+ ? Collections.<Method> emptyList() : Collections.unmodifiableList(addTypeMethods);
+ }
+
+ /**
+ * Creates an implementation of AttributeSetter for the given
+ * attribute type. Conversions (where necessary) are automatically
+ * made for the following types:
+ * <ul>
+ * <li>String (left as it is)
+ * <li>Character/char (first character is used)
+ * <li>Boolean/boolean
+ * ({@link Project#toBoolean(String) Project.toBoolean(String)} is used)
+ * <li>Class (Class.forName is used)
+ * <li>File (resolved relative to the appropriate project)
+ * <li>Path (resolve relative to the appropriate project)
+ * <li>Resource (resolved as a FileResource relative to the appropriate project)
+ * <li>FileProvider (resolved as a FileResource relative to the appropriate project)
+ * <li>EnumeratedAttribute (uses its own
+ * {@link EnumeratedAttribute#setValue(String) setValue} method)
+ * <li>Other primitive types (wrapper classes are used with constructors
+ * taking String)
+ * </ul>
+ *
+ * If none of the above covers the given parameters, a constructor for the
+ * appropriate class taking a String parameter is used if it is available.
+ *
+ * @param m The method to invoke on the bean when the setter is invoked.
+ * Must not be <code>null</code>.
+ * @param arg The type of the single argument of the bean's method.
+ * Must not be <code>null</code>.
+ * @param attrName the name of the attribute for which the setter is being
+ * created.
+ *
+ * @return an appropriate AttributeSetter instance, or <code>null</code>
+ * if no appropriate conversion is available.
+ */
+ private AttributeSetter createAttributeSetter(final Method m,
+ final Class<?> arg,
+ final String attrName) {
+ // use wrappers for primitive classes, e.g. int and
+ // Integer are treated identically
+ final Class<?> reflectedArg = PRIMITIVE_TYPE_MAP.containsKey(arg)
+ ? PRIMITIVE_TYPE_MAP.get(arg) : arg;
+
+ // Object.class - it gets handled differently by AttributeSetter
+ if (java.lang.Object.class == reflectedArg) {
+ return new AttributeSetter(m, arg) {
+ @Override
+ public void set(final Project p, final Object parent, final String value)
+ throws InvocationTargetException,
+ IllegalAccessException {
+ throw new BuildException(
+ "Internal ant problem - this should not get called");
+ }
+ };
+ }
+ // simplest case - setAttribute expects String
+ if (java.lang.String.class.equals(reflectedArg)) {
+ return new AttributeSetter(m, arg) {
+ @Override
+ public void set(final Project p, final Object parent, final String value)
+ throws InvocationTargetException, IllegalAccessException {
+ m.invoke(parent, (Object[]) new String[] {value});
+ }
+ };
+ }
+ // char and Character get special treatment - take the first character
+ if (java.lang.Character.class.equals(reflectedArg)) {
+ return new AttributeSetter(m, arg) {
+ @Override
+ public void set(final Project p, final Object parent, final String value)
+ throws InvocationTargetException, IllegalAccessException {
+ if (value.length() == 0) {
+ throw new BuildException("The value \"\" is not a "
+ + "legal value for attribute \"" + attrName + "\"");
+ }
+ m.invoke(parent, (Object[]) new Character[] {new Character(value.charAt(0))});
+ }
+ };
+ }
+ // boolean and Boolean get special treatment because we have a nice method in Project
+ if (java.lang.Boolean.class.equals(reflectedArg)) {
+ return new AttributeSetter(m, arg) {
+ @Override
+ public void set(final Project p, final Object parent, final String value)
+ throws InvocationTargetException, IllegalAccessException {
+ m.invoke(parent, (Object[]) new Boolean[] {
+ Project.toBoolean(value) ? Boolean.TRUE : Boolean.FALSE });
+ }
+ };
+ }
+ // Class doesn't have a String constructor but a decent factory method
+ if (java.lang.Class.class.equals(reflectedArg)) {
+ return new AttributeSetter(m, arg) {
+ @Override
+ public void set(final Project p, final Object parent, final String value)
+ throws InvocationTargetException, IllegalAccessException, BuildException {
+ try {
+ m.invoke(parent, new Object[] {Class.forName(value)});
+ } catch (final ClassNotFoundException ce) {
+ throw new BuildException(ce);
+ }
+ }
+ };
+ }
+ // resolve relative paths through Project
+ if (java.io.File.class.equals(reflectedArg)) {
+ return new AttributeSetter(m, arg) {
+ @Override
+ public void set(final Project p, final Object parent, final String value)
+ throws InvocationTargetException, IllegalAccessException {
+ m.invoke(parent, new Object[] {p.resolveFile(value)});
+ }
+ };
+ }
+ // resolve Resources/FileProviders as FileResources relative to Project:
+ if (Resource.class.equals(reflectedArg) || FileProvider.class.equals(reflectedArg)) {
+ return new AttributeSetter(m, arg) {
+ @Override
+ void set(final Project p, final Object parent, final String value) throws InvocationTargetException,
+ IllegalAccessException, BuildException {
+ m.invoke(parent, new Object[] {new FileResource(p, p.resolveFile(value))});
+ };
+ };
+ }
+ // EnumeratedAttributes have their own helper class
+ if (EnumeratedAttribute.class.isAssignableFrom(reflectedArg)) {
+ return new AttributeSetter(m, arg) {
+ @Override
+ public void set(final Project p, final Object parent, final String value)
+ throws InvocationTargetException, IllegalAccessException, BuildException {
+ try {
+ final EnumeratedAttribute ea = (EnumeratedAttribute) reflectedArg.newInstance();
+ ea.setValue(value);
+ m.invoke(parent, new Object[] {ea});
+ } catch (final InstantiationException ie) {
+ throw new BuildException(ie);
+ }
+ }
+ };
+ }
+
+ final AttributeSetter setter = getEnumSetter(reflectedArg, m, arg);
+ if (setter != null) {
+ return setter;
+ }
+
+ if (java.lang.Long.class.equals(reflectedArg)) {
+ return new AttributeSetter(m, arg) {
+ @Override
+ public void set(final Project p, final Object parent, final String value)
+ throws InvocationTargetException, IllegalAccessException, BuildException {
+ try {
+ m.invoke(parent, new Object[] {
+ new Long(StringUtils.parseHumanSizes(value)) });
+ } catch (final NumberFormatException e) {
+ throw new BuildException("Can't assign non-numeric"
+ + " value '" + value + "' to"
+ + " attribute " + attrName);
+ } catch (final InvocationTargetException e) {
+ throw e;
+ } catch (final IllegalAccessException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new BuildException(e);
+ }
+ }
+ };
+ }
+ // worst case. look for a public String constructor and use it
+ // also supports new Whatever(Project, String) as for Path or Reference
+ // This is used (deliberately) for all primitives/wrappers other than
+ // char, boolean, and long.
+ boolean includeProject;
+ Constructor<?> c;
+ try {
+ // First try with Project.
+ c = reflectedArg.getConstructor(Project.class, String.class);
+ includeProject = true;
+ } catch (final NoSuchMethodException nme) {
+ // OK, try without.
+ try {
+ c = reflectedArg.getConstructor(String.class);
+ includeProject = false;
+ } catch (final NoSuchMethodException nme2) {
+ // Well, no matching constructor.
+ return null;
+ }
+ }
+ final boolean finalIncludeProject = includeProject;
+ final Constructor<?> finalConstructor = c;
+
+ return new AttributeSetter(m, arg) {
+ @Override
+ public void set(final Project p, final Object parent, final String value)
+ throws InvocationTargetException, IllegalAccessException, BuildException {
+ try {
+ final Object[] args = finalIncludeProject
+ ? new Object[] {p, value} : new Object[] {value};
+
+ final Object attribute = finalConstructor.newInstance(args);
+ if (p != null) {
+ p.setProjectReference(attribute);
+ }
+ m.invoke(parent, new Object[] {attribute});
+ } catch (final InvocationTargetException e) {
+ final Throwable cause = e.getCause();
+ if (cause instanceof IllegalArgumentException) {
+ throw new BuildException("Can't assign value '" + value
+ + "' to attribute " + attrName
+ + ", reason: "
+ + cause.getClass()
+ + " with message '"
+ + cause.getMessage() + "'");
+ }
+ throw e;
+ } catch (final InstantiationException ie) {
+ throw new BuildException(ie);
+ }
+ }
+ };
+ }
+
+ private AttributeSetter getEnumSetter(
+ final Class<?> reflectedArg, final Method m, final Class<?> arg) {
+ if (reflectedArg.isEnum()) {
+ return new AttributeSetter(m, arg) {
+ @Override
+ public void set(final Project p, final Object parent, final String value)
+ throws InvocationTargetException, IllegalAccessException,
+ BuildException {
+ Enum<?> setValue;
+ try {
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ final Enum<?> enumValue = Enum.valueOf((Class<? extends Enum>) reflectedArg,
+ value);
+ setValue = enumValue;
+ } catch (final IllegalArgumentException e) {
+ //there is specific logic here for the value
+ // being out of the allowed set of enumerations.
+ throw new BuildException("'" + value + "' is not a permitted value for "
+ + reflectedArg.getName());
+ }
+ m.invoke(parent, setValue);
+ }
+ };
+ }
+ return null;
+ }
+
+ /**
+ * Returns a description of the type of the given element in
+ * relation to a given project. This is used for logging purposes
+ * when the element is asked to cope with some data it has no way of handling.
+ *
+ * @param project The project the element is defined in. Must not be <code>null</code>.
+ *
+ * @param element The element to describe. Must not be <code>null</code>.
+ *
+ * @return a description of the element type
+ */
+ private String getElementName(final Project project, final Object element) {
+ return project.getElementName(element);
+ }
+
+ /**
+ * Extracts the name of a property from a method name by subtracting
+ * a given prefix and converting into lower case. It is up to calling
+ * code to make sure the method name does actually begin with the
+ * specified prefix - no checking is done in this method.
+ *
+ * @param methodName The name of the method in question. Must not be <code>null</code>.
+ * @param prefix The prefix to remove. Must not be <code>null</code>.
+ *
+ * @return the lower-cased method name with the prefix removed.
+ */
+ private static String getPropertyName(final String methodName, final String prefix) {
+ return methodName.substring(prefix.length()).toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * creator - allows use of create/store external
+ * to IntrospectionHelper.
+ * The class is final as it has a private constructor.
+ */
+ public static final class Creator {
+ private final NestedCreator nestedCreator;
+ private final Object parent;
+ private final Project project;
+ private Object nestedObject;
+ private String polyType;
+
+ /**
+ * Creates a new Creator instance.
+ * This object is given to the UnknownElement to create
+ * objects for sub-elements. UnknownElement calls
+ * create to create an object, the object then gets
+ * configured and then UnknownElement calls store.
+ * SetPolyType may be used to override the type used
+ * to create the object with. SetPolyType gets called before create.
+ *
+ * @param project the current project
+ * @param parent the parent object to create the object in
+ * @param nestedCreator the nested creator object to use
+ */
+ private Creator(final Project project, final Object parent, final NestedCreator nestedCreator) {
+ this.project = project;
+ this.parent = parent;
+ this.nestedCreator = nestedCreator;
+ }
+
+ /**
+ * Used to override the class used to create the object.
+ *
+ * @param polyType a ant component type name
+ */
+ public void setPolyType(final String polyType) {
+ this.polyType = polyType;
+ }
+
+ /**
+ * Create an object using this creator, which is determined by introspection.
+ *
+ * @return the created object
+ */
+ public Object create() {
+ if (polyType != null) {
+ if (!nestedCreator.isPolyMorphic()) {
+ throw new BuildException(
+ "Not allowed to use the polymorphic form for this element");
+ }
+ final ComponentHelper helper = ComponentHelper.getComponentHelper(project);
+ nestedObject = helper.createComponent(polyType);
+ if (nestedObject == null) {
+ throw new BuildException("Unable to create object of type " + polyType);
+ }
+ }
+ try {
+ nestedObject = nestedCreator.create(project, parent, nestedObject);
+ if (project != null) {
+ project.setProjectReference(nestedObject);
+ }
+ return nestedObject;
+ } catch (final IllegalAccessException ex) {
+ throw new BuildException(ex);
+ } catch (final InstantiationException ex) {
+ throw new BuildException(ex);
+ } catch (final IllegalArgumentException ex) {
+ if (polyType == null) {
+ throw ex;
+ }
+ throw new BuildException("Invalid type used " + polyType);
+ } catch (final InvocationTargetException ex) {
+ throw extractBuildException(ex);
+ }
+ }
+
+ /**
+ * @return the real object (used currently only for presetdef).
+ */
+ public Object getRealObject() {
+ return nestedCreator.getRealObject();
+ }
+
+ /**
+ * Stores the nested element object using a storage method determined by introspection.
+ *
+ */
+ public void store() {
+ try {
+ nestedCreator.store(parent, nestedObject);
+ } catch (final IllegalAccessException ex) {
+ throw new BuildException(ex);
+ } catch (final InstantiationException ex) {
+ throw new BuildException(ex);
+ } catch (final IllegalArgumentException ex) {
+ if (polyType == null) {
+ throw ex;
+ }
+ throw new BuildException("Invalid type used " + polyType);
+ } catch (final InvocationTargetException ex) {
+ throw extractBuildException(ex);
+ }
+ }
+ }
+
+ /**
+ * Internal interface used to create nested elements. Not documented
+ * in detail for reasons of source code readability.
+ */
+ private abstract static class NestedCreator {
+ private final Method method; // the method called to add/create the nested element
+
+ protected NestedCreator(final Method m) {
+ method = m;
+ }
+ Method getMethod() {
+ return method;
+ }
+ boolean isPolyMorphic() {
+ return false;
+ }
+ Object getRealObject() {
+ return null;
+ }
+ abstract Object create(Project project, Object parent, Object child)
+ throws InvocationTargetException, IllegalAccessException, InstantiationException;
+
+ void store(final Object parent, final Object child)
+ throws InvocationTargetException, IllegalAccessException, InstantiationException {
+ // DO NOTHING
+ }
+ }
+
+ private static class CreateNestedCreator extends NestedCreator {
+ CreateNestedCreator(final Method m) {
+ super(m);
+ }
+
+ @Override
+ Object create(final Project project, final Object parent, final Object ignore)
+ throws InvocationTargetException, IllegalAccessException {
+ return getMethod().invoke(parent, new Object[] {});
+ }
+ }
+
+ /** Version to use for addXXX and addConfiguredXXX */
+ private static class AddNestedCreator extends NestedCreator {
+
+ static final int ADD = 1;
+ static final int ADD_CONFIGURED = 2;
+
+ private final Constructor<?> constructor;
+ private final int behavior; // ADD or ADD_CONFIGURED
+
+ AddNestedCreator(final Method m, final Constructor<?> c, final int behavior) {
+ super(m);
+ this.constructor = c;
+ this.behavior = behavior;
+ }
+
+ @Override
+ boolean isPolyMorphic() {
+ return true;
+ }
+
+ @Override
+ Object create(final Project project, final Object parent, Object child)
+ throws InvocationTargetException, IllegalAccessException, InstantiationException {
+ if (child == null) {
+ child = constructor.newInstance(
+ constructor.getParameterTypes().length == 0
+ ? new Object[] {} : new Object[] {project});
+ }
+ if (child instanceof PreSetDef.PreSetDefinition) {
+ child = ((PreSetDef.PreSetDefinition) child).createObject(project);
+ }
+ if (behavior == ADD) {
+ istore(parent, child);
+ }
+ return child;
+ }
+
+ @Override
+ void store(final Object parent, final Object child)
+ throws InvocationTargetException, IllegalAccessException, InstantiationException {
+ if (behavior == ADD_CONFIGURED) {
+ istore(parent, child);
+ }
+ }
+
+ private void istore(final Object parent, final Object child)
+ throws InvocationTargetException, IllegalAccessException, InstantiationException {
+ getMethod().invoke(parent, new Object[] {child});
+ }
+ }
+
+ /**
+ * Internal interface used to setting element attributes. Not documented
+ * in detail for reasons of source code readability.
+ */
+ private abstract static class AttributeSetter {
+ private final Method method; // the method called to set the attribute
+ private final Class<?> type;
+ protected AttributeSetter(final Method m, final Class<?> type) {
+ method = m;
+ this.type = type;
+ }
+ void setObject(final Project p, final Object parent, final Object value)
+ throws InvocationTargetException, IllegalAccessException, BuildException {
+ if (type != null) {
+ Class<?> useType = type;
+ if (type.isPrimitive()) {
+ if (value == null) {
+ throw new BuildException(
+ "Attempt to set primitive "
+ + getPropertyName(method.getName(), "set")
+ + " to null on " + parent);
+ }
+ useType = PRIMITIVE_TYPE_MAP.get(type);
+ }
+ if (value == null || useType.isInstance(value)) {
+ method.invoke(parent, new Object[] {value});
+ return;
+ }
+ }
+ set(p, parent, value.toString());
+ }
+ abstract void set(Project p, Object parent, String value)
+ throws InvocationTargetException, IllegalAccessException, BuildException;
+ }
+
+ /**
+ * Clears the static cache of on build finished.
+ */
+ public static synchronized void clearCache() {
+ HELPERS.clear();
+ }
+
+ /**
+ * Create a NestedCreator for the given element.
+ * @param project owning project
+ * @param parent Parent object used to create the instance.
+ * @param elementName name of the element
+ * @return a nested creator, or null if there is no component of the given name, or it
+ * has no matching add type methods
+ * @throws BuildException
+ */
+ private NestedCreator createAddTypeCreator(
+ final Project project, final Object parent, final String elementName) throws BuildException {
+ if (addTypeMethods.size() == 0) {
+ return null;
+ }
+ final ComponentHelper helper = ComponentHelper.getComponentHelper(project);
+
+ final MethodAndObject restricted = createRestricted(
+ helper, elementName, addTypeMethods);
+ final MethodAndObject topLevel = createTopLevel(
+ helper, elementName, addTypeMethods);
+
+ if (restricted == null && topLevel == null) {
+ return null;
+ }
+
+ if (restricted != null && topLevel != null) {
+ throw new BuildException(
+ "ambiguous: type and component definitions for "
+ + elementName);
+ }
+
+ final MethodAndObject methodAndObject
+ = restricted != null ? restricted : topLevel;
+
+ Object rObject = methodAndObject.object;
+ if (methodAndObject.object instanceof PreSetDef.PreSetDefinition) {
+ rObject = ((PreSetDef.PreSetDefinition) methodAndObject.object)
+ .createObject(project);
+ }
+ final Object nestedObject = methodAndObject.object;
+ final Object realObject = rObject;
+
+ return new NestedCreator(methodAndObject.method) {
+ @Override
+ Object create(final Project project, final Object parent, final Object ignore)
+ throws InvocationTargetException, IllegalAccessException {
+ if (!getMethod().getName().endsWith("Configured")) {
+ getMethod().invoke(parent, new Object[] {realObject});
+ }
+ return nestedObject;
+ }
+
+ @Override
+ Object getRealObject() {
+ return realObject;
+ }
+
+ @Override
+ void store(final Object parent, final Object child) throws InvocationTargetException,
+ IllegalAccessException, InstantiationException {
+ if (getMethod().getName().endsWith("Configured")) {
+ getMethod().invoke(parent, new Object[] {realObject});
+ }
+ }
+ };
+ }
+
+ /**
+ * Inserts an add or addConfigured method into
+ * the addTypeMethods array. The array is
+ * ordered so that the more derived classes are first.
+ * If both add and addConfigured are present, the addConfigured will take priority.
+ * @param method the <code>Method</code> to insert.
+ */
+ private void insertAddTypeMethod(final Method method) {
+ final Class<?> argClass = method.getParameterTypes()[0];
+ final int size = addTypeMethods.size();
+ for (int c = 0; c < size; ++c) {
+ final Method current = addTypeMethods.get(c);
+ if (current.getParameterTypes()[0].equals(argClass)) {
+ if (method.getName().equals("addConfigured")) {
+ // add configured replaces the add method
+ addTypeMethods.set(c, method);
+ }
+ return; // Already present
+ }
+ if (current.getParameterTypes()[0].isAssignableFrom(argClass)) {
+ addTypeMethods.add(c, method);
+ return; // higher derived
+ }
+ }
+ addTypeMethods.add(method);
+ }
+
+ /**
+ * Search the list of methods to find the first method
+ * that has a parameter that accepts the nested element object.
+ * @param paramClass the <code>Class</code> type to search for.
+ * @param methods the <code>List</code> of methods to search.
+ * @return a matching <code>Method</code>; null if none found.
+ */
+ private Method findMatchingMethod(final Class<?> paramClass, final List<Method> methods) {
+ if (paramClass == null) {
+ return null;
+ }
+ Class<?> matchedClass = null;
+ Method matchedMethod = null;
+
+ final int size = methods.size();
+ for (int i = 0; i < size; ++i) {
+ final Method method = methods.get(i);
+ final Class<?> methodClass = method.getParameterTypes()[0];
+ if (methodClass.isAssignableFrom(paramClass)) {
+ if (matchedClass == null) {
+ matchedClass = methodClass;
+ matchedMethod = method;
+ } else if (!methodClass.isAssignableFrom(matchedClass)) {
+ throw new BuildException("ambiguous: types " + matchedClass.getName() + " and "
+ + methodClass.getName() + " match " + paramClass.getName());
+ }
+ }
+ }
+ return matchedMethod;
+ }
+
+ private String condenseText(final String text) {
+ if (text.length() <= MAX_REPORT_NESTED_TEXT) {
+ return text;
+ }
+ final int ends = (MAX_REPORT_NESTED_TEXT - ELLIPSIS.length()) / 2;
+ return new StringBuffer(text).replace(ends, text.length() - ends, ELLIPSIS).toString();
+ }
+
+
+ private static class MethodAndObject {
+ private final Method method;
+ private final Object object;
+ public MethodAndObject(final Method method, final Object object) {
+ this.method = method;
+ this.object = object;
+ }
+ }
+
+ /**
+ *
+ */
+ private AntTypeDefinition findRestrictedDefinition(
+ final ComponentHelper helper, final String componentName, final List<Method> methods) {
+ AntTypeDefinition definition = null;
+ Class<?> matchedDefinitionClass = null;
+
+ final List<AntTypeDefinition> definitions = helper.getRestrictedDefinitions(componentName);
+ if (definitions == null) {
+ return null;
+ }
+ synchronized (definitions) {
+ final int size = definitions.size();
+ for (int i = 0; i < size; ++i) {
+ final AntTypeDefinition d = definitions.get(i);
+ final Class<?> exposedClass = d.getExposedClass(helper.getProject());
+ if (exposedClass == null) {
+ continue;
+ }
+ final Method method = findMatchingMethod(exposedClass, methods);
+ if (method == null) {
+ continue;
+ }
+ if (matchedDefinitionClass != null) {
+ throw new BuildException(
+ "ambiguous: restricted definitions for "
+ + componentName + " "
+ + matchedDefinitionClass + " and " + exposedClass);
+ }
+ matchedDefinitionClass = exposedClass;
+ definition = d;
+ }
+ }
+ return definition;
+ }
+
+ private MethodAndObject createRestricted(
+ final ComponentHelper helper, final String elementName, final List<Method> addTypeMethods) {
+
+ final Project project = helper.getProject();
+
+ final AntTypeDefinition restrictedDefinition =
+ findRestrictedDefinition(helper, elementName, addTypeMethods);
+
+ if (restrictedDefinition == null) {
+ return null;
+ }
+
+ final Method addMethod = findMatchingMethod(
+ restrictedDefinition.getExposedClass(project), addTypeMethods);
+ if (addMethod == null) {
+ throw new BuildException(
+ "Ant Internal Error - contract mismatch for "
+ + elementName);
+ }
+ final Object addedObject = restrictedDefinition.create(project);
+ if (addedObject == null) {
+ throw new BuildException(
+ "Failed to create object " + elementName
+ + " of type " + restrictedDefinition.getTypeClass(project));
+ }
+ return new MethodAndObject(addMethod, addedObject);
+ }
+
+ private MethodAndObject createTopLevel(
+ final ComponentHelper helper, final String elementName, final List<Method> methods) {
+ final Class<?> clazz = helper.getComponentClass(elementName);
+ if (clazz == null) {
+ return null;
+ }
+ final Method addMethod = findMatchingMethod(clazz, addTypeMethods);
+ if (addMethod == null) {
+ return null;
+ }
+ final Object addedObject = helper.createComponent(elementName);
+ return new MethodAndObject(addMethod, addedObject);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Location.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Location.java
new file mode 100644
index 00000000..8e25d107
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Location.java
@@ -0,0 +1,179 @@
+/*
+ * 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.tools.ant;
+
+import java.io.Serializable;
+
+import org.apache.tools.ant.util.FileUtils;
+import org.xml.sax.Locator;
+
+/**
+ * Stores the location of a piece of text within a file (file name,
+ * line number and column number). Note that the column number is
+ * currently ignored.
+ *
+ */
+public class Location implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /** Name of the file. */
+ private final String fileName;
+ /** Line number within the file. */
+ private final int lineNumber;
+ /** Column number within the file. */
+ private final int columnNumber;
+
+ /** Location to use when one is needed but no information is available */
+ public static final Location UNKNOWN_LOCATION = new Location();
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Creates an "unknown" location.
+ */
+ private Location() {
+ this(null, 0, 0);
+ }
+
+ /**
+ * Creates a location consisting of a file name but no line number or
+ * column number.
+ *
+ * @param fileName The name of the file. May be <code>null</code>,
+ * in which case the location is equivalent to
+ * {@link #UNKNOWN_LOCATION UNKNOWN_LOCATION}.
+ */
+ public Location(String fileName) {
+ this(fileName, 0, 0);
+ }
+
+ /**
+ * Creates a location from the SAX locator using the system ID as
+ * the filename.
+ *
+ * @param loc Must not be <code>null</code>.
+ *
+ * @since Ant 1.6
+ */
+ public Location(Locator loc) {
+ this(loc.getSystemId(), loc.getLineNumber(), loc.getColumnNumber());
+ }
+
+ /**
+ * Creates a location consisting of a file name, line number and
+ * column number.
+ *
+ * @param fileName The name of the file. May be <code>null</code>,
+ * in which case the location is equivalent to
+ * {@link #UNKNOWN_LOCATION UNKNOWN_LOCATION}.
+ *
+ * @param lineNumber Line number within the file. Use 0 for unknown
+ * positions within a file.
+ * @param columnNumber Column number within the line.
+ */
+ public Location(String fileName, int lineNumber, int columnNumber) {
+ if (fileName != null && fileName.startsWith("file:")) {
+ this.fileName = FILE_UTILS.fromURI(fileName);
+ } else {
+ this.fileName = fileName;
+ }
+ this.lineNumber = lineNumber;
+ this.columnNumber = columnNumber;
+ }
+
+ /**
+ * @return the filename portion of the location
+ * @since Ant 1.6
+ */
+ public String getFileName() {
+ return fileName;
+ }
+
+ /**
+ * @return the line number
+ * @since Ant 1.6
+ */
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * @return the column number
+ * @since Ant 1.7
+ */
+ public int getColumnNumber() {
+ return columnNumber;
+ }
+
+ /**
+ * Returns the file name, line number, a colon and a trailing space.
+ * An error message can be appended easily. For unknown locations, an
+ * empty string is returned.
+ *
+ * @return a String of the form <code>"fileName:lineNumber: "</code>
+ * if both file name and line number are known,
+ * <code>"fileName: "</code> if only the file name is known,
+ * and the empty string for unknown locations.
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+
+ if (fileName != null) {
+ buf.append(fileName);
+
+ if (lineNumber != 0) {
+ buf.append(":");
+ buf.append(lineNumber);
+ }
+
+ buf.append(": ");
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Equality operation.
+ * @param other the object to compare to.
+ * @return true if the other object contains the same information
+ * as this object.
+ * @since Ant 1.6.3
+ */
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null) {
+ return false;
+ }
+ if (!(other.getClass() == getClass())) {
+ return false;
+ }
+ return toString().equals(other.toString());
+ }
+
+ /**
+ * Hash operation.
+ * @return a hash code value for this location.
+ * @since Ant 1.6.3
+ */
+ public int hashCode() {
+ return toString().hashCode();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/MagicNames.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/MagicNames.java
new file mode 100644
index 00000000..bc39a257
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/MagicNames.java
@@ -0,0 +1,293 @@
+/*
+ * 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.tools.ant;
+
+import org.apache.tools.ant.launch.Launcher;
+
+/**
+ * Magic names used within Ant.
+ *
+ * Not all magic names are here yet.
+ *
+ * @since Ant 1.6
+ */
+public final class MagicNames {
+
+ private MagicNames() {
+ }
+
+ /**
+ * prefix for antlib URIs:
+ * {@value}
+ */
+ public static final String ANTLIB_PREFIX = "antlib:";
+
+ /**
+ * Ant version property.
+ * Value: {@value}
+ */
+ public static final String ANT_VERSION = "ant.version";
+
+ /**
+ * System classpath policy.
+ * Value: {@value}
+ */
+ public static final String BUILD_SYSCLASSPATH = "build.sysclasspath";
+
+ /**
+ * The name of the script repository used by the script repo task.
+ * Value {@value}
+ */
+ public static final String SCRIPT_REPOSITORY = "org.apache.ant.scriptrepo";
+
+ /**
+ * The name of the reference to the System Class Loader.
+ * Value {@value}
+ **/
+ public static final String SYSTEM_LOADER_REF = "ant.coreLoader";
+
+ /**
+ * Name of the property which can provide an override of the repository dir.
+ * for the libraries task
+ * Value {@value}
+ */
+ public static final String REPOSITORY_DIR_PROPERTY = "ant.maven.repository.dir";
+
+ /**
+ * Name of the property which can provide an override of the repository URL.
+ * for the libraries task
+ * Value {@value}
+ */
+ public static final String REPOSITORY_URL_PROPERTY = "ant.maven.repository.url";
+
+ /**
+ * name of the resource that taskdefs are stored under.
+ * Value: {@value}
+ */
+ public static final String TASKDEF_PROPERTIES_RESOURCE =
+ "/org/apache/tools/ant/taskdefs/defaults.properties";
+
+ /**
+ * name of the resource that typedefs are stored under.
+ * Value: {@value}
+ */
+ public static final String TYPEDEFS_PROPERTIES_RESOURCE =
+ "/org/apache/tools/ant/types/defaults.properties";
+
+ /**
+ * Reference to the current Ant executor.
+ * Value: {@value}
+ */
+ public static final String ANT_EXECUTOR_REFERENCE = "ant.executor";
+
+ /**
+ * Property defining the classname of an executor.
+ * Value: {@value}
+ */
+ public static final String ANT_EXECUTOR_CLASSNAME = "ant.executor.class";
+
+ /**
+ * property name for basedir of the project.
+ * Value: {@value}
+ */
+ public static final String PROJECT_BASEDIR = "basedir";
+
+ /**
+ * property for ant file name.
+ * Value: {@value}
+ */
+ public static final String ANT_FILE = "ant.file";
+
+ /**
+ * property for type of ant build file (either file or url)
+ * Value: {@value}
+ * @since Ant 1.8.0
+ */
+ public static final String ANT_FILE_TYPE = "ant.file.type";
+
+ /**
+ * ant build file of type file
+ * Value: {@value}
+ * @since Ant 1.8.0
+ */
+ public static final String ANT_FILE_TYPE_FILE = "file";
+
+ /**
+ * ant build file of type url
+ * Value: {@value}
+ * @since Ant 1.8.0
+ */
+ public static final String ANT_FILE_TYPE_URL = "url";
+
+ /**
+ * Property used to store the java version ant is running in.
+ * Value: {@value}
+ * @since Ant 1.7
+ */
+ public static final String ANT_JAVA_VERSION = "ant.java.version";
+
+ /**
+ * Property used to store the location of ant.
+ * Value: {@value}
+ * @since Ant 1.7
+ */
+ public static final String ANT_HOME = Launcher.ANTHOME_PROPERTY;
+
+ /**
+ * Property used to store the location of the ant library (typically the ant.jar file.)
+ * Value: {@value}
+ * @since Ant 1.7
+ */
+ public static final String ANT_LIB = "ant.core.lib";
+
+ /**
+ * property for regular expression implementation.
+ * Value: {@value}
+ */
+ public static final String REGEXP_IMPL = "ant.regexp.regexpimpl";
+
+ /**
+ * property that provides the default value for javac's and
+ * javadoc's source attribute.
+ * Value: {@value}
+ * @since Ant 1.7
+ */
+ public static final String BUILD_JAVAC_SOURCE = "ant.build.javac.source";
+
+ /**
+ * property that provides the default value for javac's target attribute.
+ * Value: {@value}
+ * @since Ant 1.7
+ */
+ public static final String BUILD_JAVAC_TARGET = "ant.build.javac.target";
+
+ /**
+ * Name of the magic property that controls classloader reuse.
+ * Value: {@value}
+ * @since Ant 1.4.
+ */
+ public static final String REFID_CLASSPATH_REUSE_LOADER = "ant.reuse.loader";
+
+ /**
+ * Prefix used to store classloader references.
+ * Value: {@value}
+ */
+ public static final String REFID_CLASSPATH_LOADER_PREFIX = "ant.loader.";
+
+ /**
+ * Reference used to store the property helper.
+ * Value: {@value}
+ */
+ public static final String REFID_PROPERTY_HELPER = "ant.PropertyHelper";
+
+ /**
+ * Reference used to store the local properties.
+ * Value: {@value}
+ */
+ public static final String REFID_LOCAL_PROPERTIES = "ant.LocalProperties";
+
+ /**
+ * Name of JVM system property which provides the name of the ProjectHelper class to use.
+ * Value: {@value}
+ */
+ public static final String PROJECT_HELPER_CLASS = "org.apache.tools.ant.ProjectHelper";
+
+ /**
+ * The service identifier in jars which provide ProjectHelper implementations.
+ * Value: {@value}
+ */
+ public static final String PROJECT_HELPER_SERVICE =
+ "META-INF/services/org.apache.tools.ant.ProjectHelper";
+
+ /**
+ * Name of ProjectHelper reference that we add to a project.
+ * Value: {@value}
+ */
+ public static final String REFID_PROJECT_HELPER = "ant.projectHelper";
+
+ /**
+ * Name of the property holding the name of the currently
+ * executing project, if one has been specified.
+ *
+ * Value: {@value}
+ * @since Ant 1.8.0
+ */
+ public static final String PROJECT_NAME = "ant.project.name";
+
+ /**
+ * Name of the property holding the default target of the
+ * currently executing project, if one has been specified.
+ *
+ * Value: {@value}
+ * @since Ant 1.8.0
+ */
+ public static final String PROJECT_DEFAULT_TARGET
+ = "ant.project.default-target";
+
+ /**
+ * Name of the property holding a comma separated list of targets
+ * that have been invoked (from the command line).
+ *
+ * Value: {@value}
+ * @since Ant 1.8.0
+ */
+ public static final String PROJECT_INVOKED_TARGETS
+ = "ant.project.invoked-targets";
+
+ /**
+ * Name of the project reference holding an instance of {@link
+ * org.apache.tools.ant.taskdefs.launcher.CommandLauncher} to use
+ * when executing commands with the help of an external skript.
+ *
+ * <p>Alternatively this is the name of a system property holding
+ * the fully qualified class name of a {@link
+ * org.apache.tools.ant.taskdefs.launcher.CommandLauncher}.</p>
+ *
+ * Value: {@value}
+ * @since Ant 1.9.0
+ */
+ public static final String ANT_SHELL_LAUNCHER_REF_ID = "ant.shellLauncher";
+
+ /**
+ * Name of the project reference holding an instance of {@link
+ * org.apache.tools.ant.taskdefs.launcher.CommandLauncher} to use
+ * when executing commands without the help of an external skript.
+ *
+ * <p>Alternatively this is the name of a system property holding
+ * the fully qualified class name of a {@link
+ * org.apache.tools.ant.taskdefs.launcher.CommandLauncher}.</p>
+ *
+ * Value: {@value}
+ * @since Ant 1.9.0
+ */
+ public static final String ANT_VM_LAUNCHER_REF_ID = "ant.vmLauncher";
+ /**
+ * Name of the namespace "type".
+ * (Note: cannot be used as an element.)
+ * @since Ant 1.9.1
+ */
+ public static final String ATTRIBUTE_NAMESPACE = "attribute namespace";
+
+ /**
+ * Name of the property which can provide an override of the
+ * User-Agent used in &lt;get&gt; tasks.
+ * Value {@value}
+ */
+ public static final String HTTP_AGENT_PROPERTY = "ant.http.agent";
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Main.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Main.java
new file mode 100644
index 00000000..a9c23a53
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Main.java
@@ -0,0 +1,1317 @@
+/*
+ * 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.tools.ant;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Vector;
+
+import org.apache.tools.ant.input.DefaultInputHandler;
+import org.apache.tools.ant.input.InputHandler;
+import org.apache.tools.ant.launch.AntMain;
+import org.apache.tools.ant.listener.SilentLogger;
+import org.apache.tools.ant.property.GetProperty;
+import org.apache.tools.ant.property.ResolvePropertyMap;
+import org.apache.tools.ant.util.ClasspathUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.ProxySetup;
+
+
+/**
+ * Command line entry point into Ant. This class is entered via the
+ * canonical `public static void main` entry point and reads the
+ * command line arguments. It then assembles and executes an Ant
+ * project.
+ * <p>
+ * If you integrating Ant into some other tool, this is not the class
+ * to use as an entry point. Please see the source code of this
+ * class to see how it manipulates the Ant project classes.
+ *
+ */
+public class Main implements AntMain {
+
+ /**
+ * A Set of args that are handled by the launcher and should
+ * not be seen by Main.
+ */
+ private static final Set<String> LAUNCH_COMMANDS = Collections
+ .unmodifiableSet(new HashSet<String>(Arrays.asList("-lib", "-cp", "-noclasspath",
+ "--noclasspath", "-nouserlib", "-main")));
+
+ /** The default build file name. {@value} */
+ public static final String DEFAULT_BUILD_FILENAME = "build.xml";
+
+ /** Our current message output status. Follows Project.MSG_XXX. */
+ private int msgOutputLevel = Project.MSG_INFO;
+
+ /** File that we are using for configuration. */
+ private File buildFile; /* null */
+
+ /** Stream to use for logging. */
+ private static PrintStream out = System.out;
+
+ /** Stream that we are using for logging error messages. */
+ private static PrintStream err = System.err;
+
+ /** The build targets. */
+ private final Vector<String> targets = new Vector<String>();
+
+ /** Set of properties that can be used by tasks. */
+ private final Properties definedProps = new Properties();
+
+ /** Names of classes to add as listeners to project. */
+ private final Vector<String> listeners = new Vector<String>(1);
+
+ /** File names of property files to load on startup. */
+ private final Vector<String> propertyFiles = new Vector<String>(1);
+
+ /** Indicates whether this build is to support interactive input */
+ private boolean allowInput = true;
+
+ /** keep going mode */
+ private boolean keepGoingMode = false;
+
+ /**
+ * The Ant logger class. There may be only one logger. It will have
+ * the right to use the 'out' PrintStream. The class must implements the
+ * BuildLogger interface.
+ */
+ private String loggerClassname = null;
+
+ /**
+ * The Ant InputHandler class. There may be only one input
+ * handler.
+ */
+ private String inputHandlerClassname = null;
+
+ /**
+ * Whether or not output to the log is to be unadorned.
+ */
+ private boolean emacsMode = false;
+
+ /**
+ * Whether or not log output should be reduced to the minimum
+ */
+ private boolean silent = false;
+
+ /**
+ * Whether or not this instance has successfully been
+ * constructed and is ready to run.
+ */
+ private boolean readyToRun = false;
+
+ /**
+ * Whether or not we should only parse and display the project help
+ * information.
+ */
+ private boolean projectHelp = false;
+
+ /**
+ * Whether or not a logfile is being used. This is used to
+ * check if the output streams must be closed.
+ */
+ private static boolean isLogFileUsed = false;
+
+ /**
+ * optional thread priority
+ */
+ private Integer threadPriority = null;
+
+ /**
+ * proxy flag: default is false
+ */
+ private boolean proxy = false;
+
+ private final Map<Class<?>, List<String>> extraArguments = new HashMap<Class<?>, List<String>>();
+
+ private static final GetProperty NOPROPERTIES = new GetProperty() {
+ public Object getProperty(final String aName) {
+ // No existing property takes precedence
+ return null;
+ }
+ };
+
+
+
+
+ /**
+ * Prints the message of the Throwable if it (the message) is not
+ * <code>null</code>.
+ *
+ * @param t Throwable to print the message of.
+ * Must not be <code>null</code>.
+ */
+ private static void printMessage(final Throwable t) {
+ final String message = t.getMessage();
+ if (message != null) {
+ System.err.println(message);
+ }
+ }
+
+ /**
+ * Creates a new instance of this class using the
+ * arguments specified, gives it any extra user properties which have been
+ * specified, and then runs the build using the classloader provided.
+ *
+ * @param args Command line arguments. Must not be <code>null</code>.
+ * @param additionalUserProperties Any extra properties to use in this
+ * build. May be <code>null</code>, which is the equivalent to
+ * passing in an empty set of properties.
+ * @param coreLoader Classloader used for core classes. May be
+ * <code>null</code> in which case the system classloader is used.
+ */
+ public static void start(final String[] args, final Properties additionalUserProperties,
+ final ClassLoader coreLoader) {
+ final Main m = new Main();
+ m.startAnt(args, additionalUserProperties, coreLoader);
+ }
+
+ /**
+ * Start Ant
+ * @param args command line args
+ * @param additionalUserProperties properties to set beyond those that
+ * may be specified on the args list
+ * @param coreLoader - not used
+ *
+ * @since Ant 1.6
+ */
+ public void startAnt(final String[] args, final Properties additionalUserProperties,
+ final ClassLoader coreLoader) {
+
+ try {
+ processArgs(args);
+ } catch (final Throwable exc) {
+ handleLogfile();
+ printMessage(exc);
+ exit(1);
+ return;
+ }
+
+ if (additionalUserProperties != null) {
+ for (final Enumeration<?> e = additionalUserProperties.keys();
+ e.hasMoreElements();) {
+ final String key = (String) e.nextElement();
+ final String property = additionalUserProperties.getProperty(key);
+ definedProps.put(key, property);
+ }
+ }
+
+ // expect the worst
+ int exitCode = 1;
+ try {
+ try {
+ runBuild(coreLoader);
+ exitCode = 0;
+ } catch (final ExitStatusException ese) {
+ exitCode = ese.getStatus();
+ if (exitCode != 0) {
+ throw ese;
+ }
+ }
+ } catch (final BuildException be) {
+ if (err != System.err) {
+ printMessage(be);
+ }
+ } catch (final Throwable exc) {
+ exc.printStackTrace();
+ printMessage(exc);
+ } finally {
+ handleLogfile();
+ }
+ exit(exitCode);
+ }
+
+ /**
+ * This operation is expected to call {@link System#exit(int)}, which
+ * is what the base version does.
+ * However, it is possible to do something else.
+ * @param exitCode code to exit with
+ */
+ protected void exit(final int exitCode) {
+ System.exit(exitCode);
+ }
+
+ /**
+ * Close logfiles, if we have been writing to them.
+ *
+ * @since Ant 1.6
+ */
+ private static void handleLogfile() {
+ if (isLogFileUsed) {
+ FileUtils.close(out);
+ FileUtils.close(err);
+ }
+ }
+
+ /**
+ * Command line entry point. This method kicks off the building
+ * of a project object and executes a build using either a given
+ * target or the default target.
+ *
+ * @param args Command line arguments. Must not be <code>null</code>.
+ */
+ public static void main(final String[] args) {
+ start(args, null, null);
+ }
+
+ /**
+ * Constructor used when creating Main for later arg processing
+ * and startup
+ */
+ public Main() {
+ }
+
+ /**
+ * Sole constructor, which parses and deals with command line
+ * arguments.
+ *
+ * @param args Command line arguments. Must not be <code>null</code>.
+ *
+ * @exception BuildException if the specified build file doesn't exist
+ * or is a directory.
+ *
+ * @deprecated since 1.6.x
+ */
+ @Deprecated
+ protected Main(final String[] args) throws BuildException {
+ processArgs(args);
+ }
+
+ /**
+ * Process command line arguments.
+ * When ant is started from Launcher, launcher-only arguments do not get
+ * passed through to this routine.
+ *
+ * @param args the command line arguments.
+ *
+ * @since Ant 1.6
+ */
+ private void processArgs(final String[] args) {
+ String searchForThis = null;
+ boolean searchForFile = false;
+ PrintStream logTo = null;
+
+ // cycle through given args
+
+ boolean justPrintUsage = false;
+ boolean justPrintVersion = false;
+ boolean justPrintDiagnostics = false;
+
+ final ArgumentProcessorRegistry processorRegistry = ArgumentProcessorRegistry.getInstance();
+
+ for (int i = 0; i < args.length; i++) {
+ final String arg = args[i];
+
+ if (arg.equals("-help") || arg.equals("-h")) {
+ justPrintUsage = true;
+ } else if (arg.equals("-version")) {
+ justPrintVersion = true;
+ } else if (arg.equals("-diagnostics")) {
+ justPrintDiagnostics = true;
+ } else if (arg.equals("-quiet") || arg.equals("-q")) {
+ msgOutputLevel = Project.MSG_WARN;
+ } else if (arg.equals("-verbose") || arg.equals("-v")) {
+ msgOutputLevel = Project.MSG_VERBOSE;
+ } else if (arg.equals("-debug") || arg.equals("-d")) {
+ msgOutputLevel = Project.MSG_DEBUG;
+ } else if (arg.equals("-silent") || arg.equals("-S")) {
+ silent = true;
+ } else if (arg.equals("-noinput")) {
+ allowInput = false;
+ } else if (arg.equals("-logfile") || arg.equals("-l")) {
+ try {
+ final File logFile = new File(args[i + 1]);
+ i++;
+ logTo = new PrintStream(new FileOutputStream(logFile));
+ isLogFileUsed = true;
+ } catch (final IOException ioe) {
+ final String msg = "Cannot write on the specified log file. "
+ + "Make sure the path exists and you have write "
+ + "permissions.";
+ throw new BuildException(msg);
+ } catch (final ArrayIndexOutOfBoundsException aioobe) {
+ final String msg = "You must specify a log file when "
+ + "using the -log argument";
+ throw new BuildException(msg);
+ }
+ } else if (arg.equals("-buildfile") || arg.equals("-file")
+ || arg.equals("-f")) {
+ i = handleArgBuildFile(args, i);
+ } else if (arg.equals("-listener")) {
+ i = handleArgListener(args, i);
+ } else if (arg.startsWith("-D")) {
+ i = handleArgDefine(args, i);
+ } else if (arg.equals("-logger")) {
+ i = handleArgLogger(args, i);
+ } else if (arg.equals("-inputhandler")) {
+ i = handleArgInputHandler(args, i);
+ } else if (arg.equals("-emacs") || arg.equals("-e")) {
+ emacsMode = true;
+ } else if (arg.equals("-projecthelp") || arg.equals("-p")) {
+ // set the flag to display the targets and quit
+ projectHelp = true;
+ } else if (arg.equals("-find") || arg.equals("-s")) {
+ searchForFile = true;
+ // eat up next arg if present, default to build.xml
+ if (i < args.length - 1) {
+ searchForThis = args[++i];
+ }
+ } else if (arg.startsWith("-propertyfile")) {
+ i = handleArgPropertyFile(args, i);
+ } else if (arg.equals("-k") || arg.equals("-keep-going")) {
+ keepGoingMode = true;
+ } else if (arg.equals("-nice")) {
+ i = handleArgNice(args, i);
+ } else if (LAUNCH_COMMANDS.contains(arg)) {
+ //catch script/ant mismatch with a meaningful message
+ //we could ignore it, but there are likely to be other
+ //version problems, so we stamp down on the configuration now
+ final String msg = "Ant's Main method is being handed "
+ + "an option " + arg + " that is only for the launcher class."
+ + "\nThis can be caused by a version mismatch between "
+ + "the ant script/.bat file and Ant itself.";
+ throw new BuildException(msg);
+ } else if (arg.equals("-autoproxy")) {
+ proxy = true;
+ } else if (arg.startsWith("-")) {
+ boolean processed = false;
+ for (final ArgumentProcessor processor : processorRegistry.getProcessors()) {
+ final int newI = processor.readArguments(args, i);
+ if (newI != -1) {
+ List<String> extraArgs = extraArguments.get(processor.getClass());
+ if (extraArgs == null) {
+ extraArgs = new ArrayList<String>();
+ extraArguments.put(processor.getClass(), extraArgs);
+ }
+ for (; i < newI && i < args.length; i++) {
+ extraArgs.add(args[i]);
+ }
+ processed = true;
+ break;
+ }
+ }
+ if (!processed) {
+ // we don't have any more args to recognize!
+ final String msg = "Unknown argument: " + arg;
+ System.err.println(msg);
+ printUsage();
+ throw new BuildException("");
+ }
+ } else {
+ // if it's no other arg, it may be the target
+ targets.addElement(arg);
+ }
+ }
+
+ if (msgOutputLevel >= Project.MSG_VERBOSE || justPrintVersion) {
+ printVersion(msgOutputLevel);
+ }
+
+ if (justPrintUsage || justPrintVersion || justPrintDiagnostics) {
+ if (justPrintUsage) {
+ printUsage();
+ }
+ if (justPrintDiagnostics) {
+ Diagnostics.doReport(System.out, msgOutputLevel);
+ }
+ return;
+ }
+
+ // if buildFile was not specified on the command line,
+ if (buildFile == null) {
+ // but -find then search for it
+ if (searchForFile) {
+ if (searchForThis != null) {
+ buildFile = findBuildFile(System.getProperty("user.dir"), searchForThis);
+ if (buildFile == null) {
+ throw new BuildException("Could not locate a build file!");
+ }
+ } else {
+ // no search file specified: so search an existing default file
+ final Iterator<ProjectHelper> it = ProjectHelperRepository.getInstance().getHelpers();
+ do {
+ final ProjectHelper helper = it.next();
+ searchForThis = helper.getDefaultBuildFile();
+ if (msgOutputLevel >= Project.MSG_VERBOSE) {
+ System.out.println("Searching the default build file: " + searchForThis);
+ }
+ buildFile = findBuildFile(System.getProperty("user.dir"), searchForThis);
+ } while (buildFile == null && it.hasNext());
+ if (buildFile == null) {
+ throw new BuildException("Could not locate a build file!");
+ }
+ }
+ } else {
+ // no build file specified: so search an existing default file
+ final Iterator<ProjectHelper> it = ProjectHelperRepository.getInstance().getHelpers();
+ do {
+ final ProjectHelper helper = it.next();
+ buildFile = new File(helper.getDefaultBuildFile());
+ if (msgOutputLevel >= Project.MSG_VERBOSE) {
+ System.out.println("Trying the default build file: " + buildFile);
+ }
+ } while (!buildFile.exists() && it.hasNext());
+ }
+ }
+
+ // make sure buildfile exists
+ if (!buildFile.exists()) {
+ System.out.println("Buildfile: " + buildFile + " does not exist!");
+ throw new BuildException("Build failed");
+ }
+
+ if (buildFile.isDirectory()) {
+ final File whatYouMeant = new File(buildFile, "build.xml");
+ if (whatYouMeant.isFile()) {
+ buildFile = whatYouMeant;
+ } else {
+ System.out.println("What? Buildfile: " + buildFile + " is a dir!");
+ throw new BuildException("Build failed");
+ }
+ }
+
+ // Normalize buildFile for re-import detection
+ buildFile =
+ FileUtils.getFileUtils().normalize(buildFile.getAbsolutePath());
+
+ // Load the property files specified by -propertyfile
+ loadPropertyFiles();
+
+ if (msgOutputLevel >= Project.MSG_INFO) {
+ System.out.println("Buildfile: " + buildFile);
+ }
+
+ if (logTo != null) {
+ out = logTo;
+ err = logTo;
+ System.setOut(out);
+ System.setErr(err);
+ }
+ readyToRun = true;
+ }
+
+ // --------------------------------------------------------
+ // Methods for handling the command line arguments
+ // --------------------------------------------------------
+
+ /** Handle the -buildfile, -file, -f argument */
+ private int handleArgBuildFile(final String[] args, int pos) {
+ try {
+ buildFile = new File(
+ args[++pos].replace('/', File.separatorChar));
+ } catch (final ArrayIndexOutOfBoundsException aioobe) {
+ throw new BuildException(
+ "You must specify a buildfile when using the -buildfile argument");
+ }
+ return pos;
+ }
+
+ /** Handle -listener argument */
+ private int handleArgListener(final String[] args, int pos) {
+ try {
+ listeners.addElement(args[pos + 1]);
+ pos++;
+ } catch (final ArrayIndexOutOfBoundsException aioobe) {
+ final String msg = "You must specify a classname when "
+ + "using the -listener argument";
+ throw new BuildException(msg);
+ }
+ return pos;
+ }
+
+ /** Handler -D argument */
+ private int handleArgDefine(final String[] args, int argPos) {
+ /* Interestingly enough, we get to here when a user
+ * uses -Dname=value. However, in some cases, the OS
+ * goes ahead and parses this out to args
+ * {"-Dname", "value"}
+ * so instead of parsing on "=", we just make the "-D"
+ * characters go away and skip one argument forward.
+ *
+ * I don't know how to predict when the JDK is going
+ * to help or not, so we simply look for the equals sign.
+ */
+ final String arg = args[argPos];
+ String name = arg.substring(2, arg.length());
+ String value = null;
+ final int posEq = name.indexOf("=");
+ if (posEq > 0) {
+ value = name.substring(posEq + 1);
+ name = name.substring(0, posEq);
+ } else if (argPos < args.length - 1) {
+ value = args[++argPos];
+ } else {
+ throw new BuildException("Missing value for property "
+ + name);
+ }
+ definedProps.put(name, value);
+ return argPos;
+ }
+
+ /** Handle the -logger argument. */
+ private int handleArgLogger(final String[] args, int pos) {
+ if (loggerClassname != null) {
+ throw new BuildException(
+ "Only one logger class may be specified.");
+ }
+ try {
+ loggerClassname = args[++pos];
+ } catch (final ArrayIndexOutOfBoundsException aioobe) {
+ throw new BuildException(
+ "You must specify a classname when using the -logger argument");
+ }
+ return pos;
+ }
+
+ /** Handle the -inputhandler argument. */
+ private int handleArgInputHandler(final String[] args, int pos) {
+ if (inputHandlerClassname != null) {
+ throw new BuildException("Only one input handler class may "
+ + "be specified.");
+ }
+ try {
+ inputHandlerClassname = args[++pos];
+ } catch (final ArrayIndexOutOfBoundsException aioobe) {
+ throw new BuildException("You must specify a classname when"
+ + " using the -inputhandler"
+ + " argument");
+ }
+ return pos;
+ }
+
+ /** Handle the -propertyfile argument. */
+ private int handleArgPropertyFile(final String[] args, int pos) {
+ try {
+ propertyFiles.addElement(args[++pos]);
+ } catch (final ArrayIndexOutOfBoundsException aioobe) {
+ final String msg = "You must specify a property filename when "
+ + "using the -propertyfile argument";
+ throw new BuildException(msg);
+ }
+ return pos;
+ }
+
+ /** Handle the -nice argument. */
+ private int handleArgNice(final String[] args, int pos) {
+ try {
+ threadPriority = Integer.decode(args[++pos]);
+ } catch (final ArrayIndexOutOfBoundsException aioobe) {
+ throw new BuildException(
+ "You must supply a niceness value (1-10)"
+ + " after the -nice option");
+ } catch (final NumberFormatException e) {
+ throw new BuildException("Unrecognized niceness value: "
+ + args[pos]);
+ }
+
+ if (threadPriority.intValue() < Thread.MIN_PRIORITY
+ || threadPriority.intValue() > Thread.MAX_PRIORITY) {
+ throw new BuildException(
+ "Niceness value is out of the range 1-10");
+ }
+ return pos;
+ }
+
+ // --------------------------------------------------------
+ // other methods
+ // --------------------------------------------------------
+
+ /** Load the property files specified by -propertyfile */
+ private void loadPropertyFiles() {
+ for (final String filename : propertyFiles) {
+ final Properties props = new Properties();
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(filename);
+ props.load(fis);
+ } catch (final IOException e) {
+ System.out.println("Could not load property file "
+ + filename + ": " + e.getMessage());
+ } finally {
+ FileUtils.close(fis);
+ }
+
+ // ensure that -D properties take precedence
+ final Enumeration<?> propertyNames = props.propertyNames();
+ while (propertyNames.hasMoreElements()) {
+ final String name = (String) propertyNames.nextElement();
+ if (definedProps.getProperty(name) == null) {
+ definedProps.put(name, props.getProperty(name));
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper to get the parent file for a given file.
+ * <p>
+ * Added to simulate File.getParentFile() from JDK 1.2.
+ * @deprecated since 1.6.x
+ *
+ * @param file File to find parent of. Must not be <code>null</code>.
+ * @return Parent file or null if none
+ */
+ @Deprecated
+ private File getParentFile(final File file) {
+ final File parent = file.getParentFile();
+
+ if (parent != null && msgOutputLevel >= Project.MSG_VERBOSE) {
+ System.out.println("Searching in " + parent.getAbsolutePath());
+ }
+
+ return parent;
+ }
+
+ /**
+ * Search parent directories for the build file.
+ * <p>
+ * Takes the given target as a suffix to append to each
+ * parent directory in search of a build file. Once the
+ * root of the file-system has been reached <code>null</code>
+ * is returned.
+ *
+ * @param start Leaf directory of search.
+ * Must not be <code>null</code>.
+ * @param suffix Suffix filename to look for in parents.
+ * Must not be <code>null</code>.
+ *
+ * @return A handle to the build file if one is found, <code>null</code> if not
+ */
+ private File findBuildFile(final String start, final String suffix) {
+ if (msgOutputLevel >= Project.MSG_INFO) {
+ System.out.println("Searching for " + suffix + " ...");
+ }
+
+ File parent = new File(new File(start).getAbsolutePath());
+ File file = new File(parent, suffix);
+
+ // check if the target file exists in the current directory
+ while (!file.exists()) {
+ // change to parent directory
+ parent = getParentFile(parent);
+
+ // if parent is null, then we are at the root of the fs,
+ // complain that we can't find the build file.
+ if (parent == null) {
+ return null;
+ }
+
+ // refresh our file handle
+ file = new File(parent, suffix);
+ }
+
+ return file;
+ }
+
+ /**
+ * Executes the build. If the constructor for this instance failed
+ * (e.g. returned after issuing a warning), this method returns
+ * immediately.
+ *
+ * @param coreLoader The classloader to use to find core classes.
+ * May be <code>null</code>, in which case the
+ * system classloader is used.
+ *
+ * @exception BuildException if the build fails
+ */
+ private void runBuild(final ClassLoader coreLoader) throws BuildException {
+
+ if (!readyToRun) {
+ return;
+ }
+
+ final ArgumentProcessorRegistry processorRegistry = ArgumentProcessorRegistry.getInstance();
+
+ for (final ArgumentProcessor processor : processorRegistry.getProcessors()) {
+ final List<String> extraArgs = extraArguments.get(processor.getClass());
+ if (extraArgs != null) {
+ if (processor.handleArg(extraArgs)) {
+ return;
+ }
+ }
+ }
+
+ final Project project = new Project();
+ project.setCoreLoader(coreLoader);
+
+ Throwable error = null;
+
+ try {
+ addBuildListeners(project);
+ addInputHandler(project);
+
+ final PrintStream savedErr = System.err;
+ final PrintStream savedOut = System.out;
+ final InputStream savedIn = System.in;
+
+ // use a system manager that prevents from System.exit()
+ SecurityManager oldsm = null;
+ oldsm = System.getSecurityManager();
+
+ //SecurityManager can not be installed here for backwards
+ //compatibility reasons (PD). Needs to be loaded prior to
+ //ant class if we are going to implement it.
+ //System.setSecurityManager(new NoExitSecurityManager());
+ try {
+ if (allowInput) {
+ project.setDefaultInputStream(System.in);
+ }
+ System.setIn(new DemuxInputStream(project));
+ System.setOut(new PrintStream(new DemuxOutputStream(project, false)));
+ System.setErr(new PrintStream(new DemuxOutputStream(project, true)));
+
+
+ if (!projectHelp) {
+ project.fireBuildStarted();
+ }
+
+ // set the thread priorities
+ if (threadPriority != null) {
+ try {
+ project.log("Setting Ant's thread priority to "
+ + threadPriority, Project.MSG_VERBOSE);
+ Thread.currentThread().setPriority(threadPriority.intValue());
+ } catch (final SecurityException swallowed) {
+ //we cannot set the priority here.
+ project.log("A security manager refused to set the -nice value");
+ }
+ }
+
+ setProperties(project);
+
+ project.setKeepGoingMode(keepGoingMode);
+ if (proxy) {
+ //proxy setup if enabled
+ final ProxySetup proxySetup = new ProxySetup(project);
+ proxySetup.enableProxies();
+ }
+
+ for (final ArgumentProcessor processor : processorRegistry.getProcessors()) {
+ final List<String> extraArgs = extraArguments.get(processor.getClass());
+ if (extraArgs != null) {
+ processor.prepareConfigure(project, extraArgs);
+ }
+ }
+
+ ProjectHelper.configureProject(project, buildFile);
+
+ for (final ArgumentProcessor processor : processorRegistry.getProcessors()) {
+ final List<String> extraArgs = extraArguments.get(processor.getClass());
+ if (extraArgs != null) {
+ if (processor.handleArg(project, extraArgs)) {
+ return;
+ }
+ }
+ }
+
+ if (projectHelp) {
+ printDescription(project);
+ printTargets(project, msgOutputLevel > Project.MSG_INFO,
+ msgOutputLevel > Project.MSG_VERBOSE);
+ return;
+ }
+
+ // make sure that we have a target to execute
+ if (targets.size() == 0) {
+ if (project.getDefaultTarget() != null) {
+ targets.addElement(project.getDefaultTarget());
+ }
+ }
+
+ project.executeTargets(targets);
+ } finally {
+ // put back the original security manager
+ //The following will never eval to true. (PD)
+ if (oldsm != null) {
+ System.setSecurityManager(oldsm);
+ }
+
+ System.setOut(savedOut);
+ System.setErr(savedErr);
+ System.setIn(savedIn);
+ }
+ } catch (final RuntimeException exc) {
+ error = exc;
+ throw exc;
+ } catch (final Error e) {
+ error = e;
+ throw e;
+ } finally {
+ if (!projectHelp) {
+ try {
+ project.fireBuildFinished(error);
+ } catch (final Throwable t) {
+ // yes, I know it is bad style to catch Throwable,
+ // but if we don't, we lose valuable information
+ System.err.println("Caught an exception while logging the"
+ + " end of the build. Exception was:");
+ t.printStackTrace();
+ if (error != null) {
+ System.err.println("There has been an error prior to"
+ + " that:");
+ error.printStackTrace();
+ }
+ throw new BuildException(t);
+ }
+ } else if (error != null) {
+ project.log(error.toString(), Project.MSG_ERR);
+ }
+ }
+ }
+
+ private void setProperties(final Project project) {
+
+ project.init();
+
+ // resolve properties
+ final PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper(project);
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ final Map raw = new HashMap(definedProps);
+ @SuppressWarnings("unchecked")
+ final Map<String, Object> props = raw;
+
+ final ResolvePropertyMap resolver = new ResolvePropertyMap(project,
+ NOPROPERTIES, propertyHelper.getExpanders());
+ resolver.resolveAllProperties(props, null, false);
+
+ // set user-define properties
+ for (final Entry<String, Object> ent : props.entrySet()) {
+ final String arg = ent.getKey();
+ final Object value = ent.getValue();
+ project.setUserProperty(arg, String.valueOf(value));
+ }
+
+ project.setUserProperty(MagicNames.ANT_FILE,
+ buildFile.getAbsolutePath());
+ project.setUserProperty(MagicNames.ANT_FILE_TYPE,
+ MagicNames.ANT_FILE_TYPE_FILE);
+ }
+
+ /**
+ * Adds the listeners specified in the command line arguments,
+ * along with the default listener, to the specified project.
+ *
+ * @param project The project to add listeners to.
+ * Must not be <code>null</code>.
+ */
+ protected void addBuildListeners(final Project project) {
+
+ // Add the default listener
+ project.addBuildListener(createLogger());
+
+ final int count = listeners.size();
+ for (int i = 0; i < count; i++) {
+ final String className = listeners.elementAt(i);
+ final BuildListener listener =
+ (BuildListener) ClasspathUtils.newInstance(className,
+ Main.class.getClassLoader(), BuildListener.class);
+ project.setProjectReference(listener);
+
+ project.addBuildListener(listener);
+ }
+ }
+
+ /**
+ * Creates the InputHandler and adds it to the project.
+ *
+ * @param project the project instance.
+ *
+ * @exception BuildException if a specified InputHandler
+ * implementation could not be loaded.
+ */
+ private void addInputHandler(final Project project) throws BuildException {
+ InputHandler handler = null;
+ if (inputHandlerClassname == null) {
+ handler = new DefaultInputHandler();
+ } else {
+ handler = (InputHandler) ClasspathUtils.newInstance(
+ inputHandlerClassname, Main.class.getClassLoader(),
+ InputHandler.class);
+ project.setProjectReference(handler);
+ }
+ project.setInputHandler(handler);
+ }
+
+ // TODO: (Jon Skeet) Any reason for writing a message and then using a bare
+ // RuntimeException rather than just using a BuildException here? Is it
+ // in case the message could end up being written to no loggers (as the
+ // loggers could have failed to be created due to this failure)?
+ /**
+ * Creates the default build logger for sending build events to the ant
+ * log.
+ *
+ * @return the logger instance for this build.
+ */
+ private BuildLogger createLogger() {
+ BuildLogger logger = null;
+ if (silent) {
+ logger = new SilentLogger();
+ msgOutputLevel = Project.MSG_WARN;
+ emacsMode = true;
+ } else if (loggerClassname != null) {
+ try {
+ logger = (BuildLogger) ClasspathUtils.newInstance(
+ loggerClassname, Main.class.getClassLoader(),
+ BuildLogger.class);
+ } catch (final BuildException e) {
+ System.err.println("The specified logger class "
+ + loggerClassname
+ + " could not be used because " + e.getMessage());
+ throw new RuntimeException();
+ }
+ } else {
+ logger = new DefaultLogger();
+ }
+
+ logger.setMessageOutputLevel(msgOutputLevel);
+ logger.setOutputPrintStream(out);
+ logger.setErrorPrintStream(err);
+ logger.setEmacsMode(emacsMode);
+
+ return logger;
+ }
+
+ /**
+ * Prints the usage information for this class to <code>System.out</code>.
+ */
+ private static void printUsage() {
+ System.out.println("ant [options] [target [target2 [target3] ...]]");
+ System.out.println("Options: ");
+ System.out.println(" -help, -h print this message and exit");
+ System.out.println(" -projecthelp, -p print project help information and exit");
+ System.out.println(" -version print the version information and exit");
+ System.out.println(" -diagnostics print information that might be helpful to");
+ System.out.println(" diagnose or report problems and exit");
+ System.out.println(" -quiet, -q be extra quiet");
+ System.out.println(" -silent, -S print nothing but task outputs and build failures");
+ System.out.println(" -verbose, -v be extra verbose");
+ System.out.println(" -debug, -d print debugging information");
+ System.out.println(" -emacs, -e produce logging information without adornments");
+ System.out.println(" -lib <path> specifies a path to search for jars and classes");
+ System.out.println(" -logfile <file> use given file for log");
+ System.out.println(" -l <file> ''");
+ System.out.println(" -logger <classname> the class which is to perform logging");
+ System.out.println(" -listener <classname> add an instance of class as a project listener");
+ System.out.println(" -noinput do not allow interactive input");
+ System.out.println(" -buildfile <file> use given buildfile");
+ System.out.println(" -file <file> ''");
+ System.out.println(" -f <file> ''");
+ System.out.println(" -D<property>=<value> use value for given property");
+ System.out.println(" -keep-going, -k execute all targets that do not depend");
+ System.out.println(" on failed target(s)");
+ System.out.println(" -propertyfile <name> load all properties from file with -D");
+ System.out.println(" properties taking precedence");
+ System.out.println(" -inputhandler <class> the class which will handle input requests");
+ System.out.println(" -find <file> (s)earch for buildfile towards the root of");
+ System.out.println(" -s <file> the filesystem and use it");
+ System.out.println(" -nice number A niceness value for the main thread:"
+ + " 1 (lowest) to 10 (highest); 5 is the default");
+ System.out.println(" -nouserlib Run ant without using the jar files from"
+ + " ${user.home}/.ant/lib");
+ System.out.println(" -noclasspath Run ant without using CLASSPATH");
+ System.out.println(" -autoproxy Java1.5+: use the OS proxy settings");
+ System.out.println(" -main <class> override Ant's normal entry point");
+ for (final ArgumentProcessor processor : ArgumentProcessorRegistry.getInstance().getProcessors()) {
+ processor.printUsage(System.out);
+ }
+ }
+
+ /**
+ * Prints the Ant version information to <code>System.out</code>.
+ *
+ * @exception BuildException if the version information is unavailable
+ */
+ private static void printVersion(final int logLevel) throws BuildException {
+ System.out.println(getAntVersion());
+ }
+
+ /**
+ * Cache of the Ant version information when it has been loaded.
+ */
+ private static String antVersion = null;
+
+ /**
+ * Cache of the short Ant version information when it has been loaded.
+ */
+ private static String shortAntVersion = null;
+
+ /**
+ * Returns the Ant version information, if available. Once the information
+ * has been loaded once, it's cached and returned from the cache on future
+ * calls.
+ *
+ * @return the Ant version information as a String
+ * (always non-<code>null</code>)
+ *
+ * @exception BuildException if the version information is unavailable
+ */
+ public static synchronized String getAntVersion() throws BuildException {
+ if (antVersion == null) {
+ try {
+ final Properties props = new Properties();
+ final InputStream in =
+ Main.class.getResourceAsStream("/org/apache/tools/ant/version.txt");
+ props.load(in);
+ in.close();
+ shortAntVersion = props.getProperty("VERSION");
+
+ final StringBuffer msg = new StringBuffer();
+ msg.append("Apache Ant(TM) version ");
+ msg.append(shortAntVersion);
+ msg.append(" compiled on ");
+ msg.append(props.getProperty("DATE"));
+ antVersion = msg.toString();
+ } catch (final IOException ioe) {
+ throw new BuildException("Could not load the version information:"
+ + ioe.getMessage());
+ } catch (final NullPointerException npe) {
+ throw new BuildException("Could not load the version information.");
+ }
+ }
+ return antVersion;
+ }
+
+ /**
+ * Returns the short Ant version information, if available. Once the information
+ * has been loaded once, it's cached and returned from the cache on future
+ * calls.
+ *
+ * @return the short Ant version information as a String
+ * (always non-<code>null</code>)
+ *
+ * @throws BuildException BuildException if the version information is unavailable
+ * @since Ant 1.9.3
+ */
+ public static String getShortAntVersion() throws BuildException {
+ if (shortAntVersion == null) {
+ getAntVersion();
+ }
+ return shortAntVersion;
+ }
+
+ /**
+ * Prints the description of a project (if there is one) to
+ * <code>System.out</code>.
+ *
+ * @param project The project to display a description of.
+ * Must not be <code>null</code>.
+ */
+ private static void printDescription(final Project project) {
+ if (project.getDescription() != null) {
+ project.log(project.getDescription());
+ }
+ }
+
+ /**
+ * Targets in imported files with a project name
+ * and not overloaded by the main build file will
+ * be in the target map twice. This method
+ * removes the duplicate target.
+ * @param targets the targets to filter.
+ * @return the filtered targets.
+ */
+ private static Map<String, Target> removeDuplicateTargets(final Map<String, Target> targets) {
+ final Map<Location, Target> locationMap = new HashMap<Location, Target>();
+ for (final Entry<String, Target> entry : targets.entrySet()) {
+ final String name = entry.getKey();
+ final Target target = entry.getValue();
+ final Target otherTarget = locationMap.get(target.getLocation());
+ // Place this entry in the location map if
+ // a) location is not in the map
+ // b) location is in map, but its name is longer
+ // (an imported target will have a name. prefix)
+ if (otherTarget == null
+ || otherTarget.getName().length() > name.length()) {
+ locationMap.put(
+ target.getLocation(), target); // Smallest name wins
+ }
+ }
+ final Map<String, Target> ret = new HashMap<String, Target>();
+ for (final Target target : locationMap.values()) {
+ ret.put(target.getName(), target);
+ }
+ return ret;
+ }
+
+ /**
+ * Prints a list of all targets in the specified project to
+ * <code>System.out</code>, optionally including subtargets.
+ *
+ * @param project The project to display a description of.
+ * Must not be <code>null</code>.
+ * @param printSubTargets Whether or not subtarget names should also be
+ * printed.
+ */
+ private static void printTargets(final Project project, boolean printSubTargets,
+ final boolean printDependencies) {
+ // find the target with the longest name
+ int maxLength = 0;
+ final Map<String, Target> ptargets = removeDuplicateTargets(project.getTargets());
+ // split the targets in top-level and sub-targets depending
+ // on the presence of a description
+ final Vector<String> topNames = new Vector<String>();
+ final Vector<String> topDescriptions = new Vector<String>();
+ final Vector<Enumeration<String>> topDependencies = new Vector<Enumeration<String>>();
+ final Vector<String> subNames = new Vector<String>();
+ final Vector<Enumeration<String>> subDependencies = new Vector<Enumeration<String>>();
+
+ for (final Target currentTarget : ptargets.values()) {
+ final String targetName = currentTarget.getName();
+ if (targetName.equals("")) {
+ continue;
+ }
+ final String targetDescription = currentTarget.getDescription();
+ // maintain a sorted list of targets
+ if (targetDescription == null) {
+ final int pos = findTargetPosition(subNames, targetName);
+ subNames.insertElementAt(targetName, pos);
+ if (printDependencies) {
+ subDependencies.insertElementAt(currentTarget.getDependencies(), pos);
+ }
+ } else {
+ final int pos = findTargetPosition(topNames, targetName);
+ topNames.insertElementAt(targetName, pos);
+ topDescriptions.insertElementAt(targetDescription, pos);
+ if (targetName.length() > maxLength) {
+ maxLength = targetName.length();
+ }
+ if (printDependencies) {
+ topDependencies.insertElementAt(currentTarget.getDependencies(), pos);
+ }
+ }
+ }
+
+ printTargets(project, topNames, topDescriptions, topDependencies,
+ "Main targets:", maxLength);
+ //if there were no main targets, we list all subtargets
+ //as it means nothing has a description
+ if (topNames.size() == 0) {
+ printSubTargets = true;
+ }
+ if (printSubTargets) {
+ printTargets(project, subNames, null, subDependencies, "Other targets:", 0);
+ }
+
+ final String defaultTarget = project.getDefaultTarget();
+ if (defaultTarget != null && !"".equals(defaultTarget)) {
+ // shouldn't need to check but...
+ project.log("Default target: " + defaultTarget);
+ }
+ }
+
+ /**
+ * Searches for the correct place to insert a name into a list so as
+ * to keep the list sorted alphabetically.
+ *
+ * @param names The current list of names. Must not be <code>null</code>.
+ * @param name The name to find a place for.
+ * Must not be <code>null</code>.
+ *
+ * @return the correct place in the list for the given name
+ */
+ private static int findTargetPosition(final Vector<String> names, final String name) {
+ final int size = names.size();
+ int res = size;
+ for (int i = 0; i < size && res == size; i++) {
+ if (name.compareTo(names.elementAt(i)) < 0) {
+ res = i;
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Writes a formatted list of target names to <code>System.out</code>
+ * with an optional description.
+ *
+ *
+ * @param project the project instance.
+ * @param names The names to be printed.
+ * Must not be <code>null</code>.
+ * @param descriptions The associated target descriptions.
+ * May be <code>null</code>, in which case
+ * no descriptions are displayed.
+ * If non-<code>null</code>, this should have
+ * as many elements as <code>names</code>.
+ * @param topDependencies The list of dependencies for each target.
+ * The dependencies are listed as a non null
+ * enumeration of String.
+ * @param heading The heading to display.
+ * Should not be <code>null</code>.
+ * @param maxlen The maximum length of the names of the targets.
+ * If descriptions are given, they are padded to this
+ * position so they line up (so long as the names really
+ * <i>are</i> shorter than this).
+ */
+ private static void printTargets(final Project project, final Vector<String> names,
+ final Vector<String> descriptions, final Vector<Enumeration<String>> dependencies,
+ final String heading,
+ final int maxlen) {
+ // now, start printing the targets and their descriptions
+ final String lSep = System.getProperty("line.separator");
+ // got a bit annoyed that I couldn't find a pad function
+ String spaces = " ";
+ while (spaces.length() <= maxlen) {
+ spaces += spaces;
+ }
+ final StringBuilder msg = new StringBuilder();
+ msg.append(heading + lSep + lSep);
+ final int size = names.size();
+ for (int i = 0; i < size; i++) {
+ msg.append(" ");
+ msg.append(names.elementAt(i));
+ if (descriptions != null) {
+ msg.append(
+ spaces.substring(0, maxlen - names.elementAt(i).length() + 2));
+ msg.append(descriptions.elementAt(i));
+ }
+ msg.append(lSep);
+ if (!dependencies.isEmpty()) {
+ final Enumeration<String> deps = dependencies.elementAt(i);
+ if (deps.hasMoreElements()) {
+ msg.append(" depends on: ");
+ while (deps.hasMoreElements()) {
+ msg.append(deps.nextElement());
+ if (deps.hasMoreElements()) {
+ msg.append(", ");
+ }
+ }
+ msg.append(lSep);
+ }
+ }
+ }
+ project.log(msg.toString(), Project.MSG_WARN);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/NoBannerLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/NoBannerLogger.java
new file mode 100644
index 00000000..a086bf5e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/NoBannerLogger.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.tools.ant;
+
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Extends DefaultLogger to strip out empty targets.
+ *
+ */
+public class NoBannerLogger extends DefaultLogger {
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ /**
+ * Name of the current target, if it should
+ * be displayed on the next message. This is
+ * set when a target starts building, and reset
+ * to <code>null</code> after the first message for
+ * the target is logged.
+ */
+ protected String targetName;
+ // CheckStyle:VisibilityModifier ON
+
+ /** Sole constructor. */
+ public NoBannerLogger() {
+ }
+
+ /**
+ * Notes the name of the target so it can be logged
+ * if it generates any messages.
+ *
+ * @param event A BuildEvent containing target information.
+ * Must not be <code>null</code>.
+ */
+ public synchronized void targetStarted(BuildEvent event) {
+ targetName = extractTargetName(event);
+ }
+
+ /**
+ * Override point, extract the target name
+ * @param event the event to work on
+ * @return the target name to print
+ * @since Ant1.7.1
+ */
+ protected String extractTargetName(BuildEvent event) {
+ return event.getTarget().getName();
+ }
+
+ /**
+ * Resets the current target name to <code>null</code>.
+ *
+ * @param event Ignored in this implementation.
+ */
+ public synchronized void targetFinished(BuildEvent event) {
+ targetName = null;
+ }
+
+ /**
+ * Logs a message for a target if it is of an appropriate
+ * priority, also logging the name of the target if this
+ * is the first message which needs to be logged for the
+ * target.
+ *
+ * @param event A BuildEvent containing message information.
+ * Must not be <code>null</code>.
+ */
+ public void messageLogged(BuildEvent event) {
+
+ if (event.getPriority() > msgOutputLevel
+ || null == event.getMessage()
+ || "".equals(event.getMessage().trim())) {
+ return;
+ }
+
+ synchronized (this) {
+ if (null != targetName) {
+ out.println(StringUtils.LINE_SEP + targetName + ":");
+ targetName = null;
+ }
+ }
+
+ super.messageLogged(event);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PathTokenizer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PathTokenizer.java
new file mode 100644
index 00000000..6e6bea62
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PathTokenizer.java
@@ -0,0 +1,166 @@
+/*
+ * 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.tools.ant;
+
+import java.io.File;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+/**
+ * A Path tokenizer takes a path and returns the components that make up
+ * that path.
+ *
+ * The path can use path separators of either ':' or ';' and file separators
+ * of either '/' or '\'.
+ *
+ */
+public class PathTokenizer {
+ /**
+ * A tokenizer to break the string up based on the ':' or ';' separators.
+ */
+ private StringTokenizer tokenizer;
+
+ /**
+ * A String which stores any path components which have been read ahead
+ * due to DOS filesystem compensation.
+ */
+ private String lookahead = null;
+
+ /**
+ * A boolean that determines if we are running on Novell NetWare, which
+ * exhibits slightly different path name characteristics (multi-character
+ * volume / drive names)
+ */
+ private boolean onNetWare = Os.isFamily("netware");
+
+ /**
+ * Flag to indicate whether or not we are running on a platform with a
+ * DOS style filesystem
+ */
+ private boolean dosStyleFilesystem;
+
+ /**
+ * Constructs a path tokenizer for the specified path.
+ *
+ * @param path The path to tokenize. Must not be <code>null</code>.
+ */
+ public PathTokenizer(String path) {
+ if (onNetWare) {
+ // For NetWare, use the boolean=true mode, so we can use delimiter
+ // information to make a better decision later.
+ tokenizer = new StringTokenizer(path, ":;", true);
+ } else {
+ // on Windows and Unix, we can ignore delimiters and still have
+ // enough information to tokenize correctly.
+ tokenizer = new StringTokenizer(path, ":;", false);
+ }
+ dosStyleFilesystem = File.pathSeparatorChar == ';';
+ }
+
+ /**
+ * Tests if there are more path elements available from this tokenizer's
+ * path. If this method returns <code>true</code>, then a subsequent call
+ * to nextToken will successfully return a token.
+ *
+ * @return <code>true</code> if and only if there is at least one token
+ * in the string after the current position; <code>false</code> otherwise.
+ */
+ public boolean hasMoreTokens() {
+ if (lookahead != null) {
+ return true;
+ }
+
+ return tokenizer.hasMoreTokens();
+ }
+
+ /**
+ * Returns the next path element from this tokenizer.
+ *
+ * @return the next path element from this tokenizer.
+ *
+ * @exception NoSuchElementException if there are no more elements in this
+ * tokenizer's path.
+ */
+ public String nextToken() throws NoSuchElementException {
+ String token = null;
+ if (lookahead != null) {
+ token = lookahead;
+ lookahead = null;
+ } else {
+ token = tokenizer.nextToken().trim();
+ }
+
+ if (!onNetWare) {
+ if (token.length() == 1 && Character.isLetter(token.charAt(0))
+ && dosStyleFilesystem
+ && tokenizer.hasMoreTokens()) {
+ // we are on a dos style system so this path could be a drive
+ // spec. We look at the next token
+ String nextToken = tokenizer.nextToken().trim();
+ if (nextToken.startsWith("\\") || nextToken.startsWith("/")) {
+ // we know we are on a DOS style platform and the next path
+ // starts with a slash or backslash, so we know this is a
+ // drive spec
+ token += ":" + nextToken;
+ } else {
+ // store the token just read for next time
+ lookahead = nextToken;
+ }
+ }
+ } else {
+ // we are on NetWare, tokenizing is handled a little differently,
+ // due to the fact that NetWare has multiple-character volume names.
+ if (token.equals(File.pathSeparator) || token.equals(":")) {
+ // ignore ";" and get the next token
+ token = tokenizer.nextToken().trim();
+ }
+
+ if (tokenizer.hasMoreTokens()) {
+ // this path could be a drive spec, so look at the next token
+ String nextToken = tokenizer.nextToken().trim();
+
+ // make sure we aren't going to get the path separator next
+ if (!nextToken.equals(File.pathSeparator)) {
+ if (nextToken.equals(":")) {
+ if (!token.startsWith("/") && !token.startsWith("\\")
+ && !token.startsWith(".")
+ && !token.startsWith("..")) {
+ // it indeed is a drive spec, get the next bit
+ String oneMore = tokenizer.nextToken().trim();
+ if (!oneMore.equals(File.pathSeparator)) {
+ token += ":" + oneMore;
+ } else {
+ token += ":";
+ lookahead = oneMore;
+ }
+ }
+ // implicit else: ignore the ':' since we have either a
+ // UNIX or a relative path
+ } else {
+ // store the token just read for next time
+ lookahead = nextToken;
+ }
+ }
+ }
+ }
+ return token;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Project.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Project.java
new file mode 100644
index 00000000..3a0c4b82
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Project.java
@@ -0,0 +1,2494 @@
+/*
+ * 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.tools.ant;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Stack;
+import java.util.Vector;
+import java.util.WeakHashMap;
+
+import org.apache.tools.ant.helper.DefaultExecutor;
+import org.apache.tools.ant.input.DefaultInputHandler;
+import org.apache.tools.ant.input.InputHandler;
+import org.apache.tools.ant.types.Description;
+import org.apache.tools.ant.types.FilterSet;
+import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.CollectionUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.VectorSet;
+
+/**
+ * Central representation of an Ant project. This class defines an
+ * Ant project with all of its targets, tasks and various other
+ * properties. It also provides the mechanism to kick off a build using
+ * a particular target name.
+ * <p>
+ * This class also encapsulates methods which allow files to be referred
+ * to using abstract path names which are translated to native system
+ * file paths at runtime.
+ *
+ */
+public class Project implements ResourceFactory {
+ /** Message priority of &quot;error&quot;. */
+ public static final int MSG_ERR = 0;
+ /** Message priority of &quot;warning&quot;. */
+ public static final int MSG_WARN = 1;
+ /** Message priority of &quot;information&quot;. */
+ public static final int MSG_INFO = 2;
+ /** Message priority of &quot;verbose&quot;. */
+ public static final int MSG_VERBOSE = 3;
+ /** Message priority of &quot;debug&quot;. */
+ public static final int MSG_DEBUG = 4;
+
+ /**
+ * Constant for the &quot;visiting&quot; state, used when
+ * traversing a DFS of target dependencies.
+ */
+ private static final String VISITING = "VISITING";
+ /**
+ * Constant for the &quot;visited&quot; state, used when
+ * traversing a DFS of target dependencies.
+ */
+ private static final String VISITED = "VISITED";
+
+ /**
+ * Version constant for Java 1.0 .
+ *
+ * @deprecated since 1.5.x.
+ * Use {@link JavaEnvUtils#JAVA_1_0} instead.
+ */
+ @Deprecated
+ public static final String JAVA_1_0 = JavaEnvUtils.JAVA_1_0;
+ /**
+ * Version constant for Java 1.1 .
+ *
+ * @deprecated since 1.5.x.
+ * Use {@link JavaEnvUtils#JAVA_1_1} instead.
+ */
+ @Deprecated
+ public static final String JAVA_1_1 = JavaEnvUtils.JAVA_1_1;
+ /**
+ * Version constant for Java 1.2 .
+ *
+ * @deprecated since 1.5.x.
+ * Use {@link JavaEnvUtils#JAVA_1_2} instead.
+ */
+ @Deprecated
+ public static final String JAVA_1_2 = JavaEnvUtils.JAVA_1_2;
+ /**
+ * Version constant for Java 1.3 .
+ *
+ * @deprecated since 1.5.x.
+ * Use {@link JavaEnvUtils#JAVA_1_3} instead.
+ */
+ @Deprecated
+ public static final String JAVA_1_3 = JavaEnvUtils.JAVA_1_3;
+ /**
+ * Version constant for Java 1.4 .
+ *
+ * @deprecated since 1.5.x.
+ * Use {@link JavaEnvUtils#JAVA_1_4} instead.
+ */
+ @Deprecated
+ public static final String JAVA_1_4 = JavaEnvUtils.JAVA_1_4;
+
+ /** Default filter start token. */
+ public static final String TOKEN_START = FilterSet.DEFAULT_TOKEN_START;
+ /** Default filter end token. */
+ public static final String TOKEN_END = FilterSet.DEFAULT_TOKEN_END;
+
+ /** Instance of a utility class to use for file operations. */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** Name of this project. */
+ private String name;
+ /** Description for this project (if any). */
+ private String description;
+
+
+ /** Map of references within the project (paths etc) (String to Object). */
+ private final Hashtable<String, Object> references = new AntRefTable();
+
+ /** Map of id references - used for indicating broken build files */
+ private final HashMap<String, Object> idReferences = new HashMap<String, Object>();
+
+ /** Name of the project's default target. */
+ private String defaultTarget;
+
+ /** Map from target names to targets (String to Target). */
+ private final Hashtable<String, Target> targets = new Hashtable<String, Target>();
+
+ /** Set of global filters. */
+ private final FilterSet globalFilterSet = new FilterSet();
+ {
+ // Initialize the globalFileSet's project
+ globalFilterSet.setProject(this);
+ }
+
+ /**
+ * Wrapper around globalFilterSet. This collection only ever
+ * contains one FilterSet, but the wrapper is needed in order to
+ * make it easier to use the FileUtils interface.
+ */
+ private final FilterSetCollection globalFilters
+ = new FilterSetCollection(globalFilterSet);
+
+ /** Project base directory. */
+ private File baseDir;
+
+ /** lock object used when adding/removing listeners */
+ private final Object listenersLock = new Object();
+
+ /** List of listeners to notify of build events. */
+ private volatile BuildListener[] listeners = new BuildListener[0];
+
+ /** for each thread, record whether it is currently executing
+ messageLogged */
+ private final ThreadLocal<Boolean> isLoggingMessage = new ThreadLocal<Boolean>() {
+ @Override
+ protected Boolean initialValue() {
+ return Boolean.FALSE;
+ }
+ };
+
+ /**
+ * The Ant core classloader--may be <code>null</code> if using
+ * parent classloader.
+ */
+ private ClassLoader coreLoader = null;
+
+ /** Records the latest task to be executed on a thread. */
+ private final Map<Thread,Task> threadTasks =
+ Collections.synchronizedMap(new WeakHashMap<Thread, Task>());
+
+ /** Records the latest task to be executed on a thread group. */
+ private final Map<ThreadGroup,Task> threadGroupTasks
+ = Collections.synchronizedMap(new WeakHashMap<ThreadGroup,Task>());
+
+ /**
+ * Called to handle any input requests.
+ */
+ private InputHandler inputHandler = null;
+
+ /**
+ * The default input stream used to read any input.
+ */
+ private InputStream defaultInputStream = null;
+
+ /**
+ * Keep going flag.
+ */
+ private boolean keepGoingMode = false;
+
+ /**
+ * Set the input handler.
+ *
+ * @param handler the InputHandler instance to use for gathering input.
+ */
+ public void setInputHandler(final InputHandler handler) {
+ inputHandler = handler;
+ }
+
+ /**
+ * Set the default System input stream. Normally this stream is set to
+ * System.in. This inputStream is used when no task input redirection is
+ * being performed.
+ *
+ * @param defaultInputStream the default input stream to use when input
+ * is requested.
+ * @since Ant 1.6
+ */
+ public void setDefaultInputStream(final InputStream defaultInputStream) {
+ this.defaultInputStream = defaultInputStream;
+ }
+
+ /**
+ * Get this project's input stream.
+ *
+ * @return the InputStream instance in use by this Project instance to
+ * read input.
+ */
+ public InputStream getDefaultInputStream() {
+ return defaultInputStream;
+ }
+
+ /**
+ * Retrieve the current input handler.
+ *
+ * @return the InputHandler instance currently in place for the project
+ * instance.
+ */
+ public InputHandler getInputHandler() {
+ return inputHandler;
+ }
+
+ /**
+ * Create a new Ant project.
+ */
+ public Project() {
+ inputHandler = new DefaultInputHandler();
+ }
+
+ /**
+ * Create and initialize a subproject. By default the subproject will be of
+ * the same type as its parent. If a no-arg constructor is unavailable, the
+ * <code>Project</code> class will be used.
+ * @return a Project instance configured as a subproject of this Project.
+ * @since Ant 1.7
+ */
+ public Project createSubProject() {
+ Project subProject = null;
+ try {
+ subProject = (getClass().newInstance());
+ } catch (final Exception e) {
+ subProject = new Project();
+ }
+ initSubProject(subProject);
+ return subProject;
+ }
+
+ /**
+ * Initialize a subproject.
+ * @param subProject the subproject to initialize.
+ */
+ public void initSubProject(final Project subProject) {
+ ComponentHelper.getComponentHelper(subProject)
+ .initSubProject(ComponentHelper.getComponentHelper(this));
+ subProject.setDefaultInputStream(getDefaultInputStream());
+ subProject.setKeepGoingMode(this.isKeepGoingMode());
+ subProject.setExecutor(getExecutor().getSubProjectExecutor());
+ }
+
+ /**
+ * Initialise the project.
+ *
+ * This involves setting the default task definitions and loading the
+ * system properties.
+ *
+ * @exception BuildException if the default task list cannot be loaded.
+ */
+ public void init() throws BuildException {
+ initProperties();
+
+ ComponentHelper.getComponentHelper(this).initDefaultDefinitions();
+ }
+
+ /**
+ * Initializes the properties.
+ * @exception BuildException if an vital property could not be set.
+ * @since Ant 1.7
+ */
+ public void initProperties() throws BuildException {
+ setJavaVersionProperty();
+ setSystemProperties();
+ setPropertyInternal(MagicNames.ANT_VERSION, Main.getAntVersion());
+ setAntLib();
+ }
+
+ /**
+ * Set a property to the location of ant.jar.
+ * Use the locator to find the location of the Project.class, and
+ * if this is not null, set the property {@link MagicNames#ANT_LIB}
+ * to the result
+ */
+ private void setAntLib() {
+ final File antlib = org.apache.tools.ant.launch.Locator.getClassSource(
+ Project.class);
+ if (antlib != null) {
+ setPropertyInternal(MagicNames.ANT_LIB, antlib.getAbsolutePath());
+ }
+ }
+ /**
+ * Factory method to create a class loader for loading classes from
+ * a given path.
+ *
+ * @param path the path from which classes are to be loaded.
+ *
+ * @return an appropriate classloader.
+ */
+ public AntClassLoader createClassLoader(final Path path) {
+ return AntClassLoader
+ .newAntClassLoader(getClass().getClassLoader(), this, path, true);
+ }
+
+ /**
+ * Factory method to create a class loader for loading classes from
+ * a given path.
+ *
+ * @param parent the parent classloader for the new loader.
+ * @param path the path from which classes are to be loaded.
+ *
+ * @return an appropriate classloader.
+ */
+ public AntClassLoader createClassLoader(
+ final ClassLoader parent, final Path path) {
+ return AntClassLoader.newAntClassLoader(parent, this, path, true);
+ }
+
+ /**
+ * Set the core classloader for the project. If a <code>null</code>
+ * classloader is specified, the parent classloader should be used.
+ *
+ * @param coreLoader The classloader to use for the project.
+ * May be <code>null</code>.
+ */
+ public void setCoreLoader(final ClassLoader coreLoader) {
+ this.coreLoader = coreLoader;
+ }
+
+ /**
+ * Return the core classloader to use for this project.
+ * This may be <code>null</code>, indicating that
+ * the parent classloader should be used.
+ *
+ * @return the core classloader to use for this project.
+ *
+ */
+ public ClassLoader getCoreLoader() {
+ return coreLoader;
+ }
+
+ /**
+ * Add a build listener to the list. This listener will
+ * be notified of build events for this project.
+ *
+ * @param listener The listener to add to the list.
+ * Must not be <code>null</code>.
+ */
+ public void addBuildListener(final BuildListener listener) {
+ synchronized (listenersLock) {
+ // If the listeners already has this listener, do nothing
+ for (int i = 0; i < listeners.length; i++) {
+ if (listeners[i] == listener) {
+ return;
+ }
+ }
+ // copy on write semantics
+ final BuildListener[] newListeners =
+ new BuildListener[listeners.length + 1];
+ System.arraycopy(listeners, 0, newListeners, 0, listeners.length);
+ newListeners[listeners.length] = listener;
+ listeners = newListeners;
+ }
+ }
+
+ /**
+ * Remove a build listener from the list. This listener
+ * will no longer be notified of build events for this project.
+ *
+ * @param listener The listener to remove from the list.
+ * Should not be <code>null</code>.
+ */
+ public void removeBuildListener(final BuildListener listener) {
+ synchronized (listenersLock) {
+ // copy on write semantics
+ for (int i = 0; i < listeners.length; i++) {
+ if (listeners[i] == listener) {
+ final BuildListener[] newListeners =
+ new BuildListener[listeners.length - 1];
+ System.arraycopy(listeners, 0, newListeners, 0, i);
+ System.arraycopy(listeners, i + 1, newListeners, i,
+ listeners.length - i - 1);
+ listeners = newListeners;
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Return a copy of the list of build listeners for the project.
+ *
+ * @return a list of build listeners for the project
+ */
+ public Vector<BuildListener> getBuildListeners() {
+ synchronized (listenersLock) {
+ final Vector<BuildListener> r = new Vector<BuildListener>(listeners.length);
+ for (int i = 0; i < listeners.length; i++) {
+ r.add(listeners[i]);
+ }
+ return r;
+ }
+ }
+
+ /**
+ * Write a message to the log with the default log level
+ * of MSG_INFO .
+ * @param message The text to log. Should not be <code>null</code>.
+ */
+
+ public void log(final String message) {
+ log(message, MSG_INFO);
+ }
+
+ /**
+ * Write a project level message to the log with the given log level.
+ * @param message The text to log. Should not be <code>null</code>.
+ * @param msgLevel The log priority level to use.
+ */
+ public void log(final String message, final int msgLevel) {
+ log(message, null, msgLevel);
+ }
+
+ /**
+ * Write a project level message to the log with the given log level.
+ * @param message The text to log. Should not be <code>null</code>.
+ * @param throwable The exception causing this log, may be <code>null</code>.
+ * @param msgLevel The log priority level to use.
+ * @since 1.7
+ */
+ public void log(final String message, final Throwable throwable, final int msgLevel) {
+ fireMessageLogged(this, message, throwable, msgLevel);
+ }
+
+ /**
+ * Write a task level message to the log with the given log level.
+ * @param task The task to use in the log. Must not be <code>null</code>.
+ * @param message The text to log. Should not be <code>null</code>.
+ * @param msgLevel The log priority level to use.
+ */
+ public void log(final Task task, final String message, final int msgLevel) {
+ fireMessageLogged(task, message, null, msgLevel);
+ }
+
+ /**
+ * Write a task level message to the log with the given log level.
+ * @param task The task to use in the log. Must not be <code>null</code>.
+ * @param message The text to log. Should not be <code>null</code>.
+ * @param throwable The exception causing this log, may be <code>null</code>.
+ * @param msgLevel The log priority level to use.
+ * @since 1.7
+ */
+ public void log(final Task task, final String message, final Throwable throwable, final int msgLevel) {
+ fireMessageLogged(task, message, throwable, msgLevel);
+ }
+
+ /**
+ * Write a target level message to the log with the given log level.
+ * @param target The target to use in the log.
+ * Must not be <code>null</code>.
+ * @param message The text to log. Should not be <code>null</code>.
+ * @param msgLevel The log priority level to use.
+ */
+ public void log(final Target target, final String message, final int msgLevel) {
+ log(target, message, null, msgLevel);
+ }
+
+ /**
+ * Write a target level message to the log with the given log level.
+ * @param target The target to use in the log.
+ * Must not be <code>null</code>.
+ * @param message The text to log. Should not be <code>null</code>.
+ * @param throwable The exception causing this log, may be <code>null</code>.
+ * @param msgLevel The log priority level to use.
+ * @since 1.7
+ */
+ public void log(final Target target, final String message, final Throwable throwable,
+ final int msgLevel) {
+ fireMessageLogged(target, message, throwable, msgLevel);
+ }
+
+ /**
+ * Return the set of global filters.
+ *
+ * @return the set of global filters.
+ */
+ public FilterSet getGlobalFilterSet() {
+ return globalFilterSet;
+ }
+
+ /**
+ * Set a property. Any existing property of the same name
+ * is overwritten, unless it is a user property.
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ */
+ public void setProperty(final String name, final String value) {
+ PropertyHelper.getPropertyHelper(this).setProperty(name, value, true);
+ }
+
+ /**
+ * Set a property if no value currently exists. If the property
+ * exists already, a message is logged and the method returns with
+ * no other effect.
+ *
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ * @since 1.5
+ */
+ public void setNewProperty(final String name, final String value) {
+ PropertyHelper.getPropertyHelper(this).setNewProperty(name, value);
+ }
+
+ /**
+ * Set a user property, which cannot be overwritten by
+ * set/unset property calls. Any previous value is overwritten.
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ * @see #setProperty(String,String)
+ */
+ public void setUserProperty(final String name, final String value) {
+ PropertyHelper.getPropertyHelper(this).setUserProperty(name, value);
+ }
+
+ /**
+ * Set a user property, which cannot be overwritten by set/unset
+ * property calls. Any previous value is overwritten. Also marks
+ * these properties as properties that have not come from the
+ * command line.
+ *
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ * @see #setProperty(String,String)
+ */
+ public void setInheritedProperty(final String name, final String value) {
+ PropertyHelper.getPropertyHelper(this).setInheritedProperty(name, value);
+ }
+
+ /**
+ * Set a property unless it is already defined as a user property
+ * (in which case the method returns silently).
+ *
+ * @param name The name of the property.
+ * Must not be <code>null</code>.
+ * @param value The property value. Must not be <code>null</code>.
+ */
+ private void setPropertyInternal(final String name, final String value) {
+ PropertyHelper.getPropertyHelper(this).setProperty(name, value, false);
+ }
+
+ /**
+ * Return the value of a property, if it is set.
+ *
+ * @param propertyName The name of the property.
+ * May be <code>null</code>, in which case
+ * the return value is also <code>null</code>.
+ * @return the property value, or <code>null</code> for no match
+ * or if a <code>null</code> name is provided.
+ */
+ public String getProperty(final String propertyName) {
+ final Object value = PropertyHelper.getPropertyHelper(this).getProperty(propertyName);
+ return value == null ? null : String.valueOf(value);
+ }
+
+ /**
+ * Replace ${} style constructions in the given value with the
+ * string value of the corresponding data types.
+ *
+ * @param value The string to be scanned for property references.
+ * May be <code>null</code>.
+ *
+ * @return the given string with embedded property names replaced
+ * by values, or <code>null</code> if the given string is
+ * <code>null</code>.
+ *
+ * @exception BuildException if the given value has an unclosed
+ * property name, e.g. <code>${xxx</code>.
+ */
+ public String replaceProperties(final String value) throws BuildException {
+ return PropertyHelper.getPropertyHelper(this).replaceProperties(null, value, null);
+ }
+
+ /**
+ * Return the value of a user property, if it is set.
+ *
+ * @param propertyName The name of the property.
+ * May be <code>null</code>, in which case
+ * the return value is also <code>null</code>.
+ * @return the property value, or <code>null</code> for no match
+ * or if a <code>null</code> name is provided.
+ */
+ public String getUserProperty(final String propertyName) {
+ return (String) PropertyHelper.getPropertyHelper(this).getUserProperty(propertyName);
+ }
+
+ /**
+ * Return a copy of the properties table.
+ * @return a hashtable containing all properties
+ * (including user properties).
+ */
+ public Hashtable<String, Object> getProperties() {
+ return PropertyHelper.getPropertyHelper(this).getProperties();
+ }
+
+ /**
+ * Return a copy of the user property hashtable.
+ * @return a hashtable containing just the user properties.
+ */
+ public Hashtable<String, Object> getUserProperties() {
+ return PropertyHelper.getPropertyHelper(this).getUserProperties();
+ }
+
+ /**
+ * Return a copy of the inherited property hashtable.
+ * @return a hashtable containing just the inherited properties.
+ * @since Ant 1.8.0
+ */
+ public Hashtable<String, Object> getInheritedProperties() {
+ return PropertyHelper.getPropertyHelper(this).getInheritedProperties();
+ }
+
+ /**
+ * Copy all user properties that have been set on the command
+ * line or a GUI tool from this instance to the Project instance
+ * given as the argument.
+ *
+ * <p>To copy all &quot;user&quot; properties, you will also have to call
+ * {@link #copyInheritedProperties copyInheritedProperties}.</p>
+ *
+ * @param other the project to copy the properties to. Must not be null.
+ *
+ * @since Ant 1.5
+ */
+ public void copyUserProperties(final Project other) {
+ PropertyHelper.getPropertyHelper(this).copyUserProperties(other);
+ }
+
+ /**
+ * Copy all user properties that have not been set on the
+ * command line or a GUI tool from this instance to the Project
+ * instance given as the argument.
+ *
+ * <p>To copy all &quot;user&quot; properties, you will also have to call
+ * {@link #copyUserProperties copyUserProperties}.</p>
+ *
+ * @param other the project to copy the properties to. Must not be null.
+ *
+ * @since Ant 1.5
+ */
+ public void copyInheritedProperties(final Project other) {
+ PropertyHelper.getPropertyHelper(this).copyInheritedProperties(other);
+ }
+
+ /**
+ * Set the default target of the project.
+ *
+ * @param defaultTarget The name of the default target for this project.
+ * May be <code>null</code>, indicating that there is
+ * no default target.
+ *
+ * @deprecated since 1.5.x.
+ * Use setDefault.
+ * @see #setDefault(String)
+ */
+ @Deprecated
+ public void setDefaultTarget(final String defaultTarget) {
+ setDefault(defaultTarget);
+ }
+
+ /**
+ * Return the name of the default target of the project.
+ * @return name of the default target or
+ * <code>null</code> if no default has been set.
+ */
+ public String getDefaultTarget() {
+ return defaultTarget;
+ }
+
+ /**
+ * Set the default target of the project.
+ *
+ * @param defaultTarget The name of the default target for this project.
+ * May be <code>null</code>, indicating that there is
+ * no default target.
+ */
+ public void setDefault(final String defaultTarget) {
+ if (defaultTarget != null) {
+ setUserProperty(MagicNames.PROJECT_DEFAULT_TARGET, defaultTarget);
+ }
+ this.defaultTarget = defaultTarget;
+ }
+
+ /**
+ * Set the name of the project, also setting the user
+ * property <code>ant.project.name</code>.
+ *
+ * @param name The name of the project.
+ * Must not be <code>null</code>.
+ */
+ public void setName(final String name) {
+ setUserProperty(MagicNames.PROJECT_NAME, name);
+ this.name = name;
+ }
+
+ /**
+ * Return the project name, if one has been set.
+ *
+ * @return the project name, or <code>null</code> if it hasn't been set.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the project description.
+ *
+ * @param description The description of the project.
+ * May be <code>null</code>.
+ */
+ public void setDescription(final String description) {
+ this.description = description;
+ }
+
+ /**
+ * Return the project description, if one has been set.
+ *
+ * @return the project description, or <code>null</code> if it hasn't
+ * been set.
+ */
+ public String getDescription() {
+ if (description == null) {
+ description = Description.getDescription(this);
+ }
+ return description;
+ }
+
+ /**
+ * Add a filter to the set of global filters.
+ *
+ * @param token The token to filter.
+ * Must not be <code>null</code>.
+ * @param value The replacement value.
+ * Must not be <code>null</code>.
+ * @deprecated since 1.4.x.
+ * Use getGlobalFilterSet().addFilter(token,value)
+ *
+ * @see #getGlobalFilterSet()
+ * @see FilterSet#addFilter(String,String)
+ */
+ @Deprecated
+ public void addFilter(final String token, final String value) {
+ if (token == null) {
+ return;
+ }
+ globalFilterSet.addFilter(new FilterSet.Filter(token, value));
+ }
+
+ /**
+ * Return a hashtable of global filters, mapping tokens to values.
+ *
+ * @return a hashtable of global filters, mapping tokens to values
+ * (String to String).
+ *
+ * @deprecated since 1.4.x
+ * Use getGlobalFilterSet().getFilterHash().
+ *
+ * @see #getGlobalFilterSet()
+ * @see FilterSet#getFilterHash()
+ */
+ @Deprecated
+ public Hashtable<String, String> getFilters() {
+ // we need to build the hashtable dynamically
+ return globalFilterSet.getFilterHash();
+ }
+
+ /**
+ * Set the base directory for the project, checking that
+ * the given filename exists and is a directory.
+ *
+ * @param baseD The project base directory.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the directory if invalid.
+ */
+ public void setBasedir(final String baseD) throws BuildException {
+ setBaseDir(new File(baseD));
+ }
+
+ /**
+ * Set the base directory for the project, checking that
+ * the given file exists and is a directory.
+ *
+ * @param baseDir The project base directory.
+ * Must not be <code>null</code>.
+ * @exception BuildException if the specified file doesn't exist or
+ * isn't a directory.
+ */
+ public void setBaseDir(File baseDir) throws BuildException {
+ baseDir = FILE_UTILS.normalize(baseDir.getAbsolutePath());
+ if (!baseDir.exists()) {
+ throw new BuildException("Basedir " + baseDir.getAbsolutePath()
+ + " does not exist");
+ }
+ if (!baseDir.isDirectory()) {
+ throw new BuildException("Basedir " + baseDir.getAbsolutePath()
+ + " is not a directory");
+ }
+ this.baseDir = baseDir;
+ setPropertyInternal(MagicNames.PROJECT_BASEDIR, this.baseDir.getPath());
+ final String msg = "Project base dir set to: " + this.baseDir;
+ log(msg, MSG_VERBOSE);
+ }
+
+ /**
+ * Return the base directory of the project as a file object.
+ *
+ * @return the project base directory, or <code>null</code> if the
+ * base directory has not been successfully set to a valid value.
+ */
+ public File getBaseDir() {
+ if (baseDir == null) {
+ try {
+ setBasedir(".");
+ } catch (final BuildException ex) {
+ ex.printStackTrace();
+ }
+ }
+ return baseDir;
+ }
+
+ /**
+ * Set &quot;keep-going&quot; mode. In this mode Ant will try to execute
+ * as many targets as possible. All targets that do not depend
+ * on failed target(s) will be executed. If the keepGoing settor/getter
+ * methods are used in conjunction with the <code>ant.executor.class</code>
+ * property, they will have no effect.
+ * @param keepGoingMode &quot;keep-going&quot; mode
+ * @since Ant 1.6
+ */
+ public void setKeepGoingMode(final boolean keepGoingMode) {
+ this.keepGoingMode = keepGoingMode;
+ }
+
+ /**
+ * Return the keep-going mode. If the keepGoing settor/getter
+ * methods are used in conjunction with the <code>ant.executor.class</code>
+ * property, they will have no effect.
+ * @return &quot;keep-going&quot; mode
+ * @since Ant 1.6
+ */
+ public boolean isKeepGoingMode() {
+ return this.keepGoingMode;
+ }
+
+ /**
+ * Return the version of Java this class is running under.
+ * @return the version of Java as a String, e.g. "1.1" .
+ * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
+ * @deprecated since 1.5.x.
+ * Use org.apache.tools.ant.util.JavaEnvUtils instead.
+ */
+ @Deprecated
+ public static String getJavaVersion() {
+ return JavaEnvUtils.getJavaVersion();
+ }
+
+ /**
+ * Set the <code>ant.java.version</code> property and tests for
+ * unsupported JVM versions. If the version is supported,
+ * verbose log messages are generated to record the Java version
+ * and operating system name.
+ *
+ * @exception BuildException if this Java version is not supported.
+ *
+ * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
+ */
+ public void setJavaVersionProperty() throws BuildException {
+ final String javaVersion = JavaEnvUtils.getJavaVersion();
+ setPropertyInternal(MagicNames.ANT_JAVA_VERSION, javaVersion);
+
+ // sanity check
+ if (!JavaEnvUtils.isAtLeastJavaVersion(JavaEnvUtils.JAVA_1_5)) {
+ throw new BuildException("Ant cannot work on Java prior to 1.5");
+ }
+ log("Detected Java version: " + javaVersion + " in: "
+ + System.getProperty("java.home"), MSG_VERBOSE);
+
+ log("Detected OS: " + System.getProperty("os.name"), MSG_VERBOSE);
+ }
+
+ /**
+ * Add all system properties which aren't already defined as
+ * user properties to the project properties.
+ */
+ public void setSystemProperties() {
+ final Properties systemP = System.getProperties();
+ final Enumeration<?> e = systemP.propertyNames();
+ while (e.hasMoreElements()) {
+ final String propertyName = (String) e.nextElement();
+ final String value = systemP.getProperty(propertyName);
+ if (value != null) {
+ this.setPropertyInternal(propertyName, value);
+ }
+ }
+ }
+
+ /**
+ * Add a new task definition to the project.
+ * Attempting to override an existing definition with an
+ * equivalent one (i.e. with the same classname) results in
+ * a verbose log message. Attempting to override an existing definition
+ * with a different one results in a warning log message and
+ * invalidates any tasks which have already been created with the
+ * old definition.
+ *
+ * @param taskName The name of the task to add.
+ * Must not be <code>null</code>.
+ * @param taskClass The full name of the class implementing the task.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the class is unsuitable for being an Ant
+ * task. An error level message is logged before
+ * this exception is thrown.
+ *
+ * @see #checkTaskClass(Class)
+ */
+ public void addTaskDefinition(final String taskName, final Class<?> taskClass)
+ throws BuildException {
+ ComponentHelper.getComponentHelper(this).addTaskDefinition(taskName,
+ taskClass);
+ }
+
+ /**
+ * Check whether or not a class is suitable for serving as Ant task.
+ * Ant task implementation classes must be public, concrete, and have
+ * a no-arg constructor.
+ *
+ * @param taskClass The class to be checked.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the class is unsuitable for being an Ant
+ * task. An error level message is logged before
+ * this exception is thrown.
+ */
+ public void checkTaskClass(final Class<?> taskClass) throws BuildException {
+ ComponentHelper.getComponentHelper(this).checkTaskClass(taskClass);
+
+ if (!Modifier.isPublic(taskClass.getModifiers())) {
+ final String message = taskClass + " is not public";
+ log(message, Project.MSG_ERR);
+ throw new BuildException(message);
+ }
+ if (Modifier.isAbstract(taskClass.getModifiers())) {
+ final String message = taskClass + " is abstract";
+ log(message, Project.MSG_ERR);
+ throw new BuildException(message);
+ }
+ try {
+ taskClass.getConstructor();
+ // don't have to check for public, since
+ // getConstructor finds public constructors only.
+ } catch (final NoSuchMethodException e) {
+ final String message = "No public no-arg constructor in "
+ + taskClass;
+ log(message, Project.MSG_ERR);
+ throw new BuildException(message);
+ } catch (final LinkageError e) {
+ final String message = "Could not load " + taskClass + ": " + e;
+ log(message, Project.MSG_ERR);
+ throw new BuildException(message, e);
+ }
+ if (!Task.class.isAssignableFrom(taskClass)) {
+ TaskAdapter.checkTaskClass(taskClass, this);
+ }
+ }
+
+ /**
+ * Return the current task definition hashtable. The returned hashtable is
+ * &quot;live&quot; and so should not be modified.
+ *
+ * @return a map of from task name to implementing class
+ * (String to Class).
+ */
+ public Hashtable<String, Class<?>> getTaskDefinitions() {
+ return ComponentHelper.getComponentHelper(this).getTaskDefinitions();
+ }
+
+ /**
+ * Return the current task definition map. The returned map is a
+ * copy of the &quot;live&quot; definitions.
+ *
+ * @return a map of from task name to implementing class
+ * (String to Class).
+ *
+ * @since Ant 1.8.1
+ */
+ public Map<String, Class<?>> getCopyOfTaskDefinitions() {
+ return new HashMap<String, Class<?>>(getTaskDefinitions());
+ }
+
+ /**
+ * Add a new datatype definition.
+ * Attempting to override an existing definition with an
+ * equivalent one (i.e. with the same classname) results in
+ * a verbose log message. Attempting to override an existing definition
+ * with a different one results in a warning log message, but the
+ * definition is changed.
+ *
+ * @param typeName The name of the datatype.
+ * Must not be <code>null</code>.
+ * @param typeClass The full name of the class implementing the datatype.
+ * Must not be <code>null</code>.
+ */
+ public void addDataTypeDefinition(final String typeName, final Class<?> typeClass) {
+ ComponentHelper.getComponentHelper(this).addDataTypeDefinition(typeName,
+ typeClass);
+ }
+
+ /**
+ * Return the current datatype definition hashtable. The returned
+ * hashtable is &quot;live&quot; and so should not be modified.
+ *
+ * @return a map of from datatype name to implementing class
+ * (String to Class).
+ */
+ public Hashtable<String, Class<?>> getDataTypeDefinitions() {
+ return ComponentHelper.getComponentHelper(this).getDataTypeDefinitions();
+ }
+
+ /**
+ * Return the current datatype definition map. The returned
+ * map is a copy pf the &quot;live&quot; definitions.
+ *
+ * @return a map of from datatype name to implementing class
+ * (String to Class).
+ *
+ * @since Ant 1.8.1
+ */
+ public Map<String, Class<?>> getCopyOfDataTypeDefinitions() {
+ return new HashMap<String, Class<?>>(getDataTypeDefinitions());
+ }
+
+ /**
+ * Add a <em>new</em> target to the project.
+ *
+ * @param target The target to be added to the project.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the target already exists in the project
+ *
+ * @see Project#addOrReplaceTarget(Target)
+ */
+ public void addTarget(final Target target) throws BuildException {
+ addTarget(target.getName(), target);
+ }
+
+ /**
+ * Add a <em>new</em> target to the project.
+ *
+ * @param targetName The name to use for the target.
+ * Must not be <code>null</code>.
+ * @param target The target to be added to the project.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the target already exists in the project.
+ *
+ * @see Project#addOrReplaceTarget(String, Target)
+ */
+ public void addTarget(final String targetName, final Target target)
+ throws BuildException {
+ if (targets.get(targetName) != null) {
+ throw new BuildException("Duplicate target: `" + targetName + "'");
+ }
+ addOrReplaceTarget(targetName, target);
+ }
+
+ /**
+ * Add a target to the project, or replaces one with the same
+ * name.
+ *
+ * @param target The target to be added or replaced in the project.
+ * Must not be <code>null</code>.
+ */
+ public void addOrReplaceTarget(final Target target) {
+ addOrReplaceTarget(target.getName(), target);
+ }
+
+ /**
+ * Add a target to the project, or replaces one with the same
+ * name.
+ *
+ * @param targetName The name to use for the target.
+ * Must not be <code>null</code>.
+ * @param target The target to be added or replaced in the project.
+ * Must not be <code>null</code>.
+ */
+ public void addOrReplaceTarget(final String targetName, final Target target) {
+ final String msg = " +Target: " + targetName;
+ log(msg, MSG_DEBUG);
+ target.setProject(this);
+ targets.put(targetName, target);
+ }
+
+ /**
+ * Return the hashtable of targets. The returned hashtable
+ * is &quot;live&quot; and so should not be modified.
+ * @return a map from name to target (String to Target).
+ */
+ public Hashtable<String, Target> getTargets() {
+ return targets;
+ }
+
+ /**
+ * Return the map of targets. The returned map
+ * is a copy of the &quot;live&quot; targets.
+ * @return a map from name to target (String to Target).
+ * @since Ant 1.8.1
+ */
+ public Map<String, Target> getCopyOfTargets() {
+ return new HashMap<String, Target>(targets);
+ }
+
+ /**
+ * Create a new instance of a task, adding it to a list of
+ * created tasks for later invalidation. This causes all tasks
+ * to be remembered until the containing project is removed
+ * @param taskType The name of the task to create an instance of.
+ * Must not be <code>null</code>.
+ *
+ * @return an instance of the specified task, or <code>null</code> if
+ * the task name is not recognised.
+ *
+ * @exception BuildException if the task name is recognised but task
+ * creation fails.
+ */
+ public Task createTask(final String taskType) throws BuildException {
+ return ComponentHelper.getComponentHelper(this).createTask(taskType);
+ }
+
+ /**
+ * Create a new instance of a data type.
+ *
+ * @param typeName The name of the data type to create an instance of.
+ * Must not be <code>null</code>.
+ *
+ * @return an instance of the specified data type, or <code>null</code> if
+ * the data type name is not recognised.
+ *
+ * @exception BuildException if the data type name is recognised but
+ * instance creation fails.
+ */
+ public Object createDataType(final String typeName) throws BuildException {
+ return ComponentHelper.getComponentHelper(this).createDataType(typeName);
+ }
+
+ /**
+ * Set the Executor instance for this Project.
+ * @param e the Executor to use.
+ */
+ public void setExecutor(final Executor e) {
+ addReference(MagicNames.ANT_EXECUTOR_REFERENCE, e);
+ }
+
+ /**
+ * Get this Project's Executor (setting it if necessary).
+ * @return an Executor instance.
+ */
+ public Executor getExecutor() {
+ Object o = getReference(MagicNames.ANT_EXECUTOR_REFERENCE);
+ if (o == null) {
+ String classname = getProperty(MagicNames.ANT_EXECUTOR_CLASSNAME);
+ if (classname == null) {
+ classname = DefaultExecutor.class.getName();
+ }
+ log("Attempting to create object of type " + classname, MSG_DEBUG);
+ try {
+ o = Class.forName(classname, true, coreLoader).newInstance();
+ } catch (final ClassNotFoundException seaEnEfEx) {
+ //try the current classloader
+ try {
+ o = Class.forName(classname).newInstance();
+ } catch (final Exception ex) {
+ log(ex.toString(), MSG_ERR);
+ }
+ } catch (final Exception ex) {
+ log(ex.toString(), MSG_ERR);
+ }
+ if (o == null) {
+ throw new BuildException(
+ "Unable to obtain a Target Executor instance.");
+ }
+ setExecutor((Executor) o);
+ }
+ return (Executor) o;
+ }
+
+ /**
+ * Execute the specified sequence of targets, and the targets
+ * they depend on.
+ *
+ * @param names A vector of target name strings to execute.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the build failed.
+ */
+ public void executeTargets(final Vector<String> names) throws BuildException {
+ setUserProperty(MagicNames.PROJECT_INVOKED_TARGETS,
+ CollectionUtils.flattenToString(names));
+ getExecutor().executeTargets(this, names.toArray(new String[names.size()]));
+ }
+
+ /**
+ * Demultiplex output so that each task receives the appropriate
+ * messages. If the current thread is not currently executing a task,
+ * the message is logged directly.
+ *
+ * @param output Message to handle. Should not be <code>null</code>.
+ * @param isWarning Whether the text represents an warning (<code>true</code>)
+ * or information (<code>false</code>).
+ */
+ public void demuxOutput(final String output, final boolean isWarning) {
+ final Task task = getThreadTask(Thread.currentThread());
+ if (task == null) {
+ log(output, isWarning ? MSG_WARN : MSG_INFO);
+ } else {
+ if (isWarning) {
+ task.handleErrorOutput(output);
+ } else {
+ task.handleOutput(output);
+ }
+ }
+ }
+
+ /**
+ * Read data from the default input stream. If no default has been
+ * specified, System.in is used.
+ *
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception IOException if the data cannot be read.
+ * @since Ant 1.6
+ */
+ public int defaultInput(final byte[] buffer, final int offset, final int length)
+ throws IOException {
+ if (defaultInputStream != null) {
+ System.out.flush();
+ return defaultInputStream.read(buffer, offset, length);
+ } else {
+ throw new EOFException("No input provided for project");
+ }
+ }
+
+ /**
+ * Demux an input request to the correct task.
+ *
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception IOException if the data cannot be read.
+ * @since Ant 1.6
+ */
+ public int demuxInput(final byte[] buffer, final int offset, final int length)
+ throws IOException {
+ final Task task = getThreadTask(Thread.currentThread());
+ if (task == null) {
+ return defaultInput(buffer, offset, length);
+ } else {
+ return task.handleInput(buffer, offset, length);
+ }
+ }
+
+ /**
+ * Demultiplex flush operations so that each task receives the appropriate
+ * messages. If the current thread is not currently executing a task,
+ * the message is logged directly.
+ *
+ * @since Ant 1.5.2
+ *
+ * @param output Message to handle. Should not be <code>null</code>.
+ * @param isError Whether the text represents an error (<code>true</code>)
+ * or information (<code>false</code>).
+ */
+ public void demuxFlush(final String output, final boolean isError) {
+ final Task task = getThreadTask(Thread.currentThread());
+ if (task == null) {
+ fireMessageLogged(this, output, isError ? MSG_ERR : MSG_INFO);
+ } else {
+ if (isError) {
+ task.handleErrorFlush(output);
+ } else {
+ task.handleFlush(output);
+ }
+ }
+ }
+
+ /**
+ * Execute the specified target and any targets it depends on.
+ *
+ * @param targetName The name of the target to execute.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the build failed.
+ */
+ public void executeTarget(final String targetName) throws BuildException {
+
+ // sanity check ourselves, if we've been asked to build nothing
+ // then we should complain
+
+ if (targetName == null) {
+ final String msg = "No target specified";
+ throw new BuildException(msg);
+ }
+
+ // Sort and run the dependency tree.
+ // Sorting checks if all the targets (and dependencies)
+ // exist, and if there is any cycle in the dependency
+ // graph.
+ executeSortedTargets(topoSort(targetName, targets, false));
+ }
+
+ /**
+ * Execute a <code>Vector</code> of sorted targets.
+ * @param sortedTargets the aforementioned <code>Vector</code>.
+ * @throws BuildException on error.
+ */
+ public void executeSortedTargets(final Vector<Target> sortedTargets)
+ throws BuildException {
+ final Set<String> succeededTargets = new HashSet<String>();
+ BuildException buildException = null; // first build exception
+ for (final Target curtarget : sortedTargets) {
+ boolean canExecute = true;
+ for (final Enumeration<String> depIter = curtarget.getDependencies();
+ depIter.hasMoreElements();) {
+ final String dependencyName = depIter.nextElement();
+ if (!succeededTargets.contains(dependencyName)) {
+ canExecute = false;
+ log(curtarget,
+ "Cannot execute '" + curtarget.getName() + "' - '"
+ + dependencyName + "' failed or was not executed.",
+ MSG_ERR);
+ break;
+ }
+ }
+ if (canExecute) {
+ Throwable thrownException = null;
+ try {
+ curtarget.performTasks();
+ succeededTargets.add(curtarget.getName());
+ } catch (final RuntimeException ex) {
+ if (!(keepGoingMode)) {
+ throw ex; // throw further
+ }
+ thrownException = ex;
+ } catch (final Throwable ex) {
+ if (!(keepGoingMode)) {
+ throw new BuildException(ex);
+ }
+ thrownException = ex;
+ }
+ if (thrownException != null) {
+ if (thrownException instanceof BuildException) {
+ log(curtarget,
+ "Target '" + curtarget.getName()
+ + "' failed with message '"
+ + thrownException.getMessage() + "'.", MSG_ERR);
+ // only the first build exception is reported
+ if (buildException == null) {
+ buildException = (BuildException) thrownException;
+ }
+ } else {
+ log(curtarget,
+ "Target '" + curtarget.getName()
+ + "' failed with message '"
+ + thrownException.getMessage() + "'.", MSG_ERR);
+ thrownException.printStackTrace(System.err);
+ if (buildException == null) {
+ buildException =
+ new BuildException(thrownException);
+ }
+ }
+ }
+ }
+ }
+ if (buildException != null) {
+ throw buildException;
+ }
+ }
+
+ /**
+ * Return the canonical form of a filename.
+ * <p>
+ * If the specified file name is relative it is resolved
+ * with respect to the given root directory.
+ *
+ * @param fileName The name of the file to resolve.
+ * Must not be <code>null</code>.
+ *
+ * @param rootDir The directory respective to which relative file names
+ * are resolved. May be <code>null</code>, in which case
+ * the current directory is used.
+ *
+ * @return the resolved File.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public File resolveFile(final String fileName, final File rootDir) {
+ return FILE_UTILS.resolveFile(rootDir, fileName);
+ }
+
+ /**
+ * Return the canonical form of a filename.
+ * <p>
+ * If the specified file name is relative it is resolved
+ * with respect to the project's base directory.
+ *
+ * @param fileName The name of the file to resolve.
+ * Must not be <code>null</code>.
+ *
+ * @return the resolved File.
+ *
+ */
+ public File resolveFile(final String fileName) {
+ return FILE_UTILS.resolveFile(baseDir, fileName);
+ }
+
+ /**
+ * Translate a path into its native (platform specific) format.
+ * <p>
+ * This method uses PathTokenizer to separate the input path
+ * into its components. This handles DOS style paths in a relatively
+ * sensible way. The file separators are then converted to their platform
+ * specific versions.
+ *
+ * @param toProcess The path to be translated.
+ * May be <code>null</code>.
+ *
+ * @return the native version of the specified path or
+ * an empty string if the path is <code>null</code> or empty.
+ *
+ * @deprecated since 1.7
+ * Use FileUtils.translatePath instead.
+ *
+ * @see PathTokenizer
+ */
+ @Deprecated
+ public static String translatePath(final String toProcess) {
+ return FileUtils.translatePath(toProcess);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination.
+ * No filtering is performed.
+ *
+ * @param sourceFile Name of file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile Name of file to copy to.
+ * Must not be <code>null</code>.
+ *
+ * @exception IOException if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final String sourceFile, final String destFile)
+ throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination
+ * specifying if token filtering should be used.
+ *
+ * @param sourceFile Name of file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile Name of file to copy to.
+ * Must not be <code>null</code>.
+ * @param filtering Whether or not token filtering should be used during
+ * the copy.
+ *
+ * @exception IOException if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final String sourceFile, final String destFile, final boolean filtering)
+ throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile,
+ filtering ? globalFilters : null);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering should be used and if
+ * source files may overwrite newer destination files.
+ *
+ * @param sourceFile Name of file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile Name of file to copy to.
+ * Must not be <code>null</code>.
+ * @param filtering Whether or not token filtering should be used during
+ * the copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ *
+ * @exception IOException if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final String sourceFile, final String destFile, final boolean filtering,
+ final boolean overwrite) throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile,
+ filtering ? globalFilters : null, overwrite);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering should be used, if
+ * source files may overwrite newer destination files, and if the
+ * last modified time of the resulting file should be set to
+ * that of the source file.
+ *
+ * @param sourceFile Name of file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile Name of file to copy to.
+ * Must not be <code>null</code>.
+ * @param filtering Whether or not token filtering should be used during
+ * the copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the resulting file should be set to that
+ * of the source file.
+ *
+ * @exception IOException if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final String sourceFile, final String destFile, final boolean filtering,
+ final boolean overwrite, final boolean preserveLastModified)
+ throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile,
+ filtering ? globalFilters : null, overwrite, preserveLastModified);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination.
+ * No filtering is performed.
+ *
+ * @param sourceFile File to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile File to copy to.
+ * Must not be <code>null</code>.
+ *
+ * @exception IOException if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final File sourceFile, final File destFile) throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination
+ * specifying if token filtering should be used.
+ *
+ * @param sourceFile File to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile File to copy to.
+ * Must not be <code>null</code>.
+ * @param filtering Whether or not token filtering should be used during
+ * the copy.
+ *
+ * @exception IOException if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final File sourceFile, final File destFile, final boolean filtering)
+ throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile,
+ filtering ? globalFilters : null);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering should be used and if
+ * source files may overwrite newer destination files.
+ *
+ * @param sourceFile File to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile File to copy to.
+ * Must not be <code>null</code>.
+ * @param filtering Whether or not token filtering should be used during
+ * the copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ *
+ * @exception IOException if the file cannot be copied.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final File sourceFile, final File destFile, final boolean filtering,
+ final boolean overwrite) throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile,
+ filtering ? globalFilters : null, overwrite);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering should be used, if
+ * source files may overwrite newer destination files, and if the
+ * last modified time of the resulting file should be set to
+ * that of the source file.
+ *
+ * @param sourceFile File to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile File to copy to.
+ * Must not be <code>null</code>.
+ * @param filtering Whether or not token filtering should be used during
+ * the copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the resulting file should be set to that
+ * of the source file.
+ *
+ * @exception IOException if the file cannot be copied.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final File sourceFile, final File destFile, final boolean filtering,
+ final boolean overwrite, final boolean preserveLastModified)
+ throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile,
+ filtering ? globalFilters : null, overwrite, preserveLastModified);
+ }
+
+ /**
+ * Call File.setLastModified(long time) on Java above 1.1, and logs
+ * a warning on Java 1.1.
+ *
+ * @param file The file to set the last modified time on.
+ * Must not be <code>null</code>.
+ *
+ * @param time the required modification time.
+ *
+ * @deprecated since 1.4.x
+ *
+ * @exception BuildException if the last modified time cannot be set
+ * despite running on a platform with a version
+ * above 1.1.
+ */
+ @Deprecated
+ public void setFileLastModified(final File file, final long time)
+ throws BuildException {
+ FILE_UTILS.setFileLastModified(file, time);
+ log("Setting modification time for " + file, MSG_VERBOSE);
+ }
+
+ /**
+ * Return the boolean equivalent of a string, which is considered
+ * <code>true</code> if either <code>"on"</code>, <code>"true"</code>,
+ * or <code>"yes"</code> is found, ignoring case.
+ *
+ * @param s The string to convert to a boolean value.
+ *
+ * @return <code>true</code> if the given string is <code>"on"</code>,
+ * <code>"true"</code> or <code>"yes"</code>, or
+ * <code>false</code> otherwise.
+ */
+ public static boolean toBoolean(final String s) {
+ return ("on".equalsIgnoreCase(s)
+ || "true".equalsIgnoreCase(s)
+ || "yes".equalsIgnoreCase(s));
+ }
+
+ /**
+ * Get the Project instance associated with the specified object.
+ * @param o the object to query.
+ * @return Project instance, if any.
+ * @since Ant 1.7.1
+ */
+ public static Project getProject(final Object o) {
+ if (o instanceof ProjectComponent) {
+ return ((ProjectComponent) o).getProject();
+ }
+ try {
+ final Method m = o.getClass().getMethod("getProject", (Class[]) null);
+ if (Project.class == m.getReturnType()) {
+ return (Project) m.invoke(o, (Object[]) null);
+ }
+ } catch (final Exception e) {
+ //too bad
+ }
+ return null;
+ }
+
+ /**
+ * Topologically sort a set of targets. Equivalent to calling
+ * <code>topoSort(new String[] {root}, targets, true)</code>.
+ *
+ * @param root The name of the root target. The sort is created in such
+ * a way that the sequence of Targets up to the root
+ * target is the minimum possible such sequence.
+ * Must not be <code>null</code>.
+ * @param targetTable A Hashtable mapping names to Targets.
+ * Must not be <code>null</code>.
+ * @return a Vector of ALL Target objects in sorted order.
+ * @exception BuildException if there is a cyclic dependency among the
+ * targets, or if a named target does not exist.
+ */
+ public final Vector<Target> topoSort(final String root, final Hashtable<String, Target> targetTable)
+ throws BuildException {
+ return topoSort(new String[] {root}, targetTable, true);
+ }
+
+ /**
+ * Topologically sort a set of targets. Equivalent to calling
+ * <code>topoSort(new String[] {root}, targets, returnAll)</code>.
+ *
+ * @param root The name of the root target. The sort is created in such
+ * a way that the sequence of Targets up to the root
+ * target is the minimum possible such sequence.
+ * Must not be <code>null</code>.
+ * @param targetTable A Hashtable mapping names to Targets.
+ * Must not be <code>null</code>.
+ * @param returnAll <code>boolean</code> indicating whether to return all
+ * targets, or the execution sequence only.
+ * @return a Vector of Target objects in sorted order.
+ * @exception BuildException if there is a cyclic dependency among the
+ * targets, or if a named target does not exist.
+ * @since Ant 1.6.3
+ */
+ public final Vector<Target> topoSort(final String root, final Hashtable<String, Target> targetTable,
+ final boolean returnAll) throws BuildException {
+ return topoSort(new String[] {root}, targetTable, returnAll);
+ }
+
+ /**
+ * Topologically sort a set of targets.
+ *
+ * @param root <code>String[]</code> containing the names of the root targets.
+ * The sort is created in such a way that the ordered sequence of
+ * Targets is the minimum possible such sequence to the specified
+ * root targets.
+ * Must not be <code>null</code>.
+ * @param targetTable A map of names to targets (String to Target).
+ * Must not be <code>null</code>.
+ * @param returnAll <code>boolean</code> indicating whether to return all
+ * targets, or the execution sequence only.
+ * @return a Vector of Target objects in sorted order.
+ * @exception BuildException if there is a cyclic dependency among the
+ * targets, or if a named target does not exist.
+ * @since Ant 1.6.3
+ */
+ public final Vector<Target> topoSort(final String[] root, final Hashtable<String, Target> targetTable,
+ final boolean returnAll) throws BuildException {
+ final Vector<Target> ret = new VectorSet<Target>();
+ final Hashtable<String, String> state = new Hashtable<String, String>();
+ final Stack<String> visiting = new Stack<String>();
+
+ // We first run a DFS based sort using each root as a starting node.
+ // This creates the minimum sequence of Targets to the root node(s).
+ // We then do a sort on any remaining unVISITED targets.
+ // This is unnecessary for doing our build, but it catches
+ // circular dependencies or missing Targets on the entire
+ // dependency tree, not just on the Targets that depend on the
+ // build Target.
+
+ for (int i = 0; i < root.length; i++) {
+ final String st = (state.get(root[i]));
+ if (st == null) {
+ tsort(root[i], targetTable, state, visiting, ret);
+ } else if (st == VISITING) {
+ throw new RuntimeException("Unexpected node in visiting state: "
+ + root[i]);
+ }
+ }
+ final StringBuffer buf = new StringBuffer("Build sequence for target(s)");
+
+ for (int j = 0; j < root.length; j++) {
+ buf.append((j == 0) ? " `" : ", `").append(root[j]).append('\'');
+ }
+ buf.append(" is " + ret);
+ log(buf.toString(), MSG_VERBOSE);
+
+ final Vector<Target> complete = (returnAll) ? ret : new Vector<Target>(ret);
+ for (final Enumeration<String> en = targetTable.keys(); en.hasMoreElements();) {
+ final String curTarget = en.nextElement();
+ final String st = state.get(curTarget);
+ if (st == null) {
+ tsort(curTarget, targetTable, state, visiting, complete);
+ } else if (st == VISITING) {
+ throw new RuntimeException("Unexpected node in visiting state: "
+ + curTarget);
+ }
+ }
+ log("Complete build sequence is " + complete, MSG_VERBOSE);
+ return ret;
+ }
+
+ /**
+ * Perform a single step in a recursive depth-first-search traversal of
+ * the target dependency tree.
+ * <p>
+ * The current target is first set to the &quot;visiting&quot; state, and
+ * pushed onto the &quot;visiting&quot; stack.
+ * <p>
+ * An exception is then thrown if any child of the current node is in the
+ * visiting state, as that implies a circular dependency. The exception
+ * contains details of the cycle, using elements of the &quot;visiting&quot;
+ * stack.
+ * <p>
+ * If any child has not already been &quot;visited&quot;, this method is
+ * called recursively on it.
+ * <p>
+ * The current target is then added to the ordered list of targets. Note
+ * that this is performed after the children have been visited in order
+ * to get the correct order. The current target is set to the
+ * &quot;visited&quot; state.
+ * <p>
+ * By the time this method returns, the ordered list contains the sequence
+ * of targets up to and including the current target.
+ *
+ * @param root The current target to inspect.
+ * Must not be <code>null</code>.
+ * @param targetTable A mapping from names to targets (String to Target).
+ * Must not be <code>null</code>.
+ * @param state A mapping from target names to states (String to String).
+ * The states in question are &quot;VISITING&quot; and
+ * &quot;VISITED&quot;. Must not be <code>null</code>.
+ * @param visiting A stack of targets which are currently being visited.
+ * Must not be <code>null</code>.
+ * @param ret The list to add target names to. This will end up
+ * containing the complete list of dependencies in
+ * dependency order.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if a non-existent target is specified or if
+ * a circular dependency is detected.
+ */
+ private void tsort(final String root, final Hashtable<String, Target> targetTable,
+ final Hashtable<String, String> state, final Stack<String> visiting,
+ final Vector<Target> ret)
+ throws BuildException {
+ state.put(root, VISITING);
+ visiting.push(root);
+
+ final Target target = targetTable.get(root);
+
+ // Make sure we exist
+ if (target == null) {
+ final StringBuilder sb = new StringBuilder("Target \"");
+ sb.append(root);
+ sb.append("\" does not exist in the project \"");
+ sb.append(name);
+ sb.append("\". ");
+ visiting.pop();
+ if (!visiting.empty()) {
+ final String parent = visiting.peek();
+ sb.append("It is used from target \"");
+ sb.append(parent);
+ sb.append("\".");
+ }
+ throw new BuildException(new String(sb));
+ }
+ for (final Enumeration<String> en = target.getDependencies(); en.hasMoreElements();) {
+ final String cur = en.nextElement();
+ final String m = state.get(cur);
+ if (m == null) {
+ // Not been visited
+ tsort(cur, targetTable, state, visiting, ret);
+ } else if (m == VISITING) {
+ // Currently visiting this node, so have a cycle
+ throw makeCircularException(cur, visiting);
+ }
+ }
+ final String p = visiting.pop();
+ if (root != p) {
+ throw new RuntimeException("Unexpected internal error: expected to "
+ + "pop " + root + " but got " + p);
+ }
+ state.put(root, VISITED);
+ ret.addElement(target);
+ }
+
+ /**
+ * Build an appropriate exception detailing a specified circular
+ * dependency.
+ *
+ * @param end The dependency to stop at. Must not be <code>null</code>.
+ * @param stk A stack of dependencies. Must not be <code>null</code>.
+ *
+ * @return a BuildException detailing the specified circular dependency.
+ */
+ private static BuildException makeCircularException(final String end, final Stack<String> stk) {
+ final StringBuilder sb = new StringBuilder("Circular dependency: ");
+ sb.append(end);
+ String c;
+ do {
+ c = stk.pop();
+ sb.append(" <- ");
+ sb.append(c);
+ } while (!c.equals(end));
+ return new BuildException(sb.toString());
+ }
+
+ /**
+ * Inherit the id references.
+ * @param parent the parent project of this project.
+ */
+ public void inheritIDReferences(final Project parent) {
+ }
+
+ /**
+ * Add an id reference.
+ * Used for broken build files.
+ * @param id the id to set.
+ * @param value the value to set it to (Unknown element in this case.
+ */
+ public void addIdReference(final String id, final Object value) {
+ idReferences.put(id, value);
+ }
+
+ /**
+ * Add a reference to the project.
+ *
+ * @param referenceName The name of the reference. Must not be <code>null</code>.
+ * @param value The value of the reference.
+ */
+ public void addReference(final String referenceName, final Object value) {
+ final Object old = ((AntRefTable) references).getReal(referenceName);
+ if (old == value) {
+ // no warning, this is not changing anything
+ return;
+ }
+ if (old != null && !(old instanceof UnknownElement)) {
+ log("Overriding previous definition of reference to " + referenceName,
+ MSG_VERBOSE);
+ }
+ log("Adding reference: " + referenceName, MSG_DEBUG);
+ references.put(referenceName, value);
+ }
+
+ /**
+ * Return a map of the references in the project (String to Object).
+ * The returned hashtable is &quot;live&quot; and so must not be modified.
+ *
+ * @return a map of the references in the project (String to Object).
+ */
+ public Hashtable<String, Object> getReferences() {
+ return references;
+ }
+
+ /**
+ * Does the project know this reference?
+ *
+ * @since Ant 1.8.0
+ */
+ public boolean hasReference(final String key) {
+ return references.containsKey(key);
+ }
+
+ /**
+ * Return a map of the references in the project (String to
+ * Object). The returned hashtable is a copy of the
+ * &quot;live&quot; references.
+ *
+ * @return a map of the references in the project (String to Object).
+ *
+ * @since Ant 1.8.1
+ */
+ public Map<String, Object> getCopyOfReferences() {
+ return new HashMap<String, Object>(references);
+ }
+
+ /**
+ * Look up a reference by its key (ID).
+ *
+ * @param key The key for the desired reference.
+ * Must not be <code>null</code>.
+ *
+ * @return the reference with the specified ID, or <code>null</code> if
+ * there is no such reference in the project, with type inference.
+ */
+ public <T> T getReference(final String key) {
+ @SuppressWarnings("unchecked")
+ final T ret = (T) references.get(key);
+ if (ret != null) {
+ return ret;
+ }
+ if (!key.equals(MagicNames.REFID_PROPERTY_HELPER)) {
+ try {
+ if (PropertyHelper.getPropertyHelper(this).containsProperties(key)) {
+ log("Unresolvable reference " + key
+ + " might be a misuse of property expansion syntax.", MSG_WARN);
+ }
+ } catch (final Exception e) {
+ //ignore
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return a description of the type of the given element, with
+ * special handling for instances of tasks and data types.
+ * <p>
+ * This is useful for logging purposes.
+ *
+ * @param element The element to describe.
+ * Must not be <code>null</code>.
+ *
+ * @return a description of the element type.
+ *
+ * @since 1.95, Ant 1.5
+ */
+ public String getElementName(final Object element) {
+ return ComponentHelper.getComponentHelper(this).getElementName(element);
+ }
+
+ /**
+ * Send a &quot;build started&quot; event
+ * to the build listeners for this project.
+ */
+ public void fireBuildStarted() {
+ final BuildEvent event = new BuildEvent(this);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].buildStarted(event);
+ }
+ }
+
+ /**
+ * Send a &quot;build finished&quot; event to the build listeners
+ * for this project.
+ * @param exception an exception indicating a reason for a build
+ * failure. May be <code>null</code>, indicating
+ * a successful build.
+ */
+ public void fireBuildFinished(final Throwable exception) {
+ final BuildEvent event = new BuildEvent(this);
+ event.setException(exception);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].buildFinished(event);
+ }
+ // Inform IH to clear the cache
+ IntrospectionHelper.clearCache();
+ }
+
+ /**
+ * Send a &quot;subbuild started&quot; event to the build listeners for
+ * this project.
+ *
+ * @since Ant 1.6.2
+ */
+ public void fireSubBuildStarted() {
+ final BuildEvent event = new BuildEvent(this);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ if (currListeners[i] instanceof SubBuildListener) {
+ ((SubBuildListener) currListeners[i]).subBuildStarted(event);
+ }
+ }
+ }
+
+ /**
+ * Send a &quot;subbuild finished&quot; event to the build listeners for
+ * this project.
+ * @param exception an exception indicating a reason for a build
+ * failure. May be <code>null</code>, indicating
+ * a successful build.
+ *
+ * @since Ant 1.6.2
+ */
+ public void fireSubBuildFinished(final Throwable exception) {
+ final BuildEvent event = new BuildEvent(this);
+ event.setException(exception);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ if (currListeners[i] instanceof SubBuildListener) {
+ ((SubBuildListener) currListeners[i]).subBuildFinished(event);
+ }
+ }
+ }
+
+ /**
+ * Send a &quot;target started&quot; event to the build listeners
+ * for this project.
+ *
+ * @param target The target which is starting to build.
+ * Must not be <code>null</code>.
+ */
+ protected void fireTargetStarted(final Target target) {
+ final BuildEvent event = new BuildEvent(target);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].targetStarted(event);
+ }
+
+ }
+
+ /**
+ * Send a &quot;target finished&quot; event to the build listeners
+ * for this project.
+ *
+ * @param target The target which has finished building.
+ * Must not be <code>null</code>.
+ * @param exception an exception indicating a reason for a build
+ * failure. May be <code>null</code>, indicating
+ * a successful build.
+ */
+ protected void fireTargetFinished(final Target target, final Throwable exception) {
+ final BuildEvent event = new BuildEvent(target);
+ event.setException(exception);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].targetFinished(event);
+ }
+
+ }
+
+ /**
+ * Send a &quot;task started&quot; event to the build listeners
+ * for this project.
+ *
+ * @param task The target which is starting to execute.
+ * Must not be <code>null</code>.
+ */
+ protected void fireTaskStarted(final Task task) {
+ // register this as the current task on the current thread.
+ registerThreadTask(Thread.currentThread(), task);
+ final BuildEvent event = new BuildEvent(task);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].taskStarted(event);
+ }
+ }
+
+ /**
+ * Send a &quot;task finished&quot; event to the build listeners for this
+ * project.
+ *
+ * @param task The task which has finished executing.
+ * Must not be <code>null</code>.
+ * @param exception an exception indicating a reason for a build
+ * failure. May be <code>null</code>, indicating
+ * a successful build.
+ */
+ protected void fireTaskFinished(final Task task, final Throwable exception) {
+ registerThreadTask(Thread.currentThread(), null);
+ System.out.flush();
+ System.err.flush();
+ final BuildEvent event = new BuildEvent(task);
+ event.setException(exception);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].taskFinished(event);
+ }
+
+ }
+
+ /**
+ * Send a &quot;message logged&quot; event to the build listeners
+ * for this project.
+ *
+ * @param event The event to send. This should be built up with the
+ * appropriate task/target/project by the caller, so that
+ * this method can set the message and priority, then send
+ * the event. Must not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param priority The priority of the message.
+ */
+ private void fireMessageLoggedEvent(final BuildEvent event, String message,
+ final int priority) {
+
+ if (message == null) {
+ message = String.valueOf(message);
+ }
+ if (message.endsWith(StringUtils.LINE_SEP)) {
+ final int endIndex = message.length() - StringUtils.LINE_SEP.length();
+ event.setMessage(message.substring(0, endIndex), priority);
+ } else {
+ event.setMessage(message, priority);
+ }
+ if (isLoggingMessage.get() != Boolean.FALSE) {
+ /*
+ * One of the Listeners has attempted to access
+ * System.err or System.out.
+ *
+ * We used to throw an exception in this case, but
+ * sometimes Listeners can't prevent it(like our own
+ * Log4jListener which invokes getLogger() which in
+ * turn wants to write to the console).
+ *
+ * @see http://marc.theaimsgroup.com/?t=110538624200006&r=1&w=2
+ *
+ * We now (Ant 1.6.3 and later) simply swallow the message.
+ */
+ return;
+ }
+ try {
+ isLoggingMessage.set(Boolean.TRUE);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].messageLogged(event);
+ }
+ } finally {
+ isLoggingMessage.set(Boolean.FALSE);
+ }
+ }
+
+ /**
+ * Send a &quot;message logged&quot; project level event
+ * to the build listeners for this project.
+ *
+ * @param project The project generating the event.
+ * Should not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param priority The priority of the message.
+ */
+ protected void fireMessageLogged(final Project project, final String message,
+ final int priority) {
+ fireMessageLogged(project, message, null, priority);
+ }
+
+ /**
+ * Send a &quot;message logged&quot; project level event
+ * to the build listeners for this project.
+ *
+ * @param project The project generating the event.
+ * Should not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param throwable The exception that caused this message. May be <code>null</code>.
+ * @param priority The priority of the message.
+ * @since 1.7
+ */
+ protected void fireMessageLogged(final Project project, final String message,
+ final Throwable throwable, final int priority) {
+ final BuildEvent event = new BuildEvent(project);
+ event.setException(throwable);
+ fireMessageLoggedEvent(event, message, priority);
+ }
+
+ /**
+ * Send a &quot;message logged&quot; target level event
+ * to the build listeners for this project.
+ *
+ * @param target The target generating the event.
+ * Must not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param priority The priority of the message.
+ */
+ protected void fireMessageLogged(final Target target, final String message,
+ final int priority) {
+ fireMessageLogged(target, message, null, priority);
+ }
+
+ /**
+ * Send a &quot;message logged&quot; target level event
+ * to the build listeners for this project.
+ *
+ * @param target The target generating the event.
+ * Must not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param throwable The exception that caused this message. May be <code>null</code>.
+ * @param priority The priority of the message.
+ * @since 1.7
+ */
+ protected void fireMessageLogged(final Target target, final String message,
+ final Throwable throwable, final int priority) {
+ final BuildEvent event = new BuildEvent(target);
+ event.setException(throwable);
+ fireMessageLoggedEvent(event, message, priority);
+ }
+
+ /**
+ * Send a &quot;message logged&quot; task level event
+ * to the build listeners for this project.
+ *
+ * @param task The task generating the event.
+ * Must not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param priority The priority of the message.
+ */
+ protected void fireMessageLogged(final Task task, final String message, final int priority) {
+ fireMessageLogged(task, message, null, priority);
+ }
+
+ /**
+ * Send a &quot;message logged&quot; task level event
+ * to the build listeners for this project.
+ *
+ * @param task The task generating the event.
+ * Must not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param throwable The exception that caused this message. May be <code>null</code>.
+ * @param priority The priority of the message.
+ * @since 1.7
+ */
+ protected void fireMessageLogged(final Task task, final String message,
+ final Throwable throwable, final int priority) {
+ final BuildEvent event = new BuildEvent(task);
+ event.setException(throwable);
+ fireMessageLoggedEvent(event, message, priority);
+ }
+
+ /**
+ * Register a task as the current task for a thread.
+ * If the task is null, the thread's entry is removed.
+ *
+ * @param thread the thread on which the task is registered.
+ * @param task the task to be registered.
+ * @since Ant 1.5
+ */
+ public void registerThreadTask(final Thread thread, final Task task) {
+ synchronized (threadTasks) {
+ if (task != null) {
+ threadTasks.put(thread, task);
+ threadGroupTasks.put(thread.getThreadGroup(), task);
+ } else {
+ threadTasks.remove(thread);
+ threadGroupTasks.remove(thread.getThreadGroup());
+ }
+ }
+ }
+
+ /**
+ * Get the current task associated with a thread, if any.
+ *
+ * @param thread the thread for which the task is required.
+ * @return the task which is currently registered for the given thread or
+ * null if no task is registered.
+ */
+ public Task getThreadTask(final Thread thread) {
+ synchronized (threadTasks) {
+ Task task = threadTasks.get(thread);
+ if (task == null) {
+ ThreadGroup group = thread.getThreadGroup();
+ while (task == null && group != null) {
+ task = threadGroupTasks.get(group);
+ group = group.getParent();
+ }
+ }
+ return task;
+ }
+ }
+
+
+ // Should move to a separate public class - and have API to add
+ // listeners, etc.
+ private static class AntRefTable extends Hashtable<String, Object> {
+ private static final long serialVersionUID = 1L;
+
+ AntRefTable() {
+ super();
+ }
+
+ /** Returns the unmodified original object.
+ * This method should be called internally to
+ * get the &quot;real&quot; object.
+ * The normal get method will do the replacement
+ * of UnknownElement (this is similar with the JDNI
+ * refs behavior).
+ */
+ private Object getReal(final Object key) {
+ return super.get(key);
+ }
+
+ /** Get method for the reference table.
+ * It can be used to hook dynamic references and to modify
+ * some references on the fly--for example for delayed
+ * evaluation.
+ *
+ * It is important to make sure that the processing that is
+ * done inside is not calling get indirectly.
+ *
+ * @param key lookup key.
+ * @return mapped value.
+ */
+ @Override
+ public Object get(final Object key) {
+ Object o = getReal(key);
+ if (o instanceof UnknownElement) {
+ // Make sure that
+ final UnknownElement ue = (UnknownElement) o;
+ ue.maybeConfigure();
+ o = ue.getRealThing();
+ }
+ return o;
+ }
+ }
+
+ /**
+ * Set a reference to this Project on the parameterized object.
+ * Need to set the project before other set/add elements
+ * are called.
+ * @param obj the object to invoke setProject(this) on.
+ */
+ public final void setProjectReference(final Object obj) {
+ if (obj instanceof ProjectComponent) {
+ ((ProjectComponent) obj).setProject(this);
+ return;
+ }
+ try {
+ final Method method =
+ obj.getClass().getMethod(
+ "setProject", new Class[] {Project.class});
+ if (method != null) {
+ method.invoke(obj, new Object[] {this});
+ }
+ } catch (final Throwable e) {
+ // ignore this if the object does not have
+ // a set project method or the method
+ // is private/protected.
+ }
+ }
+
+ /**
+ * Resolve the file relative to the project's basedir and return it as a
+ * FileResource.
+ * @param name the name of the file to resolve.
+ * @return the file resource.
+ * @since Ant 1.7
+ */
+ public Resource getResource(final String name) {
+ return new FileResource(getBaseDir(), name);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ProjectComponent.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ProjectComponent.java
new file mode 100644
index 00000000..ad92a317
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ProjectComponent.java
@@ -0,0 +1,169 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Base class for components of a project, including tasks and data types.
+ * Provides common facilities.
+ *
+ */
+public abstract class ProjectComponent implements Cloneable {
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ /**
+ * Project object of this component.
+ * @deprecated since 1.6.x.
+ * You should not be directly accessing this variable directly.
+ * You should access project object via the getProject()
+ * or setProject() accessor/mutators.
+ */
+ protected Project project;
+
+ /**
+ * Location within the build file of this task definition.
+ * @deprecated since 1.6.x.
+ * You should not be accessing this variable directly.
+ * Please use the {@link #getLocation()} method.
+ */
+ protected Location location = Location.UNKNOWN_LOCATION;
+
+ /**
+ * Description of this component, if any.
+ * @deprecated since 1.6.x.
+ * You should not be accessing this variable directly.
+ */
+ protected String description;
+ // CheckStyle:VisibilityModifier ON
+
+ /** Sole constructor. */
+ public ProjectComponent() {
+ }
+
+ /**
+ * Sets the project object of this component. This method is used by
+ * Project when a component is added to it so that the component has
+ * access to the functions of the project. It should not be used
+ * for any other purpose.
+ *
+ * @param project Project in whose scope this component belongs.
+ * Must not be <code>null</code>.
+ */
+ public void setProject(Project project) {
+ this.project = project;
+ }
+
+ /**
+ * Returns the project to which this component belongs.
+ *
+ * @return the components's project.
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * Returns the file/location where this task was defined.
+ *
+ * @return the file/location where this task was defined.
+ * Should not return <code>null</code>. Location.UNKNOWN_LOCATION
+ * is used for unknown locations.
+ *
+ * @see Location#UNKNOWN_LOCATION
+ */
+ public Location getLocation() {
+ return location;
+ }
+
+ /**
+ * Sets the file/location where this task was defined.
+ *
+ * @param location The file/location where this task was defined.
+ * Should not be <code>null</code>--use
+ * Location.UNKNOWN_LOCATION if the location isn't known.
+ *
+ * @see Location#UNKNOWN_LOCATION
+ */
+ public void setLocation(Location location) {
+ this.location = location;
+ }
+
+ /**
+ * Sets a description of the current action. This may be used for logging
+ * purposes.
+ *
+ * @param desc Description of the current action.
+ * May be <code>null</code>, indicating that no description is
+ * available.
+ *
+ */
+ public void setDescription(String desc) {
+ description = desc;
+ }
+
+ /**
+ * Returns the description of the current action.
+ *
+ * @return the description of the current action, or <code>null</code> if
+ * no description is available.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Logs a message with the default (INFO) priority.
+ *
+ * @param msg The message to be logged. Should not be <code>null</code>.
+ */
+ public void log(String msg) {
+ log(msg, Project.MSG_INFO);
+ }
+
+ /**
+ * Logs a message with the given priority.
+ *
+ * @param msg The message to be logged. Should not be <code>null</code>.
+ * @param msgLevel the message priority at which this message is
+ * to be logged.
+ */
+ public void log(String msg, int msgLevel) {
+ if (getProject() != null) {
+ getProject().log(msg, msgLevel);
+ } else {
+ // 'reasonable' default, if the component is used without
+ // a Project ( for example as a standalone Bean ).
+ // Most ant components can be used this way.
+ if (msgLevel <= Project.MSG_INFO) {
+ System.err.println(msg);
+ }
+ }
+ }
+
+ /**
+ * @since Ant 1.7
+ * @return a shallow copy of this projectcomponent.
+ * @throws CloneNotSupportedException does not happen,
+ * but is declared to allow subclasses to do so.
+ */
+ public Object clone() throws CloneNotSupportedException {
+ ProjectComponent pc = (ProjectComponent) super.clone();
+ pc.setLocation(getLocation());
+ pc.setProject(getProject());
+ return pc;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ProjectHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ProjectHelper.java
new file mode 100644
index 00000000..c6eaa077
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ProjectHelper.java
@@ -0,0 +1,698 @@
+/*
+ * 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.tools.ant;
+
+import java.io.File;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Vector;
+
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.LoaderUtils;
+import org.xml.sax.AttributeList;
+
+/**
+ * Configures a Project (complete with Targets and Tasks) based on
+ * a build file. It'll rely on a plugin to do the actual processing
+ * of the file.
+ * <p>
+ * This class also provide static wrappers for common introspection.
+ */
+public class ProjectHelper {
+ /** The URI for ant name space */
+ public static final String ANT_CORE_URI = "antlib:org.apache.tools.ant";
+
+ /** The URI for antlib current definitions */
+ public static final String ANT_CURRENT_URI = "ant:current";
+
+ /** The URI for ant specific attributes
+ * @since Ant 1.9.1
+ * */
+ public static final String ANT_ATTRIBUTE_URI = "ant:attribute";
+
+ /** The URI for defined types/tasks - the format is antlib:&lt;package&gt; */
+ public static final String ANTLIB_URI = "antlib:";
+
+ /** Polymorphic attribute */
+ public static final String ANT_TYPE = "ant-type";
+
+ /**
+ * Name of JVM system property which provides the name of the
+ * ProjectHelper class to use.
+ */
+ public static final String HELPER_PROPERTY = MagicNames.PROJECT_HELPER_CLASS;
+
+ /**
+ * The service identifier in jars which provide Project Helper
+ * implementations.
+ */
+ public static final String SERVICE_ID = MagicNames.PROJECT_HELPER_SERVICE;
+
+ /**
+ * name of project helper reference that we add to a project
+ */
+ public static final String PROJECTHELPER_REFERENCE = MagicNames.REFID_PROJECT_HELPER;
+
+ /**
+ * constant to denote use project name as target prefix
+ * @since Ant 1.9.1
+ */
+ public static final String USE_PROJECT_NAME_AS_TARGET_PREFIX = "USE_PROJECT_NAME_AS_TARGET_PREFIX";
+
+ /**
+ * Configures the project with the contents of the specified build file.
+ *
+ * @param project The project to configure. Must not be <code>null</code>.
+ * @param buildFile A build file giving the project's configuration.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the configuration is invalid or cannot be read
+ */
+ public static void configureProject(Project project, File buildFile) throws BuildException {
+ FileResource resource = new FileResource(buildFile);
+ ProjectHelper helper = ProjectHelperRepository.getInstance().getProjectHelperForBuildFile(resource);
+ project.addReference(PROJECTHELPER_REFERENCE, helper);
+ helper.parse(project, buildFile);
+ }
+
+ /**
+ * Possible value for target's onMissingExtensionPoint attribute. It determines how to deal with
+ * targets that want to extend missing extension-points.
+ * <p>
+ * This class behaves like a Java 1.5 Enum class.
+ *
+ * @since 1.8.2
+ */
+ public static final class OnMissingExtensionPoint {
+
+ /** fail if the extension-point is not defined */
+ public static final OnMissingExtensionPoint FAIL = new OnMissingExtensionPoint(
+ "fail");
+
+ /** warn if the extension-point is not defined */
+ public static final OnMissingExtensionPoint WARN = new OnMissingExtensionPoint(
+ "warn");
+
+ /** ignore the extensionOf attribute if the extension-point is not defined */
+ public static final OnMissingExtensionPoint IGNORE = new OnMissingExtensionPoint(
+ "ignore");
+
+ private static final OnMissingExtensionPoint[] values = new OnMissingExtensionPoint[] {
+ FAIL, WARN, IGNORE };
+
+ private final String name;
+
+ private OnMissingExtensionPoint(String name) {
+ this.name = name;
+ }
+
+ public String name() {
+ return name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public static OnMissingExtensionPoint valueOf(String name) {
+ if (name == null) {
+ throw new NullPointerException();
+ }
+ for (int i = 0; i < values.length; i++) {
+ if (name.equals(values[i].name())) {
+ return values[i];
+ }
+ }
+ throw new IllegalArgumentException(
+ "Unknown onMissingExtensionPoint " + name);
+ }
+ }
+
+ /** Default constructor */
+ public ProjectHelper() {
+ }
+
+ // -------------------- Common properties --------------------
+ // The following properties are required by import ( and other tasks
+ // that read build files using ProjectHelper ).
+
+ private Vector<Object> importStack = new Vector<Object>();
+ private List<String[]> extensionStack = new LinkedList<String[]>();
+
+ /**
+ * Import stack.
+ * Used to keep track of imported files. Error reporting should
+ * display the import path.
+ *
+ * @return the stack of import source objects.
+ */
+ public Vector<Object> getImportStack() {
+ return importStack;
+ }
+
+ /**
+ * Extension stack.
+ * Used to keep track of targets that extend extension points.
+ *
+ * @return a list of three element string arrays where the first
+ * element is the name of the extensionpoint, the second the name
+ * of the target and the third the name of the enum like class
+ * {@link OnMissingExtensionPoint}.
+ */
+ public List<String[]> getExtensionStack() {
+ return extensionStack;
+ }
+
+ private static final ThreadLocal<String> targetPrefix = new ThreadLocal<String>();
+
+ /**
+ * The prefix to prepend to imported target names.
+ *
+ * <p>May be set by &lt;import&gt;'s as attribute.</p>
+ *
+ * @return the configured prefix or null
+ *
+ * @since Ant 1.8.0
+ */
+ public static String getCurrentTargetPrefix() {
+ return targetPrefix.get();
+ }
+
+ /**
+ * Sets the prefix to prepend to imported target names.
+ *
+ * @since Ant 1.8.0
+ */
+ public static void setCurrentTargetPrefix(String prefix) {
+ targetPrefix.set(prefix);
+ }
+
+ private static final ThreadLocal<String> prefixSeparator = new ThreadLocal<String>() {
+ protected String initialValue() {
+ return ".";
+ }
+ };
+
+ /**
+ * The separator between the prefix and the target name.
+ *
+ * <p>May be set by &lt;import&gt;'s prefixSeparator attribute.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public static String getCurrentPrefixSeparator() {
+ return prefixSeparator.get();
+ }
+
+ /**
+ * Sets the separator between the prefix and the target name.
+ *
+ * @since Ant 1.8.0
+ */
+ public static void setCurrentPrefixSeparator(String sep) {
+ prefixSeparator.set(sep);
+ }
+
+ private static final ThreadLocal<Boolean> inIncludeMode = new ThreadLocal<Boolean>() {
+ protected Boolean initialValue() {
+ return Boolean.FALSE;
+ }
+ };
+
+ /**
+ * Whether the current file should be read in include as opposed
+ * to import mode.
+ *
+ * <p>In include mode included targets are only known by their
+ * prefixed names and their depends lists get rewritten so that
+ * all dependencies get the prefix as well.</p>
+ *
+ * <p>In import mode imported targets are known by an adorned as
+ * well as a prefixed name and the unadorned target may be
+ * overwritten in the importing build file. The depends list of
+ * the imported targets is not modified at all.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public static boolean isInIncludeMode() {
+ return Boolean.TRUE.equals(inIncludeMode.get());
+ }
+
+ /**
+ * Sets whether the current file should be read in include as
+ * opposed to import mode.
+ *
+ * @since Ant 1.8.0
+ */
+ public static void setInIncludeMode(boolean includeMode) {
+ inIncludeMode.set(Boolean.valueOf(includeMode));
+ }
+
+ // -------------------- Parse method --------------------
+ /**
+ * Parses the project file, configuring the project as it goes.
+ *
+ * @param project The project for the resulting ProjectHelper to configure.
+ * Must not be <code>null</code>.
+ * @param source The source for XML configuration. A helper must support
+ * at least File, for backward compatibility. Helpers may
+ * support URL, InputStream, etc or specialized types.
+ *
+ * @since Ant1.5
+ * @exception BuildException if the configuration is invalid or cannot
+ * be read
+ */
+ public void parse(Project project, Object source) throws BuildException {
+ throw new BuildException("ProjectHelper.parse() must be implemented "
+ + "in a helper plugin " + this.getClass().getName());
+ }
+
+ /**
+ * Get the first project helper found in the classpath
+ *
+ * @return an project helper, never <code>null</code>
+ * @see org.apache.tools.ant.ProjectHelperRepository#getHelpers()
+ */
+ public static ProjectHelper getProjectHelper() {
+ return (ProjectHelper) ProjectHelperRepository.getInstance().getHelpers().next();
+ }
+
+ /**
+ * JDK1.1 compatible access to the context class loader. Cut &amp; paste from JAXP.
+ *
+ * @deprecated since 1.6.x.
+ * Use LoaderUtils.getContextClassLoader()
+ *
+ * @return the current context class loader, or <code>null</code>
+ * if the context class loader is unavailable.
+ */
+ public static ClassLoader getContextClassLoader() {
+ return LoaderUtils.isContextLoaderAvailable() ? LoaderUtils.getContextClassLoader() : null;
+ }
+
+ // -------------------- Static utils, used by most helpers ----------------
+
+ /**
+ * Configures an object using an introspection handler.
+ *
+ * @param target The target object to be configured.
+ * Must not be <code>null</code>.
+ * @param attrs A list of attributes to configure within the target.
+ * Must not be <code>null</code>.
+ * @param project The project containing the target.
+ * Must not be <code>null</code>.
+ *
+ * @deprecated since 1.6.x.
+ * Use IntrospectionHelper for each property.
+ *
+ * @exception BuildException if any of the attributes can't be handled by
+ * the target
+ */
+ public static void configure(Object target, AttributeList attrs,
+ Project project) throws BuildException {
+ if (target instanceof TypeAdapter) {
+ target = ((TypeAdapter) target).getProxy();
+ }
+ IntrospectionHelper ih = IntrospectionHelper.getHelper(project, target.getClass());
+
+ for (int i = 0, length = attrs.getLength(); i < length; i++) {
+ // reflect these into the target
+ String value = replaceProperties(project, attrs.getValue(i), project.getProperties());
+ try {
+ ih.setAttribute(project, target, attrs.getName(i).toLowerCase(Locale.ENGLISH), value);
+ } catch (BuildException be) {
+ // id attribute must be set externally
+ if (!attrs.getName(i).equals("id")) {
+ throw be;
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds the content of #PCDATA sections to an element.
+ *
+ * @param project The project containing the target.
+ * Must not be <code>null</code>.
+ * @param target The target object to be configured.
+ * Must not be <code>null</code>.
+ * @param buf A character array of the text within the element.
+ * Will not be <code>null</code>.
+ * @param start The start element in the array.
+ * @param count The number of characters to read from the array.
+ *
+ * @exception BuildException if the target object doesn't accept text
+ */
+ public static void addText(Project project, Object target, char[] buf,
+ int start, int count) throws BuildException {
+ addText(project, target, new String(buf, start, count));
+ }
+
+ /**
+ * Adds the content of #PCDATA sections to an element.
+ *
+ * @param project The project containing the target.
+ * Must not be <code>null</code>.
+ * @param target The target object to be configured.
+ * Must not be <code>null</code>.
+ * @param text Text to add to the target.
+ * May be <code>null</code>, in which case this
+ * method call is a no-op.
+ *
+ * @exception BuildException if the target object doesn't accept text
+ */
+ public static void addText(Project project, Object target, String text)
+ throws BuildException {
+
+ if (text == null) {
+ return;
+ }
+ if (target instanceof TypeAdapter) {
+ target = ((TypeAdapter) target).getProxy();
+ }
+ IntrospectionHelper.getHelper(project, target.getClass()).addText(project, target, text);
+ }
+
+ /**
+ * Stores a configured child element within its parent object.
+ *
+ * @param project Project containing the objects.
+ * May be <code>null</code>.
+ * @param parent Parent object to add child to.
+ * Must not be <code>null</code>.
+ * @param child Child object to store in parent.
+ * Should not be <code>null</code>.
+ * @param tag Name of element which generated the child.
+ * May be <code>null</code>, in which case
+ * the child is not stored.
+ */
+ public static void storeChild(Project project, Object parent, Object child, String tag) {
+ IntrospectionHelper ih = IntrospectionHelper.getHelper(project, parent.getClass());
+ ih.storeElement(project, parent, child, tag);
+ }
+
+ /**
+ * Replaces <code>${xxx}</code> style constructions in the given value with
+ * the string value of the corresponding properties.
+ *
+ * @param project The project containing the properties to replace.
+ * Must not be <code>null</code>.
+ *
+ * @param value The string to be scanned for property references.
+ * May be <code>null</code>.
+ *
+ * @exception BuildException if the string contains an opening
+ * <code>${</code> without a closing
+ * <code>}</code>
+ * @return the original string with the properties replaced, or
+ * <code>null</code> if the original string is <code>null</code>.
+ *
+ * @deprecated since 1.6.x.
+ * Use project.replaceProperties().
+ * @since 1.5
+ */
+ public static String replaceProperties(Project project, String value) throws BuildException {
+ // needed since project properties are not accessible
+ return project.replaceProperties(value);
+ }
+
+ /**
+ * Replaces <code>${xxx}</code> style constructions in the given value
+ * with the string value of the corresponding data types.
+ *
+ * @param project The container project. This is used solely for
+ * logging purposes. Must not be <code>null</code>.
+ * @param value The string to be scanned for property references.
+ * May be <code>null</code>, in which case this
+ * method returns immediately with no effect.
+ * @param keys Mapping (String to Object) of property names to their
+ * values. Must not be <code>null</code>.
+ *
+ * @exception BuildException if the string contains an opening
+ * <code>${</code> without a closing
+ * <code>}</code>
+ * @return the original string with the properties replaced, or
+ * <code>null</code> if the original string is <code>null</code>.
+ * @deprecated since 1.6.x.
+ * Use PropertyHelper.
+ */
+ public static String replaceProperties(Project project, String value, Hashtable<String, Object> keys)
+ throws BuildException {
+ PropertyHelper ph = PropertyHelper.getPropertyHelper(project);
+ return ph.replaceProperties(null, value, keys);
+ }
+
+ /**
+ * Parses a string containing <code>${xxx}</code> style property
+ * references into two lists. The first list is a collection
+ * of text fragments, while the other is a set of string property names.
+ * <code>null</code> entries in the first list indicate a property
+ * reference from the second list.
+ *
+ * <p>As of Ant 1.8.0 this method is never invoked by any code
+ * inside of Ant itself.</p>
+ *
+ * @param value Text to parse. Must not be <code>null</code>.
+ * @param fragments List to add text fragments to.
+ * Must not be <code>null</code>.
+ * @param propertyRefs List to add property names to.
+ * Must not be <code>null</code>.
+ *
+ * @deprecated since 1.6.x.
+ * Use PropertyHelper.
+ * @exception BuildException if the string contains an opening
+ * <code>${</code> without a closing <code>}</code>
+ */
+ public static void parsePropertyString(String value, Vector<String> fragments, Vector<String> propertyRefs)
+ throws BuildException {
+ PropertyHelper.parsePropertyStringDefault(value, fragments, propertyRefs);
+ }
+
+ /**
+ * Map a namespaced {uri,name} to an internal string format.
+ * For BC purposes the names from the ant core uri will be
+ * mapped to "name", other names will be mapped to
+ * uri + ":" + name.
+ * @param uri The namespace URI
+ * @param name The localname
+ * @return The stringified form of the ns name
+ */
+ public static String genComponentName(String uri, String name) {
+ if (uri == null || uri.equals("") || uri.equals(ANT_CORE_URI)) {
+ return name;
+ }
+ return uri + ":" + name;
+ }
+
+ /**
+ * extract a uri from a component name
+ *
+ * @param componentName The stringified form for {uri, name}
+ * @return The uri or "" if not present
+ */
+ public static String extractUriFromComponentName(String componentName) {
+ if (componentName == null) {
+ return "";
+ }
+ int index = componentName.lastIndexOf(':');
+ if (index == -1) {
+ return "";
+ }
+ return componentName.substring(0, index);
+ }
+
+ /**
+ * extract the element name from a component name
+ *
+ * @param componentName The stringified form for {uri, name}
+ * @return The element name of the component
+ */
+ public static String extractNameFromComponentName(String componentName) {
+ int index = componentName.lastIndexOf(':');
+ if (index == -1) {
+ return componentName;
+ }
+ return componentName.substring(index + 1);
+ }
+
+ /**
+ * Convert an attribute namespace to a "component name".
+ * @param ns the xml namespace uri.
+ * @return the converted value.
+ * @since Ant 1.9.1
+ */
+ public static String nsToComponentName(String ns) {
+ return "attribute namespace:" + ns;
+ }
+
+ /**
+ * Add location to build exception.
+ * @param ex the build exception, if the build exception
+ * does not include
+ * @param newLocation the location of the calling task (may be null)
+ * @return a new build exception based in the build exception with
+ * location set to newLocation. If the original exception
+ * did not have a location, just return the build exception
+ */
+ public static BuildException addLocationToBuildException(
+ BuildException ex, Location newLocation) {
+ if (ex.getLocation() == null || ex.getMessage() == null) {
+ return ex;
+ }
+ String errorMessage
+ = "The following error occurred while executing this line:"
+ + System.getProperty("line.separator")
+ + ex.getLocation().toString()
+ + ex.getMessage();
+ if (newLocation == null) {
+ return new BuildException(errorMessage, ex);
+ }
+ return new BuildException(errorMessage, ex, newLocation);
+ }
+
+ /**
+ * Whether this instance of ProjectHelper can parse an Antlib
+ * descriptor given by the URL and return its content as an
+ * UnknownElement ready to be turned into an Antlib task.
+ *
+ * <p>This method should not try to parse the content of the
+ * descriptor, the URL is only given as an argument to allow
+ * subclasses to decide whether they can support a given URL
+ * scheme or not.</p>
+ *
+ * <p>Subclasses that return true in this method must also
+ * override {@link #parseAntlibDescriptor
+ * parseAntlibDescriptor}.</p>
+ *
+ * <p>This implementation returns false.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public boolean canParseAntlibDescriptor(Resource r) {
+ return false;
+ }
+
+ /**
+ * Parse the given URL as an antlib descriptor and return the
+ * content as something that can be turned into an Antlib task.
+ *
+ * @since ant 1.8.0
+ */
+ public UnknownElement parseAntlibDescriptor(Project containingProject,
+ Resource source) {
+ throw new BuildException("can't parse antlib descriptors");
+ }
+
+ /**
+ * Check if the helper supports the kind of file. Some basic check on the
+ * extension's file should be done here.
+ *
+ * @param buildFile
+ * the file expected to be parsed (never <code>null</code>)
+ * @return true if the helper supports it
+ * @since Ant 1.8.0
+ */
+ public boolean canParseBuildFile(Resource buildFile) {
+ return true;
+ }
+
+ /**
+ * The file name of the build script to be parsed if none specified on the command line
+ *
+ * @return the name of the default file (never <code>null</code>)
+ * @since Ant 1.8.0
+ */
+ public String getDefaultBuildFile() {
+ return Main.DEFAULT_BUILD_FILENAME;
+ }
+
+ /**
+ * Check extensionStack and inject all targets having extensionOf attributes
+ * into extensionPoint.
+ * <p>
+ * This method allow you to defer injection and have a powerful control of
+ * extensionPoint wiring.
+ * </p>
+ * <p>
+ * This should be invoked by each concrete implementation of ProjectHelper
+ * when the root "buildfile" and all imported/included buildfile are loaded.
+ * </p>
+ *
+ * @param project The project containing the target. Must not be
+ * <code>null</code>.
+ * @exception BuildException if OnMissingExtensionPoint.FAIL and
+ * extensionPoint does not exist
+ * @see OnMissingExtensionPoint
+ * @since 1.9
+ */
+ public void resolveExtensionOfAttributes(Project project)
+ throws BuildException {
+ for (String[] extensionInfo : getExtensionStack()) {
+ String extPointName = extensionInfo[0];
+ String targetName = extensionInfo[1];
+ OnMissingExtensionPoint missingBehaviour = OnMissingExtensionPoint.valueOf(extensionInfo[2]);
+ // if the file has been included or imported, it may have a prefix
+ // we should consider when trying to resolve the target it is
+ // extending
+ String prefixAndSep = extensionInfo.length > 3 ? extensionInfo[3] : null;
+
+ // find the target we're extending
+ Hashtable<String, Target> projectTargets = project.getTargets();
+ Target extPoint = null;
+ if (prefixAndSep == null) {
+ // no prefix - not from an imported/included build file
+ extPoint = projectTargets.get(extPointName);
+ } else {
+ // we have a prefix, which means we came from an include/import
+
+ // FIXME: here we handle no particular level of include. We try
+ // the fully prefixed name, and then the non-prefixed name. But
+ // there might be intermediate project in the import stack,
+ // which prefix should be tested before testing the non-prefix
+ // root name.
+
+ extPoint = projectTargets.get(prefixAndSep + extPointName);
+ if (extPoint == null) {
+ extPoint = projectTargets.get(extPointName);
+ }
+ }
+
+ // make sure we found a point to extend on
+ if (extPoint == null) {
+ String message = "can't add target " + targetName
+ + " to extension-point " + extPointName
+ + " because the extension-point is unknown.";
+ if (missingBehaviour == OnMissingExtensionPoint.FAIL) {
+ throw new BuildException(message);
+ } else if (missingBehaviour == OnMissingExtensionPoint.WARN) {
+ Target t = projectTargets.get(targetName);
+ project.log(t, "Warning: " + message, Project.MSG_WARN);
+ }
+ } else {
+ if (!(extPoint instanceof ExtensionPoint)) {
+ throw new BuildException("referenced target " + extPointName
+ + " is not an extension-point");
+ }
+ extPoint.addDependency(targetName);
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ProjectHelperRepository.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ProjectHelperRepository.java
new file mode 100644
index 00000000..1dd44124
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ProjectHelperRepository.java
@@ -0,0 +1,337 @@
+/*
+ * 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.tools.ant;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Constructor;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.helper.ProjectHelper2;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.util.LoaderUtils;
+
+/**
+ * Repository of {@link ProjectHelper} found in the classpath or via
+ * some System properties.
+ *
+ * <p>See the ProjectHelper documentation in the manual.</p>
+ *
+ * @since Ant 1.8.0
+ */
+public class ProjectHelperRepository {
+
+ private static final String DEBUG_PROJECT_HELPER_REPOSITORY =
+ "ant.project-helper-repo.debug";
+
+ // The message log level is not accessible here because everything
+ // is instanciated statically
+ private static final boolean DEBUG =
+ "true".equals(System.getProperty(DEBUG_PROJECT_HELPER_REPOSITORY));
+
+ private static ProjectHelperRepository instance =
+ new ProjectHelperRepository();
+
+ private List<Constructor<? extends ProjectHelper>> helpers = new ArrayList<Constructor<? extends ProjectHelper>>();
+
+ private static Constructor<ProjectHelper2> PROJECTHELPER2_CONSTRUCTOR;
+
+ static {
+ try {
+ PROJECTHELPER2_CONSTRUCTOR = ProjectHelper2.class.getConstructor();
+ } catch (Exception e) {
+ // ProjectHelper2 must be available
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static ProjectHelperRepository getInstance() {
+ return instance;
+ }
+
+ private ProjectHelperRepository() {
+ collectProjectHelpers();
+ }
+
+ private void collectProjectHelpers() {
+ // First, try the system property
+ Constructor<? extends ProjectHelper> projectHelper = getProjectHelperBySystemProperty();
+ registerProjectHelper(projectHelper);
+
+ // A JDK1.3 'service' ( like in JAXP ). That will plug a helper
+ // automatically if in CLASSPATH, with the right META-INF/services.
+ try {
+ ClassLoader classLoader = LoaderUtils.getContextClassLoader();
+ if (classLoader != null) {
+ Enumeration<URL> resources =
+ classLoader.getResources(ProjectHelper.SERVICE_ID);
+ while (resources.hasMoreElements()) {
+ URL resource = resources.nextElement();
+ URLConnection conn = resource.openConnection();
+ conn.setUseCaches(false);
+ projectHelper =
+ getProjectHelperByService(conn.getInputStream());
+ registerProjectHelper(projectHelper);
+ }
+ }
+
+ InputStream systemResource =
+ ClassLoader.getSystemResourceAsStream(ProjectHelper.SERVICE_ID);
+ if (systemResource != null) {
+ projectHelper = getProjectHelperByService(systemResource);
+ registerProjectHelper(projectHelper);
+ }
+ } catch (Exception e) {
+ System.err.println("Unable to load ProjectHelper from service "
+ + ProjectHelper.SERVICE_ID + " ("
+ + e.getClass().getName()
+ + ": " + e.getMessage() + ")");
+ if (DEBUG) {
+ e.printStackTrace(System.err);
+ }
+ }
+ }
+
+ /**
+ * Register the specified project helper into the repository.
+ * <p>
+ * The helper will be added after all the already registered helpers, but
+ * before the default one (ProjectHelper2)
+ *
+ * @param helperClassName
+ * the fully qualified name of the helper
+ * @throws BuildException
+ * if the class cannot be loaded or if there is no constructor
+ * with no argument
+ * @since Ant 1.8.2
+ */
+ public void registerProjectHelper(String helperClassName)
+ throws BuildException {
+ registerProjectHelper(getHelperConstructor(helperClassName));
+ }
+
+ /**
+ * Register the specified project helper into the repository.
+ * <p>
+ * The helper will be added after all the already registered helpers, but
+ * before the default one (ProjectHelper2)
+ *
+ * @param helperClass
+ * the class of the helper
+ * @throws BuildException
+ * if there is no constructor with no argument
+ * @since Ant 1.8.2
+ */
+ public void registerProjectHelper(Class<? extends ProjectHelper> helperClass) throws BuildException {
+ try {
+ registerProjectHelper(helperClass.getConstructor());
+ } catch (NoSuchMethodException e) {
+ throw new BuildException("Couldn't find no-arg constructor in "
+ + helperClass.getName());
+ }
+ }
+
+ private void registerProjectHelper(Constructor<? extends ProjectHelper> helperConstructor) {
+ if (helperConstructor == null) {
+ return;
+ }
+ if (DEBUG) {
+ System.out.println("ProjectHelper "
+ + helperConstructor.getClass().getName() + " registered.");
+ }
+ helpers.add(helperConstructor);
+ }
+
+ private Constructor<? extends ProjectHelper> getProjectHelperBySystemProperty() {
+ String helperClass = System.getProperty(ProjectHelper.HELPER_PROPERTY);
+ try {
+ if (helperClass != null) {
+ return getHelperConstructor(helperClass);
+ }
+ } catch (SecurityException e) {
+ System.err.println("Unable to load ProjectHelper class \""
+ + helperClass + " specified in system property "
+ + ProjectHelper.HELPER_PROPERTY + " ("
+ + e.getMessage() + ")");
+ if (DEBUG) {
+ e.printStackTrace(System.err);
+ }
+ }
+ return null;
+ }
+
+ private Constructor<? extends ProjectHelper> getProjectHelperByService(InputStream is) {
+ try {
+ // This code is needed by EBCDIC and other strange systems.
+ // It's a fix for bugs reported in xerces
+ InputStreamReader isr;
+ try {
+ isr = new InputStreamReader(is, "UTF-8");
+ } catch (java.io.UnsupportedEncodingException e) {
+ isr = new InputStreamReader(is);
+ }
+ BufferedReader rd = new BufferedReader(isr);
+
+ String helperClassName = rd.readLine();
+ rd.close();
+
+ if (helperClassName != null && !"".equals(helperClassName)) {
+ return getHelperConstructor(helperClassName);
+ }
+ } catch (Exception e) {
+ System.out.println("Unable to load ProjectHelper from service "
+ + ProjectHelper.SERVICE_ID + " (" + e.getMessage() + ")");
+ if (DEBUG) {
+ e.printStackTrace(System.err);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the constructor with not argument of an helper from its class name.
+ * It'll first try the thread class loader, then Class.forName() will load
+ * from the same loader that loaded this class.
+ *
+ * @param helperClass
+ * The name of the class to create an instance of. Must not be
+ * <code>null</code>.
+ *
+ * @return the constructor of the specified class.
+ *
+ * @exception BuildException
+ * if the class cannot be found or if a constructor with no
+ * argument cannot be found.
+ */
+ private Constructor<? extends ProjectHelper> getHelperConstructor(String helperClass) throws BuildException {
+ ClassLoader classLoader = LoaderUtils.getContextClassLoader();
+ try {
+ Class<?> clazz = null;
+ if (classLoader != null) {
+ try {
+ clazz = classLoader.loadClass(helperClass);
+ } catch (ClassNotFoundException ex) {
+ // try next method
+ }
+ }
+ if (clazz == null) {
+ clazz = Class.forName(helperClass);
+ }
+ return clazz.asSubclass(ProjectHelper.class).getConstructor();
+ } catch (Exception e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Get the helper that will be able to parse the specified build file. The helper
+ * will be chosen among the ones found in the classpath
+ *
+ * @return the first ProjectHelper that fit the requirement (never <code>null</code>).
+ */
+ public ProjectHelper getProjectHelperForBuildFile(Resource buildFile) throws BuildException {
+ for (Iterator<ProjectHelper> it = getHelpers(); it.hasNext();) {
+ ProjectHelper helper = it.next();
+ if (helper.canParseBuildFile(buildFile)) {
+ if (DEBUG) {
+ System.out.println("ProjectHelper "
+ + helper.getClass().getName()
+ + " selected for the build file "
+ + buildFile);
+ }
+ return helper;
+ }
+ }
+ throw new RuntimeException("BUG: at least the ProjectHelper2 should "
+ + "have supported the file " + buildFile);
+ }
+
+ /**
+ * Get the helper that will be able to parse the specified antlib. The helper
+ * will be chosen among the ones found in the classpath
+ *
+ * @return the first ProjectHelper that fit the requirement (never <code>null</code>).
+ */
+ public ProjectHelper getProjectHelperForAntlib(Resource antlib) throws BuildException {
+ for (Iterator<ProjectHelper> it = getHelpers(); it.hasNext();) {
+ ProjectHelper helper = it.next();
+ if (helper.canParseAntlibDescriptor(antlib)) {
+ if (DEBUG) {
+ System.out.println("ProjectHelper "
+ + helper.getClass().getName()
+ + " selected for the antlib "
+ + antlib);
+ }
+ return helper;
+ }
+ }
+ throw new RuntimeException("BUG: at least the ProjectHelper2 should "
+ + "have supported the file " + antlib);
+ }
+
+ /**
+ * Get an iterator on the list of project helpers configured. The iterator
+ * will always return at least one element as there will always be the
+ * default project helper configured.
+ *
+ * @return an iterator of {@link ProjectHelper}
+ */
+ public Iterator<ProjectHelper> getHelpers() {
+ return new ConstructingIterator(helpers.iterator());
+ }
+
+ private static class ConstructingIterator implements Iterator<ProjectHelper> {
+ private final Iterator<Constructor<? extends ProjectHelper>> nested;
+ private boolean empty = false;
+
+ ConstructingIterator(Iterator<Constructor<? extends ProjectHelper>> nested) {
+ this.nested = nested;
+ }
+
+ public boolean hasNext() {
+ return nested.hasNext() || !empty;
+ }
+
+ public ProjectHelper next() {
+ Constructor<? extends ProjectHelper> c;
+ if (nested.hasNext()) {
+ c = nested.next();
+ } else {
+ // last but not least, ant default project helper
+ empty = true;
+ c = PROJECTHELPER2_CONSTRUCTOR;
+ }
+ try {
+ return c.newInstance();
+ } catch (Exception e) {
+ throw new BuildException("Failed to invoke no-arg constructor"
+ + " on " + c.getName());
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove is not supported");
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PropertyHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PropertyHelper.java
new file mode 100644
index 00000000..1dbb280c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PropertyHelper.java
@@ -0,0 +1,1215 @@
+/*
+ * 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.tools.ant;
+
+import java.text.ParsePosition;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+import org.apache.tools.ant.property.GetProperty;
+import org.apache.tools.ant.property.NullReturn;
+import org.apache.tools.ant.property.ParseNextProperty;
+import org.apache.tools.ant.property.ParseProperties;
+import org.apache.tools.ant.property.PropertyExpander;
+
+/* ISSUES:
+ - ns param. It could be used to provide "namespaces" for properties, which
+ may be more flexible.
+ - Object value. In ant1.5 String is used for Properties - but it would be nice
+ to support generic Objects (the property remains immutable - you can't change
+ the associated object). This will also allow JSP-EL style setting using the
+ Object if an attribute contains only the property (name="${property}" could
+ avoid Object->String->Object conversion)
+ - Currently we "chain" only for get and set property (probably most users
+ will only need that - if they need more they can replace the top helper).
+ Need to discuss this and find if we need more.
+ */
+
+/* update for impending Ant 1.8.0:
+
+ - I can't see any reason for ns and would like to deprecate it.
+ - Replacing chaining with delegates for certain behavioral aspects.
+ - Object value seems valuable as outlined.
+
+ */
+
+/**
+ * Deals with properties - substitution, dynamic properties, etc.
+ *
+ * <p>This code has been heavily restructured for Ant 1.8.0. It is
+ * expected that custom PropertyHelper implementation that used the
+ * older chaining mechanism of Ant 1.6 won't work in all cases, and
+ * its usage is deprecated. The preferred way to customize Ant's
+ * property handling is by {@link #add adding} {@link
+ * PropertyHelper.Delegate delegates} of the appropriate subinterface
+ * and have this implementation use them.</p>
+ *
+ * <p>When {@link #parseProperties expanding a string that may contain
+ * properties} this class will delegate the actual parsing to {@link
+ * org.apache.tools.ant.property.ParseProperties#parseProperties
+ * parseProperties} inside the ParseProperties class which in turn
+ * uses the {@link org.apache.tools.ant.property.PropertyExpander
+ * PropertyExpander delegates} to find properties inside the string
+ * and this class to expand the propertiy names found into the
+ * corresponding values.</p>
+ *
+ * <p>When {@link #getProperty looking up a property value} this class
+ * will first consult all {@link PropertyHelper.PropertyEvaluator
+ * PropertyEvaluator} delegates and fall back to an internal map of
+ * "project properties" if no evaluator matched the property name.</p>
+ *
+ * <p>When {@link #setProperty setting a property value} this class
+ * will first consult all {@link PropertyHelper.PropertySetter
+ * PropertySetter} delegates and fall back to an internal map of
+ * "project properties" if no setter matched the property name.</p>
+ *
+ * @since Ant 1.6
+ */
+public class PropertyHelper implements GetProperty {
+
+ // --------------------------------------------------------
+ //
+ // The property delegate interfaces
+ //
+ // --------------------------------------------------------
+
+ /**
+ * Marker interface for a PropertyHelper delegate.
+ * @since Ant 1.8.0
+ */
+ public interface Delegate {
+ }
+
+ /**
+ * Looks up a property's value based on its name.
+ *
+ * <p>Can be used to look up properties in a different storage
+ * than the project instance (like local properties for example)
+ * or to implement custom "protocols" like Ant's
+ * <code>${toString:refid}</code> syntax.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public interface PropertyEvaluator extends Delegate {
+ /**
+ * Evaluate a property.
+ *
+ * @param property the property's String "identifier".
+ * @param propertyHelper the invoking PropertyHelper.
+ * @return null if the property name could not be found, an
+ * instance of {@link org.apache.tools.ant.property.NullReturn
+ * NullReturn} to indicate a property with a name that can be
+ * matched but a value of <code>null</code> and the property's
+ * value otherwise.
+ */
+ Object evaluate(String property, PropertyHelper propertyHelper);
+ }
+
+ /**
+ * Sets or overrides a property.
+ *
+ * <p>Can be used to store properties in a different storage than
+ * the project instance (like local properties for example).</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public interface PropertySetter extends Delegate {
+ /**
+ * Set a *new" property.
+ *
+ * <p>Should not replace the value of an existing property.</p>
+ *
+ * @param property the property's String "identifier".
+ * @param value the value to set.
+ * @param propertyHelper the invoking PropertyHelper.
+ * @return true if this entity 'owns' the property.
+ */
+ boolean setNew(
+ String property, Object value, PropertyHelper propertyHelper);
+
+ /**
+ * Set a property.
+ *
+ * <p>May replace the value of an existing property.</p>
+ *
+ * @param property the property's String "identifier".
+ * @param value the value to set.
+ * @param propertyHelper the invoking PropertyHelper.
+ * @return true if this entity 'owns' the property.
+ */
+ boolean set(
+ String property, Object value, PropertyHelper propertyHelper);
+ }
+
+ //TODO PropertyEnumerator Delegate type, would improve PropertySet
+
+ // --------------------------------------------------------
+ //
+ // The predefined property delegates
+ //
+ // --------------------------------------------------------
+
+ private static final PropertyEvaluator TO_STRING = new PropertyEvaluator() {
+ private final String PREFIX = "toString:";
+ private final int PREFIX_LEN = PREFIX.length();
+
+ public Object evaluate(String property, PropertyHelper propertyHelper) {
+ Object o = null;
+ if (property.startsWith(PREFIX) && propertyHelper.getProject() != null) {
+ o = propertyHelper.getProject().getReference(property.substring(PREFIX_LEN));
+ }
+ return o == null ? null : o.toString();
+ }
+ };
+
+ private static final PropertyExpander DEFAULT_EXPANDER = new PropertyExpander() {
+ public String parsePropertyName(
+ String s, ParsePosition pos, ParseNextProperty notUsed) {
+ int index = pos.getIndex();
+ //directly check near, triggering characters:
+ if (s.length() - index >= 3
+ && '$' == s.charAt(index) && '{' == s.charAt(index + 1)) {
+ int start = index + 2;
+ //defer to String.indexOf() for protracted check:
+ int end = s.indexOf('}', start);
+ if (end < 0) {
+ throw new BuildException("Syntax error in property: "
+ + s.substring(index));
+ }
+ pos.setIndex(end + 1);
+ return start == end ? "" : s.substring(start, end);
+ }
+ return null;
+ }
+ };
+
+ /** dummy */
+ private static final PropertyExpander SKIP_DOUBLE_DOLLAR
+ = new PropertyExpander() {
+ // CheckStyle:LineLengthCheck OFF see too long
+ /**
+ * {@inheritDoc}
+ * @see org.apache.tools.ant.property.PropertyExpander#parsePropertyName(java.lang.String, java.text.ParsePosition, org.apache.tools.ant.PropertyHelper)
+ */
+ // CheckStyle:LineLengthCheck ON
+ public String parsePropertyName(
+ String s, ParsePosition pos, ParseNextProperty notUsed) {
+ int index = pos.getIndex();
+ if (s.length() - index >= 2) {
+ /* check for $$; if found, advance by one--
+ * this expander is at the bottom of the stack
+ * and will thus be the last consulted,
+ * so the next thing that ParseProperties will do
+ * is advance the parse position beyond the second $
+ */
+ if ('$' == s.charAt(index) && '$' == s.charAt(++index)) {
+ pos.setIndex(index);
+ }
+ }
+ return null;
+ }
+ };
+
+ /**
+ * @since Ant 1.8.0
+ */
+ private static final PropertyEvaluator FROM_REF = new PropertyEvaluator() {
+ private final String PREFIX = "ant.refid:";
+ private final int PREFIX_LEN = PREFIX.length();
+
+ public Object evaluate(String prop, PropertyHelper helper) {
+ return prop.startsWith(PREFIX) && helper.getProject() != null
+ ? helper.getProject().getReference(prop.substring(PREFIX_LEN))
+ : null;
+ }
+ };
+
+ private Project project;
+ private PropertyHelper next;
+ private final Hashtable<Class<? extends Delegate>, List<Delegate>> delegates = new Hashtable<Class<? extends Delegate>, List<Delegate>>();
+
+ /** Project properties map (usually String to String). */
+ private Hashtable<String, Object> properties = new Hashtable<String, Object>();
+
+ /**
+ * Map of "user" properties (as created in the Ant task, for example).
+ * Note that these key/value pairs are also always put into the
+ * project properties, so only the project properties need to be queried.
+ */
+ private Hashtable<String, Object> userProperties = new Hashtable<String, Object>();
+
+ /**
+ * Map of inherited "user" properties - that are those "user"
+ * properties that have been created by tasks and not been set
+ * from the command line or a GUI tool.
+ */
+ private Hashtable<String, Object> inheritedProperties = new Hashtable<String, Object>();
+
+ /**
+ * Default constructor.
+ */
+ protected PropertyHelper() {
+ add(FROM_REF);
+ add(TO_STRING);
+ add(SKIP_DOUBLE_DOLLAR);
+ add(DEFAULT_EXPANDER);
+ }
+
+ // --------------------------------------------------------
+ //
+ // Some helper static methods to get and set properties
+ //
+ // --------------------------------------------------------
+
+ /**
+ * A helper static method to get a property
+ * from a particular project.
+ * @param project the project in question.
+ * @param name the property name
+ * @return the value of the property if present, null otherwise.
+ * @since Ant 1.8.0
+ */
+ public static Object getProperty(Project project, String name) {
+ return PropertyHelper.getPropertyHelper(project)
+ .getProperty(name);
+ }
+
+ /**
+ * A helper static method to set a property
+ * from a particular project.
+ * @param project the project in question.
+ * @param name the property name
+ * @param value the value to use.
+ * @since Ant 1.8.0
+ */
+ public static void setProperty(Project project, String name, Object value) {
+ PropertyHelper.getPropertyHelper(project)
+ .setProperty(name, value, true);
+ }
+
+ /**
+ * A helper static method to set a new property
+ * from a particular project.
+ * @param project the project in question.
+ * @param name the property name
+ * @param value the value to use.
+ * @since Ant 1.8.0
+ */
+ public static void setNewProperty(
+ Project project, String name, Object value) {
+ PropertyHelper.getPropertyHelper(project)
+ .setNewProperty(name, value);
+ }
+
+ //override facility for subclasses to put custom hashtables in
+
+ // -------------------- Hook management --------------------
+
+ /**
+ * Set the project for which this helper is performing property resolution.
+ *
+ * @param p the project instance.
+ */
+ public void setProject(Project p) {
+ this.project = p;
+ }
+
+ /**
+ * Get this PropertyHelper's Project.
+ * @return Project
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * Prior to Ant 1.8.0 there have been 2 ways to hook into property handling:
+ *
+ * - you can replace the main PropertyHelper. The replacement is required
+ * to support the same semantics (of course :-)
+ *
+ * - you can chain a property helper capable of storing some properties.
+ * Again, you are required to respect the immutability semantics (at
+ * least for non-dynamic properties)
+ *
+ * <p>As of Ant 1.8.0 this method is never invoked by any code
+ * inside of Ant itself.</p>
+ *
+ * @param next the next property helper in the chain.
+ * @deprecated use the delegate mechanism instead
+ */
+ public void setNext(PropertyHelper next) {
+ this.next = next;
+ }
+
+ /**
+ * Get the next property helper in the chain.
+ *
+ * <p>As of Ant 1.8.0 this method is never invoked by any code
+ * inside of Ant itself except the {@link #setPropertyHook
+ * setPropertyHook} and {@link #getPropertyHook getPropertyHook}
+ * methods in this class.</p>
+ *
+ * @return the next property helper.
+ * @deprecated use the delegate mechanism instead
+ */
+ public PropertyHelper getNext() {
+ return next;
+ }
+
+ /**
+ * Factory method to create a property processor.
+ * Users can provide their own or replace it using "ant.PropertyHelper"
+ * reference. User tasks can also add themselves to the chain, and provide
+ * dynamic properties.
+ *
+ * @param project the project for which the property helper is required.
+ *
+ * @return the project's property helper.
+ */
+ public static synchronized PropertyHelper getPropertyHelper(Project project) {
+ PropertyHelper helper = null;
+ if (project != null) {
+ helper = (PropertyHelper) project.getReference(MagicNames
+ .REFID_PROPERTY_HELPER);
+ }
+ if (helper != null) {
+ return helper;
+ }
+
+ helper = new PropertyHelper();
+ helper.setProject(project);
+
+ if (project != null) {
+ project.addReference(MagicNames.REFID_PROPERTY_HELPER, helper);
+ }
+
+ return helper;
+ }
+
+ /**
+ * Get the {@link PropertyExpander expanders}.
+ * @since Ant 1.8.0
+ * @return the expanders.
+ */
+ public Collection<PropertyExpander> getExpanders() {
+ return getDelegates(PropertyExpander.class);
+ }
+
+
+ // -------------------- Methods to override --------------------
+
+ /**
+ * Sets a property. Any existing property of the same name
+ * is overwritten, unless it is a user property.
+ *
+ * If all helpers return false, the property will be saved in
+ * the default properties table by setProperty.
+ *
+ * <p>As of Ant 1.8.0 this method is never invoked by any code
+ * inside of Ant itself.</p>
+ *
+ * @param ns The namespace that the property is in (currently
+ * not used.
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ * @param inherited True if this property is inherited (an [sub]ant[call] property).
+ * @param user True if this property is a user property.
+ * @param isNew True is this is a new property.
+ * @return true if this helper has stored the property, false if it
+ * couldn't. Each helper should delegate to the next one (unless it
+ * has a good reason not to).
+ * @deprecated PropertyHelper chaining is deprecated.
+ */
+ public boolean setPropertyHook(String ns, String name,
+ Object value,
+ boolean inherited, boolean user,
+ boolean isNew) {
+ if (getNext() != null) {
+ boolean subst = getNext().setPropertyHook(ns, name, value, inherited, user, isNew);
+ // If next has handled the property
+ if (subst) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get a property. If all hooks return null, the default
+ * tables will be used.
+ *
+ * <p>As of Ant 1.8.0 this method is never invoked by any code
+ * inside of Ant itself.</p>
+ *
+ * @param ns namespace of the sought property.
+ * @param name name of the sought property.
+ * @param user True if this is a user property.
+ * @return The property, if returned by a hook, or null if none.
+ * @deprecated PropertyHelper chaining is deprecated.
+ */
+ public Object getPropertyHook(String ns, String name, boolean user) {
+ if (getNext() != null) {
+ Object o = getNext().getPropertyHook(ns, name, user);
+ if (o != null) {
+ return o;
+ }
+ }
+ // Experimental/Testing, will be removed
+ if (project != null && name.startsWith("toString:")) {
+ name = name.substring("toString:".length());
+ Object v = project.getReference(name);
+ return (v == null) ? null : v.toString();
+ }
+ return null;
+ }
+
+ // -------------------- Optional methods --------------------
+ // You can override those methods if you want to optimize or
+ // do advanced things (like support a special syntax).
+ // The methods do not chain - you should use them when embedding ant
+ // (by replacing the main helper)
+
+ /**
+ * Parses a string containing <code>${xxx}</code> style property
+ * references into two lists. The first list is a collection
+ * of text fragments, while the other is a set of string property names.
+ * <code>null</code> entries in the first list indicate a property
+ * reference from the second list.
+ *
+ * <p>Delegates to {@link #parsePropertyStringDefault
+ * parsePropertyStringDefault}.</p>
+ *
+ * <p>As of Ant 1.8.0 this method is never invoked by any code
+ * inside of Ant itself except {ProjectHelper#parsePropertyString
+ * ProjectHelper.parsePropertyString}.</p>
+ *
+ * @param value Text to parse. Must not be <code>null</code>.
+ * @param fragments List to add text fragments to.
+ * Must not be <code>null</code>.
+ * @param propertyRefs List to add property names to.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the string contains an opening
+ * <code>${</code> without a closing
+ * <code>}</code>
+ * @deprecated use the other mechanisms of this class instead
+ */
+ public void parsePropertyString(String value, Vector<String> fragments,
+ Vector<String> propertyRefs) throws BuildException {
+ parsePropertyStringDefault(value, fragments, propertyRefs);
+ }
+
+ /**
+ * Replaces <code>${xxx}</code> style constructions in the given value
+ * with the string value of the corresponding data types.
+ *
+ * <p>Delegates to the one-arg version, completely ignoring the ns
+ * and keys parameters.</p>
+ *
+ * @param ns The namespace for the property.
+ * @param value The string to be scanned for property references.
+ * May be <code>null</code>, in which case this
+ * method returns immediately with no effect.
+ * @param keys Mapping (String to Object) of property names to their
+ * values. If <code>null</code>, only project properties will
+ * be used.
+ *
+ * @exception BuildException if the string contains an opening
+ * <code>${</code> without a closing
+ * <code>}</code>
+ * @return the original string with the properties replaced, or
+ * <code>null</code> if the original string is <code>null</code>.
+ */
+ //TODO deprecate? Recall why no longer using ns/keys params
+ public String replaceProperties(String ns, String value, Hashtable<String, Object> keys) throws BuildException {
+ return replaceProperties(value);
+ }
+
+ /**
+ * Replaces <code>${xxx}</code> style constructions in the given value
+ * with the string value of the corresponding data types.
+ *
+ * @param value The string to be scanned for property references.
+ * May be <code>null</code>, in which case this
+ * method returns immediately with no effect.
+ *
+ * @exception BuildException if the string contains an opening
+ * <code>${</code> without a closing
+ * <code>}</code>
+ * @return the original string with the properties replaced, or
+ * <code>null</code> if the original string is <code>null</code>.
+ */
+ public String replaceProperties(String value) throws BuildException {
+ Object o = parseProperties(value);
+ return o == null || o instanceof String ? (String) o : o.toString();
+ }
+
+ /**
+ * Decode properties from a String representation. If the entire
+ * contents of the String resolve to a single property, that value
+ * is returned. Otherwise a String is returned.
+ *
+ * @param value The string to be scanned for property references.
+ * May be <code>null</code>, in which case this
+ * method returns immediately with no effect.
+ *
+ * @exception BuildException if the string contains an opening
+ * <code>${</code> without a closing
+ * <code>}</code>
+ * @return the original string with the properties replaced, or
+ * <code>null</code> if the original string is <code>null</code>.
+ */
+ public Object parseProperties(String value) throws BuildException {
+ return new ParseProperties(getProject(), getExpanders(), this)
+ .parseProperties(value);
+ }
+
+ /**
+ * Learn whether a String contains replaceable properties.
+ * @param value the String to check.
+ * @return <code>true</code> if <code>value</code> contains property notation.
+ */
+ public boolean containsProperties(String value) {
+ return new ParseProperties(getProject(), getExpanders(), this)
+ .containsProperties(value);
+ }
+
+ // -------------------- Default implementation --------------------
+ // Methods used to support the default behavior and provide backward
+ // compatibility. Some will be deprecated, you should avoid calling them.
+
+ /**
+ * Default implementation of setProperty. Will be called from Project.
+ * This is the original 1.5 implementation, with calls to the hook
+ * added.
+ *
+ * <p>Delegates to the three-arg version, completely ignoring the
+ * ns parameter.</p>
+ *
+ * @param ns The namespace for the property (currently not used).
+ * @param name The name of the property.
+ * @param value The value to set the property to.
+ * @param verbose If this is true output extra log messages.
+ * @return true if the property is set.
+ * @deprecated namespaces are unnecessary.
+ */
+ public boolean setProperty(String ns, String name, Object value, boolean verbose) {
+ return setProperty(name, value, verbose);
+ }
+
+ /**
+ * Default implementation of setProperty. Will be called from Project.
+ * @param name The name of the property.
+ * @param value The value to set the property to.
+ * @param verbose If this is true output extra log messages.
+ * @return true if the property is set.
+ */
+ public boolean setProperty(String name, Object value, boolean verbose) {
+ for (PropertySetter setter : getDelegates(PropertySetter.class)) {
+ if (setter.set(name, value, this)) {
+ return true;
+ }
+ }
+ synchronized (this) {
+ // user (CLI) properties take precedence
+ if (userProperties.containsKey(name)) {
+ if (project != null && verbose) {
+ project.log("Override ignored for user property \""
+ + name + "\"", Project.MSG_VERBOSE);
+ }
+ return false;
+ }
+ if (project != null && verbose) {
+ if (properties.containsKey(name)) {
+ project.log("Overriding previous definition of property \""
+ + name + "\"", Project.MSG_VERBOSE);
+ }
+ project.log("Setting project property: " + name + " -> "
+ + value, Project.MSG_DEBUG);
+ }
+ if (name != null && value != null) {
+ properties.put(name, value);
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Sets a property if no value currently exists. If the property
+ * exists already, a message is logged and the method returns with
+ * no other effect.
+ *
+ * <p>Delegates to the two-arg version, completely ignoring the
+ * ns parameter.</p>
+ *
+ * @param ns The namespace for the property (currently not used).
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ * @since Ant 1.6
+ * @deprecated namespaces are unnecessary.
+ */
+ public void setNewProperty(String ns, String name, Object value) {
+ setNewProperty(name, value);
+ }
+
+ /**
+ * Sets a property if no value currently exists. If the property
+ * exists already, a message is logged and the method returns with
+ * no other effect.
+ *
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ * @since Ant 1.8.0
+ */
+ public void setNewProperty(String name, Object value) {
+ for (PropertySetter setter : getDelegates(PropertySetter.class)) {
+ if (setter.setNew(name, value, this)) {
+ return;
+ }
+ }
+ synchronized (this) {
+ if (project != null && properties.containsKey(name)) {
+ project.log("Override ignored for property \"" + name
+ + "\"", Project.MSG_VERBOSE);
+ return;
+ }
+ if (project != null) {
+ project.log("Setting project property: " + name
+ + " -> " + value, Project.MSG_DEBUG);
+ }
+ if (name != null && value != null) {
+ properties.put(name, value);
+ }
+ }
+ }
+
+ /**
+ * Sets a user property, which cannot be overwritten by
+ * set/unset property calls. Any previous value is overwritten.
+ *
+ * <p>Delegates to the two-arg version, completely ignoring the
+ * ns parameter.</p>
+ *
+ * @param ns The namespace for the property (currently not used).
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ * @deprecated namespaces are unnecessary.
+ */
+ public void setUserProperty(String ns, String name, Object value) {
+ setUserProperty(name, value);
+ }
+
+ /**
+ * Sets a user property, which cannot be overwritten by
+ * set/unset property calls. Any previous value is overwritten.
+ *
+ * <p>Does <code>not</code> consult any delegates.</p>
+ *
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ */
+ public void setUserProperty(String name, Object value) {
+ if (project != null) {
+ project.log("Setting ro project property: "
+ + name + " -> " + value, Project.MSG_DEBUG);
+ }
+ synchronized (this) {
+ userProperties.put(name, value);
+ properties.put(name, value);
+ }
+ }
+
+ /**
+ * Sets an inherited user property, which cannot be overwritten by set/unset
+ * property calls. Any previous value is overwritten. Also marks
+ * these properties as properties that have not come from the
+ * command line.
+ *
+ * <p>Delegates to the two-arg version, completely ignoring the
+ * ns parameter.</p>
+ *
+ * @param ns The namespace for the property (currently not used).
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ * @deprecated namespaces are unnecessary.
+ */
+ public void setInheritedProperty(String ns, String name, Object value) {
+ setInheritedProperty(name, value);
+ }
+
+ /**
+ * Sets an inherited user property, which cannot be overwritten by set/unset
+ * property calls. Any previous value is overwritten. Also marks
+ * these properties as properties that have not come from the
+ * command line.
+ *
+ * <p>Does <code>not</code> consult any delegates.</p>
+ *
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ */
+ public void setInheritedProperty(String name, Object value) {
+ if (project != null) {
+ project.log("Setting ro project property: " + name + " -> "
+ + value, Project.MSG_DEBUG);
+ }
+
+ synchronized (this) {
+ inheritedProperties.put(name, value);
+ userProperties.put(name, value);
+ properties.put(name, value);
+ }
+ }
+
+ // -------------------- Getting properties --------------------
+
+ /**
+ * Returns the value of a property, if it is set. You can override
+ * this method in order to plug your own storage.
+ *
+ * <p>Delegates to the one-arg version ignoring the ns parameter.</p>
+ *
+ * @param ns The namespace for the property (currently not used).
+ * @param name The name of the property.
+ * May be <code>null</code>, in which case
+ * the return value is also <code>null</code>.
+ * @return the property value, or <code>null</code> for no match
+ * or if a <code>null</code> name is provided.
+ * @deprecated namespaces are unnecessary.
+ */
+ public Object getProperty(String ns, String name) {
+ return getProperty(name);
+ }
+
+ /**
+ * Returns the value of a property, if it is set.
+ *
+ * <p>This is the method that is invoked by {Project#getProperty
+ * Project.getProperty}.</p>
+ *
+ * <p>You can override this method in order to plug your own
+ * storage but the recommended approach is to add your own
+ * implementation of {@link PropertyEvaluator PropertyEvaluator}
+ * instead.</p>
+ *
+ * @param name The name of the property.
+ * May be <code>null</code>, in which case
+ * the return value is also <code>null</code>.
+ * @return the property value, or <code>null</code> for no match
+ * or if a <code>null</code> name is provided.
+ */
+ public Object getProperty(String name) {
+ if (name == null) {
+ return null;
+ }
+ for (PropertyEvaluator evaluator : getDelegates(PropertyEvaluator.class)) {
+ final Object o = evaluator.evaluate(name, this);
+ if (o == null) {
+ continue;
+ }
+ return o instanceof NullReturn ? null : o;
+ }
+ return properties.get(name);
+ }
+
+ /**
+ * Returns the value of a user property, if it is set.
+ *
+ * <p>Delegates to the one-arg version ignoring the ns parameter.</p>
+ *
+ * @param ns The namespace for the property (currently not used).
+ * @param name The name of the property.
+ * May be <code>null</code>, in which case
+ * the return value is also <code>null</code>.
+ * @return the property value, or <code>null</code> for no match
+ * or if a <code>null</code> name is provided.
+ * @deprecated namespaces are unnecessary.
+ */
+ public Object getUserProperty(String ns, String name) {
+ return getUserProperty(name);
+ }
+
+ /**
+ * Returns the value of a user property, if it is set.
+ *
+ * <p>Does <code>not</code> consult any delegates.</p>
+ *
+ * @param name The name of the property.
+ * May be <code>null</code>, in which case
+ * the return value is also <code>null</code>.
+ * @return the property value, or <code>null</code> for no match
+ * or if a <code>null</code> name is provided.
+ */
+ public Object getUserProperty(String name) {
+ if (name == null) {
+ return null;
+ }
+ return userProperties.get(name);
+ }
+
+ // -------------------- Access to property tables --------------------
+ // This is used to support ant call and similar tasks. It should be
+ // deprecated, it is possible to use a better (more efficient)
+ // mechanism to preserve the context.
+
+ /**
+ * Returns a copy of the properties table.
+ *
+ * <p>Does not contain properties held by implementations of
+ * delegates (like local properties).</p>
+ *
+ * @return a hashtable containing all properties (including user properties).
+ */
+ public Hashtable<String, Object> getProperties() {
+ //avoid concurrent modification:
+ synchronized (properties) {
+ return new Hashtable<String, Object>(properties);
+ }
+ // There is a better way to save the context. This shouldn't
+ // delegate to next, it's for backward compatibility only.
+ }
+
+ /**
+ * Returns a copy of the user property hashtable
+ *
+ * <p>Does not contain properties held by implementations of
+ * delegates (like local properties).</p>
+ *
+ * @return a hashtable containing just the user properties
+ */
+ public Hashtable<String, Object> getUserProperties() {
+ //avoid concurrent modification:
+ synchronized (userProperties) {
+ return new Hashtable<String, Object>(userProperties);
+ }
+ }
+
+ /**
+ * Returns a copy of the inherited property hashtable
+ *
+ * <p>Does not contain properties held by implementations of
+ * delegates (like local properties).</p>
+ *
+ * @return a hashtable containing just the inherited properties
+ */
+ public Hashtable<String, Object> getInheritedProperties() {
+ //avoid concurrent modification:
+ synchronized (inheritedProperties) {
+ return new Hashtable<String, Object>(inheritedProperties);
+ }
+ }
+
+ /**
+ * special back door for subclasses, internal access to the hashtables
+ * @return the live hashtable of all properties
+ */
+ protected Hashtable<String, Object> getInternalProperties() {
+ return properties;
+ }
+
+ /**
+ * special back door for subclasses, internal access to the hashtables
+ *
+ * @return the live hashtable of user properties
+ */
+ protected Hashtable<String, Object> getInternalUserProperties() {
+ return userProperties;
+ }
+
+ /**
+ * special back door for subclasses, internal access to the hashtables
+ *
+ * @return the live hashtable inherited properties
+ */
+ protected Hashtable<String, Object> getInternalInheritedProperties() {
+ return inheritedProperties;
+ }
+
+ /**
+ * Copies all user properties that have not been set on the
+ * command line or a GUI tool from this instance to the Project
+ * instance given as the argument.
+ *
+ * <p>To copy all "user" properties, you will also have to call
+ * {@link #copyUserProperties copyUserProperties}.</p>
+ *
+ * <p>Does not copy properties held by implementations of
+ * delegates (like local properties).</p>
+ *
+ * @param other the project to copy the properties to. Must not be null.
+ *
+ * @since Ant 1.6
+ */
+ public void copyInheritedProperties(Project other) {
+ //avoid concurrent modification:
+ synchronized (inheritedProperties) {
+ Enumeration<String> e = inheritedProperties.keys();
+ while (e.hasMoreElements()) {
+ String arg = e.nextElement().toString();
+ if (other.getUserProperty(arg) != null) {
+ continue;
+ }
+ Object value = inheritedProperties.get(arg);
+ other.setInheritedProperty(arg, value.toString());
+ }
+ }
+ }
+
+ /**
+ * Copies all user properties that have been set on the command
+ * line or a GUI tool from this instance to the Project instance
+ * given as the argument.
+ *
+ * <p>To copy all "user" properties, you will also have to call
+ * {@link #copyInheritedProperties copyInheritedProperties}.</p>
+ *
+ * <p>Does not copy properties held by implementations of
+ * delegates (like local properties).</p>
+ *
+ * @param other the project to copy the properties to. Must not be null.
+ *
+ * @since Ant 1.6
+ */
+ public void copyUserProperties(Project other) {
+ //avoid concurrent modification:
+ synchronized (userProperties) {
+ Enumeration<String> e = userProperties.keys();
+ while (e.hasMoreElements()) {
+ Object arg = e.nextElement();
+ if (inheritedProperties.containsKey(arg)) {
+ continue;
+ }
+ Object value = userProperties.get(arg);
+ other.setUserProperty(arg.toString(), value.toString());
+ }
+ }
+ }
+
+ // -------------------- Property parsing --------------------
+ // Moved from ProjectHelper. You can override the static method -
+ // this is used for backward compatibility (for code that calls
+ // the parse method in ProjectHelper).
+
+ /**
+ * Default parsing method. It is here only to support backward compatibility
+ * for the static ProjectHelper.parsePropertyString().
+ */
+ static void parsePropertyStringDefault(String value, Vector<String> fragments, Vector<String> propertyRefs)
+ throws BuildException {
+ int prev = 0;
+ int pos;
+ //search for the next instance of $ from the 'prev' position
+ while ((pos = value.indexOf("$", prev)) >= 0) {
+
+ //if there was any text before this, add it as a fragment
+ //TODO, this check could be modified to go if pos>prev;
+ //seems like this current version could stick empty strings
+ //into the list
+ if (pos > 0) {
+ fragments.addElement(value.substring(prev, pos));
+ }
+ //if we are at the end of the string, we tack on a $
+ //then move past it
+ if (pos == (value.length() - 1)) {
+ fragments.addElement("$");
+ prev = pos + 1;
+ } else if (value.charAt(pos + 1) != '{') {
+ //peek ahead to see if the next char is a property or not
+ //not a property: insert the char as a literal
+ /*
+ fragments.addElement(value.substring(pos + 1, pos + 2));
+ prev = pos + 2;
+ */
+ if (value.charAt(pos + 1) == '$') {
+ //backwards compatibility two $ map to one mode
+ fragments.addElement("$");
+ prev = pos + 2;
+ } else {
+ //new behaviour: $X maps to $X for all values of X!='$'
+ fragments.addElement(value.substring(pos, pos + 2));
+ prev = pos + 2;
+ }
+ } else {
+ //property found, extract its name or bail on a typo
+ int endName = value.indexOf('}', pos);
+ if (endName < 0) {
+ throw new BuildException("Syntax error in property: " + value);
+ }
+ String propertyName = value.substring(pos + 2, endName);
+ fragments.addElement(null);
+ propertyRefs.addElement(propertyName);
+ prev = endName + 1;
+ }
+ }
+ //no more $ signs found
+ //if there is any tail to the file, append it
+ if (prev < value.length()) {
+ fragments.addElement(value.substring(prev));
+ }
+ }
+
+ /**
+ * Add the specified delegate object to this PropertyHelper.
+ * Delegates are processed in LIFO order.
+ * @param delegate the delegate to add.
+ * @since Ant 1.8.0
+ */
+ public void add(Delegate delegate) {
+ synchronized (delegates) {
+ for (Class<? extends Delegate> key : getDelegateInterfaces(delegate)) {
+ List<Delegate> list = delegates.get(key);
+ if (list == null) {
+ list = new ArrayList<Delegate>();
+ } else {
+ //copy on write, top priority
+ list = new ArrayList<Delegate>(list);
+ list.remove(delegate);
+ }
+ list.add(0, delegate);
+ delegates.put(key, Collections.unmodifiableList(list));
+ }
+ }
+ }
+
+ /**
+ * Get the Collection of delegates of the specified type.
+ *
+ * @param type
+ * delegate type.
+ * @return Collection.
+ * @since Ant 1.8.0
+ */
+ protected <D extends Delegate> List<D> getDelegates(Class<D> type) {
+ @SuppressWarnings("unchecked")
+ final List<D> result = (List<D>) delegates.get(type);
+ return result == null ? Collections.<D> emptyList() : result;
+ }
+
+ /**
+ * Get all Delegate interfaces (excluding Delegate itself) from the specified Delegate.
+ * @param d the Delegate to inspect.
+ * @return Set&lt;Class&gt;
+ * @since Ant 1.8.0
+ */
+ protected static Set<Class<? extends Delegate>> getDelegateInterfaces(Delegate d) {
+ final HashSet<Class<? extends Delegate>> result = new HashSet<Class<? extends Delegate>>();
+ Class<?> c = d.getClass();
+ while (c != null) {
+ Class<?>[] ifs = c.getInterfaces();
+ for (int i = 0; i < ifs.length; i++) {
+ if (Delegate.class.isAssignableFrom(ifs[i])) {
+ @SuppressWarnings("unchecked")
+ final Class<? extends Delegate> delegateInterface = (Class<? extends Delegate>) ifs[i];
+ result.add(delegateInterface);
+ }
+ }
+ c = c.getSuperclass();
+ }
+ result.remove(Delegate.class);
+ return result;
+ }
+
+ /**
+ * If the given object can be interpreted as a true/false value,
+ * turn it into a matching Boolean - otherwise return null.
+ * @since Ant 1.8.0
+ */
+ public static Boolean toBoolean(Object value) {
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ }
+ if (value instanceof String) {
+ String s = (String) value;
+ if (Project.toBoolean(s)) {
+ return Boolean.TRUE;
+ }
+ if ("off".equalsIgnoreCase(s)
+ || "false".equalsIgnoreCase(s)
+ || "no".equalsIgnoreCase(s)) {
+ return Boolean.FALSE;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the object is null or an empty string.
+ *
+ * @since Ant 1.8.0
+ */
+ private static boolean nullOrEmpty(Object value) {
+ return value == null || "".equals(value);
+
+ }
+
+ /**
+ * Returns true if the value can be interpreted as a true value or
+ * cannot be interpreted as a false value and a property of the
+ * value's name exists.
+ * @since Ant 1.8.0
+ */
+ private boolean evalAsBooleanOrPropertyName(Object value) {
+ Boolean b = toBoolean(value);
+ if (b != null) {
+ return b.booleanValue();
+ }
+ return getProperty(String.valueOf(value)) != null;
+ }
+
+ /**
+ * Returns true if the value is null or an empty string, can be
+ * interpreted as a true value or cannot be interpreted as a false
+ * value and a property of the value's name exists.
+ * @since Ant 1.8.0
+ */
+ public boolean testIfCondition(Object value) {
+ return nullOrEmpty(value) || evalAsBooleanOrPropertyName(value);
+ }
+
+ /**
+ * Returns true if the value is null or an empty string, can be
+ * interpreted as a false value or cannot be interpreted as a true
+ * value and a property of the value's name doesn't exist.
+ * @since Ant 1.8.0
+ */
+ public boolean testUnlessCondition(Object value) {
+ return nullOrEmpty(value) || !evalAsBooleanOrPropertyName(value);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/RuntimeConfigurable.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/RuntimeConfigurable.java
new file mode 100644
index 00000000..26f68dfe
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/RuntimeConfigurable.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.tools.ant;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map.Entry;
+
+import org.apache.tools.ant.attribute.EnableAttribute;
+import org.apache.tools.ant.taskdefs.MacroDef.Attribute;
+import org.apache.tools.ant.taskdefs.MacroInstance;
+import org.apache.tools.ant.util.CollectionUtils;
+import org.xml.sax.AttributeList;
+import org.xml.sax.helpers.AttributeListImpl;
+
+/**
+ * Wrapper class that holds the attributes of an element, its children, and
+ * any text within it. It then takes care of configuring that element at
+ * runtime.
+ */
+public class RuntimeConfigurable implements Serializable {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ /** Empty Hashtable. */
+ private static final Hashtable<String, Object> EMPTY_HASHTABLE =
+ new Hashtable<String, Object>(0);
+
+ /** Name of the element to configure. */
+ private String elementTag = null;
+
+ /** List of child element wrappers. */
+ private List<RuntimeConfigurable> children = null;
+
+ /** The element to configure. It is only used during
+ * maybeConfigure.
+ */
+ private transient Object wrappedObject = null;
+
+ /**
+ * XML attributes for the element.
+ * @deprecated since 1.6.x
+ */
+ private transient AttributeList attributes;
+
+ // The following is set to true if any of the attributes are namespaced
+ private transient boolean namespacedAttribute = false;
+
+ /** Attribute names and values. While the XML spec doesn't require
+ * preserving the order ( AFAIK ), some ant tests do rely on the
+ * exact order.
+ * The only exception to this order is the treatment of
+ * refid. A number of datatypes check if refid is set
+ * when other attributes are set. This check will not
+ * work if the build script has the other attribute before
+ * the "refid" attribute, so now (ANT 1.7) the refid
+ * attribute will be processed first.
+ */
+ private LinkedHashMap<String, Object> attributeMap = null;
+
+ /** Text appearing within the element. */
+ private StringBuffer characters = null;
+
+ /** Indicates if the wrapped object has been configured */
+ private boolean proxyConfigured = false;
+
+ /** the polymorphic type */
+ private String polyType = null;
+
+ /** the "id" of this Element if it has one */
+ private String id = null;
+
+ /**
+ * Sole constructor creating a wrapper for the specified object.
+ *
+ * @param proxy The element to configure. Must not be <code>null</code>.
+ * @param elementTag The tag name generating this element.
+ */
+ public RuntimeConfigurable(Object proxy, String elementTag) {
+ setProxy(proxy);
+ setElementTag(elementTag);
+ // Most likely an UnknownElement
+ if (proxy instanceof Task) {
+ ((Task) proxy).setRuntimeConfigurableWrapper(this);
+ }
+ }
+
+ /**
+ * Sets the element to configure.
+ *
+ * @param proxy The element to configure. Must not be <code>null</code>.
+ */
+ public synchronized void setProxy(Object proxy) {
+ wrappedObject = proxy;
+ proxyConfigured = false;
+ }
+
+ private static class EnableAttributeConsumer {
+ public void add(EnableAttribute b) {
+ // Ignore
+ }
+ }
+
+ /**
+ * contains the attribute component name and boolean restricted set to true when
+ * the attribute is in one of the name spaces managed by ant (if and unless currently)
+ * @since Ant 1.9.3
+ */
+ private static class AttributeComponentInformation {
+ String componentName;
+ boolean restricted;
+
+ private AttributeComponentInformation(String componentName, boolean restricted) {
+ this.componentName = componentName;
+ this.restricted = restricted;
+ }
+
+ public String getComponentName() {
+ return componentName;
+ }
+
+ public boolean isRestricted() {
+ return restricted;
+ }
+ }
+
+ /**
+ *
+ * @param name the name of the attribute.
+ * @param componentHelper current component helper
+ * @return AttributeComponentInformation instance
+ */
+ private AttributeComponentInformation isRestrictedAttribute(String name, ComponentHelper componentHelper) {
+ if (name.indexOf(':') == -1) {
+ return new AttributeComponentInformation(null, false);
+ }
+ String componentName = attrToComponent(name);
+ String ns = ProjectHelper.extractUriFromComponentName(componentName);
+ if (componentHelper.getRestrictedDefinitions(
+ ProjectHelper.nsToComponentName(ns)) == null) {
+ return new AttributeComponentInformation(null, false);
+ }
+ return new AttributeComponentInformation(componentName, true);
+ }
+
+ /**
+ * Check if an UE is enabled.
+ * This looks tru the attributes and checks if there
+ * are any Ant attributes, and if so, the method calls the
+ * isEnabled() method on them.
+ * @param owner the UE that owns this RC.
+ * @return true if enabled, false if any of the ant attribures return
+ * false.
+ * @since 1.9.1
+ */
+ public boolean isEnabled(UnknownElement owner) {
+ if (!namespacedAttribute) {
+ return true;
+ }
+ ComponentHelper componentHelper = ComponentHelper
+ .getComponentHelper(owner.getProject());
+
+ IntrospectionHelper ih
+ = IntrospectionHelper.getHelper(
+ owner.getProject(), EnableAttributeConsumer.class);
+ for (int i = 0; i < attributeMap.keySet().size(); ++i) {
+ String name = (String) attributeMap.keySet().toArray()[i];
+ AttributeComponentInformation attributeComponentInformation = isRestrictedAttribute(name, componentHelper);
+ if (!attributeComponentInformation.isRestricted()) {
+ continue;
+ }
+ String value = (String) attributeMap.get(name);
+ EnableAttribute enable = null;
+ try {
+ enable = (EnableAttribute)
+ ih.createElement(
+ owner.getProject(), new EnableAttributeConsumer(),
+ attributeComponentInformation.getComponentName());
+ } catch (BuildException ex) {
+ throw new BuildException(
+ "Unsupported attribute " + attributeComponentInformation.getComponentName());
+ }
+ if (enable == null) {
+ continue;
+ }
+ value = owner.getProject().replaceProperties(value); // FixMe: need to make config
+ if (!enable.isEnabled(owner, value)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private String attrToComponent(String a) {
+ // need to remove the prefix
+ int p1 = a.lastIndexOf(':');
+ int p2 = a.lastIndexOf(':', p1 - 1);
+ return a.substring(0, p2) + a.substring(p1);
+ }
+
+ /**
+ * Sets the creator of the element to be configured
+ * used to store the element in the parent.
+ *
+ * @param creator the creator object.
+ */
+ synchronized void setCreator(IntrospectionHelper.Creator creator) {
+ }
+
+ /**
+ * Get the object for which this RuntimeConfigurable holds the configuration
+ * information.
+ *
+ * @return the object whose configure is held by this instance.
+ */
+ public synchronized Object getProxy() {
+ return wrappedObject;
+ }
+
+ /**
+ * Returns the id for this element.
+ * @return the id.
+ */
+ public synchronized String getId() {
+ return id;
+ }
+
+ /**
+ * Get the polymorphic type for this element.
+ * @return the ant component type name, null if not set.
+ */
+ public synchronized String getPolyType() {
+ return polyType;
+ }
+
+ /**
+ * Set the polymorphic type for this element.
+ * @param polyType the ant component type name, null if not set.
+ */
+ public synchronized void setPolyType(String polyType) {
+ this.polyType = polyType;
+ }
+
+ /**
+ * Sets the attributes for the wrapped element.
+ *
+ * @deprecated since 1.6.x.
+ * @param attributes List of attributes defined in the XML for this
+ * element. May be <code>null</code>.
+ */
+ public synchronized void setAttributes(AttributeList attributes) {
+ this.attributes = new AttributeListImpl(attributes);
+ for (int i = 0; i < attributes.getLength(); i++) {
+ setAttribute(attributes.getName(i), attributes.getValue(i));
+ }
+ }
+
+ /**
+ * Set an attribute to a given value.
+ *
+ * @param name the name of the attribute.
+ * @param value the attribute's value.
+ */
+ public synchronized void setAttribute(String name, String value) {
+ if (name.indexOf(':') != -1) {
+ namespacedAttribute = true;
+ }
+ setAttribute(name, (Object) value);
+ }
+
+ /**
+ * Set an attribute to a given value.
+ *
+ * @param name the name of the attribute.
+ * @param value the attribute's value.
+ * @since 1.9
+ */
+ public synchronized void setAttribute(String name, Object value) {
+ if (name.equalsIgnoreCase(ProjectHelper.ANT_TYPE)) {
+ this.polyType = value == null ? null : value.toString();
+ } else {
+ if (attributeMap == null) {
+ attributeMap = new LinkedHashMap<String, Object>();
+ }
+ if (name.equalsIgnoreCase("refid") && !attributeMap.isEmpty()) {
+ LinkedHashMap<String, Object> newAttributeMap = new LinkedHashMap<String, Object>();
+ newAttributeMap.put(name, value);
+ newAttributeMap.putAll(attributeMap);
+ attributeMap = newAttributeMap;
+ } else {
+ attributeMap.put(name, value);
+ }
+ if (name.equals("id")) {
+ this.id = value == null ? null : value.toString();
+ }
+ }
+ }
+
+ /**
+ * Delete an attribute. Not for the faint of heart.
+ * @param name the name of the attribute to be removed.
+ */
+ public synchronized void removeAttribute(String name) {
+ attributeMap.remove(name);
+ }
+
+ /**
+ * Return the attribute map.
+ *
+ * @return Attribute name to attribute value map.
+ * @since Ant 1.6
+ */
+ public synchronized Hashtable<String, Object> getAttributeMap() {
+ return (attributeMap == null)
+ ? EMPTY_HASHTABLE : new Hashtable<String, Object>(attributeMap);
+ }
+
+ /**
+ * Returns the list of attributes for the wrapped element.
+ *
+ * @deprecated Deprecated since Ant 1.6 in favor of {@link #getAttributeMap}.
+ * @return An AttributeList representing the attributes defined in the
+ * XML for this element. May be <code>null</code>.
+ */
+ public synchronized AttributeList getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * Adds a child element to the wrapped element.
+ *
+ * @param child The child element wrapper to add to this one.
+ * Must not be <code>null</code>.
+ */
+ public synchronized void addChild(RuntimeConfigurable child) {
+ children = (children == null) ? new ArrayList<RuntimeConfigurable>() : children;
+ children.add(child);
+ }
+
+ /**
+ * Returns the child wrapper at the specified position within the list.
+ *
+ * @param index The index of the child to return.
+ *
+ * @return The child wrapper at position <code>index</code> within the
+ * list.
+ */
+ synchronized RuntimeConfigurable getChild(int index) {
+ return children.get(index);
+ }
+
+ /**
+ * Returns an enumeration of all child wrappers.
+ * @return an enumeration of the child wrappers.
+ * @since Ant 1.6
+ */
+ public synchronized Enumeration<RuntimeConfigurable> getChildren() {
+ return (children == null) ? new CollectionUtils.EmptyEnumeration<RuntimeConfigurable>()
+ : Collections.enumeration(children);
+ }
+
+ /**
+ * Adds characters from #PCDATA areas to the wrapped element.
+ *
+ * @param data Text to add to the wrapped element.
+ * Should not be <code>null</code>.
+ */
+ public synchronized void addText(String data) {
+ if (data.length() == 0) {
+ return;
+ }
+ characters = (characters == null)
+ ? new StringBuffer(data) : characters.append(data);
+ }
+
+ /**
+ * Adds characters from #PCDATA areas to the wrapped element.
+ *
+ * @param buf A character array of the text within the element.
+ * Must not be <code>null</code>.
+ * @param start The start element in the array.
+ * @param count The number of characters to read from the array.
+ *
+ */
+ public synchronized void addText(char[] buf, int start, int count) {
+ if (count == 0) {
+ return;
+ }
+ characters = ((characters == null)
+ ? new StringBuffer(count) : characters).append(buf, start, count);
+ }
+
+ /**
+ * Get the text content of this element. Various text chunks are
+ * concatenated, there is no way ( currently ) of keeping track of
+ * multiple fragments.
+ *
+ * @return the text content of this element.
+ * @since Ant 1.6
+ */
+ public synchronized StringBuffer getText() {
+ return (characters == null) ? new StringBuffer(0) : characters;
+ }
+
+ /**
+ * Set the element tag.
+ * @param elementTag The tag name generating this element.
+ */
+ public synchronized void setElementTag(String elementTag) {
+ this.elementTag = elementTag;
+ }
+
+ /**
+ * Returns the tag name of the wrapped element.
+ *
+ * @return The tag name of the wrapped element. This is unlikely
+ * to be <code>null</code>, but may be.
+ */
+ public synchronized String getElementTag() {
+ return elementTag;
+ }
+
+ /**
+ * Configures the wrapped element and all its children.
+ * The attributes and text for the wrapped element are configured,
+ * and then each child is configured and added. Each time the
+ * wrapper is configured, the attributes and text for it are
+ * reset.
+ *
+ * If the element has an <code>id</code> attribute, a reference
+ * is added to the project as well.
+ *
+ * @param p The project containing the wrapped element.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the configuration fails, for instance due
+ * to invalid attributes or children, or text being added to
+ * an element which doesn't accept it.
+ */
+ public void maybeConfigure(Project p) throws BuildException {
+ maybeConfigure(p, true);
+ }
+
+ /**
+ * Configures the wrapped element. The attributes and text for
+ * the wrapped element are configured. Each time the wrapper is
+ * configured, the attributes and text for it are reset.
+ *
+ * If the element has an <code>id</code> attribute, a reference
+ * is added to the project as well.
+ *
+ * @param p The project containing the wrapped element.
+ * Must not be <code>null</code>.
+ *
+ * @param configureChildren ignored.
+
+ *
+ * @exception BuildException if the configuration fails, for instance due
+ * to invalid attributes , or text being added to
+ * an element which doesn't accept it.
+ */
+ public synchronized void maybeConfigure(Project p, boolean configureChildren)
+ throws BuildException {
+
+ if (proxyConfigured) {
+ return;
+ }
+
+ // Configure the object
+ Object target = (wrappedObject instanceof TypeAdapter)
+ ? ((TypeAdapter) wrappedObject).getProxy() : wrappedObject;
+
+ IntrospectionHelper ih =
+ IntrospectionHelper.getHelper(p, target.getClass());
+ ComponentHelper componentHelper = ComponentHelper.getComponentHelper(p);
+ if (attributeMap != null) {
+ for (Entry<String, Object> entry : attributeMap.entrySet()) {
+ String name = entry.getKey();
+ // skip restricted attributes such as if:set
+ AttributeComponentInformation attributeComponentInformation = isRestrictedAttribute(name, componentHelper);
+ if (attributeComponentInformation.isRestricted()) {
+ continue;
+ }
+ Object value = entry.getValue();
+ // reflect these into the target, defer for
+ // MacroInstance where properties are expanded for the
+ // nested sequential
+ Object attrValue;
+ if (value instanceof Evaluable) {
+ attrValue = ((Evaluable) value).eval();
+ } else {
+ attrValue = PropertyHelper.getPropertyHelper(p).parseProperties(value.toString());
+ }
+ if (target instanceof MacroInstance) {
+ for (Attribute attr : ((MacroInstance) target).getMacroDef().getAttributes()) {
+ if (attr.getName().equals(name)) {
+ if (!attr.isDoubleExpanding()) {
+ attrValue = value;
+ }
+ break;
+ }
+ }
+ }
+ try {
+ ih.setAttribute(p, target, name, attrValue);
+ } catch (UnsupportedAttributeException be) {
+ // id attribute must be set externally
+ if (name.equals("id")) {
+ // Do nothing
+ } else if (getElementTag() == null) {
+ throw be;
+ } else {
+ throw new BuildException(
+ getElementTag() + " doesn't support the \""
+ + be.getAttribute() + "\" attribute", be);
+ }
+ } catch (BuildException be) {
+ if (name.equals("id")) {
+ // Assume that this is an not supported attribute type
+ // thrown for example by a dymanic attribute task
+ // Do nothing
+ } else {
+ throw be;
+ }
+ }
+ }
+ }
+
+ if (characters != null) {
+ ProjectHelper.addText(p, wrappedObject, characters.substring(0));
+ }
+
+ if (id != null) {
+ p.addReference(id, wrappedObject);
+ }
+ proxyConfigured = true;
+ }
+
+ /**
+ * Reconfigure the element, even if it has already been configured.
+ *
+ * @param p the project instance for this configuration.
+ */
+ public void reconfigure(Project p) {
+ proxyConfigured = false;
+ maybeConfigure(p);
+ }
+
+ /**
+ * Apply presets, attributes and text are set if not currently set.
+ * Nested elements are prepended.
+ *
+ * @param r a <code>RuntimeConfigurable</code> value.
+ */
+ public void applyPreSet(RuntimeConfigurable r) {
+ // Attributes
+ if (r.attributeMap != null) {
+ for (String name : r.attributeMap.keySet()) {
+ if (attributeMap == null || attributeMap.get(name) == null) {
+ setAttribute(name, (String) r.attributeMap.get(name));
+ }
+ }
+ }
+ // poly type
+
+ polyType = (polyType == null) ? r.polyType : polyType;
+
+ // Children (this is a shadow of UnknownElement#children)
+ if (r.children != null) {
+ List<RuntimeConfigurable> newChildren = new ArrayList<RuntimeConfigurable>();
+ newChildren.addAll(r.children);
+ if (children != null) {
+ newChildren.addAll(children);
+ }
+ children = newChildren;
+ }
+
+ // Text
+ if (r.characters != null) {
+ if (characters == null
+ || characters.toString().trim().length() == 0) {
+ characters = new StringBuffer(r.characters.toString());
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/SubBuildListener.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/SubBuildListener.java
new file mode 100644
index 00000000..196a88b5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/SubBuildListener.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.tools.ant;
+
+/**
+ * Instances of classes that implement this interface can register
+ * to be also notified when things happened during a subbuild.
+ *
+ * <p>A subbuild is a separate project instance created by the
+ * <code>&lt;ant&gt;</code> task family. These project instances will
+ * never fire the buildStarted and buildFinished events, but they will
+ * fire subBuildStarted/ and subBuildFinished. The main project
+ * instance - the one created by running Ant in the first place - will
+ * never invoke one of the methods of this interface.</p>
+ *
+ * @see BuildEvent
+ * @see Project#addBuildListener(BuildListener)
+ *
+ * @since Ant 1.6.2
+ */
+public interface SubBuildListener extends BuildListener {
+
+ /**
+ * Signals that a subbuild has started. This event
+ * is fired before any targets have started.
+ *
+ * @param event An event with any relevant extra information.
+ * Must not be <code>null</code>.
+ */
+ void subBuildStarted(BuildEvent event);
+
+ /**
+ * Signals that the last target has finished. This event
+ * will still be fired if an error occurred during the build.
+ *
+ * @param event An event with any relevant extra information.
+ * Must not be <code>null</code>.
+ *
+ * @see BuildEvent#getException()
+ */
+ void subBuildFinished(BuildEvent event);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Target.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Target.java
new file mode 100644
index 00000000..796b7e18
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Target.java
@@ -0,0 +1,532 @@
+/*
+ * 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.tools.ant;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.property.LocalProperties;
+import org.apache.tools.ant.taskdefs.condition.And;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.taskdefs.condition.Or;
+
+/**
+ * Class to implement a target object with required parameters.
+ *
+ * <p>If you are creating Targets programmatically, make sure you set
+ * the Location to a useful value. In particular all targets should
+ * have different location values.</p>
+ */
+public class Target implements TaskContainer {
+
+ /** Name of this target. */
+ private String name;
+
+ /** The "if" condition to test on execution. */
+ private String ifString = "";
+
+ /** The "unless" condition to test on execution. */
+ private String unlessString = "";
+
+ private Condition ifCondition;
+
+ private Condition unlessCondition;
+
+ /** List of targets this target is dependent on. */
+ private List<String> dependencies = null;
+
+ /** Children of this target (tasks and data types). */
+ private List<Object> children = new ArrayList<Object>();
+
+ /** Since Ant 1.6.2 */
+ private Location location = Location.UNKNOWN_LOCATION;
+
+ /** Project this target belongs to. */
+ private Project project;
+
+ /** Description of this target, if any. */
+ private String description = null;
+
+ /** Default constructor. */
+ public Target() {
+ //empty
+ }
+
+ /**
+ * Cloning constructor.
+ * @param other the Target to clone.
+ */
+ public Target(Target other) {
+ this.name = other.name;
+ this.ifString = other.ifString;
+ this.unlessString = other.unlessString;
+ this.ifCondition = other.ifCondition;
+ this.unlessCondition = other.unlessCondition;
+ this.dependencies = other.dependencies;
+ this.location = other.location;
+ this.project = other.project;
+ this.description = other.description;
+ // The children are added to after this cloning
+ this.children = other.children;
+ }
+
+ /**
+ * Sets the project this target belongs to.
+ *
+ * @param project The project this target belongs to.
+ * Must not be <code>null</code>.
+ */
+ public void setProject(Project project) {
+ this.project = project;
+ }
+
+ /**
+ * Returns the project this target belongs to.
+ *
+ * @return The project this target belongs to, or <code>null</code> if
+ * the project has not been set yet.
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * Sets the location of this target's definition.
+ *
+ * @param location <code>Location</code>
+ * @since 1.6.2
+ */
+ public void setLocation(Location location) {
+ this.location = location;
+ }
+
+ /**
+ * Get the location of this target's definition.
+ *
+ * @return <code>Location</code>
+ * @since 1.6.2
+ */
+ public Location getLocation() {
+ return location;
+ }
+
+ /**
+ * Sets the list of targets this target is dependent on.
+ * The targets themselves are not resolved at this time.
+ *
+ * @param depS A comma-separated list of targets this target
+ * depends on. Must not be <code>null</code>.
+ */
+ public void setDepends(String depS) {
+ for (String dep : parseDepends(depS, getName(), "depends")) {
+ addDependency(dep);
+ }
+ }
+
+ public static List<String> parseDepends(String depends,
+ String targetName,
+ String attributeName) {
+ List<String> list = new ArrayList<String>();
+ if (depends.length() > 0) {
+ StringTokenizer tok =
+ new StringTokenizer(depends, ",", true);
+ while (tok.hasMoreTokens()) {
+ String token = tok.nextToken().trim();
+
+ // Make sure the dependency is not empty string
+ if ("".equals(token) || ",".equals(token)) {
+ throw new BuildException("Syntax Error: "
+ + attributeName
+ + " attribute of target \""
+ + targetName
+ + "\" contains an empty string.");
+ }
+
+ list.add(token);
+
+ // Make sure that depends attribute does not
+ // end in a ,
+ if (tok.hasMoreTokens()) {
+ token = tok.nextToken();
+ if (!tok.hasMoreTokens() || !",".equals(token)) {
+ throw new BuildException("Syntax Error: "
+ + attributeName
+ + " attribute for target \""
+ + targetName
+ + "\" ends with a \",\" "
+ + "character");
+ }
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Sets the name of this target.
+ *
+ * @param name The name of this target. Should not be <code>null</code>.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the name of this target.
+ *
+ * @return the name of this target, or <code>null</code> if the
+ * name has not been set yet.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Adds a task to this target.
+ *
+ * @param task The task to be added. Must not be <code>null</code>.
+ */
+ public void addTask(Task task) {
+ children.add(task);
+ }
+
+ /**
+ * Adds the wrapper for a data type element to this target.
+ *
+ * @param r The wrapper for the data type element to be added.
+ * Must not be <code>null</code>.
+ */
+ public void addDataType(RuntimeConfigurable r) {
+ children.add(r);
+ }
+
+ /**
+ * Returns the current set of tasks to be executed by this target.
+ *
+ * @return an array of the tasks currently within this target
+ */
+ public Task[] getTasks() {
+ List<Task> tasks = new ArrayList<Task>(children.size());
+ for (Object o : children) {
+ if (o instanceof Task) {
+ tasks.add((Task) o);
+ }
+ }
+ return tasks.toArray(new Task[tasks.size()]);
+ }
+
+ /**
+ * Adds a dependency to this target.
+ *
+ * @param dependency The name of a target this target is dependent on.
+ * Must not be <code>null</code>.
+ */
+ public void addDependency(String dependency) {
+ if (dependencies == null) {
+ dependencies = new ArrayList<String>(2);
+ }
+ dependencies.add(dependency);
+ }
+
+ /**
+ * Returns an enumeration of the dependencies of this target.
+ *
+ * @return an enumeration of the dependencies of this target (enumeration of String)
+ */
+ public Enumeration<String> getDependencies() {
+ return Collections
+ .enumeration(dependencies == null ? Collections.<String> emptyList() : dependencies);
+ }
+
+ /**
+ * Does this target depend on the named target?
+ * @param other the other named target.
+ * @return true if the target does depend on the named target
+ * @since Ant 1.6
+ */
+ public boolean dependsOn(String other) {
+ Project p = getProject();
+ Hashtable<String, Target> t = p == null ? null : p.getTargets();
+ return p != null && p.topoSort(getName(), t, false).contains(t.get(other));
+ }
+
+ /**
+ * Sets the "if" condition to test on execution. This is the
+ * name of a property to test for existence - if the property
+ * is not set, the task will not execute. The property goes
+ * through property substitution once before testing, so if
+ * property <code>foo</code> has value <code>bar</code>, setting
+ * the "if" condition to <code>${foo}_x</code> will mean that the
+ * task will only execute if property <code>bar_x</code> is set.
+ *
+ * @param property The property condition to test on execution.
+ * May be <code>null</code>, in which case
+ * no "if" test is performed.
+ */
+ public void setIf(String property) {
+ ifString = property == null ? "" : property;
+ setIf(new IfStringCondition(ifString));
+ }
+
+ /**
+ * Returns the "if" property condition of this target.
+ *
+ * @return the "if" property condition or <code>null</code> if no
+ * "if" condition had been defined.
+ * @since 1.6.2
+ */
+ public String getIf() {
+ return "".equals(ifString) ? null : ifString;
+ }
+
+ /**
+ * Same as {@link #setIf(String)} but requires a {@link Condition} instance
+ *
+ * @since 1.9
+ */
+ public void setIf(Condition condition) {
+ if (ifCondition == null) {
+ ifCondition = condition;
+ } else {
+ And andCondition = new And();
+ andCondition.setProject(getProject());
+ andCondition.setLocation(getLocation());
+ andCondition.add(ifCondition);
+ andCondition.add(condition);
+ ifCondition = andCondition;
+ }
+ }
+
+ /**
+ * Sets the "unless" condition to test on execution. This is the
+ * name of a property to test for existence - if the property
+ * is set, the task will not execute. The property goes
+ * through property substitution once before testing, so if
+ * property <code>foo</code> has value <code>bar</code>, setting
+ * the "unless" condition to <code>${foo}_x</code> will mean that the
+ * task will only execute if property <code>bar_x</code> isn't set.
+ *
+ * @param property The property condition to test on execution.
+ * May be <code>null</code>, in which case
+ * no "unless" test is performed.
+ */
+ public void setUnless(String property) {
+ unlessString = property == null ? "" : property;
+ setUnless(new UnlessStringCondition(unlessString));
+ }
+
+ /**
+ * Returns the "unless" property condition of this target.
+ *
+ * @return the "unless" property condition or <code>null</code>
+ * if no "unless" condition had been defined.
+ * @since 1.6.2
+ */
+ public String getUnless() {
+ return "".equals(unlessString) ? null : unlessString;
+ }
+
+ /**
+ * Same as {@link #setUnless(String)} but requires a {@link Condition} instance
+ *
+ * @since 1.9
+ */
+ public void setUnless(Condition condition) {
+ if (unlessCondition == null) {
+ unlessCondition = condition;
+ } else {
+ Or orCondition = new Or();
+ orCondition.setProject(getProject());
+ orCondition.setLocation(getLocation());
+ orCondition.add(unlessCondition);
+ orCondition.add(condition);
+ unlessCondition = orCondition;
+ }
+ }
+
+ /**
+ * Sets the description of this target.
+ *
+ * @param description The description for this target.
+ * May be <code>null</code>, indicating that no
+ * description is available.
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Returns the description of this target.
+ *
+ * @return the description of this target, or <code>null</code> if no
+ * description is available.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Returns the name of this target.
+ *
+ * @return the name of this target, or <code>null</code> if the
+ * name has not been set yet.
+ */
+ public String toString() {
+ return name;
+ }
+
+ /**
+ * Executes the target if the "if" and "unless" conditions are
+ * satisfied. Dependency checking should be done before calling this
+ * method, as it does no checking of its own. If either the "if"
+ * or "unless" test prevents this target from being executed, a verbose
+ * message is logged giving the reason. It is recommended that clients
+ * of this class call performTasks rather than this method so that
+ * appropriate build events are fired.
+ *
+ * @exception BuildException if any of the tasks fail or if a data type
+ * configuration fails.
+ *
+ * @see #performTasks()
+ * @see #setIf(String)
+ * @see #setUnless(String)
+ */
+ public void execute() throws BuildException {
+ if (ifCondition != null && !ifCondition.eval()) {
+ project.log(this, "Skipped because property '" + project.replaceProperties(ifString)
+ + "' not set.", Project.MSG_VERBOSE);
+ return;
+ }
+ if (unlessCondition != null && unlessCondition.eval()) {
+ project.log(this, "Skipped because property '"
+ + project.replaceProperties(unlessString) + "' set.", Project.MSG_VERBOSE);
+ return;
+ }
+ LocalProperties localProperties = LocalProperties.get(getProject());
+ localProperties.enterScope();
+ try {
+ // use index-based approach to avoid ConcurrentModificationExceptions;
+ // also account for growing target children
+ // do not optimize this loop by replacing children.size() by a variable
+ // as children can be added dynamically as in RhinoScriptTest where a target is adding work for itself
+ for (int i = 0; i < children.size(); i++) {
+ Object o = children.get(i);
+ if (o instanceof Task) {
+ Task task = (Task) o;
+ task.perform();
+ } else {
+ ((RuntimeConfigurable) o).maybeConfigure(project);
+ }
+ }
+ } finally {
+ localProperties.exitScope();
+ }
+ }
+
+ /**
+ * Performs the tasks within this target (if the conditions are met),
+ * firing target started/target finished messages around a call to
+ * execute.
+ *
+ * @see #execute()
+ */
+ public final void performTasks() {
+ RuntimeException thrown = null;
+ project.fireTargetStarted(this);
+ try {
+ execute();
+ } catch (RuntimeException exc) {
+ thrown = exc;
+ throw exc;
+ } finally {
+ project.fireTargetFinished(this, thrown);
+ }
+ }
+
+ /**
+ * Replaces all occurrences of the given task in the list
+ * of children with the replacement data type wrapper.
+ *
+ * @param el The task to replace.
+ * Must not be <code>null</code>.
+ * @param o The data type wrapper to replace <code>el</code> with.
+ */
+ void replaceChild(Task el, RuntimeConfigurable o) {
+ int index;
+ while ((index = children.indexOf(el)) >= 0) {
+ children.set(index, o);
+ }
+ }
+
+ /**
+ * Replaces all occurrences of the given task in the list
+ * of children with the replacement task.
+ *
+ * @param el The task to replace.
+ * Must not be <code>null</code>.
+ * @param o The task to replace <code>el</code> with.
+ */
+ void replaceChild(Task el, Task o) {
+ int index;
+ while ((index = children.indexOf(el)) >= 0) {
+ children.set(index, o);
+ }
+ }
+
+ /**
+ * Condition evaluating the 'if' attribute with the PropertyHelper.
+ */
+ private class IfStringCondition implements Condition {
+
+ private String condition;
+
+ public IfStringCondition(String condition) {
+ this.condition = condition;
+ }
+
+ public boolean eval() throws BuildException {
+ PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper(getProject());
+ Object o = propertyHelper.parseProperties(condition);
+ return propertyHelper.testIfCondition(o);
+ }
+
+ }
+
+ /**
+ * Condition evaluating the 'unless' attribute with the PropertyHelper.
+ */
+ private class UnlessStringCondition implements Condition {
+
+ private String condition;
+
+ public UnlessStringCondition(String condition) {
+ this.condition = condition;
+ }
+
+ public boolean eval() throws BuildException {
+ PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper(getProject());
+ Object o = propertyHelper.parseProperties(condition);
+ return !propertyHelper.testUnlessCondition(o);
+ }
+
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Task.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Task.java
new file mode 100644
index 00000000..0d08eb0e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Task.java
@@ -0,0 +1,481 @@
+/*
+ * 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.tools.ant;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.tools.ant.dispatch.DispatchUtils;
+
+/**
+ * Base class for all tasks.
+ *
+ * Use Project.createTask to create a new task instance rather than
+ * using this class directly for construction.
+ *
+ * @see Project#createTask
+ */
+public abstract class Task extends ProjectComponent {
+ // CheckStyle:VisibilityModifier OFF - bc
+ /**
+ * Target this task belongs to, if any.
+ * @deprecated since 1.6.x.
+ * You should not be accessing this variable directly.
+ * Please use the {@link #getOwningTarget()} method.
+ */
+ protected Target target;
+
+ /**
+ * Name of this task to be used for logging purposes.
+ * This defaults to the same as the type, but may be
+ * overridden by the user. For instance, the name "java"
+ * isn't terribly descriptive for a task used within
+ * another task - the outer task code can probably
+ * provide a better one.
+ * @deprecated since 1.6.x.
+ * You should not be accessing this variable directly.
+ * Please use the {@link #getTaskName()} method.
+ */
+ protected String taskName;
+
+ /**
+ * Type of this task.
+ *
+ * @deprecated since 1.6.x.
+ * You should not be accessing this variable directly.
+ * Please use the {@link #getTaskType()} method.
+ */
+ protected String taskType;
+
+ /**
+ * Wrapper for this object, used to configure it at runtime.
+ *
+ * @deprecated since 1.6.x.
+ * You should not be accessing this variable directly.
+ * Please use the {@link #getWrapper()} method.
+ */
+ protected RuntimeConfigurable wrapper;
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Whether or not this task is invalid. A task becomes invalid
+ * if a conflicting class is specified as the implementation for
+ * its type.
+ */
+ private boolean invalid;
+
+ /** Sole constructor. */
+ public Task() {
+ }
+
+ /**
+ * Sets the target container of this task.
+ *
+ * @param target Target in whose scope this task belongs.
+ * May be <code>null</code>, indicating a top-level task.
+ */
+ public void setOwningTarget(Target target) {
+ this.target = target;
+ }
+
+ /**
+ * Returns the container target of this task.
+ *
+ * @return The target containing this task, or <code>null</code> if
+ * this task is a top-level task.
+ */
+ public Target getOwningTarget() {
+ return target;
+ }
+
+ /**
+ * Sets the name to use in logging messages.
+ *
+ * @param name The name to use in logging messages.
+ * Should not be <code>null</code>.
+ */
+ public void setTaskName(String name) {
+ this.taskName = name;
+ }
+
+ /**
+ * Returns the name to use in logging messages.
+ *
+ * @return the name to use in logging messages.
+ */
+ public String getTaskName() {
+ return taskName;
+ }
+
+ /**
+ * Sets the name with which the task has been invoked.
+ *
+ * @param type The name the task has been invoked as.
+ * Should not be <code>null</code>.
+ */
+ public void setTaskType(String type) {
+ this.taskType = type;
+ }
+
+ /**
+ * Called by the project to let the task initialize properly.
+ * The default implementation is a no-op.
+ *
+ * @exception BuildException if something goes wrong with the build
+ */
+ public void init() throws BuildException {
+ }
+
+ /**
+ * Called by the project to let the task do its work. This method may be
+ * called more than once, if the task is invoked more than once.
+ * For example,
+ * if target1 and target2 both depend on target3, then running
+ * "ant target1 target2" will run all tasks in target3 twice.
+ *
+ * @exception BuildException if something goes wrong with the build.
+ */
+ public void execute() throws BuildException {
+ }
+
+ /**
+ * Returns the wrapper used for runtime configuration.
+ *
+ * @return the wrapper used for runtime configuration. This
+ * method will generate a new wrapper (and cache it)
+ * if one isn't set already.
+ */
+ public RuntimeConfigurable getRuntimeConfigurableWrapper() {
+ if (wrapper == null) {
+ wrapper = new RuntimeConfigurable(this, getTaskName());
+ }
+ return wrapper;
+ }
+
+ /**
+ * Sets the wrapper to be used for runtime configuration.
+ *
+ * This method should be used only by the ProjectHelper and Ant internals.
+ * It is public to allow helper plugins to operate on tasks, normal tasks
+ * should never use it.
+ *
+ * @param wrapper The wrapper to be used for runtime configuration.
+ * May be <code>null</code>, in which case the next call
+ * to getRuntimeConfigurableWrapper will generate a new
+ * wrapper.
+ */
+ public void setRuntimeConfigurableWrapper(RuntimeConfigurable wrapper) {
+ this.wrapper = wrapper;
+ }
+
+ // TODO: (Jon Skeet) The comment "if it hasn't been done already" may
+ // not be strictly true. wrapper.maybeConfigure() won't configure the same
+ // attributes/text more than once, but it may well add the children again,
+ // unless I've missed something.
+ /**
+ * Configures this task - if it hasn't been done already.
+ * If the task has been invalidated, it is replaced with an
+ * UnknownElement task which uses the new definition in the project.
+ *
+ * @exception BuildException if the task cannot be configured.
+ */
+ public void maybeConfigure() throws BuildException {
+ if (!invalid) {
+ if (wrapper != null) {
+ wrapper.maybeConfigure(getProject());
+ }
+ } else {
+ getReplacement();
+ }
+ }
+
+ /**
+ * Force the task to be reconfigured from its RuntimeConfigurable.
+ */
+ public void reconfigure() {
+ if (wrapper != null) {
+ wrapper.reconfigure(getProject());
+ }
+ }
+
+ /**
+ * Handles output by logging it with the INFO priority.
+ *
+ * @param output The output to log. Should not be <code>null</code>.
+ */
+ protected void handleOutput(String output) {
+ log(output, Project.MSG_INFO);
+ }
+
+ /**
+ * Handles output by logging it with the INFO priority.
+ *
+ * @param output The output to log. Should not be <code>null</code>.
+ *
+ * @since Ant 1.5.2
+ */
+ protected void handleFlush(String output) {
+ handleOutput(output);
+ }
+
+ /**
+ * Handle an input request by this task.
+ *
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception IOException if the data cannot be read.
+ * @since Ant 1.6
+ */
+ protected int handleInput(byte[] buffer, int offset, int length)
+ throws IOException {
+ return getProject().defaultInput(buffer, offset, length);
+ }
+
+ /**
+ * Handles an error output by logging it with the WARN priority.
+ *
+ * @param output The error output to log. Should not be <code>null</code>.
+ */
+ protected void handleErrorOutput(String output) {
+ log(output, Project.MSG_WARN);
+ }
+
+ /**
+ * Handles an error line by logging it with the WARN priority.
+ *
+ * @param output The error output to log. Should not be <code>null</code>.
+ *
+ * @since Ant 1.5.2
+ */
+ protected void handleErrorFlush(String output) {
+ handleErrorOutput(output);
+ }
+
+ /**
+ * Logs a message with the default (INFO) priority.
+ *
+ * @param msg The message to be logged. Should not be <code>null</code>.
+ */
+ public void log(String msg) {
+ log(msg, Project.MSG_INFO);
+ }
+
+ /**
+ * Logs a message with the given priority. This delegates
+ * the actual logging to the project.
+ *
+ * @param msg The message to be logged. Should not be <code>null</code>.
+ * @param msgLevel The message priority at which this message is to
+ * be logged.
+ */
+ public void log(String msg, int msgLevel) {
+ if (getProject() != null) {
+ getProject().log(this, msg, msgLevel);
+ } else {
+ super.log(msg, msgLevel);
+ }
+ }
+
+ /**
+ * Logs a message with the given priority. This delegates
+ * the actual logging to the project.
+ *
+ * @param t The exception to be logged. Should not be <code>null</code>.
+ * @param msgLevel The message priority at which this message is to
+ * be logged.
+ * @since 1.7
+ */
+ public void log(Throwable t, int msgLevel) {
+ if (t != null) {
+ log(t.getMessage(), t, msgLevel);
+ }
+ }
+
+ /**
+ * Logs a message with the given priority. This delegates
+ * the actual logging to the project.
+ *
+ * @param msg The message to be logged. Should not be <code>null</code>.
+ * @param t The exception to be logged. May be <code>null</code>.
+ * @param msgLevel The message priority at which this message is to
+ * be logged.
+ * @since 1.7
+ */
+ public void log(String msg, Throwable t, int msgLevel) {
+ if (getProject() != null) {
+ getProject().log(this, msg, t, msgLevel);
+ } else {
+ super.log(msg, msgLevel);
+ }
+ }
+
+ /**
+ * Performs this task if it's still valid, or gets a replacement
+ * version and performs that otherwise.
+ *
+ * Performing a task consists of firing a task started event,
+ * configuring the task, executing it, and then firing task finished
+ * event. If a runtime exception is thrown, the task finished event
+ * is still fired, but with the exception as the cause.
+ */
+ public final void perform() {
+ if (!invalid) {
+ getProject().fireTaskStarted(this);
+ Throwable reason = null;
+ try {
+ maybeConfigure();
+ DispatchUtils.execute(this);
+ } catch (BuildException ex) {
+ if (ex.getLocation() == Location.UNKNOWN_LOCATION) {
+ ex.setLocation(getLocation());
+ }
+ reason = ex;
+ throw ex;
+ } catch (Exception ex) {
+ reason = ex;
+ BuildException be = new BuildException(ex);
+ be.setLocation(getLocation());
+ throw be;
+ } catch (Error ex) {
+ reason = ex;
+ throw ex;
+ } finally {
+ getProject().fireTaskFinished(this, reason);
+ }
+ } else {
+ UnknownElement ue = getReplacement();
+ Task task = ue.getTask();
+ task.perform();
+ }
+ }
+
+ /**
+ * Marks this task as invalid. Any further use of this task
+ * will go through a replacement with the updated definition.
+ */
+ final void markInvalid() {
+ invalid = true;
+ }
+
+ /**
+ * Has this task been marked invalid?
+ *
+ * @return true if this task is no longer valid. A new task should be
+ * configured in this case.
+ *
+ * @since Ant 1.5
+ */
+ protected final boolean isInvalid() {
+ return invalid;
+ }
+
+ /**
+ * Replacement element used if this task is invalidated.
+ */
+ private UnknownElement replacement;
+
+ /**
+ * Creates an UnknownElement that can be used to replace this task.
+ * Once this has been created once, it is cached and returned by
+ * future calls.
+ *
+ * @return the UnknownElement instance for the new definition of this task.
+ */
+ private UnknownElement getReplacement() {
+ if (replacement == null) {
+ replacement = new UnknownElement(taskType);
+ replacement.setProject(getProject());
+ replacement.setTaskType(taskType);
+ replacement.setTaskName(taskName);
+ replacement.setLocation(getLocation());
+ replacement.setOwningTarget(target);
+ replacement.setRuntimeConfigurableWrapper(wrapper);
+ wrapper.setProxy(replacement);
+ replaceChildren(wrapper, replacement);
+ target.replaceChild(this, replacement);
+ replacement.maybeConfigure();
+ }
+ return replacement;
+ }
+
+ /**
+ * Recursively adds an UnknownElement instance for each child
+ * element of replacement.
+ *
+ * @since Ant 1.5.1
+ */
+ private void replaceChildren(RuntimeConfigurable wrapper,
+ UnknownElement parentElement) {
+ Enumeration<RuntimeConfigurable> e = wrapper.getChildren();
+ while (e.hasMoreElements()) {
+ RuntimeConfigurable childWrapper = e.nextElement();
+ UnknownElement childElement =
+ new UnknownElement(childWrapper.getElementTag());
+ parentElement.addChild(childElement);
+ childElement.setProject(getProject());
+ childElement.setRuntimeConfigurableWrapper(childWrapper);
+ childWrapper.setProxy(childElement);
+ replaceChildren(childWrapper, childElement);
+ }
+ }
+
+ /**
+ * Return the type of task.
+ *
+ * @return the type of task.
+ */
+ public String getTaskType() {
+ return taskType;
+ }
+
+ /**
+ * Return the runtime configurable structure for this task.
+ *
+ * @return the runtime structure for this task.
+ */
+ protected RuntimeConfigurable getWrapper() {
+ return wrapper;
+ }
+
+ /**
+ * Bind a task to another; use this when configuring a newly created
+ * task to do work on behalf of another.
+ * Project, OwningTarget, TaskName, Location and Description are all copied
+ *
+ * Important: this method does not call {@link Task#init()}.
+ * If you are creating a task to delegate work to, call {@link Task#init()}
+ * to initialize it.
+ *
+ * @param owner owning target
+ * @since Ant1.7
+ */
+ public final void bindToOwner(Task owner) {
+ setProject(owner.getProject());
+ setOwningTarget(owner.getOwningTarget());
+ setTaskName(owner.getTaskName());
+ setDescription(owner.getDescription());
+ setLocation(owner.getLocation());
+ setTaskType(owner.getTaskType());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TaskAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TaskAdapter.java
new file mode 100644
index 00000000..3a3001f6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TaskAdapter.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.tools.ant;
+
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.dispatch.DispatchUtils;
+import org.apache.tools.ant.dispatch.Dispatchable;
+
+/**
+ * Uses introspection to "adapt" an arbitrary Bean which doesn't
+ * itself extend Task, but still contains an execute method and optionally
+ * a setProject method.
+ *
+ */
+public class TaskAdapter extends Task implements TypeAdapter {
+
+ /** Object to act as a proxy for. */
+ private Object proxy;
+
+ /**
+ * No-arg constructor for reflection.
+ */
+ public TaskAdapter() {
+ }
+
+ /**
+ * Constructor for given proxy.
+ * So you could write easier code
+ * <pre>
+ * myTaskContainer.addTask( new TaskAdapter(myProxy) );
+ * </pre>
+ *
+ * @param proxy The object which Ant should use as task.
+ */
+ public TaskAdapter(Object proxy) {
+ this();
+ setProxy(proxy);
+ }
+
+ /**
+ * Checks whether or not a class is suitable to be adapted by TaskAdapter.
+ * If the class is of type Dispatchable, the check is not performed because
+ * the method that will be executed will be determined only at runtime of
+ * the actual task and not during parse time.
+ *
+ * This only checks conditions which are additionally required for
+ * tasks adapted by TaskAdapter. Thus, this method should be called by
+ * Project.checkTaskClass.
+ *
+ * Throws a BuildException and logs as Project.MSG_ERR for
+ * conditions that will cause the task execution to fail.
+ * Logs other suspicious conditions with Project.MSG_WARN.
+ *
+ * @param taskClass Class to test for suitability.
+ * Must not be <code>null</code>.
+ * @param project Project to log warnings/errors to.
+ * Must not be <code>null</code>.
+ *
+ * @see Project#checkTaskClass(Class)
+ */
+ public static void checkTaskClass(final Class<?> taskClass,
+ final Project project) {
+ if (!Dispatchable.class.isAssignableFrom(taskClass)) {
+ // don't have to check for interface, since then
+ // taskClass would be abstract too.
+ try {
+ final Method executeM = taskClass.getMethod("execute", (Class[]) null);
+ // don't have to check for public, since
+ // getMethod finds public method only.
+ // don't have to check for abstract, since then
+ // taskClass would be abstract too.
+ if (!Void.TYPE.equals(executeM.getReturnType())) {
+ final String message = "return type of execute() should be "
+ + "void but was \"" + executeM.getReturnType() + "\" in "
+ + taskClass;
+ project.log(message, Project.MSG_WARN);
+ }
+ } catch (NoSuchMethodException e) {
+ final String message = "No public execute() in " + taskClass;
+ project.log(message, Project.MSG_ERR);
+ throw new BuildException(message);
+ } catch (LinkageError e) {
+ String message = "Could not load " + taskClass + ": " + e;
+ project.log(message, Project.MSG_ERR);
+ throw new BuildException(message, e);
+ }
+ }
+ }
+
+ /**
+ * Check if the proxy class is a valid class to use
+ * with this adapter.
+ * The class must have a public no-arg "execute()" method.
+ * @param proxyClass the class to check.
+ */
+ public void checkProxyClass(Class<?> proxyClass) {
+ checkTaskClass(proxyClass, getProject());
+ }
+
+ /**
+ * Executes the proxied task.
+ *
+ * @exception BuildException if the project could not be set
+ * or the method could not be executed.
+ */
+ public void execute() throws BuildException {
+ try {
+ Method setLocationM = proxy.getClass().getMethod(
+ "setLocation", new Class[] {Location.class});
+ if (setLocationM != null) {
+ setLocationM.invoke(proxy, new Object[] {getLocation()});
+ }
+ } catch (NoSuchMethodException e) {
+ // ignore this if the class being used as a task does not have
+ // a set location method.
+ } catch (Exception ex) {
+ log("Error setting location in " + proxy.getClass(),
+ Project.MSG_ERR);
+ throw new BuildException(ex);
+ }
+
+ try {
+ Method setProjectM = proxy.getClass().getMethod(
+ "setProject", new Class[] {Project.class});
+ if (setProjectM != null) {
+ setProjectM.invoke(proxy, new Object[] {getProject()});
+ }
+ } catch (NoSuchMethodException e) {
+ // ignore this if the class being used as a task does not have
+ // a set project method.
+ } catch (Exception ex) {
+ log("Error setting project in " + proxy.getClass(),
+ Project.MSG_ERR);
+ throw new BuildException(ex);
+ }
+
+ try {
+ DispatchUtils.execute(proxy);
+ } catch (BuildException be) {
+ throw be;
+ } catch (Exception ex) {
+ log("Error in " + proxy.getClass(), Project.MSG_VERBOSE);
+ throw new BuildException(ex);
+ }
+ }
+
+ /**
+ * Sets the target object to proxy for.
+ *
+ * @param o The target object. Must not be <code>null</code>.
+ */
+ public void setProxy(Object o) {
+ this.proxy = o;
+ }
+
+ /**
+ * Returns the target object being proxied.
+ *
+ * @return the target proxy object.
+ */
+ public Object getProxy() {
+ return proxy;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TaskConfigurationChecker.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TaskConfigurationChecker.java
new file mode 100644
index 00000000..e8e74441
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TaskConfigurationChecker.java
@@ -0,0 +1,111 @@
+/*
+ * 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.tools.ant;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>Helper class for the check of the configuration of a given task.
+ * This class provides methods for making assumptions about the task configuration.
+ * After collecting all violations with <tt>assert*</tt> and <tt>fail</tt>
+ * methods the <tt>checkErrors</tt> will throw a BuildException with all collected
+ * messages or does nothing if there wasn't any error.</p>
+ *
+ * <p>Example:</p>
+ *
+ * <pre>
+ * public class MyTask extends Task {
+ * ...
+ * public void execute() {
+ * TaskConfigurationChecker checker = TaskConfigurationChecker(this);
+ * checker.assertConfig(
+ * srcdir != null,
+ * "Attribute 'srcdir' must be set.
+ * );
+ * checker.assertConfig(
+ * srcdir.exists(),
+ * "Srcdir (" + srcdir + ") must exist."
+ * );
+ * if (someComplexCondition()) {
+ * fail("Complex condition failed.");
+ * }
+ * checker.checkErrors();
+ * }
+ * }
+ * </pre>
+ *
+ * @see <a href="http://martinfowler.com/eaaDev/Notification.html">Notification Pattern</a>
+ */
+public class TaskConfigurationChecker {
+
+ /** List of all collected error messages. */
+ private List<String> errors = new ArrayList<String>();
+
+ /** Task for which the configuration should be checked. */
+ private final Task task;
+
+ /**
+ * Constructor.
+ * @param task which task should be checked
+ */
+ public TaskConfigurationChecker(Task task) {
+ this.task = task;
+ }
+
+ /**
+ * Asserts that a condition is true.
+ * @param condition which condition to check
+ * @param errormessage errormessage to throw if a condition failed
+ */
+ public void assertConfig(boolean condition, String errormessage) {
+ if (!condition) {
+ errors.add(errormessage);
+ }
+ }
+
+ /**
+ * Registers an error.
+ * @param errormessage the message for the registered error
+ */
+ public void fail(String errormessage) {
+ errors.add(errormessage);
+ }
+
+ /**
+ * Checks if there are any collected errors and throws a BuildException
+ * with all messages if there was one or more.
+ * @throws BuildException if one or more errors were registered
+ */
+ public void checkErrors() throws BuildException {
+ if (!errors.isEmpty()) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Configurationerror on <");
+ sb.append(task.getTaskName());
+ sb.append(">:");
+ sb.append(System.getProperty("line.separator"));
+ for (String msg : errors) {
+ sb.append("- ");
+ sb.append(msg);
+ sb.append(System.getProperty("line.separator"));
+ }
+ throw new BuildException(sb.toString(), task.getLocation());
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TaskContainer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TaskContainer.java
new file mode 100644
index 00000000..12e8c075
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TaskContainer.java
@@ -0,0 +1,41 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Interface for objects which can contain tasks.
+ * <p>
+ * It is recommended that implementations call perform rather than
+ * execute for the tasks they contain, as this method ensures that the
+ * appropriate BuildEvents will be generated.
+ *
+ * @see Task#perform
+ * @see Task#execute
+ * @see BuildEvent
+ *
+ */
+public interface TaskContainer {
+ /**
+ * Adds a task to this task container
+ *
+ * @param task The task to be added to this container.
+ * Must not be <code>null</code>.
+ */
+ void addTask(Task task);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TypeAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TypeAdapter.java
new file mode 100644
index 00000000..b8520fce
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/TypeAdapter.java
@@ -0,0 +1,66 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Used to wrap types.
+ *
+ */
+public interface TypeAdapter {
+
+ /**
+ * Sets the project
+ *
+ * @param p the project instance.
+ */
+ void setProject(Project p);
+
+ /**
+ * Gets the project
+ *
+ * @return the project instance.
+ */
+ Project getProject();
+
+ /**
+ * Sets the proxy object, whose methods are going to be
+ * invoked by ant.
+ * A proxy object is normally the object defined by
+ * a &lt;typedef/&gt; task that is adapted by the "adapter"
+ * attribute.
+ *
+ * @param o The target object. Must not be <code>null</code>.
+ */
+ void setProxy(Object o);
+
+ /**
+ * Returns the proxy object.
+ *
+ * @return the target proxy object
+ */
+ Object getProxy();
+
+ /**
+ * Check if the proxy class is compatible with this adapter - i.e.
+ * the adapter will be able to adapt instances of the give class.
+ *
+ * @param proxyClass the class to be checked.
+ */
+ void checkProxyClass(Class<?> proxyClass);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/UnknownElement.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/UnknownElement.java
new file mode 100644
index 00000000..88cd4989
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/UnknownElement.java
@@ -0,0 +1,699 @@
+/*
+ * 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.tools.ant;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tools.ant.taskdefs.PreSetDef;
+
+/**
+ * Wrapper class that holds all the information necessary to create a task
+ * or data type that did not exist when Ant started, or one which
+ * has had its definition updated to use a different implementation class.
+ *
+ */
+public class UnknownElement extends Task {
+
+ /**
+ * Holds the name of the task/type or nested child element of a
+ * task/type that hasn't been defined at parser time or has
+ * been redefined since original creation.
+ */
+ private final String elementName;
+
+ /**
+ * Holds the namespace of the element.
+ */
+ private String namespace = "";
+
+ /**
+ * Holds the namespace qname of the element.
+ */
+ private String qname;
+
+ /**
+ * The real object after it has been loaded.
+ */
+ private Object realThing;
+
+ /**
+ * List of child elements (UnknownElements).
+ */
+ private List<UnknownElement> children = null;
+
+ /** Specifies if a predefined definition has been done */
+ private boolean presetDefed = false;
+
+ /**
+ * Creates an UnknownElement for the given element name.
+ *
+ * @param elementName The name of the unknown element.
+ * Must not be <code>null</code>.
+ */
+ public UnknownElement(String elementName) {
+ this.elementName = elementName;
+ }
+
+ /**
+ * @return the list of nested UnknownElements for this UnknownElement.
+ */
+ public List<UnknownElement> getChildren() {
+ return children;
+ }
+
+ /**
+ * Returns the name of the XML element which generated this unknown
+ * element.
+ *
+ * @return the name of the XML element which generated this unknown
+ * element.
+ */
+ public String getTag() {
+ return elementName;
+ }
+
+ /**
+ * Return the namespace of the XML element associated with this component.
+ *
+ * @return Namespace URI used in the xmlns declaration.
+ */
+ public String getNamespace() {
+ return namespace;
+ }
+
+ /**
+ * Set the namespace of the XML element associated with this component.
+ * This method is typically called by the XML processor.
+ * If the namespace is "ant:current", the component helper
+ * is used to get the current antlib uri.
+ *
+ * @param namespace URI used in the xmlns declaration.
+ */
+ public void setNamespace(String namespace) {
+ if (namespace.equals(ProjectHelper.ANT_CURRENT_URI)) {
+ ComponentHelper helper = ComponentHelper.getComponentHelper(
+ getProject());
+ namespace = helper.getCurrentAntlibUri();
+ }
+ this.namespace = namespace == null ? "" : namespace;
+ }
+
+ /**
+ * Return the qname of the XML element associated with this component.
+ *
+ * @return namespace Qname used in the element declaration.
+ */
+ public String getQName() {
+ return qname;
+ }
+
+ /**
+ * Set the namespace qname of the XML element.
+ * This method is typically called by the XML processor.
+ *
+ * @param qname the qualified name of the element
+ */
+ public void setQName(String qname) {
+ this.qname = qname;
+ }
+
+
+ /**
+ * Get the RuntimeConfigurable instance for this UnknownElement, containing
+ * the configuration information.
+ *
+ * @return the configuration info.
+ */
+ public RuntimeConfigurable getWrapper() {
+ return super.getWrapper();
+ }
+
+ /**
+ * Creates the real object instance and child elements, then configures
+ * the attributes and text of the real object. This unknown element
+ * is then replaced with the real object in the containing target's list
+ * of children.
+ *
+ * @exception BuildException if the configuration fails
+ */
+ public void maybeConfigure() throws BuildException {
+ if (realThing != null) {
+ return;
+ }
+ configure(makeObject(this, getWrapper()));
+ }
+
+ /**
+ * Configure the given object from this UnknownElement
+ *
+ * @param realObject the real object this UnknownElement is representing.
+ *
+ */
+ public void configure(Object realObject) {
+ if (realObject == null) {
+ return;
+ }
+ realThing = realObject;
+
+ getWrapper().setProxy(realThing);
+ Task task = null;
+ if (realThing instanceof Task) {
+ task = (Task) realThing;
+
+ task.setRuntimeConfigurableWrapper(getWrapper());
+
+ // For Script example that modifies id'ed tasks in other
+ // targets to work. *very* Ugly
+ // The reference is replaced by RuntimeConfigurable
+ if (getWrapper().getId() != null) {
+ this.getOwningTarget().replaceChild(this, (Task) realThing);
+ }
+ }
+
+
+ // configure attributes of the object and it's children. If it is
+ // a task container, defer the configuration till the task container
+ // attempts to use the task
+
+ if (task != null) {
+ task.maybeConfigure();
+ } else {
+ getWrapper().maybeConfigure(getProject());
+ }
+
+ handleChildren(realThing, getWrapper());
+ }
+
+ /**
+ * Handles output sent to System.out by this task or its real task.
+ *
+ * @param output The output to log. Should not be <code>null</code>.
+ */
+ protected void handleOutput(String output) {
+ if (realThing instanceof Task) {
+ ((Task) realThing).handleOutput(output);
+ } else {
+ super.handleOutput(output);
+ }
+ }
+
+ /**
+ * Delegate to realThing if present and if it as task.
+ * @see Task#handleInput(byte[], int, int)
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception IOException if the data cannot be read.
+ * @since Ant 1.6
+ */
+ protected int handleInput(byte[] buffer, int offset, int length)
+ throws IOException {
+ if (realThing instanceof Task) {
+ return ((Task) realThing).handleInput(buffer, offset, length);
+ } else {
+ return super.handleInput(buffer, offset, length);
+ }
+
+ }
+
+ /**
+ * Handles output sent to System.out by this task or its real task.
+ *
+ * @param output The output to log. Should not be <code>null</code>.
+ */
+ protected void handleFlush(String output) {
+ if (realThing instanceof Task) {
+ ((Task) realThing).handleFlush(output);
+ } else {
+ super.handleFlush(output);
+ }
+ }
+
+ /**
+ * Handles error output sent to System.err by this task or its real task.
+ *
+ * @param output The error output to log. Should not be <code>null</code>.
+ */
+ protected void handleErrorOutput(String output) {
+ if (realThing instanceof Task) {
+ ((Task) realThing).handleErrorOutput(output);
+ } else {
+ super.handleErrorOutput(output);
+ }
+ }
+
+ /**
+ * Handles error output sent to System.err by this task or its real task.
+ *
+ * @param output The error output to log. Should not be <code>null</code>.
+ */
+ protected void handleErrorFlush(String output) {
+ if (realThing instanceof Task) {
+ ((Task) realThing).handleErrorFlush(output);
+ } else {
+ super.handleErrorFlush(output);
+ }
+ }
+
+ /**
+ * Executes the real object if it's a task. If it's not a task
+ * (e.g. a data type) then this method does nothing.
+ */
+ public void execute() {
+ if (realThing == null) {
+ // Got here if the runtimeconfigurable is not enabled.
+ return;
+ }
+ try {
+ if (realThing instanceof Task) {
+ ((Task) realThing).execute();
+ }
+ } finally {
+ // Finished executing the task
+ // null it (unless it has an ID) to allow
+ // GC do its job
+ // If this UE is used again, a new "realthing" will be made
+ if (getWrapper().getId() == null) {
+ realThing = null;
+ getWrapper().setProxy(null);
+ }
+ }
+ }
+
+ /**
+ * Adds a child element to this element.
+ *
+ * @param child The child element to add. Must not be <code>null</code>.
+ */
+ public void addChild(UnknownElement child) {
+ if (children == null) {
+ children = new ArrayList<UnknownElement>();
+ }
+ children.add(child);
+ }
+
+ /**
+ * Creates child elements, creates children of the children
+ * (recursively), and sets attributes of the child elements.
+ *
+ * @param parent The configured object for the parent.
+ * Must not be <code>null</code>.
+ *
+ * @param parentWrapper The wrapper containing child wrappers
+ * to be configured. Must not be <code>null</code>
+ * if there are any children.
+ *
+ * @exception BuildException if the children cannot be configured.
+ */
+ protected void handleChildren(
+ Object parent,
+ RuntimeConfigurable parentWrapper)
+ throws BuildException {
+ if (parent instanceof TypeAdapter) {
+ parent = ((TypeAdapter) parent).getProxy();
+ }
+
+ String parentUri = getNamespace();
+ Class<?> parentClass = parent.getClass();
+ IntrospectionHelper ih = IntrospectionHelper.getHelper(getProject(), parentClass);
+
+
+ if (children != null) {
+ Iterator<UnknownElement> it = children.iterator();
+ for (int i = 0; it.hasNext(); i++) {
+ RuntimeConfigurable childWrapper = parentWrapper.getChild(i);
+ UnknownElement child = it.next();
+ try {
+ if (!childWrapper.isEnabled(child)) {
+ if (ih.supportsNestedElement(
+ parentUri, ProjectHelper.genComponentName(
+ child.getNamespace(), child.getTag()))) {
+ continue;
+ }
+ // fall tru and fail in handlechild (unsupported element)
+ }
+ if (!handleChild(
+ parentUri, ih, parent, child, childWrapper)) {
+ if (!(parent instanceof TaskContainer)) {
+ ih.throwNotSupported(getProject(), parent,
+ child.getTag());
+ } else {
+ // a task container - anything could happen - just add the
+ // child to the container
+ TaskContainer container = (TaskContainer) parent;
+ container.addTask(child);
+ }
+ }
+ } catch (UnsupportedElementException ex) {
+ throw new BuildException(
+ parentWrapper.getElementTag()
+ + " doesn't support the nested \"" + ex.getElement()
+ + "\" element.", ex);
+ }
+ }
+ }
+ }
+
+ /**
+ * @return the component name - uses ProjectHelper#genComponentName()
+ */
+ protected String getComponentName() {
+ return ProjectHelper.genComponentName(getNamespace(), getTag());
+ }
+
+ /**
+ * This is used then the realobject of the UE is a PreSetDefinition.
+ * This is also used when a presetdef is used on a presetdef
+ * The attributes, elements and text are applied to this
+ * UE.
+ *
+ * @param u an UnknownElement containing the attributes, elements and text
+ */
+ public void applyPreSet(UnknownElement u) {
+ if (presetDefed) {
+ return;
+ }
+ // Do the runtime
+ getWrapper().applyPreSet(u.getWrapper());
+ if (u.children != null) {
+ List<UnknownElement> newChildren = new ArrayList<UnknownElement>();
+ newChildren.addAll(u.children);
+ if (children != null) {
+ newChildren.addAll(children);
+ }
+ children = newChildren;
+ }
+ presetDefed = true;
+ }
+
+ /**
+ * Creates a named task or data type. If the real object is a task,
+ * it is configured up to the init() stage.
+ *
+ * @param ue The unknown element to create the real object for.
+ * Must not be <code>null</code>.
+ * @param w Ignored in this implementation.
+ *
+ * @return the task or data type represented by the given unknown element.
+ */
+ protected Object makeObject(UnknownElement ue, RuntimeConfigurable w) {
+ if (!w.isEnabled(ue)) {
+ return null;
+ }
+ ComponentHelper helper = ComponentHelper.getComponentHelper(
+ getProject());
+ String name = ue.getComponentName();
+ Object o = helper.createComponent(ue, ue.getNamespace(), name);
+ if (o == null) {
+ throw getNotFoundException("task or type", name);
+ }
+ if (o instanceof PreSetDef.PreSetDefinition) {
+ PreSetDef.PreSetDefinition def = (PreSetDef.PreSetDefinition) o;
+ o = def.createObject(ue.getProject());
+ if (o == null) {
+ throw getNotFoundException(
+ "preset " + name,
+ def.getPreSets().getComponentName());
+ }
+ ue.applyPreSet(def.getPreSets());
+ if (o instanceof Task) {
+ Task task = (Task) o;
+ task.setTaskType(ue.getTaskType());
+ task.setTaskName(ue.getTaskName());
+ task.init();
+ }
+ }
+ if (o instanceof UnknownElement) {
+ o = ((UnknownElement) o).makeObject((UnknownElement) o, w);
+ }
+ if (o instanceof Task) {
+ ((Task) o).setOwningTarget(getOwningTarget());
+ }
+ if (o instanceof ProjectComponent) {
+ ((ProjectComponent) o).setLocation(getLocation());
+ }
+ return o;
+ }
+
+ /**
+ * Creates a named task and configures it up to the init() stage.
+ *
+ * @param ue The UnknownElement to create the real task for.
+ * Must not be <code>null</code>.
+ * @param w Ignored.
+ *
+ * @return the task specified by the given unknown element, or
+ * <code>null</code> if the task name is not recognised.
+ */
+ protected Task makeTask(UnknownElement ue, RuntimeConfigurable w) {
+ Task task = getProject().createTask(ue.getTag());
+
+ if (task != null) {
+ task.setLocation(getLocation());
+ // UnknownElement always has an associated target
+ task.setOwningTarget(getOwningTarget());
+ task.init();
+ }
+ return task;
+ }
+
+ /**
+ * Returns a very verbose exception for when a task/data type cannot
+ * be found.
+ *
+ * @param what The kind of thing being created. For example, when
+ * a task name could not be found, this would be
+ * <code>"task"</code>. Should not be <code>null</code>.
+ * @param name The name of the element which could not be found.
+ * Should not be <code>null</code>.
+ *
+ * @return a detailed description of what might have caused the problem.
+ */
+ protected BuildException getNotFoundException(String what,
+ String name) {
+ ComponentHelper helper = ComponentHelper.getComponentHelper(getProject());
+ String msg = helper.diagnoseCreationFailure(name, what);
+ return new BuildException(msg, getLocation());
+ }
+
+ /**
+ * Returns the name to use in logging messages.
+ *
+ * @return the name to use in logging messages.
+ */
+ public String getTaskName() {
+ //return elementName;
+ return realThing == null
+ || !(realThing instanceof Task) ? super.getTaskName()
+ : ((Task) realThing).getTaskName();
+ }
+
+ /**
+ * Returns the task instance after it has been created and if it is a task.
+ *
+ * @return a task instance or <code>null</code> if the real object is not
+ * a task.
+ */
+ public Task getTask() {
+ if (realThing instanceof Task) {
+ return (Task) realThing;
+ }
+ return null;
+ }
+
+ /**
+ * Return the configured object
+ *
+ * @return the real thing whatever it is
+ *
+ * @since ant 1.6
+ */
+ public Object getRealThing() {
+ return realThing;
+ }
+
+ /**
+ * Set the configured object
+ * @param realThing the configured object
+ * @since ant 1.7
+ */
+ public void setRealThing(Object realThing) {
+ this.realThing = realThing;
+ }
+
+ /**
+ * Try to create a nested element of <code>parent</code> for the
+ * given tag.
+ *
+ * @return whether the creation has been successful
+ */
+ private boolean handleChild(
+ String parentUri,
+ IntrospectionHelper ih,
+ Object parent, UnknownElement child,
+ RuntimeConfigurable childWrapper) {
+ String childName = ProjectHelper.genComponentName(
+ child.getNamespace(), child.getTag());
+ if (ih.supportsNestedElement(parentUri, childName, getProject(),
+ parent)) {
+ IntrospectionHelper.Creator creator = null;
+ try {
+ creator = ih.getElementCreator(getProject(), parentUri,
+ parent, childName, child);
+ } catch (UnsupportedElementException use) {
+ if (!ih.isDynamic()) {
+ throw use;
+ }
+ // can't trust supportsNestedElement for dynamic elements
+ return false;
+ }
+ creator.setPolyType(childWrapper.getPolyType());
+ Object realChild = creator.create();
+ if (realChild instanceof PreSetDef.PreSetDefinition) {
+ PreSetDef.PreSetDefinition def =
+ (PreSetDef.PreSetDefinition) realChild;
+ realChild = creator.getRealObject();
+ child.applyPreSet(def.getPreSets());
+ }
+ childWrapper.setCreator(creator);
+ childWrapper.setProxy(realChild);
+ if (realChild instanceof Task) {
+ Task childTask = (Task) realChild;
+ childTask.setRuntimeConfigurableWrapper(childWrapper);
+ childTask.setTaskName(childName);
+ childTask.setTaskType(childName);
+ }
+ if (realChild instanceof ProjectComponent) {
+ ((ProjectComponent) realChild).setLocation(child.getLocation());
+ }
+ childWrapper.maybeConfigure(getProject());
+ child.handleChildren(realChild, childWrapper);
+ creator.store();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * like contents equals, but ignores project
+ * @param obj the object to check against
+ * @return true if this unknownelement has the same contents the other
+ */
+ public boolean similar(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (!getClass().getName().equals(obj.getClass().getName())) {
+ return false;
+ }
+ UnknownElement other = (UnknownElement) obj;
+ // Are the names the same ?
+ if (!equalsString(elementName, other.elementName)) {
+ return false;
+ }
+ if (!namespace.equals(other.namespace)) {
+ return false;
+ }
+ if (!qname.equals(other.qname)) {
+ return false;
+ }
+ // Are attributes the same ?
+ if (!getWrapper().getAttributeMap().equals(
+ other.getWrapper().getAttributeMap())) {
+ return false;
+ }
+ // Is the text the same?
+ // Need to use equals on the string and not
+ // on the stringbuffer as equals on the string buffer
+ // does not compare the contents.
+ if (!getWrapper().getText().toString().equals(
+ other.getWrapper().getText().toString())) {
+ return false;
+ }
+ // Are the sub elements the same ?
+ final int childrenSize = children == null ? 0 : children.size();
+ if (childrenSize == 0) {
+ return other.children == null || other.children.size() == 0;
+ }
+ if (other.children == null) {
+ return false;
+ }
+ if (childrenSize != other.children.size()) {
+ return false;
+ }
+ for (int i = 0; i < childrenSize; ++i) {
+ UnknownElement child = (UnknownElement) children.get(i);
+ if (!child.similar(other.children.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean equalsString(String a, String b) {
+ return (a == null) ? (b == null) : a.equals(b);
+ }
+
+ /**
+ * Make a copy of the unknown element and set it in the new project.
+ * @param newProject the project to create the UE in.
+ * @return the copied UE.
+ */
+ public UnknownElement copy(Project newProject) {
+ UnknownElement ret = new UnknownElement(getTag());
+ ret.setNamespace(getNamespace());
+ ret.setProject(newProject);
+ ret.setQName(getQName());
+ ret.setTaskType(getTaskType());
+ ret.setTaskName(getTaskName());
+ ret.setLocation(getLocation());
+ if (getOwningTarget() == null) {
+ Target t = new Target();
+ t.setProject(getProject());
+ ret.setOwningTarget(t);
+ } else {
+ ret.setOwningTarget(getOwningTarget());
+ }
+ RuntimeConfigurable copyRC = new RuntimeConfigurable(
+ ret, getTaskName());
+ copyRC.setPolyType(getWrapper().getPolyType());
+ Map<String, Object> m = getWrapper().getAttributeMap();
+ for (Map.Entry<String, Object> entry : m.entrySet()) {
+ copyRC.setAttribute(entry.getKey(), (String) entry.getValue());
+ }
+ copyRC.addText(getWrapper().getText().toString());
+
+ for (Enumeration<RuntimeConfigurable> e = getWrapper().getChildren(); e.hasMoreElements();) {
+ RuntimeConfigurable r = e.nextElement();
+ UnknownElement ueChild = (UnknownElement) r.getProxy();
+ UnknownElement copyChild = ueChild.copy(newProject);
+ copyRC.addChild(copyChild.getWrapper());
+ ret.addChild(copyChild);
+ }
+ return ret;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/UnsupportedAttributeException.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/UnsupportedAttributeException.java
new file mode 100644
index 00000000..1fd9ce51
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/UnsupportedAttributeException.java
@@ -0,0 +1,49 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Used to report attempts to set an unsupported attribute
+ *
+ * @since Ant 1.6.3
+ */
+public class UnsupportedAttributeException extends BuildException {
+ private static final long serialVersionUID = 1L;
+
+ private final String attribute;
+
+ /**
+ * Constructs an unsupported attribute exception.
+ * @param msg The string containing the message.
+ * @param attribute The unsupported attribute.
+ */
+ public UnsupportedAttributeException(String msg, String attribute) {
+ super(msg);
+ this.attribute = attribute;
+ }
+
+ /**
+ * Get the attribute that is wrong.
+ *
+ * @return the attribute name.
+ */
+ public String getAttribute() {
+ return attribute;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/UnsupportedElementException.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/UnsupportedElementException.java
new file mode 100644
index 00000000..2a38120c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/UnsupportedElementException.java
@@ -0,0 +1,57 @@
+/*
+ * 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.tools.ant;
+
+/**
+ * Used to report attempts to set an unsupported element
+ * When the attempt to set the element is made,
+ * the code does not not know the name of the task/type
+ * based on a mapping from the classname to the task/type.
+ * However one class may be used by a lot of task/types.
+ * This exception may be caught by code that does know
+ * the task/type and it will reset the message to the
+ * correct message.
+ * This will be done once (in the case of a recursive
+ * call to handlechildren).
+ *
+ * @since Ant 1.6.3
+ */
+public class UnsupportedElementException extends BuildException {
+ private static final long serialVersionUID = 1L;
+
+ private final String element;
+
+ /**
+ * Constructs an unsupported element exception.
+ * @param msg The string containing the message.
+ * @param element The name of the incorrect element.
+ */
+ public UnsupportedElementException(String msg, String element) {
+ super(msg);
+ this.element = element;
+ }
+
+ /**
+ * Get the element that is wrong.
+ *
+ * @return the element name.
+ */
+ public String getElement() {
+ return element;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/XmlLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/XmlLogger.java
new file mode 100644
index 00000000..a67a260e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/XmlLogger.java
@@ -0,0 +1,474 @@
+/*
+ * 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.tools.ant;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Stack;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+
+/**
+ * Generates a file in the current directory with
+ * an XML description of what happened during a build.
+ * The default filename is "log.xml", but this can be overridden
+ * with the property <code>XmlLogger.file</code>.
+ *
+ * This implementation assumes in its sanity checking that only one
+ * thread runs a particular target/task at a time. This is enforced
+ * by the way that parallel builds and antcalls are done - and
+ * indeed all but the simplest of tasks could run into problems
+ * if executed in parallel.
+ *
+ * @see Project#addBuildListener(BuildListener)
+ */
+public class XmlLogger implements BuildLogger {
+
+ private int msgOutputLevel = Project.MSG_DEBUG;
+ private PrintStream outStream;
+
+ /** DocumentBuilder to use when creating the document to start with. */
+ private static DocumentBuilder builder = getDocumentBuilder();
+
+ /**
+ * Returns a default DocumentBuilder instance or throws an
+ * ExceptionInInitializerError if it can't be created.
+ *
+ * @return a default DocumentBuilder instance.
+ */
+ private static DocumentBuilder getDocumentBuilder() {
+ try {
+ return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ } catch (Exception exc) {
+ throw new ExceptionInInitializerError(exc);
+ }
+ }
+
+ /** XML element name for a build. */
+ private static final String BUILD_TAG = "build";
+
+ /** XML element name for a target. */
+ private static final String TARGET_TAG = "target";
+
+ /** XML element name for a task. */
+ private static final String TASK_TAG = "task";
+
+ /** XML element name for a message. */
+ private static final String MESSAGE_TAG = "message";
+
+ /** XML attribute name for a name. */
+ private static final String NAME_ATTR = "name";
+
+ /** XML attribute name for a time. */
+ private static final String TIME_ATTR = "time";
+
+ /** XML attribute name for a message priority. */
+ private static final String PRIORITY_ATTR = "priority";
+
+ /** XML attribute name for a file location. */
+ private static final String LOCATION_ATTR = "location";
+
+ /** XML attribute name for an error description. */
+ private static final String ERROR_ATTR = "error";
+
+ /** XML element name for a stack trace. */
+ private static final String STACKTRACE_TAG = "stacktrace";
+
+ /** The complete log document for this build. */
+ private Document doc = builder.newDocument();
+
+ /** Mapping for when tasks started (Task to TimedElement). */
+ private Hashtable<Task, TimedElement> tasks = new Hashtable<Task, TimedElement>();
+
+ /** Mapping for when targets started (Target to TimedElement). */
+ private Hashtable<Target, TimedElement> targets = new Hashtable<Target, XmlLogger.TimedElement>();
+
+ /**
+ * Mapping of threads to stacks of elements
+ * (Thread to Stack of TimedElement).
+ */
+ private Hashtable<Thread, Stack<TimedElement>> threadStacks = new Hashtable<Thread, Stack<TimedElement>>();
+
+ /**
+ * When the build started.
+ */
+ private TimedElement buildElement = null;
+
+ /** Utility class representing the time an element started. */
+ private static class TimedElement {
+ /**
+ * Start time in milliseconds
+ * (as returned by <code>System.currentTimeMillis()</code>).
+ */
+ private long startTime;
+ /** Element created at the start time. */
+ private Element element;
+ public String toString() {
+ return element.getTagName() + ":" + element.getAttribute("name");
+ }
+ }
+
+ /**
+ * Constructs a new BuildListener that logs build events to an XML file.
+ */
+ public XmlLogger() {
+ }
+
+ /**
+ * Fired when the build starts, this builds the top-level element for the
+ * document and remembers the time of the start of the build.
+ *
+ * @param event Ignored.
+ */
+ public void buildStarted(BuildEvent event) {
+ buildElement = new TimedElement();
+ buildElement.startTime = System.currentTimeMillis();
+ buildElement.element = doc.createElement(BUILD_TAG);
+ }
+
+ /**
+ * Fired when the build finishes, this adds the time taken and any
+ * error stacktrace to the build element and writes the document to disk.
+ *
+ * @param event An event with any relevant extra information.
+ * Will not be <code>null</code>.
+ */
+ public void buildFinished(BuildEvent event) {
+ long totalTime = System.currentTimeMillis() - buildElement.startTime;
+ buildElement.element.setAttribute(TIME_ATTR, DefaultLogger.formatTime(totalTime));
+
+ if (event.getException() != null) {
+ buildElement.element.setAttribute(ERROR_ATTR, event.getException().toString());
+ // print the stacktrace in the build file it is always useful...
+ // better have too much info than not enough.
+ Throwable t = event.getException();
+ Text errText = doc.createCDATASection(StringUtils.getStackTrace(t));
+ Element stacktrace = doc.createElement(STACKTRACE_TAG);
+ stacktrace.appendChild(errText);
+ synchronizedAppend(buildElement.element, stacktrace);
+ }
+ String outFilename = getProperty(event, "XmlLogger.file", "log.xml");
+ String xslUri = getProperty(event, "ant.XmlLogger.stylesheet.uri", "log.xsl");
+ Writer out = null;
+ try {
+ // specify output in UTF8 otherwise accented characters will blow
+ // up everything
+ OutputStream stream = outStream;
+ if (stream == null) {
+ stream = new FileOutputStream(outFilename);
+ }
+ out = new OutputStreamWriter(stream, "UTF8");
+ out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ if (xslUri.length() > 0) {
+ out.write("<?xml-stylesheet type=\"text/xsl\" href=\"" + xslUri + "\"?>\n\n");
+ }
+ new DOMElementWriter().write(buildElement.element, out, 0, "\t");
+ out.flush();
+ } catch (IOException exc) {
+ throw new BuildException("Unable to write log file", exc);
+ } finally {
+ FileUtils.close(out);
+ }
+ buildElement = null;
+ }
+
+ private String getProperty(BuildEvent event, String propertyName, String defaultValue) {
+ String rv = defaultValue;
+ if (event != null && event.getProject() != null && event.getProject().getProperty(propertyName) != null) {
+ rv = event.getProject().getProperty(propertyName);
+ }
+ return rv;
+ }
+
+ /**
+ * Returns the stack of timed elements for the current thread.
+ * @return the stack of timed elements for the current thread
+ */
+ private Stack<TimedElement> getStack() {
+ Stack<TimedElement> threadStack = threadStacks.get(Thread.currentThread());
+ if (threadStack == null) {
+ threadStack = new Stack<TimedElement>();
+ threadStacks.put(Thread.currentThread(), threadStack);
+ }
+ /* For debugging purposes uncomment:
+ org.w3c.dom.Comment s = doc.createComment("stack=" + threadStack);
+ buildElement.element.appendChild(s);
+ */
+ return threadStack;
+ }
+
+ /**
+ * Fired when a target starts building, this pushes a timed element
+ * for the target onto the stack of elements for the current thread,
+ * remembering the current time and the name of the target.
+ *
+ * @param event An event with any relevant extra information.
+ * Will not be <code>null</code>.
+ */
+ public void targetStarted(BuildEvent event) {
+ Target target = event.getTarget();
+ TimedElement targetElement = new TimedElement();
+ targetElement.startTime = System.currentTimeMillis();
+ targetElement.element = doc.createElement(TARGET_TAG);
+ targetElement.element.setAttribute(NAME_ATTR, target.getName());
+ targets.put(target, targetElement);
+ getStack().push(targetElement);
+ }
+
+ /**
+ * Fired when a target finishes building, this adds the time taken
+ * and any error stacktrace to the appropriate target element in the log.
+ *
+ * @param event An event with any relevant extra information.
+ * Will not be <code>null</code>.
+ */
+ public void targetFinished(BuildEvent event) {
+ Target target = event.getTarget();
+ TimedElement targetElement = targets.get(target);
+ if (targetElement != null) {
+ long totalTime = System.currentTimeMillis() - targetElement.startTime;
+ targetElement.element.setAttribute(TIME_ATTR, DefaultLogger.formatTime(totalTime));
+
+ TimedElement parentElement = null;
+ Stack<TimedElement> threadStack = getStack();
+ if (!threadStack.empty()) {
+ TimedElement poppedStack = threadStack.pop();
+ if (poppedStack != targetElement) {
+ throw new RuntimeException("Mismatch - popped element = " + poppedStack
+ + " finished target element = " + targetElement);
+ }
+ if (!threadStack.empty()) {
+ parentElement = threadStack.peek();
+ }
+ }
+ if (parentElement == null) {
+ synchronizedAppend(buildElement.element, targetElement.element);
+ } else {
+ synchronizedAppend(parentElement.element,
+ targetElement.element);
+ }
+ }
+ targets.remove(target);
+ }
+
+ /**
+ * Fired when a task starts building, this pushes a timed element
+ * for the task onto the stack of elements for the current thread,
+ * remembering the current time and the name of the task.
+ *
+ * @param event An event with any relevant extra information.
+ * Will not be <code>null</code>.
+ */
+ public void taskStarted(BuildEvent event) {
+ TimedElement taskElement = new TimedElement();
+ taskElement.startTime = System.currentTimeMillis();
+ taskElement.element = doc.createElement(TASK_TAG);
+
+ Task task = event.getTask();
+ String name = event.getTask().getTaskName();
+ if (name == null) {
+ name = "";
+ }
+ taskElement.element.setAttribute(NAME_ATTR, name);
+ taskElement.element.setAttribute(LOCATION_ATTR, event.getTask().getLocation().toString());
+ tasks.put(task, taskElement);
+ getStack().push(taskElement);
+ }
+
+ /**
+ * Fired when a task finishes building, this adds the time taken
+ * and any error stacktrace to the appropriate task element in the log.
+ *
+ * @param event An event with any relevant extra information.
+ * Will not be <code>null</code>.
+ */
+ public void taskFinished(BuildEvent event) {
+ Task task = event.getTask();
+ TimedElement taskElement = tasks.get(task);
+ if (taskElement == null) {
+ throw new RuntimeException("Unknown task " + task + " not in " + tasks);
+ }
+ long totalTime = System.currentTimeMillis() - taskElement.startTime;
+ taskElement.element.setAttribute(TIME_ATTR, DefaultLogger.formatTime(totalTime));
+ Target target = task.getOwningTarget();
+ TimedElement targetElement = null;
+ if (target != null) {
+ targetElement = targets.get(target);
+ }
+ if (targetElement == null) {
+ synchronizedAppend(buildElement.element, taskElement.element);
+ } else {
+ synchronizedAppend(targetElement.element, taskElement.element);
+ }
+ Stack<TimedElement> threadStack = getStack();
+ if (!threadStack.empty()) {
+ TimedElement poppedStack = threadStack.pop();
+ if (poppedStack != taskElement) {
+ throw new RuntimeException("Mismatch - popped element = " + poppedStack
+ + " finished task element = " + taskElement);
+ }
+ }
+ tasks.remove(task);
+ }
+
+ /**
+ * Get the TimedElement associated with a task.
+ *
+ * Where the task is not found directly, search for unknown elements which
+ * may be hiding the real task
+ */
+ private TimedElement getTaskElement(Task task) {
+ TimedElement element = tasks.get(task);
+ if (element != null) {
+ return element;
+ }
+ for (Enumeration<Task> e = tasks.keys(); e.hasMoreElements();) {
+ Task key = e.nextElement();
+ if (key instanceof UnknownElement) {
+ if (((UnknownElement) key).getTask() == task) {
+ return tasks.get(key);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Fired when a message is logged, this adds a message element to the
+ * most appropriate parent element (task, target or build) and records
+ * the priority and text of the message.
+ *
+ * @param event An event with any relevant extra information.
+ * Will not be <code>null</code>.
+ */
+ public void messageLogged(BuildEvent event) {
+ int priority = event.getPriority();
+ if (priority > msgOutputLevel) {
+ return;
+ }
+ Element messageElement = doc.createElement(MESSAGE_TAG);
+
+ String name = "debug";
+ switch (priority) {
+ case Project.MSG_ERR:
+ name = "error";
+ break;
+ case Project.MSG_WARN:
+ name = "warn";
+ break;
+ case Project.MSG_INFO:
+ name = "info";
+ break;
+ default:
+ name = "debug";
+ break;
+ }
+ messageElement.setAttribute(PRIORITY_ATTR, name);
+
+ Throwable ex = event.getException();
+ if (Project.MSG_DEBUG <= msgOutputLevel && ex != null) {
+ Text errText = doc.createCDATASection(StringUtils.getStackTrace(ex));
+ Element stacktrace = doc.createElement(STACKTRACE_TAG);
+ stacktrace.appendChild(errText);
+ synchronizedAppend(buildElement.element, stacktrace);
+ }
+ Text messageText = doc.createCDATASection(event.getMessage());
+ messageElement.appendChild(messageText);
+
+ TimedElement parentElement = null;
+
+ Task task = event.getTask();
+
+ Target target = event.getTarget();
+ if (task != null) {
+ parentElement = getTaskElement(task);
+ }
+ if (parentElement == null && target != null) {
+ parentElement = targets.get(target);
+ }
+ if (parentElement != null) {
+ synchronizedAppend(parentElement.element, messageElement);
+ } else {
+ synchronizedAppend(buildElement.element, messageElement);
+ }
+ }
+
+ // -------------------------------------------------- BuildLogger interface
+
+ /**
+ * Set the logging level when using this as a Logger
+ *
+ * @param level the logging level -
+ * see {@link org.apache.tools.ant.Project#MSG_ERR Project}
+ * class for level definitions
+ */
+ public void setMessageOutputLevel(int level) {
+ msgOutputLevel = level;
+ }
+
+ /**
+ * Set the output stream to which logging output is sent when operating
+ * as a logger.
+ *
+ * @param output the output PrintStream.
+ */
+ public void setOutputPrintStream(PrintStream output) {
+ this.outStream = new PrintStream(output, true);
+ }
+
+ /**
+ * Ignore emacs mode, as it has no meaning in XML format
+ *
+ * @param emacsMode true if logger should produce emacs compatible
+ * output
+ */
+ public void setEmacsMode(boolean emacsMode) {
+ }
+
+ /**
+ * Ignore error print stream. All output will be written to
+ * either the XML log file or the PrintStream provided to
+ * setOutputPrintStream
+ *
+ * @param err the stream we are going to ignore.
+ */
+ public void setErrorPrintStream(PrintStream err) {
+ }
+
+ private void synchronizedAppend(Node parent, Node child) {
+ synchronized(parent) {
+ parent.appendChild(child);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/antlib.xml b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/antlib.xml
new file mode 100644
index 00000000..b11bac52
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/antlib.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0"?>
+ <!--
+/*
+ * 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.
+ *
+ */
+-->
+<antlib>
+ <!--
+ This is the ant lib definition for ant.
+ Currently it only contains componentdefinitions (restricted
+ types that are not allowed at the top level)
+ - conditions, selectors and comparators
+ (those that are not top-level types (taskdefs or typedefs).
+ defined in defaults.properties of taskdefs and types
+ packages).
+
+ This is currently experimental and it is most
+ likely that these definitions will be placed
+ in a Java Ant definition class.
+ -->
+ <!-- conditions -->
+ <componentdef name="and" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.And"/>
+ <componentdef name="contains" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Contains"/>
+ <componentdef name="equals" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Equals"/>
+ <componentdef name="filesmatch" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.FilesMatch"/>
+ <componentdef name="hasfreespace" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.HasFreeSpace"/>
+ <componentdef name="hasmethod" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.HasMethod"/>
+ <componentdef name="http" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Http"/>
+ <componentdef name="isfailure" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsFailure"/>
+ <componentdef name="isfalse" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsFalse"/>
+ <componentdef name="islastmodified" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsLastModified"/>
+ <componentdef name="isreachable" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsReachable"/>
+ <componentdef name="isreference" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsReference"/>
+ <componentdef name="isset" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsSet"/>
+ <componentdef name="issigned" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsSigned"/>
+ <componentdef name="istrue" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsTrue"/>
+ <componentdef name="matches" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Matches"/>
+ <componentdef name="not" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.selectors.Not"/>
+ <componentdef name="or" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Or"/>
+ <componentdef name="os" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Os"/>
+ <componentdef name="parsersupports" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.ParserSupports"/>
+ <componentdef name="resourcecontains" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.ResourceContains"/>
+ <componentdef name="resourceexists" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.ResourceExists"/>
+ <componentdef name="resourcesmatch" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.ResourcesMatch"/>
+ <componentdef name="socket" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Socket"/>
+ <componentdef name="typefound" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.TypeFound"/>
+ <componentdef name="xor" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Xor"/>
+
+ <!-- selectors -->
+ <componentdef name="and" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.selectors.And" />
+ <componentdef name="compare" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.selectors.Compare" />
+ <componentdef name="contains" onerror="ignore"
+ classname="org.apache.tools.ant.types.selectors.ContainsSelector" />
+ <componentdef name="containsregexp" onerror="ignore"
+ classname="org.apache.tools.ant.types.selectors.ContainsRegexpSelector" />
+ <componentdef name="date" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.selectors.Date" />
+ <componentdef name="exists" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.selectors.Exists" />
+ <componentdef name="instanceof" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.selectors.InstanceOf" />
+ <componentdef name="majority" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.selectors.Majority" />
+ <componentdef name="modified" onerror="ignore"
+ classname="org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector" />
+ <componentdef name="name" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.selectors.Name" />
+ <componentdef name="none" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.selectors.None" />
+ <componentdef name="not" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.selectors.Not" />
+ <componentdef name="or" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.selectors.Or" />
+ <componentdef name="size" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.selectors.Size" />
+ <componentdef name="type" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.selectors.Type" />
+
+
+ <!-- comparators -->
+ <componentdef name="name" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.comparators.Name" />
+ <componentdef name="size" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.comparators.Size" />
+ <componentdef name="date" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.comparators.Date" />
+ <componentdef name="exists" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.comparators.Exists" />
+ <componentdef name="type" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.comparators.Type" />
+ <componentdef name="content" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.comparators.Content" />
+ <componentdef name="reverse" onerror="ignore"
+ classname="org.apache.tools.ant.types.resources.comparators.Reverse" />
+
+ <!-- filters -->
+ <componentdef name="sortfilter" onerror="ignore"
+ classname="org.apache.tools.ant.filters.SortFilter"/>
+ <componentdef name="uniqfilter" onerror="ignore"
+ classname="org.apache.tools.ant.filters.UniqFilter"/>
+</antlib>
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/AttributeNamespace.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/AttributeNamespace.java
new file mode 100644
index 00000000..fba4c992
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/AttributeNamespace.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.tools.ant.attribute;
+
+/**
+ * This class is used to indicate that the XML namespace (URI)
+ * can be used to look for namespace attributes.
+ * @see org.apache.tools.ant.taskdefs.AttributeNamespaceDef
+ * @since Ant 1.9.1
+ */
+public final class AttributeNamespace {
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/BaseIfAttribute.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/BaseIfAttribute.java
new file mode 100644
index 00000000..c2ec08ab
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/BaseIfAttribute.java
@@ -0,0 +1,86 @@
+/*
+ * 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.tools.ant.attribute;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.RuntimeConfigurable;
+import org.apache.tools.ant.UnknownElement;
+
+
+/**
+ * An abstract class for if/unless attributes.
+ * This contains a boolean flag to specify whether this is an
+ * if or unless attribute.
+ * @since Ant 1.9.1
+ */
+public abstract class BaseIfAttribute
+ extends ProjectComponent implements EnableAttribute {
+ private boolean positive = true;
+ /**
+ * Set the positive flag.
+ * @param positive the value to use.
+ */
+ protected void setPositive(boolean positive) {
+ this.positive = positive;
+ }
+
+ /**
+ * Get the positive flag.
+ * @return the flag.
+ */
+ protected boolean isPositive() {
+ return positive;
+ }
+
+ /**
+ * convert the result.
+ * @param val the result to convert
+ * @return val if positive or !val if not.
+ */
+ protected boolean convertResult(boolean val) {
+ return positive ? val : !val;
+ }
+
+ /**
+ * Get all the attributes in the ant-attribute:param
+ * namespace and place them in a map.
+ * @param el the element this attribute is in.
+ * @return a map of attributes.
+ */
+ protected Map getParams(UnknownElement el) {
+ Map ret = new HashMap();
+ RuntimeConfigurable rc = el.getWrapper();
+ Map attributes = rc.getAttributeMap(); // This does a copy!
+ for (Iterator i = attributes.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ String key = (String) entry.getKey();
+ String value = (String) entry.getValue();
+ if (key.startsWith("ant-attribute:param")) {
+ int pos = key.lastIndexOf(':');
+ ret.put(key.substring(pos + 1),
+ el.getProject().replaceProperties(value));
+ }
+ }
+ return ret;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/EnableAttribute.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/EnableAttribute.java
new file mode 100644
index 00000000..06c1186f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/EnableAttribute.java
@@ -0,0 +1,35 @@
+/*
+ * 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.tools.ant.attribute;
+
+import org.apache.tools.ant.UnknownElement;
+
+/**
+ * This interface is used by Ant attributes.
+ * @since Ant 1.9.1
+ */
+public interface EnableAttribute {
+ /**
+ * is enabled.
+ * @param el the unknown element this attribute is in.
+ * @param value the value of the attribute.
+ * @return true if the attribute enables the element, false otherwise.
+ */
+ boolean isEnabled(UnknownElement el, String value);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/IfBlankAttribute.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/IfBlankAttribute.java
new file mode 100644
index 00000000..384476c8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/IfBlankAttribute.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.tools.ant.attribute;
+
+import org.apache.tools.ant.UnknownElement;
+
+/**
+ * Check if an attribute is blank or not.
+ * @since Ant 1.9.1
+ */
+public class IfBlankAttribute extends BaseIfAttribute {
+ /** The unless version */
+ public static class Unless extends IfBlankAttribute {
+ { setPositive(false); }
+ }
+ /**
+ * check if the attribute value is blank or not
+ * {@inheritDoc}
+ */
+ public boolean isEnabled(UnknownElement el, String value) {
+ return convertResult((value == null || "".equals(value)));
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/IfSetAttribute.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/IfSetAttribute.java
new file mode 100644
index 00000000..d4531c59
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/IfSetAttribute.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.tools.ant.attribute;
+
+import org.apache.tools.ant.UnknownElement;
+
+/**
+ * Check if an attribute value as a property is set or not
+ * @since Ant 1.9.1
+ */
+public class IfSetAttribute extends BaseIfAttribute {
+ /** The unless version */
+ public static class Unless extends IfSetAttribute {
+ { setPositive(false); }
+ }
+ /**
+ * check if the attribute value is blank or not
+ * {@inheritDoc}
+ */
+ public boolean isEnabled(UnknownElement el, String value) {
+ return convertResult(getProject().getProperty(value) != null);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/IfTrueAttribute.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/IfTrueAttribute.java
new file mode 100644
index 00000000..5c10ea2f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/attribute/IfTrueAttribute.java
@@ -0,0 +1,41 @@
+/*
+ * 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.tools.ant.attribute;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.UnknownElement;
+
+/**
+ * Check if an attribute value is true or not.
+ * @since Ant 1.9.1
+ */
+public class IfTrueAttribute extends BaseIfAttribute {
+ /** The unless version */
+ public static class Unless extends IfTrueAttribute {
+ { setPositive(false); }
+ }
+
+ /**
+ * check if the attribute value is true or not
+ * {@inheritDoc}
+ */
+ public boolean isEnabled(UnknownElement el, String value) {
+ return convertResult(Project.toBoolean(value));
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/defaultManifest.mf b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/defaultManifest.mf
new file mode 100644
index 00000000..fd30a23a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/defaultManifest.mf
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Ant-Version: Apache Ant ${project.version}
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/dispatch/DispatchTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/dispatch/DispatchTask.java
new file mode 100644
index 00000000..a848ed13
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/dispatch/DispatchTask.java
@@ -0,0 +1,59 @@
+/*
+ * 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.tools.ant.dispatch;
+
+import org.apache.tools.ant.Task;
+
+/**
+ * Tasks extending this class may contain multiple actions.
+ * The method that is invoked for execution depends upon the
+ * value of the action attribute of the task.
+ * <br>
+ * Example:<br>
+ * &lt;mytask action=&quot;list&quot;/&gt; will invoke the method
+ * with the signature public void list() in mytask's class.
+ * If the action attribute is not defined in the task or is empty,
+ * the execute() method will be called.
+ */
+public abstract class DispatchTask extends Task implements Dispatchable {
+ private String action;
+
+ /**
+ * Get the action parameter name.
+ * @return the <code>String</code> "action" by default (can be overridden).
+ */
+ public String getActionParameterName() {
+ return "action";
+ }
+
+ /**
+ * Set the action.
+ * @param action the method name.
+ */
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ /**
+ * Get the action.
+ * @return the action.
+ */
+ public String getAction() {
+ return action;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/dispatch/DispatchUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/dispatch/DispatchUtils.java
new file mode 100644
index 00000000..1a7c1f98
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/dispatch/DispatchUtils.java
@@ -0,0 +1,124 @@
+/*
+ * 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.tools.ant.dispatch;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.UnknownElement;
+
+/**
+ * Determines and Executes the action method for the task.
+ */
+// CheckStyle:HideUtilityClassConstructorCheck OFF - (bc)
+public class DispatchUtils {
+ /**
+ * Determines and Executes the action method for the task.
+ * @param task the task to execute.
+ * @throws BuildException on error.
+ */
+ public static final void execute(Object task) throws BuildException {
+ String methodName = "execute";
+ Dispatchable dispatchable = null;
+ try {
+ if (task instanceof Dispatchable) {
+ dispatchable = (Dispatchable) task;
+ } else if (task instanceof UnknownElement) {
+ UnknownElement ue = (UnknownElement) task;
+ Object realThing = ue.getRealThing();
+ if (realThing != null
+ && realThing instanceof Dispatchable
+ && realThing instanceof Task) {
+ dispatchable = (Dispatchable) realThing;
+ }
+ }
+ if (dispatchable != null) {
+ String mName = null;
+ try {
+ final String name = dispatchable.getActionParameterName();
+ if (name != null && name.trim().length() > 0) {
+ mName = "get" + name.trim().substring(0, 1).toUpperCase();
+ if (name.length() > 1) {
+ mName += name.substring(1);
+ }
+ final Class<? extends Dispatchable> c = dispatchable.getClass();
+ final Method actionM = c.getMethod(mName, new Class[0]);
+ if (actionM != null) {
+ final Object o = actionM.invoke(dispatchable, (Object[]) null);
+ if (o != null) {
+ final String s = o.toString();
+ if (s != null && s.trim().length() > 0) {
+ methodName = s.trim();
+ Method executeM = null;
+ executeM = dispatchable.getClass().getMethod(
+ methodName, new Class[0]);
+ if (executeM == null) {
+ throw new BuildException(
+ "No public " + methodName + "() in "
+ + dispatchable.getClass());
+ }
+ executeM.invoke(dispatchable, (Object[]) null);
+ if (task instanceof UnknownElement) {
+ ((UnknownElement) task).setRealThing(null);
+ }
+ } else {
+ throw new BuildException(
+ "Dispatchable Task attribute '" + name.trim()
+ + "' not set or value is empty.");
+ }
+ } else {
+ throw new BuildException(
+ "Dispatchable Task attribute '" + name.trim()
+ + "' not set or value is empty.");
+ }
+ }
+ } else {
+ throw new BuildException(
+ "Action Parameter Name must not be empty for Dispatchable Task.");
+ }
+ } catch (NoSuchMethodException nsme) {
+ throw new BuildException("No public " + mName + "() in " + task.getClass());
+ }
+ } else {
+ Method executeM = null;
+ executeM = task.getClass().getMethod(methodName, new Class[0]);
+ if (executeM == null) {
+ throw new BuildException("No public " + methodName + "() in "
+ + task.getClass());
+ }
+ executeM.invoke(task, (Object[]) null);
+ if (task instanceof UnknownElement) {
+ ((UnknownElement) task).setRealThing(null);
+ }
+ }
+ } catch (InvocationTargetException ie) {
+ Throwable t = ie.getTargetException();
+ if (t instanceof BuildException) {
+ throw ((BuildException) t);
+ } else {
+ throw new BuildException(t);
+ }
+ } catch (NoSuchMethodException e) {
+ throw new BuildException(e);
+ } catch (IllegalAccessException e) {
+ throw new BuildException(e);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/dispatch/Dispatchable.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/dispatch/Dispatchable.java
new file mode 100644
index 00000000..41684f48
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/dispatch/Dispatchable.java
@@ -0,0 +1,31 @@
+/*
+ * 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.tools.ant.dispatch;
+
+/**
+ * Classes implementing this interface specify the
+ * name of the parameter that contains the name
+ * of the task's method to execute.
+ */
+public interface Dispatchable {
+ /**
+ * Get the name of the parameter.
+ * @return the name of the parameter that contains the name of the method.
+ */
+ String getActionParameterName();
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/BaseFilterReader.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/BaseFilterReader.java
new file mode 100644
index 00000000..3b3b0270
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/BaseFilterReader.java
@@ -0,0 +1,199 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.FilterReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Base class for core filter readers.
+ *
+ */
+public abstract class BaseFilterReader extends FilterReader {
+ /** Buffer size used when reading */
+ private static final int BUFFER_SIZE = 8192;
+
+ /** Have the parameters passed been interpreted? */
+ private boolean initialized = false;
+
+ /** The Ant project this filter is part of. */
+ private Project project = null;
+
+ /**
+ * Constructor used by Ant's introspection mechanism.
+ * The original filter reader is only used for chaining
+ * purposes, never for filtering purposes (and indeed
+ * it would be useless for filtering purposes, as it has
+ * no real data to filter). ChainedReaderHelper uses
+ * this placeholder instance to create a chain of real filters.
+ */
+ public BaseFilterReader() {
+ super(new StringReader(""));
+ FileUtils.close(this);
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ */
+ public BaseFilterReader(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Reads characters into a portion of an array. This method will block
+ * until some input is available, an I/O error occurs, or the end of the
+ * stream is reached.
+ *
+ * @param cbuf Destination buffer to write characters to.
+ * Must not be <code>null</code>.
+ * @param off Offset at which to start storing characters.
+ * @param len Maximum number of characters to read.
+ *
+ * @return the number of characters read, or -1 if the end of the
+ * stream has been reached
+ *
+ * @exception IOException If an I/O error occurs
+ */
+ public final int read(final char[] cbuf, final int off,
+ final int len) throws IOException {
+ for (int i = 0; i < len; i++) {
+ final int ch = read();
+ if (ch == -1) {
+ if (i == 0) {
+ return -1;
+ } else {
+ return i;
+ }
+ }
+ cbuf[off + i] = (char) ch;
+ }
+ return len;
+ }
+
+ /**
+ * Skips characters. This method will block until some characters are
+ * available, an I/O error occurs, or the end of the stream is reached.
+ *
+ * @param n The number of characters to skip
+ *
+ * @return the number of characters actually skipped
+ *
+ * @exception IllegalArgumentException If <code>n</code> is negative.
+ * @exception IOException If an I/O error occurs
+ */
+ public final long skip(final long n)
+ throws IOException, IllegalArgumentException {
+ if (n < 0L) {
+ throw new IllegalArgumentException("skip value is negative");
+ }
+
+ for (long i = 0; i < n; i++) {
+ if (read() == -1) {
+ return i;
+ }
+ }
+ return n;
+ }
+
+ /**
+ * Sets the initialized status.
+ *
+ * @param initialized Whether or not the filter is initialized.
+ */
+ protected final void setInitialized(final boolean initialized) {
+ this.initialized = initialized;
+ }
+
+ /**
+ * Returns the initialized status.
+ *
+ * @return whether or not the filter is initialized
+ */
+ protected final boolean getInitialized() {
+ return initialized;
+ }
+
+ /**
+ * Sets the project to work with.
+ *
+ * @param project The project this filter is part of.
+ * Should not be <code>null</code>.
+ */
+ public final void setProject(final Project project) {
+ this.project = project;
+ }
+
+ /**
+ * Returns the project this filter is part of.
+ *
+ * @return the project this filter is part of
+ */
+ protected final Project getProject() {
+ return project;
+ }
+
+ /**
+ * Reads a line of text ending with '\n' (or until the end of the stream).
+ * The returned String retains the '\n'.
+ *
+ * @return the line read, or <code>null</code> if the end of the stream
+ * has already been reached
+ *
+ * @exception IOException if the underlying reader throws one during
+ * reading
+ */
+ protected final String readLine() throws IOException {
+ int ch = in.read();
+
+ if (ch == -1) {
+ return null;
+ }
+
+ StringBuffer line = new StringBuffer();
+
+ while (ch != -1) {
+ line.append ((char) ch);
+ if (ch == '\n') {
+ break;
+ }
+ ch = in.read();
+ }
+ return line.toString();
+ }
+
+ /**
+ * Reads to the end of the stream, returning the contents as a String.
+ *
+ * @return the remaining contents of the reader, as a String
+ *
+ * @exception IOException if the underlying reader throws one during
+ * reading
+ */
+ protected final String readFully() throws IOException {
+ return FileUtils.readFully(in, BUFFER_SIZE);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/BaseParamFilterReader.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/BaseParamFilterReader.java
new file mode 100644
index 00000000..54bc9ff9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/BaseParamFilterReader.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.tools.ant.filters;
+
+import java.io.Reader;
+
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.Parameterizable;
+
+/**
+ * Parameterized base class for core filter readers.
+ *
+ */
+public abstract class BaseParamFilterReader
+ extends BaseFilterReader
+ implements Parameterizable {
+ /** The passed in parameter array. */
+ private Parameter[] parameters;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public BaseParamFilterReader() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public BaseParamFilterReader(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Sets the parameters used by this filter, and sets
+ * the filter to an uninitialized status.
+ *
+ * @param parameters The parameters to be used by this filter.
+ * Should not be <code>null</code>.
+ */
+ public final void setParameters(final Parameter[] parameters) {
+ this.parameters = parameters;
+ setInitialized(false);
+ }
+
+ /**
+ * Returns the parameters to be used by this filter.
+ *
+ * @return the parameters to be used by this filter
+ */
+ protected final Parameter[] getParameters() {
+ return parameters;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ChainableReader.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ChainableReader.java
new file mode 100644
index 00000000..67060393
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ChainableReader.java
@@ -0,0 +1,37 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.Reader;
+
+/**
+ * Interface indicating that a reader may be chained to another one.
+ *
+ */
+public interface ChainableReader {
+ /**
+ * Returns a reader with the same configuration as this one,
+ * but filtering input from the specified reader.
+ *
+ * @param rdr the reader which the returned reader should be filtering
+ *
+ * @return a reader with the same configuration as this one, but
+ * filtering input from the specified reader
+ */
+ Reader chain(Reader rdr);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ClassConstants.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ClassConstants.java
new file mode 100644
index 00000000..35443012
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ClassConstants.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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.ResourceUtils;
+
+/**
+ * Assembles the constants declared in a Java class in
+ * <code>key1=value1(line separator)key2=value2</code>
+ * format.
+ *<p>
+ * Notes:
+ * <ol>
+ * <li>This filter uses the BCEL external toolkit.
+ * <li>This assembles only those constants that are not created
+ * using the syntax <code>new whatever()</code>
+ * <li>This assembles constants declared using the basic datatypes
+ * and String only.</li>
+ * <li>The access modifiers of the declared constants do not matter.</li>
+ *</ol>
+ * Example:<br>
+ * <pre>&lt;classconstants/&gt;</pre>
+ * Or:
+ * <pre>&lt;filterreader
+ * classname=&quot;org.apache.tools.ant.filters.ClassConstants&quot;/&gt;</pre>
+ */
+public final class ClassConstants
+ extends BaseFilterReader
+ implements ChainableReader {
+ /** Data that must be read from, if not null. */
+ private String queuedData = null;
+
+ /** Helper Class to be invoked via reflection. */
+ private static final String JAVA_CLASS_HELPER =
+ "org.apache.tools.ant.filters.util.JavaClassHelper";
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public ClassConstants() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader. The contents of the passed-in reader
+ * are expected to be the name of the class from which to produce a
+ * list of constants.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public ClassConstants(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Reads and assembles the constants declared in a class file.
+ *
+ * @return the next character in the list of constants, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading, or if the constants for the specified class cannot
+ * be read (for example due to the class not being found).
+ */
+ public int read() throws IOException {
+
+ int ch = -1;
+
+ if (queuedData != null && queuedData.length() == 0) {
+ queuedData = null;
+ }
+
+ if (queuedData != null) {
+ ch = queuedData.charAt(0);
+ queuedData = queuedData.substring(1);
+ if (queuedData.length() == 0) {
+ queuedData = null;
+ }
+ } else {
+ final String clazz = readFully();
+ if (clazz == null || clazz.length() == 0) {
+ ch = -1;
+ } else {
+ final byte[] bytes = clazz.getBytes(ResourceUtils.ISO_8859_1);
+ try {
+ final Class<?> javaClassHelper =
+ Class.forName(JAVA_CLASS_HELPER);
+ if (javaClassHelper != null) {
+ final Class<?>[] params = {
+ byte[].class
+ };
+ final Method getConstants =
+ javaClassHelper.getMethod("getConstants", params);
+ final Object[] args = {
+ bytes
+ };
+ // getConstants is a static method, no need to
+ // pass in the object
+ final StringBuffer sb = (StringBuffer)
+ getConstants.invoke(null, args);
+ if (sb.length() > 0) {
+ queuedData = sb.toString();
+ return read();
+ }
+ }
+ } catch (NoClassDefFoundError ex) {
+ throw ex;
+ } catch (RuntimeException ex) {
+ throw ex;
+ } catch (InvocationTargetException ex) {
+ Throwable t = ex.getTargetException();
+ if (t instanceof NoClassDefFoundError) {
+ throw (NoClassDefFoundError) t;
+ }
+ if (t instanceof RuntimeException) {
+ throw (RuntimeException) t;
+ }
+ throw new BuildException(t);
+ } catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+ }
+ }
+ return ch;
+ }
+
+ /**
+ * Creates a new ClassConstants using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ ClassConstants newFilter = new ClassConstants(rdr);
+ return newFilter;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ConcatFilter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ConcatFilter.java
new file mode 100644
index 00000000..0ac6024b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ConcatFilter.java
@@ -0,0 +1,218 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Concats a file before and/or after the file.
+ *
+ * <p>Example:</p><pre>
+ * &lt;copy todir="build"&gt;
+ * &lt;fileset dir="src" includes="*.java"/&gt;
+ * &lt;filterchain&gt;
+ * &lt;concatfilter prepend="apache-license-java.txt"/&gt;
+ * &lt;/filterchain&gt;
+ * &lt;/copy&gt;
+ * </pre>
+ *
+ * <p>Copies all java sources from <i>src</i> to <i>build</i> and adds the
+ * content of <i>apache-license-java.txt</i> add the beginning of each
+ * file.</p>
+ *
+ * @since 1.6
+ * @version 2003-09-23
+ */
+public final class ConcatFilter extends BaseParamFilterReader
+ implements ChainableReader {
+
+ /** File to add before the content. */
+ private File prepend;
+
+ /** File to add after the content. */
+ private File append;
+
+ /** Reader for prepend-file. */
+ private Reader prependReader = null;
+
+ /** Reader for append-file. */
+ private Reader appendReader = null;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public ConcatFilter() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public ConcatFilter(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Returns the next character in the filtered stream. If the desired
+ * number of lines have already been read, the resulting stream is
+ * effectively at an end. Otherwise, the next character from the
+ * underlying stream is read and returned.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+ @Override
+ public int read() throws IOException {
+ // do the "singleton" initialization
+ if (!getInitialized()) {
+ initialize();
+ setInitialized(true);
+ }
+
+ int ch = -1;
+
+ // The readers return -1 if they end. So simply read the "prepend"
+ // after that the "content" and at the end the "append" file.
+ if (prependReader != null) {
+ ch = prependReader.read();
+ if (ch == -1) {
+ // I am the only one so I have to close the reader
+ prependReader.close();
+ prependReader = null;
+ }
+ }
+ if (ch == -1) {
+ ch = super.read();
+ }
+ if (ch == -1) {
+ // don't call super.close() because that reader is used
+ // on other places ...
+ if (appendReader != null) {
+ ch = appendReader.read();
+ if (ch == -1) {
+ // I am the only one so I have to close the reader
+ appendReader.close();
+ appendReader = null;
+ }
+ }
+ }
+
+ return ch;
+ }
+
+ /**
+ * Sets <i>prepend</i> attribute.
+ * @param prepend new value
+ */
+ public void setPrepend(final File prepend) {
+ this.prepend = prepend;
+ }
+
+ /**
+ * Returns <i>prepend</i> attribute.
+ * @return prepend attribute
+ */
+ public File getPrepend() {
+ return prepend;
+ }
+
+ /**
+ * Sets <i>append</i> attribute.
+ * @param append new value
+ */
+ public void setAppend(final File append) {
+ this.append = append;
+ }
+
+ /**
+ * Returns <i>append</i> attribute.
+ * @return append attribute
+ */
+ public File getAppend() {
+ return append;
+ }
+
+ /**
+ * Creates a new ConcatReader using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ final ConcatFilter newFilter = new ConcatFilter(rdr);
+ newFilter.setPrepend(getPrepend());
+ newFilter.setAppend(getAppend());
+ // Usually the initialized is set to true. But here it must not.
+ // Because the prepend and append readers have to be instantiated
+ // on runtime
+ //newFilter.setInitialized(true);
+ return newFilter;
+ }
+
+ /**
+ * Scans the parameters list for the "lines" parameter and uses
+ * it to set the number of lines to be returned in the filtered stream.
+ * also scan for skip parameter.
+ */
+ private void initialize() throws IOException {
+ // get parameters
+ final Parameter[] params = getParameters();
+ if (params != null) {
+ for (int i = 0; i < params.length; i++) {
+ if ("prepend".equals(params[i].getName())) {
+ setPrepend(new File(params[i].getValue()));
+ continue;
+ }
+ if ("append".equals(params[i].getName())) {
+ setAppend(new File(params[i].getValue()));
+ continue;
+ }
+ }
+ }
+ if (prepend != null) {
+ if (!prepend.isAbsolute()) {
+ prepend = new File(getProject().getBaseDir(), prepend.getPath());
+ }
+ prependReader = new BufferedReader(new FileReader(prepend));
+ }
+ if (append != null) {
+ if (!append.isAbsolute()) {
+ append = new File(getProject().getBaseDir(), append.getPath());
+ }
+ appendReader = new BufferedReader(new FileReader(append));
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/EscapeUnicode.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/EscapeUnicode.java
new file mode 100644
index 00000000..dd454395
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/EscapeUnicode.java
@@ -0,0 +1,123 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.tools.ant.util.UnicodeUtil;
+
+/**
+ * This method converts non-latin characters to unicode escapes.
+ * Useful to load properties containing non latin
+ * Example:
+ *
+ * <pre>&lt;escapeunicode&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader
+ classname=&quot;org.apache.tools.ant.filters.EscapeUnicode&quot;/&gt;
+ * </pre>
+ *
+ * @since Ant 1.6
+ */
+public class EscapeUnicode
+ extends BaseParamFilterReader
+ implements ChainableReader {
+ //this field will hold unnnn right after reading a non latin character
+ //afterwards it will be truncated of one char every call to read
+ private StringBuffer unicodeBuf;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public EscapeUnicode() {
+ super();
+ unicodeBuf = new StringBuffer();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public EscapeUnicode(final Reader in) {
+ super(in);
+ unicodeBuf = new StringBuffer();
+ }
+
+ /**
+ * Returns the next character in the filtered stream, converting non latin
+ * characters to unicode escapes.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws
+ * an IOException during reading
+ */
+ public final int read() throws IOException {
+ if (!getInitialized()) {
+ initialize();
+ setInitialized(true);
+ }
+
+ int ch = -1;
+ if (unicodeBuf.length() == 0) {
+ ch = in.read();
+ if (ch != -1) {
+ char achar = (char) ch;
+ if (achar >= '\u0080') {
+ unicodeBuf = UnicodeUtil.EscapeUnicode(achar);
+ ch = '\\';
+ }
+ }
+ } else {
+ ch = (int) unicodeBuf.charAt(0);
+ unicodeBuf.deleteCharAt(0);
+ }
+ return ch;
+ }
+
+ /**
+ * Creates a new EscapeUnicode using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public final Reader chain(final Reader rdr) {
+ EscapeUnicode newFilter = new EscapeUnicode(rdr);
+ newFilter.setInitialized(true);
+ return newFilter;
+ }
+
+ /**
+ * Parses the parameters (currently unused)
+ */
+ private void initialize() {
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ExpandProperties.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ExpandProperties.java
new file mode 100644
index 00000000..524a799b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ExpandProperties.java
@@ -0,0 +1,142 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Properties;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.property.GetProperty;
+import org.apache.tools.ant.property.ParseProperties;
+import org.apache.tools.ant.types.PropertySet;
+
+/**
+ * Expands Ant properties, if any, in the data.
+ * <p>
+ * Example:<br>
+ * <pre>&lt;expandproperties/&gt;</pre>
+ * Or:
+ * <pre>&lt;filterreader
+ * classname=&quot;org.apache.tools.ant.filters.ExpandProperties&quot;/&gt;</pre>
+ *
+ */
+public final class ExpandProperties
+ extends BaseFilterReader
+ implements ChainableReader {
+
+ private static final int EOF = -1;
+
+ private char[] buffer;
+ private int index;
+ private PropertySet propertySet;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public ExpandProperties() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public ExpandProperties(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Restrict the expanded properties using a PropertySet.
+ * @param propertySet replacement lookup
+ */
+ public void add(PropertySet propertySet) {
+ if (this.propertySet != null) {
+ throw new BuildException("expandproperties filter accepts only one propertyset");
+ }
+ this.propertySet = propertySet;
+ }
+
+ /**
+ * Returns the next character in the filtered stream. The original
+ * stream is first read in fully, and the Ant properties are expanded.
+ * The results of this expansion are then queued so they can be read
+ * character-by-character.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+ public int read() throws IOException {
+ if (index > EOF) {
+ if (buffer == null) {
+ String data = readFully();
+ Project project = getProject();
+ GetProperty getProperty;
+ if (propertySet == null) {
+ getProperty = PropertyHelper.getPropertyHelper(project);
+ } else {
+ final Properties props = propertySet.getProperties();
+ getProperty = new GetProperty() {
+
+ public Object getProperty(String name) {
+ return props.getProperty(name);
+ }
+ };
+ }
+ Object expanded = new ParseProperties(project, PropertyHelper
+ .getPropertyHelper(project)
+ .getExpanders(),
+ getProperty)
+ .parseProperties(data);
+ buffer = expanded == null ? new char[0]
+ : expanded.toString().toCharArray();
+ }
+ if (index < buffer.length) {
+ return buffer[index++];
+ }
+ index = EOF;
+ }
+ return EOF;
+ }
+
+ /**
+ * Creates a new ExpandProperties filter using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ ExpandProperties newFilter = new ExpandProperties(rdr);
+ newFilter.setProject(getProject());
+ newFilter.add(propertySet);
+ return newFilter;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/FixCrLfFilter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/FixCrLfFilter.java
new file mode 100644
index 00000000..8a37924c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/FixCrLfFilter.java
@@ -0,0 +1,1004 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ * Converts text to local OS formatting conventions, as well as repair text
+ * damaged by misconfigured or misguided editors or file transfer programs.
+ * <p>
+ * This filter can take the following arguments:
+ * <ul>
+ * <li>eof
+ * <li>eol
+ * <li>fixlast
+ * <li>javafiles
+ * <li>tab
+ * <li>tablength
+ * </ul>
+ * None of which are required.
+ * <p>
+ * This version generalises the handling of EOL characters, and allows for
+ * CR-only line endings (the standard on Mac systems prior to OS X). Tab
+ * handling has also been generalised to accommodate any tabwidth from 2 to 80,
+ * inclusive. Importantly, it can leave untouched any literal TAB characters
+ * embedded within Java string or character constants.
+ * <p>
+ * <em>Caution:</em> run with care on carefully formatted files. This may
+ * sound obvious, but if you don't specify asis, presume that your files are
+ * going to be modified. If "tabs" is "add" or "remove", whitespace characters
+ * may be added or removed as necessary. Similarly, for EOLs, eol="asis"
+ * actually means convert to your native O/S EOL convention while eol="crlf" or
+ * cr="add" can result in CR characters being removed in one special case
+ * accommodated, i.e., CRCRLF is regarded as a single EOL to handle cases where
+ * other programs have converted CRLF into CRCRLF.
+ *
+ * <P>
+ * Example:
+ *
+ * <pre>
+ * &lt;&lt;fixcrlf tab=&quot;add&quot; eol=&quot;crlf&quot; eof=&quot;asis&quot;/&gt;
+ * </pre>
+ *
+ * Or:
+ *
+ * <pre>
+ * &lt;filterreader classname=&quot;org.apache.tools.ant.filters.FixCrLfFilter&quot;&gt;
+ * &lt;param eol=&quot;crlf&quot; tab=&quot;asis&quot;/&gt;
+ * &lt;/filterreader&gt;
+ * </pre>
+ *
+ */
+public final class FixCrLfFilter extends BaseParamFilterReader implements ChainableReader {
+ private static final int DEFAULT_TAB_LENGTH = 8;
+ private static final int MIN_TAB_LENGTH = 2;
+ private static final int MAX_TAB_LENGTH = 80;
+ private static final char CTRLZ = '\u001A';
+
+ private int tabLength = DEFAULT_TAB_LENGTH;
+
+ private CrLf eol;
+
+ private AddAsisRemove ctrlz;
+
+ private AddAsisRemove tabs;
+
+ private boolean javafiles = false;
+
+ private boolean fixlast = true;
+
+ private boolean initialized = false;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public FixCrLfFilter() {
+ super();
+ }
+
+ /**
+ * Create a new filtered reader.
+ *
+ * @param in
+ * A Reader object providing the underlying stream. Must not be
+ * <code>null</code>.
+ * @throws IOException on error.
+ */
+ public FixCrLfFilter(final Reader in) throws IOException {
+ super(in);
+ }
+
+ // Instance initializer: Executes just after the super() call in this
+ // class's constructor.
+ {
+ tabs = AddAsisRemove.ASIS;
+ if (Os.isFamily("mac") && !Os.isFamily("unix")) {
+ ctrlz = AddAsisRemove.REMOVE;
+ setEol(CrLf.MAC);
+ } else if (Os.isFamily("dos")) {
+ ctrlz = AddAsisRemove.ASIS;
+ setEol(CrLf.DOS);
+ } else {
+ ctrlz = AddAsisRemove.REMOVE;
+ setEol(CrLf.UNIX);
+ }
+ }
+
+ /**
+ * Create a new FixCrLfFilter using the passed in Reader for instantiation.
+ *
+ * @param rdr
+ * A Reader object providing the underlying stream. Must not be
+ * <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering the
+ * specified reader.
+ */
+ public Reader chain(final Reader rdr) {
+ try {
+ FixCrLfFilter newFilter = new FixCrLfFilter(rdr);
+
+ newFilter.setJavafiles(getJavafiles());
+ newFilter.setEol(getEol());
+ newFilter.setTab(getTab());
+ newFilter.setTablength(getTablength());
+ newFilter.setEof(getEof());
+ newFilter.setFixlast(getFixlast());
+ newFilter.initInternalFilters();
+
+ return newFilter;
+ } catch (IOException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Get how DOS EOF (control-z) characters are being handled.
+ *
+ * @return values:
+ * <ul>
+ * <li>add: ensure that there is an eof at the end of the file
+ * <li>asis: leave eof characters alone
+ * <li>remove: remove any eof character found at the end
+ * </ul>
+ */
+ public AddAsisRemove getEof() {
+ // Return copy so that the call must call setEof() to change the state
+ // of fixCRLF
+ return ctrlz.newInstance();
+ }
+
+ /**
+ * Get how EndOfLine characters are being handled.
+ *
+ * @return values:
+ * <ul>
+ * <li>asis: convert line endings to your O/S convention
+ * <li>cr: convert line endings to CR
+ * <li>lf: convert line endings to LF
+ * <li>crlf: convert line endings to CRLF
+ * </ul>
+ */
+ public CrLf getEol() {
+ // Return copy so that the call must call setEol() to change the state
+ // of fixCRLF
+ return eol.newInstance();
+ }
+
+ /**
+ * Get whether a missing EOL be added to the final line of the stream.
+ *
+ * @return true if a filtered file will always end with an EOL
+ */
+ public boolean getFixlast() {
+ return fixlast;
+ }
+
+ /**
+ * Get whether the stream is to be treated as though it contains Java
+ * source.
+ * <P>
+ * This attribute is only used in association with the &quot;<i><b>tab</b></i>&quot;
+ * attribute. Tabs found in Java literals are protected from changes by this
+ * filter.
+ *
+ * @return true if whitespace in Java character and string literals is
+ * ignored.
+ */
+ public boolean getJavafiles() {
+ return javafiles;
+ }
+
+ /**
+ * Return how tab characters are being handled.
+ *
+ * @return values:
+ * <ul>
+ * <li>add: convert sequences of spaces which span a tab stop to
+ * tabs
+ * <li>asis: leave tab and space characters alone
+ * <li>remove: convert tabs to spaces
+ * </ul>
+ */
+ public AddAsisRemove getTab() {
+ // Return copy so that the caller must call setTab() to change the state
+ // of fixCRLF.
+ return tabs.newInstance();
+ }
+
+ /**
+ * Get the tab length to use.
+ *
+ * @return the length of tab in spaces
+ */
+ public int getTablength() {
+ return tabLength;
+ }
+
+ private static String calculateEolString(CrLf eol) {
+ // Calculate the EOL string per the current config
+ if (eol == CrLf.CR || eol == CrLf.MAC) {
+ return "\r";
+ }
+ if (eol == CrLf.CRLF || eol == CrLf.DOS) {
+ return "\r\n";
+ }
+ // assume (eol == CrLf.LF || eol == CrLf.UNIX)
+ return "\n";
+ }
+
+ /**
+ * Wrap the input stream with the internal filters necessary to perform the
+ * configuration settings.
+ */
+ private void initInternalFilters() {
+
+ // If I'm removing an EOF character, do so first so that the other
+ // filters don't see that character.
+ in = (ctrlz == AddAsisRemove.REMOVE) ? new RemoveEofFilter(in) : in;
+
+ // Change all EOL characters to match the calculated EOL string. If
+ // configured to do so, append a trailing EOL so that the file ends on
+ // a EOL.
+ if (eol != CrLf.ASIS) {
+ in = new NormalizeEolFilter(in, calculateEolString(eol), getFixlast());
+ }
+
+ if (tabs != AddAsisRemove.ASIS) {
+ // If filtering Java source, prevent changes to whitespace in
+ // character and string literals.
+ if (getJavafiles()) {
+ in = new MaskJavaTabLiteralsFilter(in);
+ }
+ // Add/Remove tabs
+ in = (tabs == AddAsisRemove.ADD) ? (Reader) new AddTabFilter(in, getTablength())
+ : (Reader) new RemoveTabFilter(in, getTablength());
+ }
+ // Add missing EOF character
+ in = (ctrlz == AddAsisRemove.ADD) ? new AddEofFilter(in) : in;
+ initialized = true;
+ }
+
+ /**
+ * Return the next character in the filtered stream.
+ *
+ * @return the next character in the resulting stream, or -1 if the end of
+ * the resulting stream has been reached.
+ *
+ * @exception IOException
+ * if the underlying stream throws an IOException during
+ * reading.
+ */
+ public synchronized int read() throws IOException {
+ if (!initialized) {
+ initInternalFilters();
+ }
+ return in.read();
+ }
+
+ /**
+ * Specify how DOS EOF (control-z) characters are to be handled.
+ *
+ * @param attr
+ * valid values:
+ * <ul>
+ * <li>add: ensure that there is an eof at the end of the file
+ * <li>asis: leave eof characters alone
+ * <li>remove: remove any eof character found at the end
+ * </ul>
+ */
+ public void setEof(AddAsisRemove attr) {
+ ctrlz = attr.resolve();
+ }
+
+ /**
+ * Specify how end of line (EOL) characters are to be handled.
+ *
+ * @param attr
+ * valid values:
+ * <ul>
+ * <li>asis: convert line endings to your O/S convention
+ * <li>cr: convert line endings to CR
+ * <li>lf: convert line endings to LF
+ * <li>crlf: convert line endings to CRLF
+ * </ul>
+ */
+ public void setEol(CrLf attr) {
+ eol = attr.resolve();
+ }
+
+ /**
+ * Specify whether a missing EOL will be added to the final line of input.
+ *
+ * @param fixlast
+ * if true a missing EOL will be appended.
+ */
+ public void setFixlast(boolean fixlast) {
+ this.fixlast = fixlast;
+ }
+
+ /**
+ * Indicate whether this stream contains Java source.
+ *
+ * This attribute is only used in association with the &quot;<i><b>tab</b></i>&quot;
+ * attribute.
+ *
+ * @param javafiles
+ * set to true to prevent this filter from changing tabs found in
+ * Java literals.
+ */
+ public void setJavafiles(boolean javafiles) {
+ this.javafiles = javafiles;
+ }
+
+ /**
+ * Specify how tab characters are to be handled.
+ *
+ * @param attr
+ * valid values:
+ * <ul>
+ * <li>add: convert sequences of spaces which span a tab stop to
+ * tabs
+ * <li>asis: leave tab and space characters alone
+ * <li>remove: convert tabs to spaces
+ * </ul>
+ */
+ public void setTab(AddAsisRemove attr) {
+ tabs = attr.resolve();
+ }
+
+ /**
+ * Specify tab length in characters.
+ *
+ * @param tabLength
+ * specify the length of tab in spaces. Valid values are between
+ * 2 and 80 inclusive. The default for this parameter is 8.
+ * @throws IOException on error.
+ */
+ public void setTablength(int tabLength) throws IOException {
+ if (tabLength < MIN_TAB_LENGTH
+ || tabLength > MAX_TAB_LENGTH) {
+ throw new IOException(
+ "tablength must be between " + MIN_TAB_LENGTH
+ + " and " + MAX_TAB_LENGTH);
+ }
+ this.tabLength = tabLength;
+ }
+
+ /**
+ * This filter reader redirects all read I/O methods through its own read()
+ * method.
+ *
+ * <P>
+ * The input stream is already buffered by the copy task so this doesn't
+ * significantly impact performance while it makes writing the individual
+ * fix filters much easier.
+ * </P>
+ */
+ private static class SimpleFilterReader extends Reader {
+ private static final int PREEMPT_BUFFER_LENGTH = 16;
+ private Reader in;
+
+ private int[] preempt = new int[PREEMPT_BUFFER_LENGTH];
+
+ private int preemptIndex = 0;
+
+ public SimpleFilterReader(Reader in) {
+ this.in = in;
+ }
+
+ public void push(char c) {
+ push((int) c);
+ }
+
+ public void push(int c) {
+ try {
+ preempt[preemptIndex++] = c;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ int[] p2 = new int[preempt.length * 2];
+ System.arraycopy(preempt, 0, p2, 0, preempt.length);
+ preempt = p2;
+ push(c);
+ }
+ }
+
+ public void push(char[] cs, int start, int length) {
+ for (int i = start + length - 1; i >= start;) {
+ push(cs[i--]);
+ }
+ }
+
+ public void push(char[] cs) {
+ push(cs, 0, cs.length);
+ }
+
+ /**
+ * Does this filter want to block edits on the last character returned
+ * by read()?
+ */
+ public boolean editsBlocked() {
+ return in instanceof SimpleFilterReader && ((SimpleFilterReader) in).editsBlocked();
+ }
+
+ public int read() throws java.io.IOException {
+ return preemptIndex > 0 ? preempt[--preemptIndex] : in.read();
+ }
+
+ public void close() throws java.io.IOException {
+ in.close();
+ }
+
+ public void reset() throws IOException {
+ in.reset();
+ }
+
+ public boolean markSupported() {
+ return in.markSupported();
+ }
+
+ public boolean ready() throws java.io.IOException {
+ return in.ready();
+ }
+
+ public void mark(int i) throws java.io.IOException {
+ in.mark(i);
+ }
+
+ public long skip(long i) throws java.io.IOException {
+ return in.skip(i);
+ }
+
+ public int read(char[] buf) throws java.io.IOException {
+ return read(buf, 0, buf.length);
+ }
+
+ public int read(char[] buf, int start, int length) throws java.io.IOException {
+ int count = 0;
+ int c = 0;
+
+ // CheckStyle:InnerAssignment OFF - leave alone
+ while (length-- > 0 && (c = this.read()) != -1) {
+ buf[start++] = (char) c;
+ count++;
+ }
+ // if at EOF with no characters in the buffer, return EOF
+ return (count == 0 && c == -1) ? -1 : count;
+ }
+ }
+
+ private static class MaskJavaTabLiteralsFilter extends SimpleFilterReader {
+ private boolean editsBlocked = false;
+
+ private static final int JAVA = 1;
+
+ private static final int IN_CHAR_CONST = 2;
+
+ private static final int IN_STR_CONST = 3;
+
+ private static final int IN_SINGLE_COMMENT = 4;
+
+ private static final int IN_MULTI_COMMENT = 5;
+
+ private static final int TRANS_TO_COMMENT = 6;
+
+ private static final int TRANS_FROM_MULTI = 8;
+
+ private int state;
+
+ public MaskJavaTabLiteralsFilter(Reader in) {
+ super(in);
+ state = JAVA;
+ }
+
+ public boolean editsBlocked() {
+ return editsBlocked || super.editsBlocked();
+ }
+
+ public int read() throws IOException {
+ int thisChar = super.read();
+ // Mask, block from being edited, all characters in constants.
+ editsBlocked = (state == IN_CHAR_CONST || state == IN_STR_CONST);
+
+ switch (state) {
+ case JAVA:
+ // The current character is always emitted.
+ switch (thisChar) {
+ case '\'':
+ state = IN_CHAR_CONST;
+ break;
+ case '"':
+ state = IN_STR_CONST;
+ break;
+ case '/':
+ state = TRANS_TO_COMMENT;
+ break;
+ default:
+ // Fall tru
+ }
+ break;
+ case IN_CHAR_CONST:
+ switch (thisChar) {
+ case '\'':
+ state = JAVA;
+ break;
+ default:
+ // Fall tru
+ }
+ break;
+ case IN_STR_CONST:
+ switch (thisChar) {
+ case '"':
+ state = JAVA;
+ break;
+ default:
+ // Fall tru
+ }
+ break;
+ case IN_SINGLE_COMMENT:
+ // The current character is always emitted.
+ switch (thisChar) {
+ case '\n':
+ case '\r': // EOL
+ state = JAVA;
+ break;
+ default:
+ // Fall tru
+ }
+ break;
+ case IN_MULTI_COMMENT:
+ // The current character is always emitted.
+ switch (thisChar) {
+ case '*':
+ state = TRANS_FROM_MULTI;
+ break;
+ default:
+ // Fall tru
+ }
+ break;
+ case TRANS_TO_COMMENT:
+ // The current character is always emitted.
+ switch (thisChar) {
+ case '*':
+ state = IN_MULTI_COMMENT;
+ break;
+ case '/':
+ state = IN_SINGLE_COMMENT;
+ break;
+ case '\'':
+ state = IN_CHAR_CONST;
+ break;
+ case '"':
+ state = IN_STR_CONST;
+ break;
+ default:
+ state = JAVA;
+ }
+ break;
+ case TRANS_FROM_MULTI:
+ // The current character is always emitted.
+ switch (thisChar) {
+ case '/':
+ state = JAVA;
+ break;
+ default:
+ // Fall tru
+ }
+ break;
+ default:
+ // Fall tru
+ }
+ return thisChar;
+ }
+ }
+
+ private static class NormalizeEolFilter extends SimpleFilterReader {
+ private boolean previousWasEOL;
+
+ private boolean fixLast;
+
+ private int normalizedEOL = 0;
+
+ private char[] eol = null;
+
+ public NormalizeEolFilter(Reader in, String eolString, boolean fixLast) {
+ super(in);
+ eol = eolString.toCharArray();
+ this.fixLast = fixLast;
+ }
+
+ public int read() throws IOException {
+ int thisChar = super.read();
+
+ if (normalizedEOL == 0) {
+ int numEOL = 0;
+ boolean atEnd = false;
+ switch (thisChar) {
+ case CTRLZ:
+ int c = super.read();
+ if (c == -1) {
+ atEnd = true;
+ if (fixLast && !previousWasEOL) {
+ numEOL = 1;
+ push(thisChar);
+ }
+ } else {
+ push(c);
+ }
+ break;
+ case -1:
+ atEnd = true;
+ if (fixLast && !previousWasEOL) {
+ numEOL = 1;
+ }
+ break;
+ case '\n':
+ // EOL was "\n"
+ numEOL = 1;
+ break;
+ case '\r':
+ numEOL = 1;
+ int c1 = super.read();
+ int c2 = super.read();
+
+ if (c1 == '\r' && c2 == '\n') {
+ // EOL was "\r\r\n"
+ } else if (c1 == '\r') {
+ // EOL was "\r\r" - handle as two consecutive "\r" and
+ // "\r"
+ numEOL = 2;
+ push(c2);
+ } else if (c1 == '\n') {
+ // EOL was "\r\n"
+ push(c2);
+ } else {
+ // EOL was "\r"
+ push(c2);
+ push(c1);
+ }
+ default:
+ // Fall tru
+ }
+ if (numEOL > 0) {
+ while (numEOL-- > 0) {
+ push(eol);
+ normalizedEOL += eol.length;
+ }
+ previousWasEOL = true;
+ thisChar = read();
+ } else if (!atEnd) {
+ previousWasEOL = false;
+ }
+ } else {
+ normalizedEOL--;
+ }
+ return thisChar;
+ }
+ }
+
+ private static class AddEofFilter extends SimpleFilterReader {
+ private int lastChar = -1;
+
+ public AddEofFilter(Reader in) {
+ super(in);
+ }
+
+ public int read() throws IOException {
+ int thisChar = super.read();
+
+ // if source is EOF but last character was NOT ctrl-z, return ctrl-z
+ if (thisChar == -1) {
+ if (lastChar != CTRLZ) {
+ lastChar = CTRLZ;
+ return lastChar;
+ }
+ } else {
+ lastChar = thisChar;
+ }
+ return thisChar;
+ }
+ }
+
+ private static class RemoveEofFilter extends SimpleFilterReader {
+ private int lookAhead = -1;
+
+ public RemoveEofFilter(Reader in) {
+ super(in);
+
+ try {
+ lookAhead = in.read();
+ } catch (IOException e) {
+ lookAhead = -1;
+ }
+ }
+
+ public int read() throws IOException {
+ int lookAhead2 = super.read();
+
+ // If source at EOF and lookAhead is ctrl-z, return EOF (NOT ctrl-z)
+ if (lookAhead2 == -1 && lookAhead == CTRLZ) {
+ return -1;
+ }
+ // Return current look-ahead
+ int i = lookAhead;
+ lookAhead = lookAhead2;
+ return i;
+ }
+ }
+
+ private static class AddTabFilter extends SimpleFilterReader {
+ private int columnNumber = 0;
+
+ private int tabLength = 0;
+
+ public AddTabFilter(Reader in, int tabLength) {
+ super(in);
+ this.tabLength = tabLength;
+ }
+
+ public int read() throws IOException {
+ int c = super.read();
+
+ switch (c) {
+ case '\r':
+ case '\n':
+ columnNumber = 0;
+ break;
+ case ' ':
+ columnNumber++;
+ if (!editsBlocked()) {
+ int colNextTab = ((columnNumber + tabLength - 1) / tabLength) * tabLength;
+ int countSpaces = 1;
+ int numTabs = 0;
+
+ scanWhitespace: while ((c = super.read()) != -1) {
+ switch (c) {
+ case ' ':
+ if (++columnNumber == colNextTab) {
+ numTabs++;
+ countSpaces = 0;
+ colNextTab += tabLength;
+ } else {
+ countSpaces++;
+ }
+ break;
+ case '\t':
+ columnNumber = colNextTab;
+ numTabs++;
+ countSpaces = 0;
+ colNextTab += tabLength;
+ break;
+ default:
+ push(c);
+ break scanWhitespace;
+ }
+ }
+ while (countSpaces-- > 0) {
+ push(' ');
+ columnNumber--;
+ }
+ while (numTabs-- > 0) {
+ push('\t');
+ columnNumber -= tabLength;
+ }
+ c = super.read();
+ switch (c) {
+ case ' ':
+ columnNumber++;
+ break;
+ case '\t':
+ columnNumber += tabLength;
+ break;
+ default:
+ // Fall tru
+ }
+ }
+ break;
+ case '\t':
+ columnNumber = ((columnNumber + tabLength - 1) / tabLength) * tabLength;
+ break;
+ default:
+ columnNumber++;
+ }
+ return c;
+ }
+ }
+
+ private static class RemoveTabFilter extends SimpleFilterReader {
+ private int columnNumber = 0;
+
+ private int tabLength = 0;
+
+ public RemoveTabFilter(Reader in, int tabLength) {
+ super(in);
+
+ this.tabLength = tabLength;
+ }
+
+ public int read() throws IOException {
+ int c = super.read();
+
+ switch (c) {
+ case '\r':
+ case '\n':
+ columnNumber = 0;
+ break;
+ case '\t':
+ int width = tabLength - columnNumber % tabLength;
+
+ if (!editsBlocked()) {
+ for (; width > 1; width--) {
+ push(' ');
+ }
+ c = ' ';
+ }
+ columnNumber += width;
+ break;
+ default:
+ columnNumber++;
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Enumerated attribute with the values "asis", "add" and "remove".
+ */
+ public static class AddAsisRemove extends EnumeratedAttribute {
+ private static final AddAsisRemove ASIS = newInstance("asis");
+
+ private static final AddAsisRemove ADD = newInstance("add");
+
+ private static final AddAsisRemove REMOVE = newInstance("remove");
+
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return new String[] {"add", "asis", "remove"};
+ }
+
+ /**
+ * Equality depending in the index.
+ * @param other the object to test equality against.
+ * @return true if the object has the same index as this.
+ */
+ public boolean equals(Object other) {
+ return other instanceof AddAsisRemove
+ && getIndex() == ((AddAsisRemove) other).getIndex();
+ }
+
+ /**
+ * Hashcode depending on the index.
+ * @return the index as the hashcode.
+ */
+ public int hashCode() {
+ return getIndex();
+ }
+
+ AddAsisRemove resolve() throws IllegalStateException {
+ if (this.equals(ASIS)) {
+ return ASIS;
+ }
+ if (this.equals(ADD)) {
+ return ADD;
+ }
+ if (this.equals(REMOVE)) {
+ return REMOVE;
+ }
+ throw new IllegalStateException("No replacement for " + this);
+ }
+
+ // Works like clone() but doesn't show up in the Javadocs
+ private AddAsisRemove newInstance() {
+ return newInstance(getValue());
+ }
+
+ /**
+ * Create an instance of this enumerated value based on the string value.
+ * @param value the value to use.
+ * @return an enumerated instance.
+ */
+ public static AddAsisRemove newInstance(String value) {
+ AddAsisRemove a = new AddAsisRemove();
+ a.setValue(value);
+ return a;
+ }
+ }
+
+ /**
+ * Enumerated attribute with the values "asis", "cr", "lf" and "crlf".
+ */
+ public static class CrLf extends EnumeratedAttribute {
+ private static final CrLf ASIS = newInstance("asis");
+
+ private static final CrLf CR = newInstance("cr");
+
+ private static final CrLf CRLF = newInstance("crlf");
+
+ private static final CrLf DOS = newInstance("dos");
+
+ private static final CrLf LF = newInstance("lf");
+
+ private static final CrLf MAC = newInstance("mac");
+
+ private static final CrLf UNIX = newInstance("unix");
+
+ /**
+ * @see EnumeratedAttribute#getValues
+ */
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return new String[] {"asis", "cr", "lf", "crlf", "mac", "unix", "dos"};
+ }
+
+ /**
+ * Equality depending in the index.
+ * @param other the object to test equality against.
+ * @return true if the object has the same index as this.
+ */
+ public boolean equals(Object other) {
+ return other instanceof CrLf && getIndex() == ((CrLf) other).getIndex();
+ }
+
+ /**
+ * Hashcode depending on the index.
+ * @return the index as the hashcode.
+ */
+ public int hashCode() {
+ return getIndex();
+ }
+
+ CrLf resolve() {
+ if (this.equals(ASIS)) {
+ return ASIS;
+ }
+ if (this.equals(CR) || this.equals(MAC)) {
+ return CR;
+ }
+ if (this.equals(CRLF) || this.equals(DOS)) {
+ return CRLF;
+ }
+ if (this.equals(LF) || this.equals(UNIX)) {
+ return LF;
+ }
+ throw new IllegalStateException("No replacement for " + this);
+ }
+
+ // Works like clone() but doesn't show up in the Javadocs
+ private CrLf newInstance() {
+ return newInstance(getValue());
+ }
+
+ /**
+ * Create an instance of this enumerated value based on the string value.
+ * @param value the value to use.
+ * @return an enumerated instance.
+ */
+ public static CrLf newInstance(String value) {
+ CrLf c = new CrLf();
+ c.setValue(value);
+ return c;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/HeadFilter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/HeadFilter.java
new file mode 100644
index 00000000..522fe57b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/HeadFilter.java
@@ -0,0 +1,222 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.util.LineTokenizer;
+
+/**
+ * Reads the first <code>n</code> lines of a stream.
+ * (Default is first 10 lines.)
+ * <p>
+ * Example:
+ * <pre>&lt;headfilter lines=&quot;3&quot;/&gt;</pre>
+ * Or:
+ * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.HeadFilter&quot;&gt;
+ * &lt;param name=&quot;lines&quot; value=&quot;3&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ */
+public final class HeadFilter extends BaseParamFilterReader
+ implements ChainableReader {
+ /** Parameter name for the number of lines to be returned. */
+ private static final String LINES_KEY = "lines";
+
+ /** Parameter name for the number of lines to be skipped. */
+ private static final String SKIP_KEY = "skip";
+
+ /** Number of lines currently read in. */
+ private long linesRead = 0;
+
+ /** Default number of lines to show */
+ private static final int DEFAULT_NUM_LINES = 10;
+
+ /** Number of lines to be returned in the filtered stream. */
+ private long lines = DEFAULT_NUM_LINES;
+
+ /** Number of lines to be skipped. */
+ private long skip = 0;
+
+ /** A line tokenizer */
+ private LineTokenizer lineTokenizer = null;
+
+ /** the current line from the input stream */
+ private String line = null;
+ /** the position in the current line */
+ private int linePos = 0;
+
+ /** Whether this filter is finished */
+ private boolean eof;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public HeadFilter() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public HeadFilter(final Reader in) {
+ super(in);
+ lineTokenizer = new LineTokenizer();
+ lineTokenizer.setIncludeDelims(true);
+ }
+
+ /**
+ * Returns the next character in the filtered stream. If the desired
+ * number of lines have already been read, the resulting stream is
+ * effectively at an end. Otherwise, the next character from the
+ * underlying stream is read and returned.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+ public int read() throws IOException {
+ if (!getInitialized()) {
+ initialize();
+ setInitialized(true);
+ }
+
+ while (line == null || line.length() == 0) {
+ line = lineTokenizer.getToken(in);
+ if (line == null) {
+ return -1;
+ }
+ line = headFilter(line);
+ if (eof) {
+ return -1;
+ }
+ linePos = 0;
+ }
+
+ int ch = line.charAt(linePos);
+ linePos++;
+ if (linePos == line.length()) {
+ line = null;
+ }
+ return ch;
+ }
+
+ /**
+ * Sets the number of lines to be returned in the filtered stream.
+ *
+ * @param lines the number of lines to be returned in the filtered stream
+ */
+ public void setLines(final long lines) {
+ this.lines = lines;
+ }
+
+ /**
+ * Returns the number of lines to be returned in the filtered stream.
+ *
+ * @return the number of lines to be returned in the filtered stream
+ */
+ private long getLines() {
+ return lines;
+ }
+
+ /**
+ * Sets the number of lines to be skipped in the filtered stream.
+ *
+ * @param skip the number of lines to be skipped in the filtered stream
+ */
+ public void setSkip(final long skip) {
+ this.skip = skip;
+ }
+
+ /**
+ * Returns the number of lines to be skipped in the filtered stream.
+ *
+ * @return the number of lines to be skipped in the filtered stream
+ */
+ private long getSkip() {
+ return skip;
+ }
+
+ /**
+ * Creates a new HeadFilter using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ HeadFilter newFilter = new HeadFilter(rdr);
+ newFilter.setLines(getLines());
+ newFilter.setSkip(getSkip());
+ newFilter.setInitialized(true);
+ return newFilter;
+ }
+
+ /**
+ * Scans the parameters list for the "lines" parameter and uses
+ * it to set the number of lines to be returned in the filtered stream.
+ * also scan for skip parameter.
+ */
+ private void initialize() {
+ Parameter[] params = getParameters();
+ if (params != null) {
+ for (int i = 0; i < params.length; i++) {
+ if (LINES_KEY.equals(params[i].getName())) {
+ lines = Long.parseLong(params[i].getValue());
+ continue;
+ }
+ if (SKIP_KEY.equals(params[i].getName())) {
+ skip = Long.parseLong(params[i].getValue());
+ continue;
+ }
+ }
+ }
+ }
+
+ /**
+ * implements a head filter on the input stream
+ */
+ private String headFilter(String line) {
+ linesRead++;
+ if (skip > 0) {
+ if ((linesRead - 1) < skip) {
+ return null;
+ }
+ }
+
+ if (lines > 0) {
+ if (linesRead > (lines + skip)) {
+ eof = true;
+ return null;
+ }
+ }
+ return line;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/LineContains.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/LineContains.java
new file mode 100644
index 00000000..c83cae28
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/LineContains.java
@@ -0,0 +1,244 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Vector;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Filter which includes only those lines that contain all the user-specified
+ * strings.
+ *
+ * Example:
+ *
+ * <pre>&lt;linecontains&gt;
+ * &lt;contains value=&quot;foo&quot;&gt;
+ * &lt;contains value=&quot;bar&quot;&gt;
+ * &lt;/linecontains&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.LineContains&quot;&gt;
+ * &lt;param type=&quot;contains&quot; value=&quot;foo&quot;/&gt;
+ * &lt;param type=&quot;contains&quot; value=&quot;bar&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ * This will include only those lines that contain <code>foo</code> and
+ * <code>bar</code>.
+ *
+ */
+public final class LineContains
+ extends BaseParamFilterReader
+ implements ChainableReader {
+ /** Parameter name for the words to filter on. */
+ private static final String CONTAINS_KEY = "contains";
+
+ /** Parameter name for the words to filter on. */
+ private static final String NEGATE_KEY = "negate";
+
+ /** Vector that holds the strings that input lines must contain. */
+ private Vector<String> contains = new Vector<String>();
+
+ /**
+ * Remaining line to be read from this filter, or <code>null</code> if
+ * the next call to <code>read()</code> should read the original stream
+ * to find the next matching line.
+ */
+ private String line = null;
+
+ private boolean negate = false;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public LineContains() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public LineContains(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Returns the next character in the filtered stream, only including
+ * lines from the original stream which contain all of the specified words.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+ public int read() throws IOException {
+ if (!getInitialized()) {
+ initialize();
+ setInitialized(true);
+ }
+
+ int ch = -1;
+
+ if (line != null) {
+ ch = line.charAt(0);
+ if (line.length() == 1) {
+ line = null;
+ } else {
+ line = line.substring(1);
+ }
+ } else {
+ final int containsSize = contains.size();
+
+ for (line = readLine(); line != null; line = readLine()) {
+ boolean matches = true;
+ for (int i = 0; matches && i < containsSize; i++) {
+ String containsStr = (String) contains.elementAt(i);
+ matches = line.indexOf(containsStr) >= 0;
+ }
+ if (matches ^ isNegated()) {
+ break;
+ }
+ }
+ if (line != null) {
+ return read();
+ }
+ }
+ return ch;
+ }
+
+ /**
+ * Adds a <code>contains</code> element.
+ *
+ * @param contains The <code>contains</code> element to add.
+ * Must not be <code>null</code>.
+ */
+ public void addConfiguredContains(final Contains contains) {
+ this.contains.addElement(contains.getValue());
+ }
+
+ /**
+ * Set the negation mode. Default false (no negation).
+ * @param b the boolean negation mode to set.
+ */
+ public void setNegate(boolean b) {
+ negate = b;
+ }
+
+ /**
+ * Find out whether we have been negated.
+ * @return boolean negation flag.
+ */
+ public boolean isNegated() {
+ return negate;
+ }
+
+ /**
+ * Sets the vector of words which must be contained within a line read
+ * from the original stream in order for it to match this filter.
+ *
+ * @param contains A vector of words which must be contained within a line
+ * in order for it to match in this filter. Must not be <code>null</code>.
+ */
+ private void setContains(final Vector<String> contains) {
+ this.contains = contains;
+ }
+
+ /**
+ * Returns the vector of words which must be contained within a line read
+ * from the original stream in order for it to match this filter.
+ *
+ * @return the vector of words which must be contained within a line read
+ * from the original stream in order for it to match this filter. The
+ * returned object is "live" - in other words, changes made to the
+ * returned object are mirrored in the filter.
+ */
+ private Vector<String> getContains() {
+ return contains;
+ }
+
+ /**
+ * Creates a new LineContains using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ LineContains newFilter = new LineContains(rdr);
+ newFilter.setContains(getContains());
+ newFilter.setNegate(isNegated());
+ return newFilter;
+ }
+
+ /**
+ * Parses the parameters to add user-defined contains strings.
+ */
+ private void initialize() {
+ Parameter[] params = getParameters();
+ if (params != null) {
+ for (int i = 0; i < params.length; i++) {
+ if (CONTAINS_KEY.equals(params[i].getType())) {
+ contains.addElement(params[i].getValue());
+ } else if (NEGATE_KEY.equals(params[i].getType())) {
+ setNegate(Project.toBoolean(params[i].getValue()));
+ }
+ }
+ }
+ }
+
+ /**
+ * Holds a contains element
+ */
+ public static class Contains {
+
+ /** User defined contains string */
+ private String value;
+
+ /**
+ * Sets the contains string
+ *
+ * @param contains The contains string to set.
+ * Must not be <code>null</code>.
+ */
+ public final void setValue(String contains) {
+ value = contains;
+ }
+
+ /**
+ * Returns the contains string.
+ *
+ * @return the contains string for this element
+ */
+ public final String getValue() {
+ return value;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/LineContainsRegExp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/LineContainsRegExp.java
new file mode 100644
index 00000000..23a2005d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/LineContainsRegExp.java
@@ -0,0 +1,242 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Vector;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.RegularExpression;
+import org.apache.tools.ant.util.regexp.Regexp;
+import org.apache.tools.ant.util.regexp.RegexpUtil;
+
+/**
+ * Filter which includes only those lines that contain the user-specified
+ * regular expression matching strings.
+ *
+ * Example:
+ * <pre>&lt;linecontainsregexp&gt;
+ * &lt;regexp pattern=&quot;foo*&quot;&gt;
+ * &lt;/linecontainsregexp&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.LineContainsRegExp&quot;&gt;
+ * &lt;param type=&quot;regexp&quot; value=&quot;foo*&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ * This will fetch all those lines that contain the pattern <code>foo</code>
+ *
+ */
+public final class LineContainsRegExp
+ extends BaseParamFilterReader
+ implements ChainableReader {
+ /** Parameter name for the regular expression to filter on. */
+ private static final String REGEXP_KEY = "regexp";
+
+ /** Parameter name for the negate attribute. */
+ private static final String NEGATE_KEY = "negate";
+
+ /** Parameter name for the casesensitive attribute. */
+ private static final String CS_KEY = "casesensitive";
+
+ /** Vector that holds the expressions that input lines must contain. */
+ private Vector<RegularExpression> regexps = new Vector<RegularExpression>();
+
+ /**
+ * Remaining line to be read from this filter, or <code>null</code> if
+ * the next call to <code>read()</code> should read the original stream
+ * to find the next matching line.
+ */
+ private String line = null;
+
+ private boolean negate = false;
+ private int regexpOptions = Regexp.MATCH_DEFAULT;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public LineContainsRegExp() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public LineContainsRegExp(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Returns the next character in the filtered stream, only including
+ * lines from the original stream which match all of the specified
+ * regular expressions.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+ public int read() throws IOException {
+ if (!getInitialized()) {
+ initialize();
+ setInitialized(true);
+ }
+
+ int ch = -1;
+
+ if (line != null) {
+ ch = line.charAt(0);
+ if (line.length() == 1) {
+ line = null;
+ } else {
+ line = line.substring(1);
+ }
+ } else {
+ final int regexpsSize = regexps.size();
+
+ for (line = readLine(); line != null; line = readLine()) {
+ boolean matches = true;
+ for (int i = 0; matches && i < regexpsSize; i++) {
+ RegularExpression regexp
+ = (RegularExpression) regexps.elementAt(i);
+ Regexp re = regexp.getRegexp(getProject());
+ matches = re.matches(line, regexpOptions);
+ }
+ if (matches ^ isNegated()) {
+ break;
+ }
+ }
+ if (line != null) {
+ return read();
+ }
+ }
+ return ch;
+ }
+
+ /**
+ * Adds a <code>regexp</code> element.
+ *
+ * @param regExp The <code>regexp</code> element to add.
+ * Must not be <code>null</code>.
+ */
+ public void addConfiguredRegexp(final RegularExpression regExp) {
+ this.regexps.addElement(regExp);
+ }
+
+ /**
+ * Sets the vector of regular expressions which must be contained within
+ * a line read from the original stream in order for it to match this
+ * filter.
+ *
+ * @param regexps A vector of regular expressions which must be contained
+ * within a line in order for it to match in this filter. Must not be
+ * <code>null</code>.
+ */
+ private void setRegexps(final Vector<RegularExpression> regexps) {
+ this.regexps = regexps;
+ }
+
+ /**
+ * Returns the vector of regular expressions which must be contained within
+ * a line read from the original stream in order for it to match this
+ * filter.
+ *
+ * @return the vector of regular expressions which must be contained within
+ * a line read from the original stream in order for it to match this
+ * filter. The returned object is "live" - in other words, changes made to
+ * the returned object are mirrored in the filter.
+ */
+ private Vector<RegularExpression> getRegexps() {
+ return regexps;
+ }
+
+ /**
+ * Creates a new LineContainsRegExp using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ LineContainsRegExp newFilter = new LineContainsRegExp(rdr);
+ newFilter.setRegexps(getRegexps());
+ newFilter.setNegate(isNegated());
+ newFilter
+ .setCaseSensitive(!RegexpUtil.hasFlag(regexpOptions,
+ Regexp.MATCH_CASE_INSENSITIVE)
+ );
+ return newFilter;
+ }
+
+ /**
+ * Set the negation mode. Default false (no negation).
+ * @param b the boolean negation mode to set.
+ */
+ public void setNegate(boolean b) {
+ negate = b;
+ }
+
+ /**
+ * Whether to match casesensitevly.
+ * @since Ant 1.8.2
+ */
+ public void setCaseSensitive(boolean b) {
+ regexpOptions = RegexpUtil.asOptions(b);
+ }
+
+ /**
+ * Find out whether we have been negated.
+ * @return boolean negation flag.
+ */
+ public boolean isNegated() {
+ return negate;
+ }
+
+ /**
+ * Parses parameters to add user defined regular expressions.
+ */
+ private void initialize() {
+ Parameter[] params = getParameters();
+ if (params != null) {
+ for (int i = 0; i < params.length; i++) {
+ if (REGEXP_KEY.equals(params[i].getType())) {
+ String pattern = params[i].getValue();
+ RegularExpression regexp = new RegularExpression();
+ regexp.setPattern(pattern);
+ regexps.addElement(regexp);
+ } else if (NEGATE_KEY.equals(params[i].getType())) {
+ setNegate(Project.toBoolean(params[i].getValue()));
+ } else if (CS_KEY.equals(params[i].getType())) {
+ setCaseSensitive(Project.toBoolean(params[i].getValue()));
+ }
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/PrefixLines.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/PrefixLines.java
new file mode 100644
index 00000000..324397e8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/PrefixLines.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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Attaches a prefix to every line.
+ *
+ * Example:
+ * <pre>&lt;prefixlines prefix=&quot;Foo&quot;/&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.PrefixLines&quot;&gt;
+ * &lt;param name=&quot;prefix&quot; value=&quot;Foo&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ */
+public final class PrefixLines
+ extends BaseParamFilterReader
+ implements ChainableReader {
+ /** Parameter name for the prefix. */
+ private static final String PREFIX_KEY = "prefix";
+
+ /** The prefix to be used. */
+ private String prefix = null;
+
+ /** Data that must be read from, if not null. */
+ private String queuedData = null;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public PrefixLines() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public PrefixLines(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Returns the next character in the filtered stream. One line is read
+ * from the original input, and the prefix added. The resulting
+ * line is then used until it ends, at which point the next original line
+ * is read, etc.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+ public int read() throws IOException {
+ if (!getInitialized()) {
+ initialize();
+ setInitialized(true);
+ }
+
+ int ch = -1;
+
+ if (queuedData != null && queuedData.length() == 0) {
+ queuedData = null;
+ }
+
+ if (queuedData != null) {
+ ch = queuedData.charAt(0);
+ queuedData = queuedData.substring(1);
+ if (queuedData.length() == 0) {
+ queuedData = null;
+ }
+ } else {
+ queuedData = readLine();
+ if (queuedData == null) {
+ ch = -1;
+ } else {
+ if (prefix != null) {
+ queuedData = prefix + queuedData;
+ }
+ return read();
+ }
+ }
+ return ch;
+ }
+
+ /**
+ * Sets the prefix to add at the start of each input line.
+ *
+ * @param prefix The prefix to add at the start of each input line.
+ * May be <code>null</code>, in which case no prefix
+ * is added.
+ */
+ public void setPrefix(final String prefix) {
+ this.prefix = prefix;
+ }
+
+ /**
+ * Returns the prefix which will be added at the start of each input line.
+ *
+ * @return the prefix which will be added at the start of each input line
+ */
+ private String getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * Creates a new PrefixLines filter using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ PrefixLines newFilter = new PrefixLines(rdr);
+ newFilter.setPrefix(getPrefix());
+ newFilter.setInitialized(true);
+ return newFilter;
+ }
+
+ /**
+ * Initializes the prefix if it is available from the parameters.
+ */
+ private void initialize() {
+ Parameter[] params = getParameters();
+ if (params != null) {
+ for (int i = 0; i < params.length; i++) {
+ if (PREFIX_KEY.equals(params[i].getName())) {
+ prefix = params[i].getValue();
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ReplaceTokens.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ReplaceTokens.java
new file mode 100644
index 00000000..21ca3bc9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/ReplaceTokens.java
@@ -0,0 +1,379 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Replaces tokens in the original input with user-supplied values.
+ *
+ * Example:
+ *
+ * <pre>&lt;replacetokens begintoken=&quot;#&quot; endtoken=&quot;#&quot;&gt;
+ * &lt;token key=&quot;DATE&quot; value=&quot;${TODAY}&quot;/&gt;
+ * &lt;/replacetokens&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader classname="org.apache.tools.ant.filters.ReplaceTokens"&gt;
+ * &lt;param type="tokenchar" name="begintoken" value="#"/&gt;
+ * &lt;param type="tokenchar" name="endtoken" value="#"/&gt;
+ * &lt;param type="token" name="DATE" value="${TODAY}"/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ */
+public final class ReplaceTokens
+ extends BaseParamFilterReader
+ implements ChainableReader {
+ /** Default "begin token" character. */
+ private static final String DEFAULT_BEGIN_TOKEN = "@";
+
+ /** Default "end token" character. */
+ private static final String DEFAULT_END_TOKEN = "@";
+
+ /** Hashtable to holds the original replacee-replacer pairs (String to String). */
+ private Hashtable<String, String> hash = new Hashtable<String, String>();
+
+ /** This map holds the "resolved" tokens (begin- and end-tokens are added to make searching simpler) */
+ private final TreeMap<String, String> resolvedTokens = new TreeMap<String, String>();
+ private boolean resolvedTokensBuilt = false;
+ /** Used for comparisons and lookup into the resolvedTokens map. */
+ private String readBuffer = "";
+
+ /** replacement test from a token */
+ private String replaceData = null;
+
+ /** Index into replacement data */
+ private int replaceIndex = -1;
+
+ /** Character marking the beginning of a token. */
+ private String beginToken = DEFAULT_BEGIN_TOKEN;
+
+ /** Character marking the end of a token. */
+ private String endToken = DEFAULT_END_TOKEN;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public ReplaceTokens() {}
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public ReplaceTokens(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Returns the next character in the filtered stream, replacing tokens
+ * from the original stream.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+ public int read() throws IOException {
+ if (!getInitialized()) {
+ initialize();
+ setInitialized(true);
+ }
+
+ if (!resolvedTokensBuilt) {
+ // build the resolved tokens tree map.
+ for (String key : hash.keySet()) {
+ resolvedTokens.put(beginToken + key + endToken, hash.get(key));
+ }
+ resolvedTokensBuilt = true;
+ }
+
+ // are we currently serving replace data?
+ if (replaceData != null) {
+ if (replaceIndex < replaceData.length()) {
+ return replaceData.charAt(replaceIndex++);
+ } else {
+ replaceData = null;
+ }
+ }
+
+ // is the read buffer empty?
+ if (readBuffer.length() == 0) {
+ int next = in.read();
+ if (next == -1) {
+ return next; // end of stream. all buffers empty.
+ }
+ readBuffer += (char)next;
+ }
+
+ for (;;) {
+ // get the closest tokens
+ SortedMap<String,String> possibleTokens = resolvedTokens.tailMap(readBuffer);
+ if (possibleTokens.isEmpty() || !possibleTokens.firstKey().startsWith(readBuffer)) { // if there is none, then deliver the first char from the buffer.
+ return getFirstCharacterFromReadBuffer();
+ } else if (readBuffer.equals(possibleTokens.firstKey())) { // there exists a nearest token - is it an exact match?
+ // we have found a token. prepare the replaceData buffer.
+ replaceData = resolvedTokens.get(readBuffer);
+ replaceIndex = 0;
+ readBuffer = ""; // destroy the readBuffer - it's contents are being replaced entirely.
+ // get the first character via recursive call.
+ return read();
+ } else { // nearest token is not matching exactly - read one character more.
+ int next = in.read();
+ if (next != -1) {
+ readBuffer += (char)next;
+ } else {
+ return getFirstCharacterFromReadBuffer(); // end of stream. deliver remaining characters from buffer.
+ }
+ }
+ }
+ }
+
+ /**
+ * @return the first character from the read buffer or -1 if read buffer is empty.
+ */
+ private int getFirstCharacterFromReadBuffer() {
+ if (readBuffer.length() > 0) {
+ int chr = readBuffer.charAt(0);
+ readBuffer = readBuffer.substring(1);
+ return chr;
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Sets the "begin token" character.
+ *
+ * @param beginToken the character used to denote the beginning of a token
+ */
+ public void setBeginToken(final String beginToken) {
+ this.beginToken = beginToken;
+ }
+
+ /**
+ * Returns the "begin token" character.
+ *
+ * @return the character used to denote the beginning of a token
+ */
+ private String getBeginToken() {
+ return beginToken;
+ }
+
+ /**
+ * Sets the "end token" character.
+ *
+ * @param endToken the character used to denote the end of a token
+ */
+ public void setEndToken(final String endToken) {
+ this.endToken = endToken;
+ }
+
+ /**
+ * Returns the "end token" character.
+ *
+ * @return the character used to denote the end of a token
+ */
+ private String getEndToken() {
+ return endToken;
+ }
+
+ /**
+ * A resource containing properties, each of which is interpreted
+ * as a token/value pair.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setPropertiesResource(Resource r) {
+ makeTokensFromProperties(r);
+ }
+
+ /**
+ * Adds a token element to the map of tokens to replace.
+ *
+ * @param token The token to add to the map of replacements.
+ * Must not be <code>null</code>.
+ */
+ public void addConfiguredToken(final Token token) {
+ hash.put(token.getKey(), token.getValue());
+ resolvedTokensBuilt = false; // invalidate to build them again if they have been built already.
+ }
+
+ /**
+ * Returns properties from a specified properties file.
+ *
+ * @param resource The resource to load properties from.
+ */
+ private Properties getProperties(Resource resource) {
+ InputStream in = null;
+ Properties props = new Properties();
+ try {
+ in = resource.getInputStream();
+ props.load(in);
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ } finally {
+ FileUtils.close(in);
+ }
+
+ return props;
+ }
+
+ /**
+ * Sets the map of tokens to replace.
+ *
+ * @param hash A map (String->String) of token keys to replacement
+ * values. Must not be <code>null</code>.
+ */
+ private void setTokens(final Hashtable<String, String> hash) {
+ this.hash = hash;
+ }
+
+ /**
+ * Returns the map of tokens which will be replaced.
+ *
+ * @return a map (String->String) of token keys to replacement
+ * values
+ */
+ private Hashtable<String, String> getTokens() {
+ return hash;
+ }
+
+ /**
+ * Creates a new ReplaceTokens using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ ReplaceTokens newFilter = new ReplaceTokens(rdr);
+ newFilter.setBeginToken(getBeginToken());
+ newFilter.setEndToken(getEndToken());
+ newFilter.setTokens(getTokens());
+ newFilter.setInitialized(true);
+ return newFilter;
+ }
+
+ /**
+ * Initializes tokens and loads the replacee-replacer hashtable.
+ */
+ private void initialize() {
+ Parameter[] params = getParameters();
+ if (params != null) {
+ for (Parameter param : params) {
+ if (param != null) {
+ final String type = param.getType();
+ if ("tokenchar".equals(type)) {
+ final String name = param.getName();
+ if ("begintoken".equals(name)) {
+ beginToken = param.getValue();
+ } else if ("endtoken".equals(name)) {
+ endToken = param.getValue();
+ }
+ } else if ("token".equals(type)) {
+ final String name = param.getName();
+ final String value = param.getValue();
+ hash.put(name, value);
+ } else if ("propertiesfile".equals(type)) {
+ makeTokensFromProperties(
+ new FileResource(new File(param.getValue())));
+ }
+ }
+ }
+ }
+ }
+
+ private void makeTokensFromProperties(Resource r) {
+ Properties props = getProperties(r);
+ for (Enumeration<?> e = props.keys(); e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ String value = props.getProperty(key);
+ hash.put(key, value);
+ }
+ }
+
+ /**
+ * Holds a token
+ */
+ public static class Token {
+
+ /** Token key */
+ private String key;
+
+ /** Token value */
+ private String value;
+
+ /**
+ * Sets the token key
+ *
+ * @param key The key for this token. Must not be <code>null</code>.
+ */
+ public final void setKey(String key) {
+ this.key = key;
+ }
+
+ /**
+ * Sets the token value
+ *
+ * @param value The value for this token. Must not be <code>null</code>.
+ */
+ public final void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Returns the key for this token.
+ *
+ * @return the key for this token
+ */
+ public final String getKey() {
+ return key;
+ }
+
+ /**
+ * Returns the value for this token.
+ *
+ * @return the value for this token
+ */
+ public final String getValue() {
+ return value;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/SortFilter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/SortFilter.java
new file mode 100644
index 00000000..471660c3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/SortFilter.java
@@ -0,0 +1,375 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * <p>
+ * Sort a file before and/or after the file.
+ * </p>
+ *
+ * <p>
+ * Examples:
+ * </p>
+ *
+ * <pre>
+ * &lt;copy todir=&quot;build&quot;&gt;
+ * &lt;fileset dir=&quot;input&quot; includes=&quot;*.txt&quot;/&gt;
+ * &lt;filterchain&gt;
+ * &lt;sortfilter/&gt;
+ * &lt;/filterchain&gt;
+ * &lt;/copy&gt;
+ * </pre>
+ *
+ * <p>
+ * Sort all files <code>*.txt</code> from <i>src</i> location and copy
+ * them into <i>build</i> location. The lines of each file are sorted
+ * in ascendant order comparing the lines via the
+ * <code>String.compareTo(Object o)</code> method.
+ * </p>
+ *
+ * <pre>
+ * &lt;copy todir=&quot;build&quot;&gt;
+ * &lt;fileset dir=&quot;input&quot; includes=&quot;*.txt&quot;/&gt;
+ * &lt;filterchain&gt;
+ * &lt;sortfilter reverse=&quot;true&quot;/&gt;
+ * &lt;/filterchain&gt;
+ * &lt;/copy&gt;
+ * </pre>
+ *
+ * <p>
+ * Sort all files <code>*.txt</code> from <i>src</i> location into reverse
+ * order and copy them into <i>build</i> location. If reverse parameter has
+ * value <code>true</code> (default value), then the output line of the files
+ * will be in ascendant order.
+ * </p>
+ *
+ * <pre>
+ * &lt;copy todir=&quot;build&quot;&gt;
+ * &lt;fileset dir=&quot;input&quot; includes=&quot;*.txt&quot;/&gt;
+ * &lt;filterchain&gt;
+ * &lt;filterreader classname=&quot;org.apache.tools.ant.filters.SortFilter&quot;&gt;
+ * &lt;param name=&quot;comparator&quot; value=&quot;org.apache.tools.ant.filters.EvenFirstCmp&quot;/&gt;
+ * &lt;/filterreader&gt;
+ * &lt;/filterchain&gt;
+ * &lt;/copy&gt;
+ * </pre>
+ *
+ * <p>
+ * Sort all files <code>*.txt</code> from <i>src</i> location using as
+ * sorting criterium <code>EvenFirstCmp</code> class, that sorts the file
+ * lines putting even lines first then odd lines for example. The modified files
+ * are copied into <i>build</i> location. The <code>EvenFirstCmp</code>,
+ * has to an instanciable class via <code>Class.newInstance()</code>,
+ * therefore in case of inner class has to be <em>static</em>. It also has to
+ * implement <code>java.util.Comparator</code> interface, for example:
+ * </p>
+ *
+ * <pre>
+ * package org.apache.tools.ant.filters;
+ * ...(omitted)
+ * public final class EvenFirstCmp implements &lt;b&gt;Comparator&lt;/b&gt; {
+ * public int compare(Object o1, Object o2) {
+ * ...(omitted)
+ * }
+ * }
+ * </pre>
+ *
+ * <p>The example above is equivalent to:</p>
+ *
+ * <blockquote><pre>
+ * &lt;componentdef name="evenfirst"
+ * classname="org.apache.tools.ant.filters.EvenFirstCmp&quot;/&gt;
+ * &lt;copy todir=&quot;build&quot;&gt;
+ * &lt;fileset dir=&quot;input&quot; includes=&quot;*.txt&quot;/&gt;
+ * &lt;filterchain&gt;
+ * &lt;sortfilter&gt;
+ * &lt;evenfirst/&gt;
+ * &lt;/sortfilter&gt;
+ * &lt;/filterchain&gt;
+ * &lt;/copy&gt;
+ * </pre></blockquote>
+ *
+ * <p> If parameter <code>comparator</code> is present, then
+ * <code>reverse</code> parameter will not be taken into account. </p>
+ *
+ * @since Ant 1.8.0
+ */
+public final class SortFilter extends BaseParamFilterReader
+ implements ChainableReader {
+
+ /** Parameter name for reverse order. */
+ private static final String REVERSE_KEY = "reverse";
+
+ /**
+ * Parameter name for specifying the comparator criteria via class that
+ * implement <code>java.util.Comparator</code> interface.
+ */
+ private static final String COMPARATOR_KEY = "comparator";
+
+ /**
+ * Instance of comparator class to be used for sorting.
+ */
+ private Comparator<? super String> comparator = null;
+
+ /**
+ * Controls if the sorting process will be in ascendant/descendant order. If
+ * If has value <code>true</code>, then the line of the file will be
+ * sorted on descendant order. Default value: <code>false</code>. It will
+ * be considered only if <code>comparator</code> is <code>null</code>.
+ */
+ private boolean reverse;
+
+ /**
+ * Stores the lines to be sorted.
+ */
+ private List<String> lines;
+
+ /**
+ * Remaining line to be read from this filter, or <code>null</code> if the
+ * next call to <code>read()</code> should read the original stream to
+ * find the next matching line.
+ */
+ private String line = null;
+
+ private Iterator<String> iterator = null;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public SortFilter() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in
+ * A Reader object providing the underlying stream. Must not be
+ * <code>null</code>.
+ */
+ public SortFilter(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Returns the next character in the filtered stream. If the desired number
+ * of lines have already been read, the resulting stream is effectively at
+ * an end. Otherwise, the next character from the underlying stream is read
+ * and returned.
+ *
+ * @return the next character in the resulting stream, or -1 if the end of
+ * the resulting stream has been reached
+ *
+ * @exception IOException
+ * if the underlying stream throws an IOException during
+ * reading
+ */
+ public int read() throws IOException {
+ if (!getInitialized()) {
+ initialize();
+ setInitialized(true);
+ }
+
+ int ch = -1;
+ if (line != null) {
+ /*
+ * We are on the state: "reading the current line", lines are
+ * already sorted
+ */
+ ch = line.charAt(0);
+ if (line.length() == 1) {
+ line = null;
+ } else {
+ line = line.substring(1);
+ }
+ } else {
+ if (lines == null) {
+ // We read all lines and sort them
+ lines = new ArrayList<String>();
+ for (line = readLine(); line != null; line = readLine()) {
+ lines.add(line);
+ }
+ sort();
+ iterator = lines.iterator();
+ }
+
+ if (iterator.hasNext()) {
+ line = (String) iterator.next();
+ } else {
+ line = null;
+ lines = null;
+ iterator = null;
+ }
+ if (line != null) {
+ return read();
+ }
+ }
+ return ch;
+ }
+
+ /**
+ * Creates a new SortReader using the passed in Reader for instantiation.
+ *
+ * @param rdr
+ * A Reader object providing the underlying stream. Must not be
+ * <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering the
+ * specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ SortFilter newFilter = new SortFilter(rdr);
+ newFilter.setReverse(isReverse());
+ newFilter.setComparator(getComparator());
+ newFilter.setInitialized(true);
+ return newFilter;
+ }
+
+ /**
+ * Returns <code>true</code> if the sorting process will be in reverse
+ * order, otherwise the sorting process will be in ascendant order.
+ *
+ * @return <code>true</code> if the sorting process will be in reverse
+ * order, otherwise the sorting process will be in ascendant order.
+ */
+ public boolean isReverse() {
+ return reverse;
+ }
+
+ /**
+ * Sets the sorting process will be in ascendant (<code>reverse=false</code>)
+ * or to descendant (<code>reverse=true</code>).
+ *
+ * @param reverse
+ * Boolean representing reverse ordering process.
+ */
+ public void setReverse(boolean reverse) {
+ this.reverse = reverse;
+ }
+
+ /**
+ * Returns the comparator to be used for sorting.
+ *
+ * @return the comparator
+ */
+ public Comparator<? super String> getComparator() {
+ return comparator;
+ }
+
+ /**
+ * Set the comparator to be used as sorting criterium.
+ *
+ * @param comparator
+ * the comparator to set
+ */
+ public void setComparator(Comparator<? super String> comparator) {
+ this.comparator = comparator;
+ }
+
+ /**
+ * Set the comparator to be used as sorting criterion as nested element.
+ *
+ * @param comparator
+ * the comparator to set
+ */
+ public void add(Comparator<? super String> comparator) {
+ if (this.comparator != null && comparator != null) {
+ throw new BuildException("can't have more than one comparator");
+ }
+ setComparator(comparator);
+ }
+
+ /**
+ * Scans the parameters list
+ */
+ private void initialize() throws IOException {
+ // get parameters
+ Parameter[] params = getParameters();
+ if (params != null) {
+ for (int i = 0; i < params.length; i++) {
+ final String paramName = params[i].getName();
+ if (REVERSE_KEY.equals(paramName)) {
+ setReverse(Boolean.valueOf(params[i].getValue())
+ .booleanValue());
+ continue;
+ }
+ if (COMPARATOR_KEY.equals(paramName)) {
+ try {
+ String className = (String) params[i].getValue();
+ @SuppressWarnings("unchecked")
+ final Comparator<? super String> comparatorInstance = (Comparator<? super String>) (Class
+ .forName(className).newInstance());
+ setComparator(comparatorInstance);
+ continue;
+ } catch (InstantiationException e) {
+ throw new BuildException(e);
+ } catch (IllegalAccessException e) {
+ /*
+ * Probably a inner non-static class, this this case is
+ * not considered
+ */
+ throw new BuildException(e);
+ } catch (ClassNotFoundException e) {
+ throw new BuildException(e);
+ } catch (ClassCastException e) {
+ throw new BuildException("Value of comparator attribute"
+ + " should implement"
+ + " java.util.Comparator"
+ + " interface");
+ } catch (Exception e) {
+ throw new BuildException(e);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Sorts the read lines (<code>lines</code>) according to the sorting
+ * criteria defined by the user.
+ *
+ */
+ private void sort() {
+ if (comparator == null) {
+ if (reverse) {
+ Collections.sort(lines, new Comparator<String>() {
+ public int compare(String s1, String s2) {
+ return (-s1.compareTo(s2));
+ }
+ });
+ } else {
+ Collections.sort(lines);
+ }
+ } else {
+ Collections.sort(lines, comparator);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StringInputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StringInputStream.java
new file mode 100644
index 00000000..e150e965
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StringInputStream.java
@@ -0,0 +1,49 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.StringReader;
+
+import org.apache.tools.ant.util.ReaderInputStream;
+
+/**
+ * Wraps a String as an InputStream.
+ *
+ */
+public class StringInputStream extends ReaderInputStream {
+
+ /**
+ * Composes a stream from a String
+ *
+ * @param source The string to read from. Must not be <code>null</code>.
+ */
+ public StringInputStream(String source) {
+ super(new StringReader(source));
+ }
+
+ /**
+ * Composes a stream from a String with the specified encoding
+ *
+ * @param source The string to read from. Must not be <code>null</code>.
+ * @param encoding The encoding scheme. Also must not be <code>null</code>.
+ */
+ public StringInputStream(String source, String encoding) {
+ super(new StringReader(source), encoding);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StripJavaComments.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StripJavaComments.java
new file mode 100644
index 00000000..65bccd70
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StripJavaComments.java
@@ -0,0 +1,145 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * This is a Java comment and string stripper reader that filters
+ * those lexical tokens out for purposes of simple Java parsing.
+ * (if you have more complex Java parsing needs, use a real lexer).
+ * Since this class heavily relies on the single char read function,
+ * you are recommended to make it work on top of a buffered reader.
+ *
+ */
+public final class StripJavaComments
+ extends BaseFilterReader
+ implements ChainableReader {
+
+ /**
+ * The read-ahead character, used for effectively pushing a single
+ * character back. A value of -1 indicates that no character is in the
+ * buffer.
+ */
+ private int readAheadCh = -1;
+
+ /**
+ * Whether or not the parser is currently in the middle of a string
+ * literal.
+ */
+ private boolean inString = false;
+
+ /**
+ * Whether or not the last char has been a backslash.
+ */
+ private boolean quoted = false;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public StripJavaComments() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public StripJavaComments(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Returns the next character in the filtered stream, not including
+ * Java comments.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+ public int read() throws IOException {
+ int ch = -1;
+ if (readAheadCh != -1) {
+ ch = readAheadCh;
+ readAheadCh = -1;
+ } else {
+ ch = in.read();
+ if (ch == '"' && !quoted) {
+ inString = !inString;
+ quoted = false;
+ } else if (ch == '\\') {
+ quoted = !quoted;
+ } else {
+ quoted = false;
+ if (!inString) {
+ if (ch == '/') {
+ ch = in.read();
+ if (ch == '/') {
+ while (ch != '\n' && ch != -1 && ch != '\r') {
+ ch = in.read();
+ }
+ } else if (ch == '*') {
+ while (ch != -1) {
+ ch = in.read();
+ if (ch == '*') {
+ ch = in.read();
+ while (ch == '*') {
+ ch = in.read();
+ }
+
+ if (ch == '/') {
+ ch = read();
+ break;
+ }
+ }
+ }
+ } else {
+ readAheadCh = ch;
+ ch = '/';
+ }
+ }
+ }
+ }
+ }
+
+ return ch;
+ }
+
+ /**
+ * Creates a new StripJavaComments using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+
+ public Reader chain(final Reader rdr) {
+ StripJavaComments newFilter = new StripJavaComments(rdr);
+ return newFilter;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StripLineBreaks.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StripLineBreaks.java
new file mode 100644
index 00000000..9a97940a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StripLineBreaks.java
@@ -0,0 +1,154 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Filter to flatten the stream to a single line.
+ *
+ * Example:
+ *
+ * <pre>&lt;striplinebreaks/&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader
+ * classname=&quot;org.apache.tools.ant.filters.StripLineBreaks&quot;/&gt;</pre>
+ *
+ */
+public final class StripLineBreaks
+ extends BaseParamFilterReader
+ implements ChainableReader {
+ /**
+ * Line-breaking characters.
+ * What should we do on funny IBM mainframes with odd line endings?
+ */
+ private static final String DEFAULT_LINE_BREAKS = "\r\n";
+
+ /** Parameter name for the line-breaking characters parameter. */
+ private static final String LINE_BREAKS_KEY = "linebreaks";
+
+ /** The characters that are recognized as line breaks. */
+ private String lineBreaks = DEFAULT_LINE_BREAKS;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public StripLineBreaks() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public StripLineBreaks(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Returns the next character in the filtered stream, only including
+ * characters not in the set of line-breaking characters.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+ public int read() throws IOException {
+ if (!getInitialized()) {
+ initialize();
+ setInitialized(true);
+ }
+
+ int ch = in.read();
+ while (ch != -1) {
+ if (lineBreaks.indexOf(ch) == -1) {
+ break;
+ } else {
+ ch = in.read();
+ }
+ }
+ return ch;
+ }
+
+ /**
+ * Sets the line-breaking characters.
+ *
+ * @param lineBreaks A String containing all the characters to be
+ * considered as line-breaking.
+ */
+ public void setLineBreaks(final String lineBreaks) {
+ this.lineBreaks = lineBreaks;
+ }
+
+ /**
+ * Returns the line-breaking characters as a String.
+ *
+ * @return a String containing all the characters considered as
+ * line-breaking
+ */
+ private String getLineBreaks() {
+ return lineBreaks;
+ }
+
+ /**
+ * Creates a new StripLineBreaks using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ StripLineBreaks newFilter = new StripLineBreaks(rdr);
+ newFilter.setLineBreaks(getLineBreaks());
+ newFilter.setInitialized(true);
+ return newFilter;
+ }
+
+ /**
+ * Parses the parameters to set the line-breaking characters.
+ */
+ private void initialize() {
+ String userDefinedLineBreaks = null;
+ Parameter[] params = getParameters();
+ if (params != null) {
+ for (int i = 0; i < params.length; i++) {
+ if (LINE_BREAKS_KEY.equals(params[i].getName())) {
+ userDefinedLineBreaks = params[i].getValue();
+ break;
+ }
+ }
+ }
+ if (userDefinedLineBreaks != null) {
+ lineBreaks = userDefinedLineBreaks;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StripLineComments.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StripLineComments.java
new file mode 100644
index 00000000..e3d240ba
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/StripLineComments.java
@@ -0,0 +1,237 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Vector;
+
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * This filter strips line comments.
+ *
+ * Example:
+ *
+ * <pre>&lt;striplinecomments&gt;
+ * &lt;comment value=&quot;#&quot;/&gt;
+ * &lt;comment value=&quot;--&quot;/&gt;
+ * &lt;comment value=&quot;REM &quot;/&gt;
+ * &lt;comment value=&quot;rem &quot;/&gt;
+ * &lt;comment value=&quot;//&quot;/&gt;
+ * &lt;/striplinecomments&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader
+ * classname=&quot;org.apache.tools.ant.filters.StripLineComments&quot;&gt;
+ * &lt;param type=&quot;comment&quot; value="#&quot;/&gt;
+ * &lt;param type=&quot;comment&quot; value=&quot;--&quot;/&gt;
+ * &lt;param type=&quot;comment&quot; value=&quot;REM &quot;/&gt;
+ * &lt;param type=&quot;comment&quot; value=&quot;rem &quot;/&gt;
+ * &lt;param type=&quot;comment&quot; value=&quot;//&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ */
+public final class StripLineComments
+ extends BaseParamFilterReader
+ implements ChainableReader {
+ /** Parameter name for the comment prefix. */
+ private static final String COMMENTS_KEY = "comment";
+
+ /** Vector that holds the comment prefixes. */
+ private Vector<String> comments = new Vector<String>();
+
+ /** The line that has been read ahead. */
+ private String line = null;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public StripLineComments() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public StripLineComments(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Returns the next character in the filtered stream, only including
+ * lines from the original stream which don't start with any of the
+ * specified comment prefixes.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+ public int read() throws IOException {
+ if (!getInitialized()) {
+ initialize();
+ setInitialized(true);
+ }
+
+ int ch = -1;
+
+ if (line != null) {
+ ch = line.charAt(0);
+ if (line.length() == 1) {
+ line = null;
+ } else {
+ line = line.substring(1);
+ }
+ } else {
+ line = readLine();
+ final int commentsSize = comments.size();
+
+ while (line != null) {
+ for (int i = 0; i < commentsSize; i++) {
+ String comment = comments.elementAt(i);
+ if (line.startsWith(comment)) {
+ line = null;
+ break;
+ }
+ }
+
+ if (line == null) {
+ // line started with comment
+ line = readLine();
+ } else {
+ break;
+ }
+ }
+
+ if (line != null) {
+ return read();
+ }
+ }
+
+ return ch;
+ }
+
+ /**
+ * Adds a <code>comment</code> element to the list of prefixes.
+ *
+ * @param comment The <code>comment</code> element to add to the
+ * list of comment prefixes to strip. Must not be <code>null</code>.
+ */
+ public void addConfiguredComment(final Comment comment) {
+ comments.addElement(comment.getValue());
+ }
+
+ /**
+ * Sets the list of comment prefixes to strip.
+ *
+ * @param comments A list of strings, each of which is a prefix
+ * for a comment line. Must not be <code>null</code>.
+ */
+ private void setComments(final Vector<String> comments) {
+ this.comments = comments;
+ }
+
+ /**
+ * Returns the list of comment prefixes to strip.
+ *
+ * @return the list of comment prefixes to strip.
+ */
+ private Vector<String> getComments() {
+ return comments;
+ }
+
+ /**
+ * Creates a new StripLineComments using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ StripLineComments newFilter = new StripLineComments(rdr);
+ newFilter.setComments(getComments());
+ newFilter.setInitialized(true);
+ return newFilter;
+ }
+
+ /**
+ * Parses the parameters to set the comment prefixes.
+ */
+ private void initialize() {
+ Parameter[] params = getParameters();
+ if (params != null) {
+ for (int i = 0; i < params.length; i++) {
+ if (COMMENTS_KEY.equals(params[i].getType())) {
+ comments.addElement(params[i].getValue());
+ }
+ }
+ }
+ }
+
+ /**
+ * The class that holds a comment representation.
+ */
+ public static class Comment {
+
+ /** The prefix for a line comment. */
+ private String value;
+
+ /**
+ * Sets the prefix for this type of line comment.
+ *
+ * @param comment The prefix for a line comment of this type.
+ * Must not be <code>null</code>.
+ */
+ public final void setValue(String comment) {
+ if (value != null) {
+ throw new IllegalStateException("Comment value already set.");
+ }
+ value = comment;
+ }
+
+ /**
+ * Returns the prefix for this type of line comment.
+ *
+ * @return the prefix for this type of line comment.
+ */
+ public final String getValue() {
+ return value;
+ }
+
+ /**
+ * Alt. syntax to set the prefix for this type of line comment.
+ *
+ * @param comment The prefix for a line comment of this type.
+ * Must not be <code>null</code>.
+ */
+ public void addText(String comment) {
+ setValue(comment);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/SuffixLines.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/SuffixLines.java
new file mode 100644
index 00000000..23d9b534
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/SuffixLines.java
@@ -0,0 +1,174 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Attaches a suffix to every line.
+ *
+ * Example:
+ * <pre>&lt;suffixlines suffix=&quot;Foo&quot;/&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.SuffixLines&quot;&gt;
+ * &lt;param name=&quot;suffix&quot; value=&quot;Foo&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ * @since Ant 1.8.0
+ */
+public final class SuffixLines
+ extends BaseParamFilterReader
+ implements ChainableReader {
+ /** Parameter name for the prefix. */
+ private static final String SUFFIX_KEY = "suffix";
+
+ /** The suffix to be used. */
+ private String suffix = null;
+
+ /** Data that must be read from, if not null. */
+ private String queuedData = null;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public SuffixLines() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public SuffixLines(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Returns the next character in the filtered stream. One line is read
+ * from the original input, and the suffix added. The resulting
+ * line is then used until it ends, at which point the next original line
+ * is read, etc.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+ public int read() throws IOException {
+ if (!getInitialized()) {
+ initialize();
+ setInitialized(true);
+ }
+
+ int ch = -1;
+
+ if (queuedData != null && queuedData.length() == 0) {
+ queuedData = null;
+ }
+
+ if (queuedData != null) {
+ ch = queuedData.charAt(0);
+ queuedData = queuedData.substring(1);
+ if (queuedData.length() == 0) {
+ queuedData = null;
+ }
+ } else {
+ queuedData = readLine();
+ if (queuedData == null) {
+ ch = -1;
+ } else {
+ if (suffix != null) {
+ String lf = "";
+ if (queuedData.endsWith("\r\n")) {
+ lf = "\r\n";
+ } else if (queuedData.endsWith("\n")) {
+ lf = "\n";
+ }
+ queuedData =
+ queuedData.substring(0,
+ queuedData.length() - lf.length())
+ + suffix + lf;
+ }
+ return read();
+ }
+ }
+ return ch;
+ }
+
+ /**
+ * Sets the suffix to add at the end of each input line.
+ *
+ * @param suffix The suffix to add at the end of each input line.
+ * May be <code>null</code>, in which case no suffix
+ * is added.
+ */
+ public void setSuffix(final String suffix) {
+ this.suffix = suffix;
+ }
+
+ /**
+ * Returns the suffix which will be added at the end of each input line.
+ *
+ * @return the suffix which will be added at the end of each input line
+ */
+ private String getSuffix() {
+ return suffix;
+ }
+
+ /**
+ * Creates a new SuffixLines filter using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ SuffixLines newFilter = new SuffixLines(rdr);
+ newFilter.setSuffix(getSuffix());
+ newFilter.setInitialized(true);
+ return newFilter;
+ }
+
+ /**
+ * Initializes the suffix if it is available from the parameters.
+ */
+ private void initialize() {
+ Parameter[] params = getParameters();
+ if (params != null) {
+ for (int i = 0; i < params.length; i++) {
+ if (SUFFIX_KEY.equals(params[i].getName())) {
+ suffix = params[i].getValue();
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/TabsToSpaces.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/TabsToSpaces.java
new file mode 100644
index 00000000..adaaa7af
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/TabsToSpaces.java
@@ -0,0 +1,155 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Converts tabs to spaces.
+ *
+ * Example:
+ *
+ * <pre>&lt;tabstospaces tablength=&quot;8&quot;/&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.TabsToSpaces&quot;&gt;
+ * &lt;param name=&quot;tablength&quot; value=&quot;8&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ */
+public final class TabsToSpaces
+ extends BaseParamFilterReader
+ implements ChainableReader {
+ /** The default tab length. */
+ private static final int DEFAULT_TAB_LENGTH = 8;
+
+ /** Parameter name for the length of a tab. */
+ private static final String TAB_LENGTH_KEY = "tablength";
+
+ /** Tab length in this filter. */
+ private int tabLength = DEFAULT_TAB_LENGTH;
+
+ /** The number of spaces still to be read to represent the last-read tab. */
+ private int spacesRemaining = 0;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public TabsToSpaces() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public TabsToSpaces(final Reader in) {
+ super(in);
+ }
+
+ /**
+ * Returns the next character in the filtered stream, converting tabs
+ * to the specified number of spaces.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+ public int read() throws IOException {
+ if (!getInitialized()) {
+ initialize();
+ setInitialized(true);
+ }
+
+ int ch = -1;
+
+ if (spacesRemaining > 0) {
+ spacesRemaining--;
+ ch = ' ';
+ } else {
+ ch = in.read();
+ if (ch == '\t') {
+ spacesRemaining = tabLength - 1;
+ ch = ' ';
+ }
+ }
+ return ch;
+ }
+
+ /**
+ * Sets the tab length.
+ *
+ * @param tabLength the number of spaces to be used when converting a tab.
+ */
+ public void setTablength(final int tabLength) {
+ this.tabLength = tabLength;
+ }
+
+ /**
+ * Returns the tab length.
+ *
+ * @return the number of spaces used when converting a tab
+ */
+ private int getTablength() {
+ return tabLength;
+ }
+
+ /**
+ * Creates a new TabsToSpaces using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ TabsToSpaces newFilter = new TabsToSpaces(rdr);
+ newFilter.setTablength(getTablength());
+ newFilter.setInitialized(true);
+ return newFilter;
+ }
+
+ /**
+ * Parses the parameters to set the tab length.
+ */
+ private void initialize() {
+ Parameter[] params = getParameters();
+ if (params != null) {
+ for (int i = 0; i < params.length; i++) {
+ if (params[i] != null) {
+ if (TAB_LENGTH_KEY.equals(params[i].getName())) {
+ tabLength = Integer.parseInt(params[i].getValue());
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/TailFilter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/TailFilter.java
new file mode 100644
index 00000000..fcc84d16
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/TailFilter.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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.LinkedList;
+
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.util.LineTokenizer;
+
+/**
+ * Reads the last <code>n</code> lines of a stream. (Default is last10 lines.)
+ *
+ * Example:
+ *
+ * <pre>&lt;tailfilter lines=&quot;3&quot;/&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.TailFilter&quot;&gt;
+ * &lt;param name=&quot;lines&quot; value=&quot;3&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ */
+public final class TailFilter extends BaseParamFilterReader
+ implements ChainableReader {
+ /** Parameter name for the number of lines to be returned. */
+ private static final String LINES_KEY = "lines";
+
+ /** Parameter name for the number of lines to be skipped. */
+ private static final String SKIP_KEY = "skip";
+
+ /** Default number of lines to show */
+ private static final int DEFAULT_NUM_LINES = 10;
+
+ /** Number of lines to be returned in the filtered stream. */
+ private long lines = DEFAULT_NUM_LINES;
+
+ /** Number of lines to be skipped. */
+ private long skip = 0;
+
+ /** Whether or not read-ahead been completed. */
+ private boolean completedReadAhead = false;
+
+ /** A line tokenizer */
+ private LineTokenizer lineTokenizer = null;
+
+ /** the current line from the input stream */
+ private String line = null;
+ /** the position in the current line */
+ private int linePos = 0;
+
+ private LinkedList<String> lineList = new LinkedList<String>();
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public TailFilter() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public TailFilter(final Reader in) {
+ super(in);
+ lineTokenizer = new LineTokenizer();
+ lineTokenizer.setIncludeDelims(true);
+ }
+
+ /**
+ * Returns the next character in the filtered stream. If the read-ahead
+ * has been completed, the next character in the buffer is returned.
+ * Otherwise, the stream is read to the end and buffered (with the buffer
+ * growing as necessary), then the appropriate position in the buffer is
+ * set to read from.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+ public int read() throws IOException {
+ if (!getInitialized()) {
+ initialize();
+ setInitialized(true);
+ }
+
+ while (line == null || line.length() == 0) {
+ line = lineTokenizer.getToken(in);
+ line = tailFilter(line);
+ if (line == null) {
+ return -1;
+ }
+ linePos = 0;
+ }
+
+ int ch = line.charAt(linePos);
+ linePos++;
+ if (linePos == line.length()) {
+ line = null;
+ }
+ return ch;
+ }
+
+ /**
+ * Sets the number of lines to be returned in the filtered stream.
+ *
+ * @param lines the number of lines to be returned in the filtered stream
+ */
+ public void setLines(final long lines) {
+ this.lines = lines;
+ }
+
+ /**
+ * Returns the number of lines to be returned in the filtered stream.
+ *
+ * @return the number of lines to be returned in the filtered stream
+ */
+ private long getLines() {
+ return lines;
+ }
+
+ /**
+ * Sets the number of lines to be skipped in the filtered stream.
+ *
+ * @param skip the number of lines to be skipped in the filtered stream
+ */
+ public void setSkip(final long skip) {
+ this.skip = skip;
+ }
+
+ /**
+ * Returns the number of lines to be skipped in the filtered stream.
+ *
+ * @return the number of lines to be skipped in the filtered stream
+ */
+ private long getSkip() {
+ return skip;
+ }
+
+ /**
+ * Creates a new TailFilter using the passed in
+ * Reader for instantiation.
+ *
+ * @param rdr A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ *
+ * @return a new filter based on this configuration, but filtering
+ * the specified reader
+ */
+ public Reader chain(final Reader rdr) {
+ TailFilter newFilter = new TailFilter(rdr);
+ newFilter.setLines(getLines());
+ newFilter.setSkip(getSkip());
+ newFilter.setInitialized(true);
+ return newFilter;
+ }
+
+ /**
+ * Scans the parameters list for the "lines" parameter and uses
+ * it to set the number of lines to be returned in the filtered stream.
+ * also scan for "skip" parameter.
+ */
+ private void initialize() {
+ Parameter[] params = getParameters();
+ if (params != null) {
+ for (int i = 0; i < params.length; i++) {
+ if (LINES_KEY.equals(params[i].getName())) {
+ setLines(Long.parseLong(params[i].getValue()));
+ continue;
+ }
+ if (SKIP_KEY.equals(params[i].getName())) {
+ skip = Long.parseLong(params[i].getValue());
+ continue;
+ }
+ }
+ }
+ }
+
+ /**
+ * implement a tail filter on a stream of lines.
+ * line = null is the end of the stream.
+ * @return "" while reading in the lines,
+ * line while outputting the lines
+ * null at the end of outputting the lines
+ */
+ private String tailFilter(String line) {
+ if (!completedReadAhead) {
+ if (line != null) {
+ lineList.add(line);
+ if (lines == -1) {
+ if (lineList.size() > skip) {
+ return lineList.removeFirst();
+ }
+ } else {
+ long linesToKeep = lines + (skip > 0 ? skip : 0);
+ if (linesToKeep < lineList.size()) {
+ lineList.removeFirst();
+ }
+ }
+ return "";
+ }
+ completedReadAhead = true;
+ if (skip > 0) {
+ for (int i = 0; i < skip; ++i) {
+ lineList.removeLast();
+ }
+ }
+ if (lines > -1) {
+ while (lineList.size() > lines) {
+ lineList.removeFirst();
+ }
+ }
+ }
+ if (lineList.size() > 0) {
+ return lineList.removeFirst();
+ }
+ return null;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/TokenFilter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/TokenFilter.java
new file mode 100644
index 00000000..ebad7602
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/TokenFilter.java
@@ -0,0 +1,712 @@
+/*
+ * 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.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.RegularExpression;
+import org.apache.tools.ant.types.Substitution;
+import org.apache.tools.ant.util.LineTokenizer;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.Tokenizer;
+import org.apache.tools.ant.util.regexp.Regexp;
+import org.apache.tools.ant.util.regexp.RegexpUtil;
+
+/**
+ * This splits up input into tokens and passes
+ * the tokens to a sequence of filters.
+ *
+ * @since Ant 1.6
+ * @see BaseFilterReader
+ * @see ChainableReader
+ * @see org.apache.tools.ant.DynamicConfigurator
+ */
+public class TokenFilter extends BaseFilterReader
+ implements ChainableReader {
+ /**
+ * string filters implement this interface
+ */
+ public interface Filter {
+ /**
+ * filter and/of modify a string
+ *
+ * @param string the string to filter
+ * @return the modified string or null if the
+ * string did not pass the filter
+ */
+ String filter(String string);
+ }
+
+
+ /** string filters */
+ private Vector<Filter> filters = new Vector<Filter>();
+ /** the tokenizer to use on the input stream */
+ private Tokenizer tokenizer = null;
+ /** the output token termination */
+ private String delimOutput = null;
+ /** the current string token from the input stream */
+ private String line = null;
+ /** the position in the current string token */
+ private int linePos = 0;
+
+ /**
+ * Constructor for "dummy" instances.
+ *
+ * @see BaseFilterReader#BaseFilterReader()
+ */
+ public TokenFilter() {
+ super();
+ }
+
+ /**
+ * Creates a new filtered reader.
+ *
+ * @param in A Reader object providing the underlying stream.
+ * Must not be <code>null</code>.
+ */
+ public TokenFilter(final Reader in) {
+ super(in);
+ }
+
+
+ /**
+ * Returns the next character in the filtered stream, only including
+ * lines from the original stream which match all of the specified
+ * regular expressions.
+ *
+ * @return the next character in the resulting stream, or -1
+ * if the end of the resulting stream has been reached
+ *
+ * @exception IOException if the underlying stream throws an IOException
+ * during reading
+ */
+
+ public int read() throws IOException {
+ if (tokenizer == null) {
+ tokenizer = new LineTokenizer();
+ }
+ while (line == null || line.length() == 0) {
+ line = tokenizer.getToken(in);
+ if (line == null) {
+ return -1;
+ }
+ for (Enumeration<Filter> e = filters.elements(); e.hasMoreElements();) {
+ Filter filter = e.nextElement();
+ line = filter.filter(line);
+ if (line == null) {
+ break;
+ }
+ }
+ linePos = 0;
+ if (line != null) {
+ if (tokenizer.getPostToken().length() != 0) {
+ if (delimOutput != null) {
+ line = line + delimOutput;
+ } else {
+ line = line + tokenizer.getPostToken();
+ }
+ }
+ }
+ }
+ int ch = line.charAt(linePos);
+ linePos++;
+ if (linePos == line.length()) {
+ line = null;
+ }
+ return ch;
+ }
+
+ /**
+ * Creates a new TokenFilter using the passed in
+ * Reader for instantiation.
+ *
+ * @param reader A Reader object providing the underlying stream.
+ *
+ * @return a new filter based on this configuration
+ */
+
+ public final Reader chain(final Reader reader) {
+ TokenFilter newFilter = new TokenFilter(reader);
+ newFilter.filters = filters;
+ newFilter.tokenizer = tokenizer;
+ newFilter.delimOutput = delimOutput;
+ newFilter.setProject(getProject());
+ return newFilter;
+ }
+
+ /**
+ * set the output delimiter.
+ * @param delimOutput replaces the delim string returned by the
+ * tokenizer, if present.
+ */
+
+ public void setDelimOutput(String delimOutput) {
+ this.delimOutput = resolveBackSlash(delimOutput);
+ }
+
+ // -----------------------------------------
+ // Predefined tokenizers
+ // -----------------------------------------
+
+ /**
+ * add a line tokenizer - this is the default.
+ * @param tokenizer the line tokenizer
+ */
+
+ public void addLineTokenizer(LineTokenizer tokenizer) {
+ add(tokenizer);
+ }
+
+ /**
+ * add a string tokenizer
+ * @param tokenizer the string tokenizer
+ */
+
+ public void addStringTokenizer(StringTokenizer tokenizer) {
+ add(tokenizer);
+ }
+
+ /**
+ * add a file tokenizer
+ * @param tokenizer the file tokenizer
+ */
+ public void addFileTokenizer(FileTokenizer tokenizer) {
+ add(tokenizer);
+ }
+
+ /**
+ * add an arbitrary tokenizer
+ * @param tokenizer the tokenizer to all, only one allowed
+ */
+
+ public void add(Tokenizer tokenizer) {
+ if (this.tokenizer != null) {
+ throw new BuildException("Only one tokenizer allowed");
+ }
+ this.tokenizer = tokenizer;
+ }
+
+ // -----------------------------------------
+ // Predefined filters
+ // -----------------------------------------
+
+ /**
+ * replace string filter
+ * @param filter the replace string filter
+ */
+ public void addReplaceString(ReplaceString filter) {
+ filters.addElement(filter);
+ }
+
+ /**
+ * contains string filter
+ * @param filter the contains string filter
+ */
+ public void addContainsString(ContainsString filter) {
+ filters.addElement(filter);
+ }
+
+ /**
+ * replace regex filter
+ * @param filter the replace regex filter
+ */
+ public void addReplaceRegex(ReplaceRegex filter) {
+ filters.addElement(filter);
+ }
+
+ /**
+ * contains regex filter
+ * @param filter the contains regex filter
+ */
+ public void addContainsRegex(ContainsRegex filter) {
+ filters.addElement(filter);
+ }
+
+ /**
+ * trim filter
+ * @param filter the trim filter
+ */
+ public void addTrim(Trim filter) {
+ filters.addElement(filter);
+ }
+
+ /**
+ * ignore blank filter
+ * @param filter the ignore blank filter
+ */
+ public void addIgnoreBlank(IgnoreBlank filter) {
+ filters.addElement(filter);
+ }
+
+ /**
+ * delete chars
+ * @param filter the delete characters filter
+ */
+ public void addDeleteCharacters(DeleteCharacters filter) {
+ filters.addElement(filter);
+ }
+
+ /**
+ * Add an arbitrary filter
+ * @param filter the filter to add
+ */
+ public void add(Filter filter) {
+ filters.addElement(filter);
+ }
+
+
+ // --------------------------------------------
+ //
+ // Tokenizer Classes (impls moved to oata.util)
+ //
+ // --------------------------------------------
+
+ /**
+ * class to read the complete input into a string
+ */
+ public static class FileTokenizer
+ extends org.apache.tools.ant.util.FileTokenizer {
+ }
+
+ /**
+ * class to tokenize the input as areas separated
+ * by white space, or by a specified list of
+ * delim characters. Behaves like java.util.StringTokenizer.
+ * if the stream starts with delim characters, the first
+ * token will be an empty string (unless the treat delims
+ * as tokens flag is set).
+ */
+ public static class StringTokenizer
+ extends org.apache.tools.ant.util.StringTokenizer {
+ }
+
+ // --------------------------------------------
+ //
+ // Filter classes
+ //
+ // --------------------------------------------
+
+ /**
+ * Abstract class that converts derived filter classes into
+ * ChainableReaderFilter's
+ */
+ public abstract static class ChainableReaderFilter extends ProjectComponent
+ implements ChainableReader, Filter {
+ private boolean byLine = true;
+
+ /**
+ * set whether to use filetokenizer or line tokenizer
+ * @param byLine if true use a linetokenizer (default) otherwise
+ * use a filetokenizer
+ */
+ public void setByLine(boolean byLine) {
+ this.byLine = byLine;
+ }
+
+ /**
+ * Chain a tokenfilter reader to a reader,
+ *
+ * @param reader the input reader object
+ * @return the chained reader object
+ */
+ public Reader chain(Reader reader) {
+ TokenFilter tokenFilter = new TokenFilter(reader);
+ if (!byLine) {
+ tokenFilter.add(new FileTokenizer());
+ }
+ tokenFilter.add(this);
+ return tokenFilter;
+ }
+ }
+
+ /**
+ * Simple replace string filter.
+ */
+ public static class ReplaceString extends ChainableReaderFilter {
+ private String from;
+ private String to;
+
+ /**
+ * the from attribute
+ *
+ * @param from the string to replace
+ */
+ public void setFrom(String from) {
+ this.from = from;
+ }
+
+ /**
+ * the to attribute
+ *
+ * @param to the string to replace 'from' with
+ */
+ public void setTo(String to) {
+ this.to = to;
+ }
+
+ /**
+ * Filter a string 'line' replacing from with to
+ * (Copy&amp;Paste from the Replace task)
+ * @param line the string to be filtered
+ * @return the filtered line
+ */
+ public String filter(String line) {
+ if (from == null) {
+ throw new BuildException("Missing from in stringreplace");
+ }
+ StringBuffer ret = new StringBuffer();
+ int start = 0;
+ int found = line.indexOf(from);
+ while (found >= 0) {
+ // write everything up to the from
+ if (found > start) {
+ ret.append(line.substring(start, found));
+ }
+
+ // write the replacement to
+ if (to != null) {
+ ret.append(to);
+ }
+
+ // search again
+ start = found + from.length();
+ found = line.indexOf(from, start);
+ }
+
+ // write the remaining characters
+ if (line.length() > start) {
+ ret.append(line.substring(start, line.length()));
+ }
+
+ return ret.toString();
+ }
+ }
+
+ /**
+ * Simple filter to filter lines contains strings
+ */
+ public static class ContainsString extends ProjectComponent
+ implements Filter {
+ private String contains;
+
+ /**
+ * the contains attribute
+ * @param contains the string that the token should contain
+ */
+ public void setContains(String contains) {
+ this.contains = contains;
+ }
+
+ /**
+ * Filter strings that contain the contains attribute
+ *
+ * @param string the string to be filtered
+ * @return null if the string does not contain "contains",
+ * string otherwise
+ */
+ public String filter(String string) {
+ if (contains == null) {
+ throw new BuildException("Missing contains in containsstring");
+ }
+ if (string.indexOf(contains) > -1) {
+ return string;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * filter to replace regex.
+ */
+ public static class ReplaceRegex extends ChainableReaderFilter {
+ private String from;
+ private String to;
+ private RegularExpression regularExpression;
+ private Substitution substitution;
+ private boolean initialized = false;
+ private String flags = "";
+ private int options;
+ private Regexp regexp;
+
+ /**
+ * the from attribute
+ * @param from the regex string
+ */
+ public void setPattern(String from) {
+ this.from = from;
+ }
+ /**
+ * the to attribute
+ * @param to the replacement string
+ */
+ public void setReplace(String to) {
+ this.to = to;
+ }
+
+ /**
+ * @param flags the regex flags
+ */
+ public void setFlags(String flags) {
+ this.flags = flags;
+ }
+
+ private void initialize() {
+ if (initialized) {
+ return;
+ }
+ options = convertRegexOptions(flags);
+ if (from == null) {
+ throw new BuildException("Missing pattern in replaceregex");
+ }
+ regularExpression = new RegularExpression();
+ regularExpression.setPattern(from);
+ regexp = regularExpression.getRegexp(getProject());
+ if (to == null) {
+ to = "";
+ }
+ substitution = new Substitution();
+ substitution.setExpression(to);
+ }
+
+ /**
+ * @param line the string to modify
+ * @return the modified string
+ */
+ public String filter(String line) {
+ initialize();
+
+ if (!regexp.matches(line, options)) {
+ return line;
+ }
+ return regexp.substitute(
+ line, substitution.getExpression(getProject()), options);
+ }
+ }
+
+ /**
+ * filter to filter tokens matching regular expressions.
+ */
+ public static class ContainsRegex extends ChainableReaderFilter {
+ private String from;
+ private String to;
+ private RegularExpression regularExpression;
+ private Substitution substitution;
+ private boolean initialized = false;
+ private String flags = "";
+ private int options;
+ private Regexp regexp;
+
+
+ /**
+ * @param from the regex pattern
+ */
+ public void setPattern(String from) {
+ this.from = from;
+ }
+
+ /**
+ * @param to the replacement string
+ */
+ public void setReplace(String to) {
+ this.to = to;
+ }
+
+ /**
+ * @param flags the regex flags
+ */
+ public void setFlags(String flags) {
+ this.flags = flags;
+ }
+
+ private void initialize() {
+ if (initialized) {
+ return;
+ }
+ options = convertRegexOptions(flags);
+ if (from == null) {
+ throw new BuildException("Missing from in containsregex");
+ }
+ regularExpression = new RegularExpression();
+ regularExpression.setPattern(from);
+ regexp = regularExpression.getRegexp(getProject());
+ if (to == null) {
+ return;
+ }
+ substitution = new Substitution();
+ substitution.setExpression(to);
+ }
+
+ /**
+ * apply regex and substitution on a string
+ * @param string the string to apply filter on
+ * @return the filtered string
+ */
+ public String filter(String string) {
+ initialize();
+ if (!regexp.matches(string, options)) {
+ return null;
+ }
+ if (substitution == null) {
+ return string;
+ }
+ return regexp.substitute(
+ string, substitution.getExpression(getProject()), options);
+ }
+ }
+
+ /** Filter to trim white space */
+ public static class Trim extends ChainableReaderFilter {
+ /**
+ * @param line the string to be trimmed
+ * @return the trimmed string
+ */
+ public String filter(String line) {
+ return line.trim();
+ }
+ }
+
+
+
+ /** Filter remove empty tokens */
+ public static class IgnoreBlank extends ChainableReaderFilter {
+ /**
+ * @param line the line to modify
+ * @return the trimmed line
+ */
+ public String filter(String line) {
+ if (line.trim().length() == 0) {
+ return null;
+ }
+ return line;
+ }
+ }
+
+ /**
+ * Filter to delete characters
+ */
+ public static class DeleteCharacters extends ProjectComponent
+ implements Filter, ChainableReader {
+ // Attributes
+ /** the list of characters to remove from the input */
+ private String deleteChars = "";
+
+ /**
+ * Set the list of characters to delete
+ * @param deleteChars the list of characters
+ */
+ public void setChars(String deleteChars) {
+ this.deleteChars = resolveBackSlash(deleteChars);
+ }
+
+ /**
+ * remove characters from a string
+ * @param string the string to remove the characters from
+ * @return the converted string
+ */
+ public String filter(String string) {
+ StringBuffer output = new StringBuffer(string.length());
+ for (int i = 0; i < string.length(); ++i) {
+ char ch = string.charAt(i);
+ if (!(isDeleteCharacter(ch))) {
+ output.append(ch);
+ }
+ }
+ return output.toString();
+ }
+
+ /**
+ * factory method to provide a reader that removes
+ * the characters from a reader as part of a filter
+ * chain
+ * @param reader the reader object
+ * @return the chained reader object
+ */
+ public Reader chain(Reader reader) {
+ return new BaseFilterReader(reader) {
+ /**
+ * @return the next non delete character
+ */
+ public int read()
+ throws IOException {
+ while (true) {
+ int c = in.read();
+ if (c == -1) {
+ return c;
+ }
+ if (!(isDeleteCharacter((char) c))) {
+ return c;
+ }
+ }
+ }
+ };
+ }
+
+ /**
+ * check if the character c is to be deleted
+ *
+ * @param c char to test
+ * @return true if the supplied char is in the list to be stripped.
+ */
+ private boolean isDeleteCharacter(char c) {
+ for (int d = 0; d < deleteChars.length(); ++d) {
+ if (deleteChars.charAt(d) == c) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ // --------------------------------------------------------
+ // static utility methods - could be placed somewhere else
+ // --------------------------------------------------------
+
+ /**
+ * xml does not do "c" like interpretation of strings.
+ * i.e. \n\r\t etc.
+ * this method processes \n, \r, \t, \f, \\
+ * also subs \s with " \n\r\t\f"
+ * a trailing '\' will be ignored
+ *
+ * @param input raw string with possible embedded '\'s
+ * @return converted string
+ */
+ public static String resolveBackSlash(String input) {
+ return StringUtils.resolveBackSlash(input);
+ }
+
+ /**
+ * convert regex option flag characters to regex options
+ * <ul>
+ * <li>g - Regexp.REPLACE_ALL</li>
+ * <li>i - Regexp.MATCH_CASE_INSENSITIVE</li>
+ * <li>m - Regexp.MATCH_MULTILINE</li>
+ * <li>s - Regexp.MATCH_SINGLELINE</li>
+ * </ul>
+ * @param flags the string containing the flags
+ * @return the Regexp option bits
+ */
+ public static int convertRegexOptions(String flags) {
+ return RegexpUtil.asOptions(flags);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/UniqFilter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/UniqFilter.java
new file mode 100644
index 00000000..e72d5f58
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/UniqFilter.java
@@ -0,0 +1,37 @@
+/*
+ * 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.tools.ant.filters;
+
+/**
+ * Like the Unix uniq(1) command, only returns tokens that are
+ * different from their ancestor token.
+ *
+ * <p>This filter is probably most useful if used together with a
+ * sortfilter.</p>
+ *
+ * @since Ant 1.8.0
+ */
+public class UniqFilter extends TokenFilter.ChainableReaderFilter {
+
+ private String lastLine = null;
+
+ public String filter(String string) {
+ return lastLine == null || !lastLine.equals(string)
+ ? (lastLine = string) : null;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/util/ChainReaderHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/util/ChainReaderHelper.java
new file mode 100644
index 00000000..f176c331
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/util/ChainReaderHelper.java
@@ -0,0 +1,288 @@
+/*
+ * 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.tools.ant.filters.util;
+
+import java.io.FilterReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.filters.BaseFilterReader;
+import org.apache.tools.ant.filters.ChainableReader;
+import org.apache.tools.ant.types.AntFilterReader;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.Parameterizable;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Process a FilterReader chain.
+ *
+ */
+public final class ChainReaderHelper {
+
+ // default buffer size
+ private static final int DEFAULT_BUFFER_SIZE = 8192;
+ // CheckStyle:VisibilityModifier OFF - bc
+ /**
+ * The primary reader to which the reader chain is to be attached.
+ */
+ public Reader primaryReader;
+
+ /**
+ * The size of the buffer to be used.
+ */
+ public int bufferSize = DEFAULT_BUFFER_SIZE;
+
+ /**
+ * Chain of filters
+ */
+ public Vector<FilterChain> filterChains = new Vector<FilterChain>();
+
+ /** The Ant project */
+ private Project project = null;
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Sets the primary reader
+ * @param rdr the reader object
+ */
+ public void setPrimaryReader(Reader rdr) {
+ primaryReader = rdr;
+ }
+
+ /**
+ * Set the project to work with
+ * @param project the current project
+ */
+ public void setProject(final Project project) {
+ this.project = project;
+ }
+
+ /**
+ * Get the project
+ *
+ * @return the current project
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * Sets the buffer size to be used. Defaults to 8192,
+ * if this method is not invoked.
+ * @param size the buffer size to use
+ */
+ public void setBufferSize(int size) {
+ bufferSize = size;
+ }
+
+ /**
+ * Sets the collection of filter reader sets
+ *
+ * @param fchain the filter chains collection
+ */
+ public void setFilterChains(Vector<FilterChain> fchain) {
+ filterChains = fchain;
+ }
+
+ /**
+ * Assemble the reader
+ * @return the assembled reader
+ * @exception BuildException if an error occurs
+ */
+ public Reader getAssembledReader() throws BuildException {
+ if (primaryReader == null) {
+ throw new BuildException("primaryReader must not be null.");
+ }
+
+ Reader instream = primaryReader;
+ final int filterReadersCount = filterChains.size();
+ final Vector<Object> finalFilters = new Vector<Object>();
+ final ArrayList<AntClassLoader> classLoadersToCleanUp =
+ new ArrayList<AntClassLoader>();
+
+ for (int i = 0; i < filterReadersCount; i++) {
+ final FilterChain filterchain =
+ filterChains.elementAt(i);
+ final Vector<Object> filterReaders = filterchain.getFilterReaders();
+ final int readerCount = filterReaders.size();
+ for (int j = 0; j < readerCount; j++) {
+ finalFilters.addElement(filterReaders.elementAt(j));
+ }
+ }
+
+ final int filtersCount = finalFilters.size();
+
+ if (filtersCount > 0) {
+ boolean success = false;
+ try {
+ for (int i = 0; i < filtersCount; i++) {
+ Object o = finalFilters.elementAt(i);
+
+ if (o instanceof AntFilterReader) {
+ instream =
+ expandReader((AntFilterReader) finalFilters.elementAt(i),
+ instream, classLoadersToCleanUp);
+ } else if (o instanceof ChainableReader) {
+ setProjectOnObject(o);
+ instream = ((ChainableReader) o).chain(instream);
+ setProjectOnObject(instream);
+ }
+ }
+ success = true;
+ } finally {
+ if (!success && classLoadersToCleanUp.size() > 0) {
+ cleanUpClassLoaders(classLoadersToCleanUp);
+ }
+ }
+ }
+ final Reader finalReader = instream;
+ return classLoadersToCleanUp.size() == 0 ? finalReader
+ : new FilterReader(finalReader) {
+ public void close() throws IOException {
+ FileUtils.close(in);
+ cleanUpClassLoaders(classLoadersToCleanUp);
+ }
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+ };
+ }
+
+ /**
+ * helper method to set the project on an object.
+ * the reflection setProject does not work for anonymous/protected/private
+ * classes, even if they have public methods.
+ */
+ private void setProjectOnObject(Object obj) {
+ if (project == null) {
+ return;
+ }
+ if (obj instanceof BaseFilterReader) {
+ ((BaseFilterReader) obj).setProject(project);
+ return;
+ }
+ project.setProjectReference(obj);
+ }
+
+ /**
+ * Deregisters Classloaders from the project so GC can remove them later.
+ */
+ private static void cleanUpClassLoaders(List<AntClassLoader> loaders) {
+ for (Iterator<AntClassLoader> it = loaders.iterator(); it.hasNext();) {
+ it.next().cleanup();
+ }
+ }
+
+ /**
+ * Read data from the reader and return the
+ * contents as a string.
+ * @param rdr the reader object
+ * @return the contents of the file as a string
+ * @exception IOException if an error occurs
+ */
+ public String readFully(Reader rdr)
+ throws IOException {
+ return FileUtils.readFully(rdr, bufferSize);
+ }
+
+ /**
+ * Creates and parameterizes a new FilterReader from a
+ * &lt;filterreader&gt; element.
+ *
+ * @since Ant 1.8.0
+ */
+ private Reader expandReader(final AntFilterReader filter,
+ final Reader ancestor,
+ final List<AntClassLoader> classLoadersToCleanUp) {
+ final String className = filter.getClassName();
+ final Path classpath = filter.getClasspath();
+ final Project pro = filter.getProject();
+ if (className != null) {
+ try {
+ Class<?> clazz = null;
+ if (classpath == null) {
+ clazz = Class.forName(className);
+ } else {
+ AntClassLoader al = pro.createClassLoader(classpath);
+ classLoadersToCleanUp.add(al);
+ clazz = Class.forName(className, true, al);
+ }
+ if (clazz != null) {
+ if (!FilterReader.class.isAssignableFrom(clazz)) {
+ throw new BuildException(className + " does not extend"
+ + " java.io.FilterReader");
+ }
+ final Constructor<?>[] constructors = clazz.getConstructors();
+ int j = 0;
+ boolean consPresent = false;
+ for (; j < constructors.length; j++) {
+ Class<?>[] types = constructors[j].getParameterTypes();
+ if (types.length == 1
+ && types[0].isAssignableFrom(Reader.class)) {
+ consPresent = true;
+ break;
+ }
+ }
+ if (!consPresent) {
+ throw new BuildException(className + " does not define"
+ + " a public constructor"
+ + " that takes in a Reader"
+ + " as its single argument.");
+ }
+ final Reader[] rdr = {ancestor};
+ Reader instream =
+ (Reader) constructors[j].newInstance((Object[]) rdr);
+ setProjectOnObject(instream);
+ if (Parameterizable.class.isAssignableFrom(clazz)) {
+ final Parameter[] params = filter.getParams();
+ ((Parameterizable) instream).setParameters(params);
+ }
+ return instream;
+ }
+ } catch (final ClassNotFoundException cnfe) {
+ throw new BuildException(cnfe);
+ } catch (final InstantiationException ie) {
+ throw new BuildException(ie);
+ } catch (final IllegalAccessException iae) {
+ throw new BuildException(iae);
+ } catch (final InvocationTargetException ite) {
+ throw new BuildException(ite);
+ }
+ }
+ // Ant 1.7.1 and earlier ignore <filterreader> without a
+ // classname attribute, not sure this is a good idea -
+ // backwards compatibility makes it hard to change, though.
+ return ancestor;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/util/JavaClassHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/util/JavaClassHelper.java
new file mode 100644
index 00000000..b0c67ce2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/filters/util/JavaClassHelper.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.tools.ant.filters.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.ConstantValue;
+import org.apache.bcel.classfile.Field;
+import org.apache.bcel.classfile.JavaClass;
+
+// CheckStyle:HideUtilityClassConstructorCheck OFF - bc
+/**
+ * Helper class that filters constants from a Java Class
+ *
+ */
+public final class JavaClassHelper {
+ /** System specific line separator. */
+ private static final String LS = System.getProperty("line.separator");
+
+ /**
+ * Get the constants declared in a file as name=value
+ *
+ * @param bytes the class as a array of bytes
+ * @return a StringBuffer contains the name=value pairs
+ * @exception IOException if an error occurs
+ */
+ public static StringBuffer getConstants(final byte[] bytes)
+ throws IOException {
+ final StringBuffer sb = new StringBuffer();
+ final ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+ final ClassParser parser = new ClassParser(bis, "");
+ final JavaClass javaClass = parser.parse();
+ final Field[] fields = javaClass.getFields();
+ for (int i = 0; i < fields.length; i++) {
+ final Field field = fields[i];
+ if (field != null) {
+ final ConstantValue cv = field.getConstantValue();
+ if (cv != null) {
+ String cvs = cv.toString();
+ //Remove start and end quotes if field is a String
+ if (cvs.startsWith("\"") && cvs.endsWith("\"")) {
+ cvs = cvs.substring(1, cvs.length() - 1);
+ }
+ sb.append(field.getName());
+ sb.append('=');
+ sb.append(cvs);
+ sb.append(LS);
+ }
+ }
+ }
+ return sb;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/AntXMLContext.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/AntXMLContext.java
new file mode 100644
index 00000000..99e3d1a1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/AntXMLContext.java
@@ -0,0 +1,417 @@
+/*
+ * 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.tools.ant.helper;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.RuntimeConfigurable;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.util.FileUtils;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+/**
+ * Context information for the ant processing.
+ *
+ */
+public class AntXMLContext {
+ /** The project to configure. */
+ private Project project;
+
+ /** The configuration file to parse. */
+ private File buildFile;
+
+ /** The configuration file to parse. */
+ private URL buildFileURL;
+
+ /** Vector with all the targets, in the order they are
+ * defined. Project maintains a Hashtable, which is not ordered.
+ * This will allow description to know the original order.
+ */
+ private Vector<Target> targetVector = new Vector<Target>();
+
+ /**
+ * Parent directory of the build file. Used for resolving entities
+ * and setting the project's base directory.
+ */
+ private File buildFileParent;
+
+ /**
+ * Parent directory of the build file. Used for resolving entities
+ * and setting the project's base directory.
+ */
+ private URL buildFileParentURL;
+
+ /** Name of the current project */
+ private String currentProjectName;
+
+ /**
+ * Locator for the configuration file parser.
+ * Used for giving locations of errors etc.
+ */
+ private Locator locator;
+
+ /**
+ * Target that all other targets will depend upon implicitly.
+ *
+ * <p>This holds all tasks and data type definitions that have
+ * been placed outside of targets.</p>
+ */
+ private Target implicitTarget = new Target();
+
+ /** Current target ( no need for a stack as the processing model
+ allows only one level of target ) */
+ private Target currentTarget = null;
+
+ /** The stack of RuntimeConfigurable2 wrapping the
+ objects.
+ */
+ private Vector<RuntimeConfigurable> wStack = new Vector<RuntimeConfigurable>();
+
+ /**
+ * Indicates whether the project tag attributes are to be ignored
+ * when processing a particular build file.
+ */
+ private boolean ignoreProjectTag = false;
+
+ /** Keeps track of prefix -> uri mapping during parsing */
+ private Map<String, List<String>> prefixMapping = new HashMap<String, List<String>>();
+
+
+ /** Keeps track of targets in files */
+ private Map<String, Target> currentTargets = null;
+
+ /**
+ * constructor
+ * @param project the project to which this antxml context belongs to
+ */
+ public AntXMLContext(Project project) {
+ this.project = project;
+ implicitTarget.setProject(project);
+ implicitTarget.setName("");
+ targetVector.addElement(implicitTarget);
+ }
+
+ /**
+ * sets the build file to which the XML context belongs
+ * @param buildFile ant build file
+ */
+ public void setBuildFile(File buildFile) {
+ this.buildFile = buildFile;
+ if (buildFile != null) {
+ this.buildFileParent = new File(buildFile.getParent());
+ implicitTarget.setLocation(new Location(buildFile.getAbsolutePath()));
+ try {
+ setBuildFile(FileUtils.getFileUtils().getFileURL(buildFile));
+ } catch (MalformedURLException ex) {
+ throw new BuildException(ex);
+ }
+ } else {
+ this.buildFileParent = null;
+ }
+ }
+
+ /**
+ * sets the build file to which the XML context belongs
+ * @param buildFile ant build file
+ * @since Ant 1.8.0
+ */
+ public void setBuildFile(URL buildFile) throws MalformedURLException {
+ this.buildFileURL = buildFile;
+ this.buildFileParentURL = new URL(buildFile, ".");
+ if (implicitTarget.getLocation() == null) {
+ implicitTarget.setLocation(new Location(buildFile.toString()));
+ }
+ }
+
+ /**
+ * find out the build file
+ * @return the build file to which the xml context belongs
+ */
+ public File getBuildFile() {
+ return buildFile;
+ }
+
+ /**
+ * find out the parent build file of this build file
+ * @return the parent build file of this build file
+ */
+ public File getBuildFileParent() {
+ return buildFileParent;
+ }
+
+ /**
+ * find out the build file
+ * @return the build file to which the xml context belongs
+ * @since Ant 1.8.0
+ */
+ public URL getBuildFileURL() {
+ return buildFileURL;
+ }
+
+ /**
+ * find out the parent build file of this build file
+ * @return the parent build file of this build file
+ * @since Ant 1.8.0
+ */
+ public URL getBuildFileParentURL() {
+ return buildFileParentURL;
+ }
+
+ /**
+ * find out the project to which this antxml context belongs
+ * @return project
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * find out the current project name
+ * @return current project name
+ */
+ public String getCurrentProjectName() {
+ return currentProjectName;
+ }
+
+ /**
+ * set the name of the current project
+ * @param name name of the current project
+ */
+ public void setCurrentProjectName(String name) {
+ this.currentProjectName = name;
+ }
+
+ /**
+ * get the current runtime configurable wrapper
+ * can return null
+ * @return runtime configurable wrapper
+ */
+ public RuntimeConfigurable currentWrapper() {
+ if (wStack.size() < 1) {
+ return null;
+ }
+ return (RuntimeConfigurable) wStack.elementAt(wStack.size() - 1);
+ }
+
+ /**
+ * get the runtime configurable wrapper of the parent project
+ * can return null
+ * @return runtime configurable wrapper of the parent project
+ */
+ public RuntimeConfigurable parentWrapper() {
+ if (wStack.size() < 2) {
+ return null;
+ }
+ return (RuntimeConfigurable) wStack.elementAt(wStack.size() - 2);
+ }
+
+ /**
+ * add a runtime configurable wrapper to the internal stack
+ * @param wrapper runtime configurable wrapper
+ */
+ public void pushWrapper(RuntimeConfigurable wrapper) {
+ wStack.addElement(wrapper);
+ }
+
+ /**
+ * remove a runtime configurable wrapper from the stack
+ */
+ public void popWrapper() {
+ if (wStack.size() > 0) {
+ wStack.removeElementAt(wStack.size() - 1);
+ }
+ }
+
+ /**
+ * access the stack of wrappers
+ * @return the stack of wrappers
+ */
+ public Vector<RuntimeConfigurable> getWrapperStack() {
+ return wStack;
+ }
+
+ /**
+ * add a new target
+ * @param target target to add
+ */
+ public void addTarget(Target target) {
+ targetVector.addElement(target);
+ currentTarget = target;
+ }
+
+ /**
+ * get the current target
+ * @return current target
+ */
+ public Target getCurrentTarget() {
+ return currentTarget;
+ }
+
+ /**
+ * get the implicit target
+ * @return implicit target
+ */
+ public Target getImplicitTarget() {
+ return implicitTarget;
+ }
+
+ /**
+ * sets the current target
+ * @param target current target
+ */
+ public void setCurrentTarget(Target target) {
+ this.currentTarget = target;
+ }
+
+ /**
+ * sets the implicit target
+ * @param target the implicit target
+ */
+ public void setImplicitTarget(Target target) {
+ this.implicitTarget = target;
+ }
+
+ /**
+ * access the vector of targets
+ * @return vector of targets
+ */
+ public Vector<Target> getTargets() {
+ return targetVector;
+ }
+
+ /**
+ * Scans an attribute list for the <code>id</code> attribute and
+ * stores a reference to the target object in the project if an
+ * id is found.
+ * <p>
+ * This method was moved out of the configure method to allow
+ * it to be executed at parse time.
+ * @param element the current element
+ * @param attr attributes of the current element
+ */
+ public void configureId(Object element, Attributes attr) {
+ String id = attr.getValue("id");
+ if (id != null) {
+ project.addIdReference(id, element);
+ }
+ }
+
+ /**
+ * access the locator
+ * @return locator
+ */
+ public Locator getLocator() {
+ return locator;
+ }
+
+ /**
+ * sets the locator
+ * @param locator locator
+ */
+ public void setLocator(Locator locator) {
+ this.locator = locator;
+ }
+
+ /**
+ * tells whether the project tag is being ignored
+ * @return whether the project tag is being ignored
+ */
+ public boolean isIgnoringProjectTag() {
+ return ignoreProjectTag;
+ }
+
+ /**
+ * sets the flag to ignore the project tag
+ * @param flag to ignore the project tag
+ */
+ public void setIgnoreProjectTag(boolean flag) {
+ this.ignoreProjectTag = flag;
+ }
+
+ /**
+ * Called during parsing, stores the prefix to uri mapping.
+ *
+ * @param prefix a namespace prefix
+ * @param uri a namespace uri
+ */
+ public void startPrefixMapping(String prefix, String uri) {
+ List<String> list = prefixMapping.get(prefix);
+ if (list == null) {
+ list = new ArrayList<String>();
+ prefixMapping.put(prefix, list);
+ }
+ list.add(uri);
+ }
+
+ /**
+ * End of prefix to uri mapping.
+ *
+ * @param prefix the namespace prefix
+ */
+ public void endPrefixMapping(String prefix) {
+ List<String> list = prefixMapping.get(prefix);
+ if (list == null || list.size() == 0) {
+ return; // Should not happen
+ }
+ list.remove(list.size() - 1);
+ }
+
+ /**
+ * prefix to namespace uri mapping
+ *
+ * @param prefix the prefix to map
+ * @return the uri for this prefix, null if not present
+ */
+ public String getPrefixMapping(String prefix) {
+ List<String> list = prefixMapping.get(prefix);
+ if (list == null || list.size() == 0) {
+ return null;
+ }
+ return (String) list.get(list.size() - 1);
+ }
+
+ /**
+ * Get the targets in the current source file.
+ * @return the current targets.
+ */
+ public Map<String, Target> getCurrentTargets() {
+ return currentTargets;
+ }
+
+ /**
+ * Set the map of the targets in the current source file.
+ * @param currentTargets a map of targets.
+ */
+ public void setCurrentTargets(Map<String, Target> currentTargets) {
+ this.currentTargets = currentTargets;
+ }
+
+}
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/DefaultExecutor.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/DefaultExecutor.java
new file mode 100644
index 00000000..cdbc587c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/DefaultExecutor.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.tools.ant.helper;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Executor;
+import org.apache.tools.ant.Project;
+
+/**
+ * Default Target executor implementation. Runs each target individually
+ * (including all of its dependencies). If an error occurs, behavior is
+ * determined by the Project's "keep-going" mode.
+ * @since Ant 1.6.3
+ */
+public class DefaultExecutor implements Executor {
+
+ private static final SingleCheckExecutor SUB_EXECUTOR = new SingleCheckExecutor();
+
+ /** {@inheritDoc}. */
+ public void executeTargets(Project project, String[] targetNames)
+ throws BuildException {
+ BuildException thrownException = null;
+ for (int i = 0; i < targetNames.length; i++) {
+ try {
+ project.executeTarget(targetNames[i]);
+ } catch (BuildException ex) {
+ if (project.isKeepGoingMode()) {
+ thrownException = ex;
+ } else {
+ throw ex;
+ }
+ }
+ }
+ if (thrownException != null) {
+ throw thrownException;
+ }
+ }
+
+ /** {@inheritDoc}. */
+ public Executor getSubProjectExecutor() {
+ return SUB_EXECUTOR;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/IgnoreDependenciesExecutor.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/IgnoreDependenciesExecutor.java
new file mode 100644
index 00000000..da85dba7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/IgnoreDependenciesExecutor.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.tools.ant.helper;
+
+import java.util.Hashtable;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Executor;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Target;
+
+/**
+ * Target executor implementation that ignores dependencies. Runs each
+ * target by calling <code>target.performTasks()</code> directly. If an
+ * error occurs, behavior is determined by the Project's "keep-going" mode.
+ * To be used when you know what you're doing.
+ *
+ * @since Ant 1.7.1
+ */
+public class IgnoreDependenciesExecutor implements Executor {
+
+ private static final SingleCheckExecutor SUB_EXECUTOR = new SingleCheckExecutor();
+
+ /** {@inheritDoc}. */
+ public void executeTargets(Project project, String[] targetNames)
+ throws BuildException {
+ Hashtable<String, Target> targets = project.getTargets();
+ BuildException thrownException = null;
+ for (int i = 0; i < targetNames.length; i++) {
+ try {
+ Target t = targets.get(targetNames[i]);
+ if (t == null) {
+ throw new BuildException("Unknown target " + targetNames[i]);
+ }
+ t.performTasks();
+ } catch (BuildException ex) {
+ if (project.isKeepGoingMode()) {
+ thrownException = ex;
+ } else {
+ throw ex;
+ }
+ }
+ }
+ if (thrownException != null) {
+ throw thrownException;
+ }
+ }
+
+ /** {@inheritDoc}. */
+ public Executor getSubProjectExecutor() {
+ return SUB_EXECUTOR;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/ProjectHelper2.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/ProjectHelper2.java
new file mode 100644
index 00000000..67e1decb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/ProjectHelper2.java
@@ -0,0 +1,1238 @@
+/*
+ * 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.tools.ant.helper;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ExtensionPoint;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.RuntimeConfigurable;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.UnknownElement;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.URLProvider;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.apache.tools.zip.ZipFile;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Sax2 based project reader
+ *
+ */
+public class ProjectHelper2 extends ProjectHelper {
+
+ /** Reference holding the (ordered) target Vector */
+ public static final String REFID_TARGETS = "ant.targets";
+
+ /* Stateless */
+
+ // singletons - since all state is in the context
+ private static AntHandler elementHandler = new ElementHandler();
+ private static AntHandler targetHandler = new TargetHandler();
+ private static AntHandler mainHandler = new MainHandler();
+ private static AntHandler projectHandler = new ProjectHandler();
+
+ /** Specific to ProjectHelper2 so not a true Ant "magic name:" */
+ private static final String REFID_CONTEXT = "ant.parsing.context";
+
+ /**
+ * helper for path -> URI and URI -> path conversions.
+ */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Whether this instance of ProjectHelper can parse an Antlib
+ * descriptor given by the URL and return its content as an
+ * UnknownElement ready to be turned into an Antlib task.
+ *
+ * <p>This implementation returns true.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public boolean canParseAntlibDescriptor(Resource resource) {
+ return true;
+ }
+
+ /**
+ * Parse the given URL as an antlib descriptor and return the
+ * content as something that can be turned into an Antlib task.
+ *
+ * <p>simply delegates to {@link #parseUnknownElement
+ * parseUnknownElement} if the resource provides an URL and throws
+ * an exception otherwise.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public UnknownElement parseAntlibDescriptor(Project containingProject,
+ Resource resource) {
+ URLProvider up = resource.as(URLProvider.class);
+ if (up == null) {
+ throw new BuildException("Unsupported resource type: " + resource);
+ }
+ return parseUnknownElement(containingProject, up.getURL());
+ }
+
+ /**
+ * Parse an unknown element from a url
+ *
+ * @param project the current project
+ * @param source the url containing the task
+ * @return a configured task
+ * @exception BuildException if an error occurs
+ */
+ public UnknownElement parseUnknownElement(Project project, URL source)
+ throws BuildException {
+ Target dummyTarget = new Target();
+ dummyTarget.setProject(project);
+
+ AntXMLContext context = new AntXMLContext(project);
+ context.addTarget(dummyTarget);
+ context.setImplicitTarget(dummyTarget);
+
+ parse(context.getProject(), source, new RootHandler(context, elementHandler));
+ Task[] tasks = dummyTarget.getTasks();
+ if (tasks.length != 1) {
+ throw new BuildException("No tasks defined");
+ }
+ return (UnknownElement) tasks[0];
+ }
+
+ /**
+ * Parse a source xml input.
+ *
+ * @param project the current project
+ * @param source the xml source
+ * @exception BuildException if an error occurs
+ */
+ public void parse(Project project, Object source) throws BuildException {
+ getImportStack().addElement(source);
+ AntXMLContext context = null;
+ context = (AntXMLContext) project.getReference(REFID_CONTEXT);
+ if (context == null) {
+ context = new AntXMLContext(project);
+ project.addReference(REFID_CONTEXT, context);
+ project.addReference(REFID_TARGETS, context.getTargets());
+ }
+ if (getImportStack().size() > 1) {
+ // we are in an imported file.
+ context.setIgnoreProjectTag(true);
+ Target currentTarget = context.getCurrentTarget();
+ Target currentImplicit = context.getImplicitTarget();
+ Map<String, Target> currentTargets = context.getCurrentTargets();
+ try {
+ Target newCurrent = new Target();
+ newCurrent.setProject(project);
+ newCurrent.setName("");
+ context.setCurrentTarget(newCurrent);
+ context.setCurrentTargets(new HashMap<String, Target>());
+ context.setImplicitTarget(newCurrent);
+ parse(project, source, new RootHandler(context, mainHandler));
+ newCurrent.execute();
+ } finally {
+ context.setCurrentTarget(currentTarget);
+ context.setImplicitTarget(currentImplicit);
+ context.setCurrentTargets(currentTargets);
+ }
+ } else {
+ // top level file
+ context.setCurrentTargets(new HashMap<String, Target>());
+ parse(project, source, new RootHandler(context, mainHandler));
+ // Execute the top-level target
+ context.getImplicitTarget().execute();
+
+ // resolve extensionOf attributes
+ resolveExtensionOfAttributes(project);
+ }
+ }
+
+ /**
+ * Parses the project file, configuring the project as it goes.
+ *
+ * @param project the current project
+ * @param source the xml source
+ * @param handler the root handler to use (contains the current context)
+ * @exception BuildException if the configuration is invalid or cannot
+ * be read
+ */
+ public void parse(Project project, Object source, RootHandler handler) throws BuildException {
+
+ AntXMLContext context = handler.context;
+
+ File buildFile = null;
+ URL url = null;
+ String buildFileName = null;
+
+ if (source instanceof File) {
+ buildFile = (File) source;
+ } else if (source instanceof URL) {
+ url = (URL) source;
+ } else if (source instanceof Resource) {
+ FileProvider fp =
+ ((Resource) source).as(FileProvider.class);
+ if (fp != null) {
+ buildFile = fp.getFile();
+ } else {
+ URLProvider up =
+ ((Resource) source).as(URLProvider.class);
+ if (up != null) {
+ url = up.getURL();
+ }
+ }
+ }
+ if (buildFile != null) {
+ buildFile = FILE_UTILS.normalize(buildFile.getAbsolutePath());
+ context.setBuildFile(buildFile);
+ buildFileName = buildFile.toString();
+ } else if (url != null) {
+ try {
+ context.setBuildFile((File) null);
+ context.setBuildFile(url);
+ } catch (java.net.MalformedURLException ex) {
+ throw new BuildException(ex);
+ }
+ buildFileName = url.toString();
+ } else {
+ throw new BuildException("Source " + source.getClass().getName()
+ + " not supported by this plugin");
+ }
+ InputStream inputStream = null;
+ InputSource inputSource = null;
+ ZipFile zf = null;
+
+ try {
+ /**
+ * SAX 2 style parser used to parse the given file.
+ */
+ XMLReader parser = JAXPUtils.getNamespaceXMLReader();
+
+ String uri = null;
+ if (buildFile != null) {
+ uri = FILE_UTILS.toURI(buildFile.getAbsolutePath());
+ inputStream = new FileInputStream(buildFile);
+ } else {
+ uri = url.toString();
+ int pling = -1;
+ if (uri.startsWith("jar:file")
+ && (pling = uri.indexOf("!/")) > -1) {
+ zf = new ZipFile(org.apache.tools.ant.launch.Locator
+ .fromJarURI(uri), "UTF-8");
+ inputStream =
+ zf.getInputStream(zf.getEntry(uri.substring(pling + 1)));
+ } else {
+ URLConnection conn = url.openConnection();
+ conn.setUseCaches(false);
+ inputStream = conn.getInputStream();
+ }
+ }
+
+ inputSource = new InputSource(inputStream);
+ if (uri != null) {
+ inputSource.setSystemId(uri);
+ }
+ project.log("parsing buildfile " + buildFileName + " with URI = "
+ + uri + (zf != null ? " from a zip file" : ""),
+ Project.MSG_VERBOSE);
+
+ DefaultHandler hb = handler;
+
+ parser.setContentHandler(hb);
+ parser.setEntityResolver(hb);
+ parser.setErrorHandler(hb);
+ parser.setDTDHandler(hb);
+ parser.parse(inputSource);
+ } catch (SAXParseException exc) {
+ Location location = new Location(exc.getSystemId(), exc.getLineNumber(), exc
+ .getColumnNumber());
+
+ Throwable t = exc.getException();
+ if (t instanceof BuildException) {
+ BuildException be = (BuildException) t;
+ if (be.getLocation() == Location.UNKNOWN_LOCATION) {
+ be.setLocation(location);
+ }
+ throw be;
+ }
+ throw new BuildException(exc.getMessage(), t == null ? exc : t, location);
+ } catch (SAXException exc) {
+ Throwable t = exc.getException();
+ if (t instanceof BuildException) {
+ throw (BuildException) t;
+ }
+ throw new BuildException(exc.getMessage(), t == null ? exc : t);
+ } catch (FileNotFoundException exc) {
+ throw new BuildException(exc);
+ } catch (UnsupportedEncodingException exc) {
+ throw new BuildException("Encoding of project file " + buildFileName + " is invalid.",
+ exc);
+ } catch (IOException exc) {
+ throw new BuildException("Error reading project file " + buildFileName + ": "
+ + exc.getMessage(), exc);
+ } finally {
+ FileUtils.close(inputStream);
+ ZipFile.closeQuietly(zf);
+ }
+ }
+
+ /**
+ * Returns main handler
+ * @return main handler
+ */
+ protected static AntHandler getMainHandler() {
+ return mainHandler;
+ }
+
+ /**
+ * Sets main handler
+ * @param handler new main handler
+ */
+ protected static void setMainHandler(AntHandler handler) {
+ mainHandler = handler;
+ }
+
+ /**
+ * Returns project handler
+ * @return project handler
+ */
+ protected static AntHandler getProjectHandler() {
+ return projectHandler;
+ }
+
+ /**
+ * Sets project handler
+ * @param handler new project handler
+ */
+ protected static void setProjectHandler(AntHandler handler) {
+ projectHandler = handler;
+ }
+
+ /**
+ * Returns target handler
+ * @return target handler
+ */
+ protected static AntHandler getTargetHandler() {
+ return targetHandler;
+ }
+
+ /**
+ * Sets target handler
+ * @param handler new target handler
+ */
+ protected static void setTargetHandler(AntHandler handler) {
+ targetHandler = handler;
+ }
+
+ /**
+ * Returns element handler
+ * @return element handler
+ */
+ protected static AntHandler getElementHandler() {
+ return elementHandler;
+ }
+
+ /**
+ * Sets element handler
+ * @param handler new element handler
+ */
+ protected static void setElementHandler(AntHandler handler) {
+ elementHandler = handler;
+ }
+
+ /**
+ * The common superclass for all SAX event handlers used to parse
+ * the configuration file.
+ *
+ * The context will hold all state information. At each time
+ * there is one active handler for the current element. It can
+ * use onStartChild() to set an alternate handler for the child.
+ */
+ public static class AntHandler {
+ /**
+ * Handles the start of an element. This base implementation does
+ * nothing.
+ *
+ * @param uri the namespace URI for the tag
+ * @param tag The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param qname The qualified name of the element.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ * @param context The context that this element is in.
+ *
+ * @exception SAXParseException if this method is not overridden, or in
+ * case of error in an overridden version
+ */
+ public void onStartElement(String uri, String tag, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ }
+
+ /**
+ * Handles the start of an element. This base implementation just
+ * throws an exception - you must override this method if you expect
+ * child elements.
+ *
+ * @param uri The namespace uri for this element.
+ * @param tag The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ * @param context The current context.
+ * @return a handler (in the derived classes)
+ *
+ * @exception SAXParseException if this method is not overridden, or in
+ * case of error in an overridden version
+ */
+ public AntHandler onStartChild(String uri, String tag, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ throw new SAXParseException("Unexpected element \"" + qname + " \"", context
+ .getLocator());
+ }
+
+ /**
+ * Handle the end of a element.
+ *
+ * @param uri the namespace uri of the element
+ * @param tag the tag of the element
+ * @param qname the qualified name of the element
+ * @param context the current context
+ * @exception SAXParseException if an error occurs
+ */
+ public void onEndChild(String uri, String tag, String qname, AntXMLContext context)
+ throws SAXParseException {
+ }
+
+ /**
+ * This method is called when this element and all elements nested into it have been
+ * handled. I.e., this happens at the &lt;/end_tag_of_the_element&gt;.
+ * @param uri the namespace uri for this element
+ * @param tag the element name
+ * @param context the current context
+ */
+ public void onEndElement(String uri, String tag, AntXMLContext context) {
+ }
+
+ /**
+ * Handles text within an element. This base implementation just
+ * throws an exception, you must override it if you expect content.
+ *
+ * @param buf A character array of the text within the element.
+ * Will not be <code>null</code>.
+ * @param start The start element in the array.
+ * @param count The number of characters to read from the array.
+ * @param context The current context.
+ *
+ * @exception SAXParseException if this method is not overridden, or in
+ * case of error in an overridden version
+ */
+ public void characters(char[] buf, int start, int count, AntXMLContext context)
+ throws SAXParseException {
+ String s = new String(buf, start, count).trim();
+
+ if (s.length() > 0) {
+ throw new SAXParseException("Unexpected text \"" + s + "\"", context.getLocator());
+ }
+ }
+
+ /**
+ * Will be called every time a namespace is reached.
+ * It'll verify if the ns was processed, and if not load the task definitions.
+ * @param uri The namespace uri.
+ */
+ protected void checkNamespace(String uri) {
+ }
+ }
+
+ /**
+ * Handler for ant processing. Uses a stack of AntHandlers to
+ * implement each element ( the original parser used a recursive behavior,
+ * with the implicit execution stack )
+ */
+ public static class RootHandler extends DefaultHandler {
+ private Stack<AntHandler> antHandlers = new Stack<AntHandler>();
+ private AntHandler currentHandler = null;
+ private AntXMLContext context;
+
+ /**
+ * Creates a new RootHandler instance.
+ *
+ * @param context The context for the handler.
+ * @param rootHandler The handler for the root element.
+ */
+ public RootHandler(AntXMLContext context, AntHandler rootHandler) {
+ currentHandler = rootHandler;
+ antHandlers.push(currentHandler);
+ this.context = context;
+ }
+
+ /**
+ * Returns the current ant handler object.
+ * @return the current ant handler.
+ */
+ public AntHandler getCurrentAntHandler() {
+ return currentHandler;
+ }
+
+ /**
+ * Resolves file: URIs relative to the build file.
+ *
+ * @param publicId The public identifier, or <code>null</code>
+ * if none is available. Ignored in this
+ * implementation.
+ * @param systemId The system identifier provided in the XML
+ * document. Will not be <code>null</code>.
+ * @return an inputsource for this identifier
+ */
+ public InputSource resolveEntity(String publicId, String systemId) {
+
+ context.getProject().log("resolving systemId: " + systemId, Project.MSG_VERBOSE);
+
+ if (systemId.startsWith("file:")) {
+ String path = FILE_UTILS.fromURI(systemId);
+
+ File file = new File(path);
+ if (!file.isAbsolute()) {
+ file = FILE_UTILS.resolveFile(context.getBuildFileParent(), path);
+ context.getProject().log(
+ "Warning: '" + systemId + "' in " + context.getBuildFile()
+ + " should be expressed simply as '" + path.replace('\\', '/')
+ + "' for compliance with other XML tools", Project.MSG_WARN);
+ }
+ context.getProject().log("file=" + file, Project.MSG_DEBUG);
+ try {
+ InputSource inputSource = new InputSource(new FileInputStream(file));
+ inputSource.setSystemId(FILE_UTILS.toURI(file.getAbsolutePath()));
+ return inputSource;
+ } catch (FileNotFoundException fne) {
+ context.getProject().log(file.getAbsolutePath() + " could not be found",
+ Project.MSG_WARN);
+ }
+
+ }
+ // use default if not file or file not found
+ context.getProject().log("could not resolve systemId", Project.MSG_DEBUG);
+ return null;
+ }
+
+ /**
+ * Handles the start of a project element. A project handler is created
+ * and initialised with the element name and attributes.
+ *
+ * @param uri The namespace uri for this element.
+ * @param tag The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ *
+ * @exception org.xml.sax.SAXParseException if the tag given is not
+ * <code>"project"</code>
+ */
+ public void startElement(String uri, String tag, String qname, Attributes attrs)
+ throws SAXParseException {
+ AntHandler next = currentHandler.onStartChild(uri, tag, qname, attrs, context);
+ antHandlers.push(currentHandler);
+ currentHandler = next;
+ currentHandler.onStartElement(uri, tag, qname, attrs, context);
+ }
+
+ /**
+ * Sets the locator in the project helper for future reference.
+ *
+ * @param locator The locator used by the parser.
+ * Will not be <code>null</code>.
+ */
+ public void setDocumentLocator(Locator locator) {
+ context.setLocator(locator);
+ }
+
+ /**
+ * Handles the end of an element. Any required clean-up is performed
+ * by the onEndElement() method and then the original handler is restored to the parser.
+ *
+ * @param uri The namespace URI for this element.
+ * @param name The name of the element which is ending.
+ * Will not be <code>null</code>.
+ * @param qName The qualified name for this element.
+ *
+ * @exception SAXException in case of error (not thrown in this implementation)
+ */
+ public void endElement(String uri, String name, String qName) throws SAXException {
+ currentHandler.onEndElement(uri, name, context);
+ AntHandler prev = (AntHandler) antHandlers.pop();
+ currentHandler = prev;
+ if (currentHandler != null) {
+ currentHandler.onEndChild(uri, name, qName, context);
+ }
+ }
+
+ /**
+ * Handle text within an element, calls currentHandler.characters.
+ *
+ * @param buf A character array of the test.
+ * @param start The start offset in the array.
+ * @param count The number of characters to read.
+ * @exception SAXParseException if an error occurs
+ */
+ public void characters(char[] buf, int start, int count) throws SAXParseException {
+ currentHandler.characters(buf, start, count, context);
+ }
+
+ /**
+ * Start a namespace prefix to uri mapping
+ *
+ * @param prefix the namespace prefix
+ * @param uri the namespace uri
+ */
+ public void startPrefixMapping(String prefix, String uri) {
+ context.startPrefixMapping(prefix, uri);
+ }
+
+ /**
+ * End a namespace prefix to uri mapping
+ *
+ * @param prefix the prefix that is not mapped anymore
+ */
+ public void endPrefixMapping(String prefix) {
+ context.endPrefixMapping(prefix);
+ }
+ }
+
+ /**
+ * The main handler - it handles the &lt;project&gt; tag.
+ *
+ * @see org.apache.tools.ant.helper.ProjectHelper2.AntHandler
+ */
+ public static class MainHandler extends AntHandler {
+
+ /**
+ * Handle the project tag
+ *
+ * @param uri The namespace uri.
+ * @param name The element tag.
+ * @param qname The element qualified name.
+ * @param attrs The attributes of the element.
+ * @param context The current context.
+ * @return The project handler that handles subelements of project
+ * @exception SAXParseException if the qualified name is not "project".
+ */
+ public AntHandler onStartChild(String uri, String name, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ if (name.equals("project")
+ && (uri.equals("") || uri.equals(ANT_CORE_URI))) {
+ return ProjectHelper2.projectHandler;
+ }
+ if (name.equals(qname)) {
+ throw new SAXParseException("Unexpected element \"{" + uri
+ + "}" + name + "\" {" + ANT_CORE_URI + "}" + name, context.getLocator());
+ }
+ throw new SAXParseException("Unexpected element \"" + qname
+ + "\" " + name, context.getLocator());
+ }
+ }
+
+ /**
+ * Handler for the top level "project" element.
+ */
+ public static class ProjectHandler extends AntHandler {
+
+ /**
+ * Initialisation routine called after handler creation
+ * with the element name and attributes. The attributes which
+ * this handler can deal with are: <code>"default"</code>,
+ * <code>"name"</code>, <code>"id"</code> and <code>"basedir"</code>.
+ *
+ * @param uri The namespace URI for this element.
+ * @param tag Name of the element which caused this handler
+ * to be created. Should not be <code>null</code>.
+ * Ignored in this implementation.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element which caused this
+ * handler to be created. Must not be <code>null</code>.
+ * @param context The current context.
+ *
+ * @exception SAXParseException if an unexpected attribute is
+ * encountered or if the <code>"default"</code> attribute
+ * is missing.
+ */
+ public void onStartElement(String uri, String tag, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ String baseDir = null;
+ boolean nameAttributeSet = false;
+
+ Project project = context.getProject();
+ // Set the location of the implicit target associated with the project tag
+ context.getImplicitTarget().setLocation(new Location(context.getLocator()));
+
+ /** TODO I really don't like this - the XML processor is still
+ * too 'involved' in the processing. A better solution (IMO)
+ * would be to create UE for Project and Target too, and
+ * then process the tree and have Project/Target deal with
+ * its attributes ( similar with Description ).
+ *
+ * If we eventually switch to ( or add support for ) DOM,
+ * things will work smoothly - UE can be avoided almost completely
+ * ( it could still be created on demand, for backward compatibility )
+ */
+
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String attrUri = attrs.getURI(i);
+ if (attrUri != null && !attrUri.equals("") && !attrUri.equals(uri)) {
+ continue; // Ignore attributes from unknown uris
+ }
+ String key = attrs.getLocalName(i);
+ String value = attrs.getValue(i);
+
+ if (key.equals("default")) {
+ if (value != null && !value.equals("")) {
+ if (!context.isIgnoringProjectTag()) {
+ project.setDefault(value);
+ }
+ }
+ } else if (key.equals("name")) {
+ if (value != null) {
+ context.setCurrentProjectName(value);
+ nameAttributeSet = true;
+ if (!context.isIgnoringProjectTag()) {
+ project.setName(value);
+ project.addReference(value, project);
+ } else if (isInIncludeMode()) {
+ if (!"".equals(value) && getCurrentTargetPrefix()!= null && getCurrentTargetPrefix().endsWith(ProjectHelper.USE_PROJECT_NAME_AS_TARGET_PREFIX)) {
+ String newTargetPrefix = getCurrentTargetPrefix().replace(ProjectHelper.USE_PROJECT_NAME_AS_TARGET_PREFIX, value);
+ // help nested include tasks
+ setCurrentTargetPrefix(newTargetPrefix);
+ }
+ }
+ }
+ } else if (key.equals("id")) {
+ if (value != null) {
+ // What's the difference between id and name ?
+ if (!context.isIgnoringProjectTag()) {
+ project.addReference(value, project);
+ }
+ }
+ } else if (key.equals("basedir")) {
+ if (!context.isIgnoringProjectTag()) {
+ baseDir = value;
+ }
+ } else {
+ // TODO ignore attributes in a different NS ( maybe store them ? )
+ throw new SAXParseException("Unexpected attribute \"" + attrs.getQName(i)
+ + "\"", context.getLocator());
+ }
+ }
+
+ // TODO Move to Project ( so it is shared by all helpers )
+ String antFileProp =
+ MagicNames.ANT_FILE + "." + context.getCurrentProjectName();
+ String dup = project.getProperty(antFileProp);
+ String typeProp =
+ MagicNames.ANT_FILE_TYPE + "." + context.getCurrentProjectName();
+ String dupType = project.getProperty(typeProp);
+ if (dup != null && nameAttributeSet) {
+ Object dupFile = null;
+ Object contextFile = null;
+ if (MagicNames.ANT_FILE_TYPE_URL.equals(dupType)) {
+ try {
+ dupFile = new URL(dup);
+ } catch (java.net.MalformedURLException mue) {
+ throw new BuildException("failed to parse "
+ + dup + " as URL while looking"
+ + " at a duplicate project"
+ + " name.", mue);
+ }
+ contextFile = context.getBuildFileURL();
+ } else {
+ dupFile = new File(dup);
+ contextFile = context.getBuildFile();
+ }
+
+ if (context.isIgnoringProjectTag() && !dupFile.equals(contextFile)) {
+ project.log("Duplicated project name in import. Project "
+ + context.getCurrentProjectName() + " defined first in " + dup
+ + " and again in " + contextFile, Project.MSG_WARN);
+ }
+ }
+ if (nameAttributeSet) {
+ if (context.getBuildFile() != null) {
+ project.setUserProperty(antFileProp,
+ context.getBuildFile().toString());
+ project.setUserProperty(typeProp,
+ MagicNames.ANT_FILE_TYPE_FILE);
+ } else if (context.getBuildFileURL() != null) {
+ project.setUserProperty(antFileProp,
+ context.getBuildFileURL().toString());
+ project.setUserProperty(typeProp,
+ MagicNames.ANT_FILE_TYPE_URL);
+ }
+ }
+ if (context.isIgnoringProjectTag()) {
+ // no further processing
+ return;
+ }
+ // set explicitly before starting ?
+ if (project.getProperty("basedir") != null) {
+ project.setBasedir(project.getProperty("basedir"));
+ } else {
+ // Default for baseDir is the location of the build file.
+ if (baseDir == null) {
+ project.setBasedir(context.getBuildFileParent().getAbsolutePath());
+ } else {
+ // check whether the user has specified an absolute path
+ if ((new File(baseDir)).isAbsolute()) {
+ project.setBasedir(baseDir);
+ } else {
+ project.setBaseDir(FILE_UTILS.resolveFile(context.getBuildFileParent(),
+ baseDir));
+ }
+ }
+ }
+ project.addTarget("", context.getImplicitTarget());
+ context.setCurrentTarget(context.getImplicitTarget());
+ }
+
+ /**
+ * Handles the start of a top-level element within the project. An
+ * appropriate handler is created and initialised with the details
+ * of the element.
+ *
+ * @param uri The namespace URI for this element.
+ * @param name The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ * @param context The context for this element.
+ * @return a target or an element handler.
+ *
+ * @exception org.xml.sax.SAXParseException if the tag given is not
+ * <code>"taskdef"</code>, <code>"typedef"</code>,
+ * <code>"property"</code>, <code>"target"</code>,
+ * <code>"extension-point"</code>
+ * or a data type definition
+ */
+ public AntHandler onStartChild(String uri, String name, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ return (name.equals("target") || name.equals("extension-point"))
+ && (uri.equals("") || uri.equals(ANT_CORE_URI))
+ ? ProjectHelper2.targetHandler : ProjectHelper2.elementHandler;
+ }
+ }
+
+ /**
+ * Handler for "target" and "extension-point" elements.
+ */
+ public static class TargetHandler extends AntHandler {
+
+ /**
+ * Initialisation routine called after handler creation
+ * with the element name and attributes. The attributes which
+ * this handler can deal with are: <code>"name"</code>,
+ * <code>"depends"</code>, <code>"if"</code>,
+ * <code>"unless"</code>, <code>"id"</code> and
+ * <code>"description"</code>.
+ *
+ * @param uri The namespace URI for this element.
+ * @param tag Name of the element which caused this handler
+ * to be created. Should not be <code>null</code>.
+ * Ignored in this implementation.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element which caused this
+ * handler to be created. Must not be <code>null</code>.
+ * @param context The current context.
+ *
+ * @exception SAXParseException if an unexpected attribute is encountered
+ * or if the <code>"name"</code> attribute is missing.
+ */
+ public void onStartElement(String uri, String tag, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ String name = null;
+ String depends = "";
+ String extensionPoint = null;
+ OnMissingExtensionPoint extensionPointMissing = null;
+
+ Project project = context.getProject();
+ Target target = "target".equals(tag)
+ ? new Target() : new ExtensionPoint();
+ target.setProject(project);
+ target.setLocation(new Location(context.getLocator()));
+ context.addTarget(target);
+
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String attrUri = attrs.getURI(i);
+ if (attrUri != null && !attrUri.equals("") && !attrUri.equals(uri)) {
+ continue; // Ignore attributes from unknown uris
+ }
+ String key = attrs.getLocalName(i);
+ String value = attrs.getValue(i);
+
+ if (key.equals("name")) {
+ name = value;
+ if ("".equals(name)) {
+ throw new BuildException("name attribute must " + "not be empty");
+ }
+ } else if (key.equals("depends")) {
+ depends = value;
+ } else if (key.equals("if")) {
+ target.setIf(value);
+ } else if (key.equals("unless")) {
+ target.setUnless(value);
+ } else if (key.equals("id")) {
+ if (value != null && !value.equals("")) {
+ context.getProject().addReference(value, target);
+ }
+ } else if (key.equals("description")) {
+ target.setDescription(value);
+ } else if (key.equals("extensionOf")) {
+ extensionPoint = value;
+ } else if (key.equals("onMissingExtensionPoint")) {
+ try {
+ extensionPointMissing = OnMissingExtensionPoint.valueOf(value);
+ } catch (IllegalArgumentException e) {
+ throw new BuildException("Invalid onMissingExtensionPoint " + value);
+ }
+ } else {
+ throw new SAXParseException("Unexpected attribute \"" + key + "\"", context
+ .getLocator());
+ }
+ }
+
+ if (name == null) {
+ throw new SAXParseException("target element appears without a name attribute",
+ context.getLocator());
+ }
+
+ String prefix = null;
+ boolean isInIncludeMode =
+ context.isIgnoringProjectTag() && isInIncludeMode();
+ String sep = getCurrentPrefixSeparator();
+
+ if (isInIncludeMode) {
+ prefix = getTargetPrefix(context);
+ if (prefix == null) {
+ throw new BuildException("can't include build file "
+ + context.getBuildFileURL()
+ + ", no as attribute has been given"
+ + " and the project tag doesn't"
+ + " specify a name attribute");
+ }
+ name = prefix + sep + name;
+ }
+
+ // Check if this target is in the current build file
+ if (context.getCurrentTargets().get(name) != null) {
+ throw new BuildException("Duplicate target '" + name + "'",
+ target.getLocation());
+ }
+ Hashtable<String, Target> projectTargets = project.getTargets();
+ boolean usedTarget = false;
+ // If the name has not already been defined define it
+ if (projectTargets.containsKey(name)) {
+ project.log("Already defined in main or a previous import, ignore " + name,
+ Project.MSG_VERBOSE);
+ } else {
+ target.setName(name);
+ context.getCurrentTargets().put(name, target);
+ project.addOrReplaceTarget(name, target);
+ usedTarget = true;
+ }
+
+ if (depends.length() > 0) {
+ if (!isInIncludeMode) {
+ target.setDepends(depends);
+ } else {
+ for (String string : Target.parseDepends(depends, name, "depends")) {
+ target.addDependency(prefix + sep + string);
+ }
+ }
+ }
+ if (!isInIncludeMode && context.isIgnoringProjectTag()
+ && (prefix = getTargetPrefix(context)) != null) {
+ // In an imported file (and not completely
+ // ignoring the project tag or having a preconfigured prefix)
+ String newName = prefix + sep + name;
+ Target newTarget = target;
+ if (usedTarget) {
+ newTarget = "target".equals(tag)
+ ? new Target(target) : new ExtensionPoint(target);
+ }
+ newTarget.setName(newName);
+ context.getCurrentTargets().put(newName, newTarget);
+ project.addOrReplaceTarget(newName, newTarget);
+ }
+ if (extensionPointMissing != null && extensionPoint == null) {
+ throw new BuildException("onMissingExtensionPoint attribute cannot " +
+ "be specified unless extensionOf is specified",
+ target.getLocation());
+
+ }
+ if (extensionPoint != null) {
+ ProjectHelper helper =
+ (ProjectHelper) context.getProject().
+ getReference(ProjectHelper.PROJECTHELPER_REFERENCE);
+ for (String extPointName : Target.parseDepends(extensionPoint, name, "extensionOf")) {
+ if (extensionPointMissing == null) {
+ extensionPointMissing = OnMissingExtensionPoint.FAIL;
+ }
+ // defer extensionpoint resolution until the full
+ // import stack has been processed
+ if (isInIncludeMode()) {
+ // if in include mode, provide prefix we're including by
+ // so that we can try and resolve extension point from
+ // the local file first
+ helper.getExtensionStack().add(
+ new String[] {extPointName, target.getName(),
+ extensionPointMissing.name(), prefix + sep});
+ } else {
+ helper.getExtensionStack().add(
+ new String[] {extPointName, target.getName(),
+ extensionPointMissing.name()});
+ }
+ }
+ }
+ }
+
+ private String getTargetPrefix(AntXMLContext context) {
+ String configuredValue = getCurrentTargetPrefix();
+ if (configuredValue != null && configuredValue.length() == 0) {
+ configuredValue = null;
+ }
+ if (configuredValue != null) {
+ return configuredValue;
+ }
+
+ String projectName = context.getCurrentProjectName();
+ if ("".equals(projectName)) {
+ projectName = null;
+ }
+
+ return projectName;
+ }
+
+ /**
+ * Handles the start of an element within a target.
+ *
+ * @param uri The namespace URI for this element.
+ * @param name The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ * @param context The current context.
+ * @return an element handler.
+ *
+ * @exception SAXParseException if an error occurs when initialising
+ * the appropriate child handler
+ */
+ public AntHandler onStartChild(String uri, String name, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ return ProjectHelper2.elementHandler;
+ }
+
+ /**
+ * Handle the end of the project, sets the current target of the
+ * context to be the implicit target.
+ *
+ * @param uri The namespace URI of the element.
+ * @param tag The name of the element.
+ * @param context The current context.
+ */
+ public void onEndElement(String uri, String tag, AntXMLContext context) {
+ context.setCurrentTarget(context.getImplicitTarget());
+ }
+ }
+
+ /**
+ * Handler for all project elements ( tasks, data types )
+ */
+ public static class ElementHandler extends AntHandler {
+
+ /**
+ * Constructor.
+ */
+ public ElementHandler() {
+ }
+
+ /**
+ * Initialisation routine called after handler creation
+ * with the element name and attributes. This configures
+ * the element with its attributes and sets it up with
+ * its parent container (if any). Nested elements are then
+ * added later as the parser encounters them.
+ *
+ * @param uri The namespace URI for this element.
+ * @param tag Name of the element which caused this handler
+ * to be created. Must not be <code>null</code>.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element which caused this
+ * handler to be created. Must not be <code>null</code>.
+ * @param context The current context.
+ *
+ * @exception SAXParseException in case of error (not thrown in
+ * this implementation)
+ */
+ public void onStartElement(String uri, String tag, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ RuntimeConfigurable parentWrapper = context.currentWrapper();
+ Object parent = null;
+
+ if (parentWrapper != null) {
+ parent = parentWrapper.getProxy();
+ }
+
+ /* UnknownElement is used for tasks and data types - with
+ delayed eval */
+ UnknownElement task = new UnknownElement(tag);
+ task.setProject(context.getProject());
+ task.setNamespace(uri);
+ task.setQName(qname);
+ task.setTaskType(ProjectHelper.genComponentName(task.getNamespace(), tag));
+ task.setTaskName(qname);
+
+ Location location = new Location(context.getLocator().getSystemId(), context
+ .getLocator().getLineNumber(), context.getLocator().getColumnNumber());
+ task.setLocation(location);
+ task.setOwningTarget(context.getCurrentTarget());
+
+ if (parent != null) {
+ // Nested element
+ ((UnknownElement) parent).addChild(task);
+ } else {
+ // Task included in a target ( including the default one ).
+ context.getCurrentTarget().addTask(task);
+ }
+
+ context.configureId(task, attrs);
+
+ // container.addTask(task);
+ // This is a nop in UE: task.init();
+
+ RuntimeConfigurable wrapper = new RuntimeConfigurable(task, task.getTaskName());
+
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String name = attrs.getLocalName(i);
+ String attrUri = attrs.getURI(i);
+ if (attrUri != null && !attrUri.equals("") && !attrUri.equals(uri)) {
+ name = attrUri + ":" + attrs.getQName(i);
+ }
+ String value = attrs.getValue(i);
+ // PR: Hack for ant-type value
+ // an ant-type is a component name which can
+ // be namespaced, need to extract the name
+ // and convert from qualified name to uri/name
+ if (ANT_TYPE.equals(name)
+ || (ANT_CORE_URI.equals(attrUri)
+ && ANT_TYPE.equals(attrs.getLocalName(i)))) {
+ name = ANT_TYPE;
+ int index = value.indexOf(":");
+ if (index >= 0) {
+ String prefix = value.substring(0, index);
+ String mappedUri = context.getPrefixMapping(prefix);
+ if (mappedUri == null) {
+ throw new BuildException("Unable to find XML NS prefix \"" + prefix
+ + "\"");
+ }
+ value = ProjectHelper.genComponentName(mappedUri, value
+ .substring(index + 1));
+ }
+ }
+ wrapper.setAttribute(name, value);
+ }
+ if (parentWrapper != null) {
+ parentWrapper.addChild(wrapper);
+ }
+ context.pushWrapper(wrapper);
+ }
+
+ /**
+ * Adds text to the task, using the wrapper
+ *
+ * @param buf A character array of the text within the element.
+ * Will not be <code>null</code>.
+ * @param start The start element in the array.
+ * @param count The number of characters to read from the array.
+ * @param context The current context.
+ *
+ * @exception SAXParseException if the element doesn't support text
+ *
+ * @see ProjectHelper#addText(Project,java.lang.Object,char[],int,int)
+ */
+ public void characters(char[] buf, int start, int count,
+ AntXMLContext context) throws SAXParseException {
+ RuntimeConfigurable wrapper = context.currentWrapper();
+ wrapper.addText(buf, start, count);
+ }
+
+ /**
+ * Handles the start of an element within a target. Task containers
+ * will always use another task handler, and all other tasks
+ * will always use a nested element handler.
+ *
+ * @param uri The namespace URI for this element.
+ * @param tag The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ * @param context The current context.
+ * @return The handler for elements.
+ *
+ * @exception SAXParseException if an error occurs when initialising
+ * the appropriate child handler
+ */
+ public AntHandler onStartChild(String uri, String tag, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ return ProjectHelper2.elementHandler;
+ }
+
+ /**
+ * Handles the end of the element. This pops the wrapper from
+ * the context.
+ *
+ * @param uri The namespace URI for the element.
+ * @param tag The name of the element.
+ * @param context The current context.
+ */
+ public void onEndElement(String uri, String tag, AntXMLContext context) {
+ context.popWrapper();
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java
new file mode 100644
index 00000000..f828d292
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java
@@ -0,0 +1,1026 @@
+/*
+ * 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.tools.ant.helper;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.IntrospectionHelper;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.RuntimeConfigurable;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.TypeAdapter;
+import org.apache.tools.ant.UnknownElement;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.xml.sax.AttributeList;
+import org.xml.sax.DocumentHandler;
+import org.xml.sax.HandlerBase;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.XMLReaderAdapter;
+
+/**
+ * Original helper.
+ *
+ */
+public class ProjectHelperImpl extends ProjectHelper {
+
+ /**
+ * helper for path -> URI and URI -> path conversions.
+ */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * SAX 1 style parser used to parse the given file. This may
+ * in fact be a SAX 2 XMLReader wrapped in an XMLReaderAdapter.
+ */
+ private org.xml.sax.Parser parser;
+
+ /** The project to configure. */
+ private Project project;
+
+ /** The configuration file to parse. */
+ private File buildFile;
+
+ /**
+ * Parent directory of the build file. Used for resolving entities
+ * and setting the project's base directory.
+ */
+ private File buildFileParent;
+
+ /**
+ * Locator for the configuration file parser.
+ * Used for giving locations of errors etc.
+ */
+ private Locator locator;
+
+ /**
+ * Target that all other targets will depend upon implicitly.
+ *
+ * <p>This holds all tasks and data type definitions that have
+ * been placed outside of targets.</p>
+ */
+ private Target implicitTarget = new Target();
+
+ /**
+ * default constructor
+ */
+ public ProjectHelperImpl() {
+ implicitTarget.setName("");
+ }
+
+ /**
+ * Parses the project file, configuring the project as it goes.
+ *
+ * @param project project instance to be configured.
+ * @param source the source from which the project is read.
+ * @exception BuildException if the configuration is invalid or cannot
+ * be read.
+ */
+ public void parse(Project project, Object source) throws BuildException {
+ if (!(source instanceof File)) {
+ throw new BuildException("Only File source supported by "
+ + "default plugin");
+ }
+ File bFile = (File) source;
+ FileInputStream inputStream = null;
+ InputSource inputSource = null;
+
+ this.project = project;
+ this.buildFile = new File(bFile.getAbsolutePath());
+ buildFileParent = new File(this.buildFile.getParent());
+
+ try {
+ try {
+ parser = JAXPUtils.getParser();
+ } catch (BuildException e) {
+ parser = new XMLReaderAdapter(JAXPUtils.getXMLReader());
+ }
+ String uri = FILE_UTILS.toURI(bFile.getAbsolutePath());
+ inputStream = new FileInputStream(bFile);
+ inputSource = new InputSource(inputStream);
+ inputSource.setSystemId(uri);
+ project.log("parsing buildfile " + bFile + " with URI = " + uri, Project.MSG_VERBOSE);
+ HandlerBase hb = new RootHandler(this);
+ parser.setDocumentHandler(hb);
+ parser.setEntityResolver(hb);
+ parser.setErrorHandler(hb);
+ parser.setDTDHandler(hb);
+ parser.parse(inputSource);
+ } catch (SAXParseException exc) {
+ Location location = new Location(exc.getSystemId(), exc.getLineNumber(), exc
+ .getColumnNumber());
+
+ Throwable t = exc.getException();
+ if (t instanceof BuildException) {
+ BuildException be = (BuildException) t;
+ if (be.getLocation() == Location.UNKNOWN_LOCATION) {
+ be.setLocation(location);
+ }
+ throw be;
+ }
+ throw new BuildException(exc.getMessage(), t, location);
+ } catch (SAXException exc) {
+ Throwable t = exc.getException();
+ if (t instanceof BuildException) {
+ throw (BuildException) t;
+ }
+ throw new BuildException(exc.getMessage(), t);
+ } catch (FileNotFoundException exc) {
+ throw new BuildException(exc);
+ } catch (UnsupportedEncodingException exc) {
+ throw new BuildException("Encoding of project file is invalid.", exc);
+ } catch (IOException exc) {
+ throw new BuildException("Error reading project file: " + exc.getMessage(), exc);
+ } finally {
+ FileUtils.close(inputStream);
+ }
+ }
+
+ /**
+ * The common superclass for all SAX event handlers used to parse
+ * the configuration file. Each method just throws an exception,
+ * so subclasses should override what they can handle.
+ *
+ * Each type of XML element (task, target, etc.) in Ant has
+ * a specific subclass.
+ *
+ * In the constructor, this class takes over the handling of SAX
+ * events from the parent handler and returns
+ * control back to the parent in the endElement method.
+ */
+ static class AbstractHandler extends HandlerBase {
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /**
+ * Previous handler for the document.
+ * When the next element is finished, control returns
+ * to this handler.
+ */
+ protected DocumentHandler parentHandler;
+
+ /** Helper impl. With non-static internal classes, the compiler will generate
+ this automatically - but this will fail with some compilers ( reporting
+ "Expecting to find object/array on stack" ). If we pass it
+ explicitly it'll work with more compilers.
+ */
+ ProjectHelperImpl helperImpl;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Creates a handler and sets the parser to use it
+ * for the current element.
+ *
+ * @param helperImpl the ProjectHelperImpl instance associated
+ * with this handler.
+ *
+ * @param parentHandler The handler which should be restored to the
+ * parser at the end of the element.
+ * Must not be <code>null</code>.
+ */
+ public AbstractHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
+ this.parentHandler = parentHandler;
+ this.helperImpl = helperImpl;
+
+ // Start handling SAX events
+ helperImpl.parser.setDocumentHandler(this);
+ }
+
+ /**
+ * Handles the start of an element. This base implementation just
+ * throws an exception.
+ *
+ * @param tag The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ *
+ * @exception SAXParseException if this method is not overridden, or in
+ * case of error in an overridden version
+ */
+ public void startElement(String tag, AttributeList attrs) throws SAXParseException {
+ throw new SAXParseException("Unexpected element \"" + tag + "\"", helperImpl.locator);
+ }
+
+ /**
+ * Handles text within an element. This base implementation just
+ * throws an exception.
+ *
+ * @param buf A character array of the text within the element.
+ * Will not be <code>null</code>.
+ * @param start The start element in the array.
+ * @param count The number of characters to read from the array.
+ *
+ * @exception SAXParseException if this method is not overridden, or in
+ * case of error in an overridden version
+ */
+ public void characters(char[] buf, int start, int count) throws SAXParseException {
+ String s = new String(buf, start, count).trim();
+
+ if (s.length() > 0) {
+ throw new SAXParseException("Unexpected text \"" + s + "\"", helperImpl.locator);
+ }
+ }
+
+ /**
+ * Handles the end of an element. Any required clean-up is performed
+ * by the finished() method and then the original handler is restored to
+ * the parser.
+ *
+ * @param name The name of the element which is ending.
+ * Will not be <code>null</code>.
+ *
+ * @exception SAXException in case of error (not thrown in
+ * this implementation)
+ */
+ public void endElement(String name) throws SAXException {
+ // Let parent resume handling SAX events
+ helperImpl.parser.setDocumentHandler(parentHandler);
+ }
+ }
+
+ /**
+ * Handler for the root element. Its only child must be the "project" element.
+ */
+ static class RootHandler extends HandlerBase {
+ // CheckStyle:VisibilityModifier OFF - bc
+ ProjectHelperImpl helperImpl;
+ // CheckStyle:VisibilityModifier ON
+
+ public RootHandler(ProjectHelperImpl helperImpl) {
+ this.helperImpl = helperImpl;
+ }
+
+ /**
+ * Resolves file: URIs relative to the build file.
+ *
+ * @param publicId The public identifier, or <code>null</code>
+ * if none is available. Ignored in this
+ * implementation.
+ * @param systemId The system identifier provided in the XML
+ * document. Will not be <code>null</code>.
+ */
+ public InputSource resolveEntity(String publicId, String systemId) {
+
+ helperImpl.project.log("resolving systemId: " + systemId, Project.MSG_VERBOSE);
+
+ if (systemId.startsWith("file:")) {
+ String path = FILE_UTILS.fromURI(systemId);
+
+ File file = new File(path);
+ if (!file.isAbsolute()) {
+ file = FILE_UTILS.resolveFile(helperImpl.buildFileParent, path);
+ helperImpl.project.log("Warning: '" + systemId + "' in " + helperImpl.buildFile
+ + " should be expressed simply as '" + path.replace('\\', '/')
+ + "' for compliance with other XML tools", Project.MSG_WARN);
+ }
+ try {
+ InputSource inputSource = new InputSource(new FileInputStream(file));
+ inputSource.setSystemId(FILE_UTILS.toURI(file.getAbsolutePath()));
+ return inputSource;
+ } catch (FileNotFoundException fne) {
+ helperImpl.project.log(file.getAbsolutePath() + " could not be found",
+ Project.MSG_WARN);
+ }
+ }
+ // use default if not file or file not found
+ return null;
+ }
+
+ /**
+ * Handles the start of a project element. A project handler is created
+ * and initialised with the element name and attributes.
+ *
+ * @param tag The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ *
+ * @exception SAXParseException if the tag given is not
+ * <code>"project"</code>
+ */
+ public void startElement(String tag, AttributeList attrs) throws SAXParseException {
+ if (tag.equals("project")) {
+ new ProjectHandler(helperImpl, this).init(tag, attrs);
+ } else {
+ throw new SAXParseException("Config file is not of expected " + "XML type",
+ helperImpl.locator);
+ }
+ }
+
+ /**
+ * Sets the locator in the project helper for future reference.
+ *
+ * @param locator The locator used by the parser.
+ * Will not be <code>null</code>.
+ */
+ public void setDocumentLocator(Locator locator) {
+ helperImpl.locator = locator;
+ }
+ }
+
+ /**
+ * Handler for the top level "project" element.
+ */
+ static class ProjectHandler extends AbstractHandler {
+
+ /**
+ * Constructor which just delegates to the superconstructor.
+ *
+ * @param parentHandler The handler which should be restored to the
+ * parser at the end of the element.
+ * Must not be <code>null</code>.
+ */
+ public ProjectHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
+ super(helperImpl, parentHandler);
+ }
+
+ /**
+ * Initialisation routine called after handler creation
+ * with the element name and attributes. The attributes which
+ * this handler can deal with are: <code>"default"</code>,
+ * <code>"name"</code>, <code>"id"</code> and <code>"basedir"</code>.
+ *
+ * @param tag Name of the element which caused this handler
+ * to be created. Should not be <code>null</code>.
+ * Ignored in this implementation.
+ * @param attrs Attributes of the element which caused this
+ * handler to be created. Must not be <code>null</code>.
+ *
+ * @exception SAXParseException if an unexpected attribute is
+ * encountered or if the <code>"default"</code> attribute
+ * is missing.
+ */
+ public void init(String tag, AttributeList attrs) throws SAXParseException {
+ String def = null;
+ String name = null;
+ String id = null;
+ String baseDir = null;
+
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String key = attrs.getName(i);
+ String value = attrs.getValue(i);
+
+ if (key.equals("default")) {
+ def = value;
+ } else if (key.equals("name")) {
+ name = value;
+ } else if (key.equals("id")) {
+ id = value;
+ } else if (key.equals("basedir")) {
+ baseDir = value;
+ } else {
+ throw new SAXParseException(
+ "Unexpected attribute \"" + attrs.getName(i)
+ + "\"", helperImpl.locator);
+ }
+ }
+
+ if (def != null && !def.equals("")) {
+ helperImpl.project.setDefault(def);
+ } else {
+ throw new BuildException("The default attribute is required");
+ }
+
+ if (name != null) {
+ helperImpl.project.setName(name);
+ helperImpl.project.addReference(name, helperImpl.project);
+ }
+
+ if (id != null) {
+ helperImpl.project.addReference(id, helperImpl.project);
+ }
+
+ if (helperImpl.project.getProperty("basedir") != null) {
+ helperImpl.project.setBasedir(helperImpl.project.getProperty("basedir"));
+ } else {
+ if (baseDir == null) {
+ helperImpl.project.setBasedir(helperImpl.buildFileParent.getAbsolutePath());
+ } else {
+ // check whether the user has specified an absolute path
+ if ((new File(baseDir)).isAbsolute()) {
+ helperImpl.project.setBasedir(baseDir);
+ } else {
+ File resolvedBaseDir = FILE_UTILS.resolveFile(helperImpl.buildFileParent,
+ baseDir);
+ helperImpl.project.setBaseDir(resolvedBaseDir);
+ }
+ }
+ }
+
+ helperImpl.project.addTarget("", helperImpl.implicitTarget);
+ }
+
+ /**
+ * Handles the start of a top-level element within the project. An
+ * appropriate handler is created and initialised with the details
+ * of the element.
+ *
+ * @param name The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ *
+ * @exception SAXParseException if the tag given is not
+ * <code>"taskdef"</code>, <code>"typedef"</code>,
+ * <code>"property"</code>, <code>"target"</code>
+ * or a data type definition
+ */
+ public void startElement(String name, AttributeList attrs) throws SAXParseException {
+ if (name.equals("target")) {
+ handleTarget(name, attrs);
+ } else {
+ handleElement(helperImpl, this, helperImpl.implicitTarget, name, attrs);
+ }
+ }
+
+ /**
+ * Handles a target definition element by creating a target handler
+ * and initialising is with the details of the element.
+ *
+ * @param tag The name of the element to be handled.
+ * Will not be <code>null</code>.
+ * @param attrs Attributes of the element to be handled.
+ * Will not be <code>null</code>.
+ *
+ * @exception SAXParseException if an error occurs initialising
+ * the handler
+ */
+ private void handleTarget(String tag, AttributeList attrs) throws SAXParseException {
+ new TargetHandler(helperImpl, this).init(tag, attrs);
+ }
+ }
+
+ /**
+ * Handler for "target" elements.
+ */
+ static class TargetHandler extends AbstractHandler {
+ private Target target;
+
+ /**
+ * Constructor which just delegates to the superconstructor.
+ *
+ * @param parentHandler The handler which should be restored to the
+ * parser at the end of the element.
+ * Must not be <code>null</code>.
+ */
+ public TargetHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
+ super(helperImpl, parentHandler);
+ }
+
+ /**
+ * Initialisation routine called after handler creation
+ * with the element name and attributes. The attributes which
+ * this handler can deal with are: <code>"name"</code>,
+ * <code>"depends"</code>, <code>"if"</code>,
+ * <code>"unless"</code>, <code>"id"</code> and
+ * <code>"description"</code>.
+ *
+ * @param tag Name of the element which caused this handler
+ * to be created. Should not be <code>null</code>.
+ * Ignored in this implementation.
+ * @param attrs Attributes of the element which caused this
+ * handler to be created. Must not be <code>null</code>.
+ *
+ * @exception SAXParseException if an unexpected attribute is encountered
+ * or if the <code>"name"</code> attribute is missing.
+ */
+ public void init(String tag, AttributeList attrs) throws SAXParseException {
+ String name = null;
+ String depends = "";
+ String ifCond = null;
+ String unlessCond = null;
+ String id = null;
+ String description = null;
+
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String key = attrs.getName(i);
+ String value = attrs.getValue(i);
+
+ if (key.equals("name")) {
+ name = value;
+ if (name.equals("")) {
+ throw new BuildException("name attribute must not" + " be empty",
+ new Location(helperImpl.locator));
+ }
+ } else if (key.equals("depends")) {
+ depends = value;
+ } else if (key.equals("if")) {
+ ifCond = value;
+ } else if (key.equals("unless")) {
+ unlessCond = value;
+ } else if (key.equals("id")) {
+ id = value;
+ } else if (key.equals("description")) {
+ description = value;
+ } else {
+ throw new SAXParseException("Unexpected attribute \"" + key + "\"",
+ helperImpl.locator);
+ }
+ }
+
+ if (name == null) {
+ throw new SAXParseException("target element appears without a name attribute",
+ helperImpl.locator);
+ }
+
+ target = new Target();
+
+ // implicit target must be first on dependency list
+ target.addDependency("");
+
+ target.setName(name);
+ target.setIf(ifCond);
+ target.setUnless(unlessCond);
+ target.setDescription(description);
+ helperImpl.project.addTarget(name, target);
+
+ if (id != null && !id.equals("")) {
+ helperImpl.project.addReference(id, target);
+ }
+
+ // take care of dependencies
+
+ if (depends.length() > 0) {
+ target.setDepends(depends);
+ }
+ }
+
+ /**
+ * Handles the start of an element within a target.
+ *
+ * @param name The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ *
+ * @exception SAXParseException if an error occurs when initialising
+ * the appropriate child handler
+ */
+ public void startElement(String name, AttributeList attrs) throws SAXParseException {
+ handleElement(helperImpl, this, target, name, attrs);
+ }
+ }
+
+ /**
+ * Start a new DataTypeHandler if element is known to be a
+ * data-type and a TaskHandler otherwise.
+ *
+ * <p>Factored out of TargetHandler.</p>
+ *
+ * @since Ant 1.6
+ */
+ private static void handleElement(ProjectHelperImpl helperImpl, DocumentHandler parent,
+ Target target, String elementName, AttributeList attrs) throws SAXParseException {
+ if (elementName.equals("description")) {
+ new DescriptionHandler(helperImpl, parent);
+ } else if (helperImpl.project.getDataTypeDefinitions().get(elementName) != null) {
+ new DataTypeHandler(helperImpl, parent, target).init(elementName, attrs);
+ } else {
+ new TaskHandler(helperImpl, parent, target, null, target).init(elementName, attrs);
+ }
+ }
+
+ /**
+ * Handler for "description" elements.
+ */
+ static class DescriptionHandler extends AbstractHandler {
+
+ /**
+ * Constructor which just delegates to the superconstructor.
+ *
+ * @param parentHandler The handler which should be restored to the
+ * parser at the end of the element.
+ * Must not be <code>null</code>.
+ */
+ public DescriptionHandler(ProjectHelperImpl helperImpl,
+ DocumentHandler parentHandler) {
+ super(helperImpl, parentHandler);
+ }
+
+ /**
+ * Adds the text as description to the project.
+ *
+ * @param buf A character array of the text within the element.
+ * Will not be <code>null</code>.
+ * @param start The start element in the array.
+ * @param count The number of characters to read from the array.
+ */
+ public void characters(char[] buf, int start, int count) {
+ String text = new String(buf, start, count);
+ String currentDescription = helperImpl.project.getDescription();
+ if (currentDescription == null) {
+ helperImpl.project.setDescription(text);
+ } else {
+ helperImpl.project.setDescription(currentDescription + text);
+ }
+ }
+
+ }
+
+ /**
+ * Handler for all task elements.
+ */
+ static class TaskHandler extends AbstractHandler {
+ /** Containing target, if any. */
+ private Target target;
+
+ /**
+ * Container for the task, if any. If target is
+ * non-<code>null</code>, this must be too.
+ */
+ private TaskContainer container;
+
+ /**
+ * Task created by this handler.
+ */
+ private Task task;
+
+ /**
+ * Wrapper for the parent element, if any. The wrapper for this
+ * element will be added to this wrapper as a child.
+ */
+ private RuntimeConfigurable parentWrapper;
+
+ /**
+ * Wrapper for this element which takes care of actually configuring
+ * the element, if this element is contained within a target.
+ * Otherwise the configuration is performed with the configure method.
+ * @see ProjectHelper#configure(Object,AttributeList,Project)
+ */
+ private RuntimeConfigurable wrapper = null;
+
+ /**
+ * Constructor.
+ *
+ * @param parentHandler The handler which should be restored to the
+ * parser at the end of the element.
+ * Must not be <code>null</code>.
+ *
+ * @param container Container for the element.
+ * Must not be <code>null</code>.
+ *
+ * @param parentWrapper Wrapper for the parent element, if any.
+ * May be <code>null</code>.
+ *
+ * @param target Target this element is part of.
+ * Must not be <code>null</code>.
+ */
+ public TaskHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler,
+ TaskContainer container,
+ RuntimeConfigurable parentWrapper, Target target) {
+ super(helperImpl, parentHandler);
+ this.container = container;
+ this.parentWrapper = parentWrapper;
+ this.target = target;
+ }
+
+ /**
+ * Initialisation routine called after handler creation
+ * with the element name and attributes. This configures
+ * the element with its attributes and sets it up with
+ * its parent container (if any). Nested elements are then
+ * added later as the parser encounters them.
+ *
+ * @param tag Name of the element which caused this handler
+ * to be created. Must not be <code>null</code>.
+ *
+ * @param attrs Attributes of the element which caused this
+ * handler to be created. Must not be <code>null</code>.
+ *
+ * @exception SAXParseException in case of error (not thrown in
+ * this implementation)
+ */
+ public void init(String tag, AttributeList attrs) throws SAXParseException {
+ try {
+ task = helperImpl.project.createTask(tag);
+ } catch (BuildException e) {
+ // swallow here, will be thrown again in
+ // UnknownElement.maybeConfigure if the problem persists.
+ }
+ if (task == null) {
+ task = new UnknownElement(tag);
+ task.setProject(helperImpl.project);
+ //TODO task.setTaskType(tag);
+ task.setTaskName(tag);
+ }
+ task.setLocation(new Location(helperImpl.locator));
+ helperImpl.configureId(task, attrs);
+
+ task.setOwningTarget(target);
+ container.addTask(task);
+ task.init();
+ wrapper = task.getRuntimeConfigurableWrapper();
+ wrapper.setAttributes(attrs);
+ if (parentWrapper != null) {
+ parentWrapper.addChild(wrapper);
+ }
+ }
+
+ /**
+ * Adds text to the task, using the wrapper.
+ *
+ * @param buf A character array of the text within the element.
+ * Will not be <code>null</code>.
+ * @param start The start element in the array.
+ * @param count The number of characters to read from the array.
+ */
+ public void characters(char[] buf, int start, int count) {
+ wrapper.addText(buf, start, count);
+ }
+
+ /**
+ * Handles the start of an element within a target. Task containers
+ * will always use another task handler, and all other tasks
+ * will always use a nested element handler.
+ *
+ * @param name The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ *
+ * @exception SAXParseException if an error occurs when initialising
+ * the appropriate child handler
+ */
+ public void startElement(String name, AttributeList attrs) throws SAXParseException {
+ if (task instanceof TaskContainer) {
+ // task can contain other tasks - no other nested elements possible
+ new TaskHandler(helperImpl, this, (TaskContainer) task, wrapper, target).init(name,
+ attrs);
+ } else {
+ new NestedElementHandler(helperImpl, this, task, wrapper, target).init(name, attrs);
+ }
+ }
+ }
+
+ /**
+ * Handler for all nested properties.
+ */
+ static class NestedElementHandler extends AbstractHandler {
+ /** Parent object (task/data type/etc). */
+ private Object parent;
+
+ /** The nested element itself. */
+ private Object child;
+
+ /**
+ * Wrapper for the parent element, if any. The wrapper for this
+ * element will be added to this wrapper as a child.
+ */
+ private RuntimeConfigurable parentWrapper;
+
+ /**
+ * Wrapper for this element which takes care of actually configuring
+ * the element, if a parent wrapper is provided.
+ * Otherwise the configuration is performed with the configure method.
+ * @see ProjectHelper#configure(Object,AttributeList,Project)
+ */
+ private RuntimeConfigurable childWrapper = null;
+
+ /** Target this element is part of, if any. */
+ private Target target;
+
+ /**
+ * Constructor.
+ *
+ * @param parentHandler The handler which should be restored to the
+ * parser at the end of the element.
+ * Must not be <code>null</code>.
+ *
+ * @param parent Parent of this element (task/data type/etc).
+ * Must not be <code>null</code>.
+ *
+ * @param parentWrapper Wrapper for the parent element, if any.
+ * Must not be <code>null</code>.
+ *
+ * @param target Target this element is part of.
+ * Must not be <code>null</code>.
+ */
+ public NestedElementHandler(ProjectHelperImpl helperImpl,
+ DocumentHandler parentHandler,
+ Object parent,
+ RuntimeConfigurable parentWrapper,
+ Target target) {
+ super(helperImpl, parentHandler);
+
+ if (parent instanceof TypeAdapter) {
+ this.parent = ((TypeAdapter) parent).getProxy();
+ } else {
+ this.parent = parent;
+ }
+ this.parentWrapper = parentWrapper;
+ this.target = target;
+ }
+
+ /**
+ * Initialisation routine called after handler creation
+ * with the element name and attributes. This configures
+ * the element with its attributes and sets it up with
+ * its parent container (if any). Nested elements are then
+ * added later as the parser encounters them.
+ *
+ * @param propType Name of the element which caused this handler
+ * to be created. Must not be <code>null</code>.
+ *
+ * @param attrs Attributes of the element which caused this
+ * handler to be created. Must not be <code>null</code>.
+ *
+ * @exception SAXParseException in case of error, such as a
+ * BuildException being thrown during configuration.
+ */
+ public void init(String propType, AttributeList attrs) throws SAXParseException {
+ Class<?> parentClass = parent.getClass();
+ IntrospectionHelper ih = IntrospectionHelper.getHelper(helperImpl.project, parentClass);
+
+ try {
+ String elementName = propType.toLowerCase(Locale.ENGLISH);
+ if (parent instanceof UnknownElement) {
+ UnknownElement uc = new UnknownElement(elementName);
+ uc.setProject(helperImpl.project);
+ ((UnknownElement) parent).addChild(uc);
+ child = uc;
+ } else {
+ child = ih.createElement(helperImpl.project, parent, elementName);
+ }
+ helperImpl.configureId(child, attrs);
+
+ childWrapper = new RuntimeConfigurable(child, propType);
+ childWrapper.setAttributes(attrs);
+ parentWrapper.addChild(childWrapper);
+ } catch (BuildException exc) {
+ throw new SAXParseException(exc.getMessage(), helperImpl.locator, exc);
+ }
+ }
+
+ /**
+ * Adds text to the element, using the wrapper.
+ *
+ * @param buf A character array of the text within the element.
+ * Will not be <code>null</code>.
+ * @param start The start element in the array.
+ * @param count The number of characters to read from the array.
+ */
+ public void characters(char[] buf, int start, int count) {
+ childWrapper.addText(buf, start, count);
+ }
+
+ /**
+ * Handles the start of an element within this one. Task containers
+ * will always use a task handler, and all other elements
+ * will always use another nested element handler.
+ *
+ * @param name The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ *
+ * @exception SAXParseException if an error occurs when initialising
+ * the appropriate child handler
+ */
+ public void startElement(String name, AttributeList attrs) throws SAXParseException {
+ if (child instanceof TaskContainer) {
+ // taskcontainer nested element can contain other tasks - no other
+ // nested elements possible
+ new TaskHandler(helperImpl, this, (TaskContainer) child, childWrapper, target)
+ .init(name, attrs);
+ } else {
+ new NestedElementHandler(helperImpl, this, child, childWrapper, target).init(name,
+ attrs);
+ }
+ }
+ }
+
+ /**
+ * Handler for all data types directly subordinate to project or target.
+ */
+ static class DataTypeHandler extends AbstractHandler {
+ /** Parent target, if any. */
+ private Target target;
+
+ /** The element being configured. */
+ private Object element;
+
+ /** Wrapper for this element, if it's part of a target. */
+ private RuntimeConfigurable wrapper = null;
+
+ /**
+ * Constructor with a target specified.
+ *
+ * @param parentHandler The handler which should be restored to the
+ * parser at the end of the element.
+ * Must not be <code>null</code>.
+ *
+ * @param target The parent target of this element.
+ * Must not be <code>null</code>.
+ */
+ public DataTypeHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler,
+ Target target) {
+ super(helperImpl, parentHandler);
+ this.target = target;
+ }
+
+ /**
+ * Initialisation routine called after handler creation
+ * with the element name and attributes. This configures
+ * the element with its attributes and sets it up with
+ * its parent container (if any). Nested elements are then
+ * added later as the parser encounters them.
+ *
+ * @param propType Name of the element which caused this handler
+ * to be created. Must not be <code>null</code>.
+ *
+ * @param attrs Attributes of the element which caused this
+ * handler to be created. Must not be <code>null</code>.
+ *
+ * @exception SAXParseException in case of error, such as a
+ * BuildException being thrown during configuration.
+ */
+ public void init(String propType, AttributeList attrs) throws SAXParseException {
+ try {
+ element = helperImpl.project.createDataType(propType);
+ if (element == null) {
+ throw new BuildException("Unknown data type " + propType);
+ }
+ wrapper = new RuntimeConfigurable(element, propType);
+ wrapper.setAttributes(attrs);
+ target.addDataType(wrapper);
+ } catch (BuildException exc) {
+ throw new SAXParseException(exc.getMessage(), helperImpl.locator, exc);
+ }
+ }
+
+ /**
+ * Adds text to the using the wrapper.
+ *
+ * @param buf A character array of the text within the element.
+ * Will not be <code>null</code>.
+ * @param start The start element in the array.
+ * @param count The number of characters to read from the array.
+ *
+ * @see ProjectHelper#addText(Project,Object,char[],int,int)
+ */
+ public void characters(char[] buf, int start, int count) {
+ wrapper.addText(buf, start, count);
+ }
+
+ /**
+ * Handles the start of an element within this one.
+ * This will always use a nested element handler.
+ *
+ * @param name The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ *
+ * @exception SAXParseException if an error occurs when initialising
+ * the child handler
+ */
+ public void startElement(String name, AttributeList attrs) throws SAXParseException {
+ new NestedElementHandler(helperImpl, this, element, wrapper, target).init(name, attrs);
+ }
+ }
+
+ /**
+ * Scans an attribute list for the <code>id</code> attribute and
+ * stores a reference to the target object in the project if an
+ * id is found.
+ * <p>
+ * This method was moved out of the configure method to allow
+ * it to be executed at parse time.
+ *
+ * @see #configure(Object,AttributeList,Project)
+ */
+ private void configureId(Object target, AttributeList attr) {
+ String id = attr.getValue("id");
+ if (id != null) {
+ project.addReference(id, target);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/SingleCheckExecutor.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/SingleCheckExecutor.java
new file mode 100644
index 00000000..1960ed03
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/helper/SingleCheckExecutor.java
@@ -0,0 +1,47 @@
+/*
+ * 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.tools.ant.helper;
+
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Executor;
+import org.apache.tools.ant.Project;
+
+
+/**
+ * "Single-check" Target executor implementation.
+ * Differs from {@link DefaultExecutor} in that the dependencies for all
+ * targets are computed together, so that shared dependencies are run just once.
+ * @since Ant 1.6.3
+ */
+public class SingleCheckExecutor implements Executor {
+
+ /** {@inheritDoc}. */
+ public void executeTargets(Project project, String[] targetNames)
+ throws BuildException {
+ project.executeSortedTargets(
+ project.topoSort(targetNames, project.getTargets(), false));
+ }
+
+ /** {@inheritDoc}. */
+ public Executor getSubProjectExecutor() {
+ return this;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/DefaultInputHandler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/DefaultInputHandler.java
new file mode 100644
index 00000000..8268d5e7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/DefaultInputHandler.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.tools.ant.input;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.KeepAliveInputStream;
+
+/**
+ * Prompts on System.err, reads input from System.in
+ *
+ * @since Ant 1.5
+ */
+public class DefaultInputHandler implements InputHandler {
+
+ /**
+ * Empty no-arg constructor
+ */
+ public DefaultInputHandler() {
+ }
+
+ /**
+ * Prompts and requests input. May loop until a valid input has
+ * been entered.
+ * @param request the request to handle
+ * @throws BuildException if not possible to read from console
+ */
+ public void handleInput(InputRequest request) throws BuildException {
+ String prompt = getPrompt(request);
+ BufferedReader r = null;
+ try {
+ r = new BufferedReader(new InputStreamReader(getInputStream()));
+ do {
+ System.err.println(prompt);
+ System.err.flush();
+ try {
+ String input = r.readLine();
+ request.setInput(input);
+ } catch (IOException e) {
+ throw new BuildException("Failed to read input from"
+ + " Console.", e);
+ }
+ } while (!request.isInputValid());
+ } finally {
+ if (r != null) {
+ try {
+ r.close();
+ } catch (IOException e) {
+ throw new BuildException("Failed to close input.", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Constructs user prompt from a request.
+ *
+ * <p>This implementation adds (choice1,choice2,choice3,...) to the
+ * prompt for <code>MultipleChoiceInputRequest</code>s.</p>
+ *
+ * @param request the request to construct the prompt for.
+ * Must not be <code>null</code>.
+ * @return the prompt to ask the user
+ */
+ protected String getPrompt(InputRequest request) {
+ String prompt = request.getPrompt();
+ String def = request.getDefaultValue();
+ if (request instanceof MultipleChoiceInputRequest) {
+ StringBuilder sb = new StringBuilder(prompt).append(" (");
+ boolean first = true;
+ for (String next : ((MultipleChoiceInputRequest) request).getChoices()) {
+ if (!first) {
+ sb.append(", ");
+ }
+ if (next.equals(def)) {
+ sb.append('[');
+ }
+ sb.append(next);
+ if (next.equals(def)) {
+ sb.append(']');
+ }
+ first = false;
+ }
+ sb.append(")");
+ return sb.toString();
+ } else if (def != null) {
+ return prompt + " [" + def + "]";
+ } else {
+ return prompt;
+ }
+ }
+
+ /**
+ * Returns the input stream from which the user input should be read.
+ * @return the input stream from which the user input should be read.
+ */
+ protected InputStream getInputStream() {
+ return KeepAliveInputStream.wrapSystemIn();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/GreedyInputHandler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/GreedyInputHandler.java
new file mode 100644
index 00000000..cb52f4f0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/GreedyInputHandler.java
@@ -0,0 +1,80 @@
+/*
+ * 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.tools.ant.input;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.StreamPumper;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Prompts on System.err, reads input from System.in until EOF
+ *
+ * @since Ant 1.7
+ */
+public class GreedyInputHandler extends DefaultInputHandler {
+
+ /**
+ * Empty no-arg constructor
+ */
+ public GreedyInputHandler() {
+ }
+
+ /**
+ * Prompts and requests input.
+ * @param request the request to handle
+ * @throws BuildException if not possible to read from console,
+ * or if input is invalid.
+ */
+ public void handleInput(InputRequest request) throws BuildException {
+ String prompt = getPrompt(request);
+ InputStream in = null;
+ try {
+ in = getInputStream();
+ System.err.println(prompt);
+ System.err.flush();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ StreamPumper p = new StreamPumper(in, baos);
+ Thread t = new Thread(p);
+ t.start();
+ try {
+ t.join();
+ } catch (InterruptedException e) {
+ try {
+ t.join();
+ } catch (InterruptedException e2) {
+ // Ignore
+ }
+ }
+ request.setInput(new String(baos.toByteArray()));
+ if (!(request.isInputValid())) {
+ throw new BuildException(
+ "Received invalid console input");
+ }
+ if (p.getException() != null) {
+ throw new BuildException(
+ "Failed to read input from console", p.getException());
+ }
+ } finally {
+ FileUtils.close(in);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/InputHandler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/InputHandler.java
new file mode 100644
index 00000000..aea60fcc
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/InputHandler.java
@@ -0,0 +1,41 @@
+/*
+ * 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.tools.ant.input;
+
+/**
+ * Plugin to Ant to handle requests for user input.
+ *
+ * @since Ant 1.5
+ */
+public interface InputHandler {
+
+ /**
+ * Handle the request encapsulated in the argument.
+ *
+ * <p>Precondition: the request.getPrompt will return a non-null
+ * value.</p>
+ *
+ * <p>Postcondition: request.getInput will return a non-null
+ * value, request.isInputValid will return true.</p>
+ * @param request the request to be processed
+ * @throws org.apache.tools.ant.BuildException if the input cannot be read from the console
+ */
+ void handleInput(InputRequest request)
+ throws org.apache.tools.ant.BuildException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/InputRequest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/InputRequest.java
new file mode 100644
index 00000000..57a77da2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/InputRequest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.tools.ant.input;
+
+/**
+ * Encapsulates an input request.
+ *
+ * @since Ant 1.5
+ */
+public class InputRequest {
+ private final String prompt;
+ private String input;
+ private String defaultValue;
+
+ /**
+ * Construct an InputRequest.
+ * @param prompt The prompt to show to the user. Must not be null.
+ */
+ public InputRequest(String prompt) {
+ if (prompt == null) {
+ throw new IllegalArgumentException("prompt must not be null");
+ }
+
+ this.prompt = prompt;
+ }
+
+ /**
+ * Retrieves the prompt text.
+ * @return the prompt.
+ */
+ public String getPrompt() {
+ return prompt;
+ }
+
+ /**
+ * Sets the user provided input.
+ * @param input the string to be used for input.
+ */
+ public void setInput(String input) {
+ this.input = input;
+ }
+
+ /**
+ * Is the user input valid?
+ * @return true if it is.
+ */
+ public boolean isInputValid() {
+ return true;
+ }
+
+ /**
+ * Retrieves the user input.
+ * @return the user input.
+ */
+ public String getInput() {
+ return input;
+ }
+
+ /**
+ * Gets a configured default value.
+ * @return the default value.
+ * @since Ant 1.7.0
+ */
+ public String getDefaultValue() {
+ return defaultValue;
+ }
+
+ /**
+ * Configures a default value.
+ * @param d the value to set.
+ * @since Ant 1.7.0
+ */
+ public void setDefaultValue(String d) {
+ defaultValue = d;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/MultipleChoiceInputRequest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/MultipleChoiceInputRequest.java
new file mode 100644
index 00000000..4baab8f5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/MultipleChoiceInputRequest.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.tools.ant.input;
+
+import java.util.LinkedHashSet;
+import java.util.Vector;
+
+/**
+ * Encapsulates an input request.
+ *
+ * @since Ant 1.5
+ */
+public class MultipleChoiceInputRequest extends InputRequest {
+ private final LinkedHashSet<String> choices;
+
+ /**
+ * @param prompt The prompt to show to the user. Must not be null.
+ * @param choices holds all input values that are allowed.
+ * Must not be null.
+ */
+ public MultipleChoiceInputRequest(String prompt, Vector<String> choices) {
+ super(prompt);
+ if (choices == null) {
+ throw new IllegalArgumentException("choices must not be null");
+ }
+ this.choices = new LinkedHashSet<String>(choices);
+ }
+
+ /**
+ * @return The possible values.
+ */
+ public Vector<String> getChoices() {
+ return new Vector<String>(choices);
+ }
+
+ /**
+ * @return true if the input is one of the allowed values.
+ */
+ public boolean isInputValid() {
+ return choices.contains(getInput()) || ("".equals(getInput()) && getDefaultValue() != null);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/PropertyFileInputHandler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/PropertyFileInputHandler.java
new file mode 100644
index 00000000..e1e3cf11
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/PropertyFileInputHandler.java
@@ -0,0 +1,92 @@
+/*
+ * 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.tools.ant.input;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Reads input from a property file, the file name is read from the
+ * system property ant.input.properties, the prompt is the key for input.
+ *
+ * @since Ant 1.5
+ */
+public class PropertyFileInputHandler implements InputHandler {
+ private Properties props = null;
+
+ /**
+ * Name of the system property we expect to hold the file name.
+ */
+ public static final String FILE_NAME_KEY = "ant.input.properties";
+
+ /**
+ * Empty no-arg constructor.
+ */
+ public PropertyFileInputHandler() {
+ }
+
+ /**
+ * Picks up the input from a property, using the prompt as the
+ * name of the property.
+ * @param request an input request.
+ *
+ * @exception BuildException if no property of that name can be found.
+ */
+ public void handleInput(InputRequest request) throws BuildException {
+ readProps();
+
+ Object o = props.get(request.getPrompt());
+ if (o == null) {
+ throw new BuildException("Unable to find input for \'"
+ + request.getPrompt() + "\'");
+ }
+ request.setInput(o.toString());
+ if (!request.isInputValid()) {
+ throw new BuildException("Found invalid input " + o
+ + " for \'" + request.getPrompt() + "\'");
+ }
+ }
+
+ /**
+ * Reads the properties file if it hasn't already been read.
+ */
+ private synchronized void readProps() throws BuildException {
+ if (props == null) {
+ String propsFile = System.getProperty(FILE_NAME_KEY);
+ if (propsFile == null) {
+ throw new BuildException("System property "
+ + FILE_NAME_KEY
+ + " for PropertyFileInputHandler not"
+ + " set");
+ }
+
+ props = new Properties();
+
+ try {
+ props.load(new FileInputStream(propsFile));
+ } catch (IOException e) {
+ throw new BuildException("Couldn't load " + propsFile, e);
+ }
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/SecureInputHandler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/SecureInputHandler.java
new file mode 100644
index 00000000..d5aecff7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/input/SecureInputHandler.java
@@ -0,0 +1,59 @@
+/*
+ * 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.tools.ant.input;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.ReflectUtil;
+
+/**
+ * Prompts and requests input. May loop until a valid input has
+ * been entered. Doesn't echo input (requires Java6). If Java6 is not
+ * available, falls back to the DefaultHandler (insecure).
+ * @since Ant 1.7.1
+ */
+public class SecureInputHandler extends DefaultInputHandler {
+
+ /**
+ * Default no-args constructor
+ */
+ public SecureInputHandler() {
+ }
+
+ /**
+ * Handle the input
+ * @param request the request to handle
+ * @throws BuildException if not possible to read from console
+ */
+ public void handleInput(InputRequest request) throws BuildException {
+ String prompt = getPrompt(request);
+ try {
+ Object console = ReflectUtil.invokeStatic(System.class, "console");
+ do {
+ char[] input = (char[]) ReflectUtil.invoke(
+ console, "readPassword", String.class, prompt,
+ Object[].class, (Object[]) null);
+ request.setInput(new String(input));
+ /* for security zero char array after retrieving value */
+ java.util.Arrays.fill(input, ' ');
+ } while (!request.isInputValid());
+ } catch (Exception e) {
+ /* Java6 not present use default handler */
+ super.handleInput(request);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/AntMain.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/AntMain.java
new file mode 100644
index 00000000..4703eaa6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/AntMain.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.tools.ant.launch;
+
+import java.util.Properties;
+
+/**
+ * Interface used to bridge to the actual Main class without any
+ * messy reflection
+ *
+ * @since Ant 1.6
+ */
+public interface AntMain {
+ /**
+ * Start Ant.
+ *
+ * @param args command line args
+ * @param additionalUserProperties properties to set beyond those that
+ * may be specified on the args list
+ * @param coreLoader - not used
+ *
+ * @since Ant 1.6
+ */
+ void startAnt(String[] args, Properties additionalUserProperties,
+ ClassLoader coreLoader);
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/LaunchException.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/LaunchException.java
new file mode 100644
index 00000000..1bb37f8f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/LaunchException.java
@@ -0,0 +1,38 @@
+/*
+ * 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.tools.ant.launch;
+
+/**
+ * Signals an error condition during launching
+ *
+ * @since Ant 1.6
+ */
+public class LaunchException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs an exception with the given descriptive message.
+ *
+ * @param message A description of or information about the exception.
+ * Should not be <code>null</code>.
+ */
+ public LaunchException(String message) {
+ super(message);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/Launcher.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/Launcher.java
new file mode 100644
index 00000000..bf881db8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/Launcher.java
@@ -0,0 +1,412 @@
+/*
+ * 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.tools.ant.launch;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+
+
+
+/**
+ * This is a launcher for Ant.
+ *
+ * @since Ant 1.6
+ */
+public class Launcher {
+
+ private Launcher() {
+ }
+
+ /**
+ * The Ant Home (installation) Directory property.
+ * {@value}
+ */
+ public static final String ANTHOME_PROPERTY = "ant.home";
+
+ /**
+ * The Ant Library Directory property.
+ * {@value}
+ */
+ public static final String ANTLIBDIR_PROPERTY = "ant.library.dir";
+
+ /**
+ * The directory name of the per-user ant directory.
+ * {@value}
+ */
+ public static final String ANT_PRIVATEDIR = ".ant";
+
+ /**
+ * The name of a per-user library directory.
+ * {@value}
+ */
+ public static final String ANT_PRIVATELIB = "lib";
+
+ /**
+ * launch diagnostics flag; for debugging trouble at launch time.
+ */
+ public static boolean launchDiag = false;
+
+ /**
+ * The location of a per-user library directory.
+ * <p>
+ * It's value is the concatenation of {@link #ANT_PRIVATEDIR}
+ * with {@link #ANT_PRIVATELIB}, with an appropriate file separator
+ * in between. For example, on Unix, it's <code>.ant/lib</code>.
+ */
+ public static final String USER_LIBDIR =
+ ANT_PRIVATEDIR + File.separatorChar + ANT_PRIVATELIB;
+
+ /**
+ * The startup class that is to be run.
+ * {@value}
+ */
+ public static final String MAIN_CLASS = "org.apache.tools.ant.Main";
+
+ /**
+ * System property with user home directory.
+ * {@value}
+ */
+ public static final String USER_HOMEDIR = "user.home";
+
+ /**
+ * System property with application classpath.
+ * {@value}
+ */
+ private static final String JAVA_CLASS_PATH = "java.class.path";
+
+ /**
+ * Exit code on trouble
+ */
+ protected static final int EXIT_CODE_ERROR = 2;
+
+ /**
+ * Entry point for starting command line Ant.
+ *
+ * @param args commandline arguments
+ */
+ public static void main(final String[] args) {
+ int exitCode;
+ try {
+ final Launcher launcher = new Launcher();
+ exitCode = launcher.run(args);
+ } catch (final LaunchException e) {
+ exitCode = EXIT_CODE_ERROR;
+ System.err.println(e.getMessage());
+ } catch (final Throwable t) {
+ exitCode = EXIT_CODE_ERROR;
+ t.printStackTrace(System.err);
+ }
+ if (exitCode != 0) {
+ if (launchDiag) {
+ System.out.println("Exit code: "+exitCode);
+ }
+ System.exit(exitCode);
+ }
+ }
+
+
+ /**
+ * Add a CLASSPATH or -lib to lib path urls.
+ * Only filesystem resources are supported.
+ *
+ * @param path the classpath or lib path to add to the libPathULRLs
+ * @param getJars if true and a path is a directory, add the jars in
+ * the directory to the path urls
+ * @param libPathURLs the list of paths to add to
+ * @throws MalformedURLException if we can't create a URL
+ */
+ private void addPath(final String path, final boolean getJars, final List<URL> libPathURLs)
+ throws MalformedURLException {
+ final StringTokenizer tokenizer = new StringTokenizer(path, File.pathSeparator);
+ while (tokenizer.hasMoreElements()) {
+ final String elementName = tokenizer.nextToken();
+ final File element = new File(elementName);
+ if (elementName.indexOf('%') != -1 && !element.exists()) {
+ continue;
+ }
+ if (getJars && element.isDirectory()) {
+ // add any jars in the directory
+ final URL[] dirURLs = Locator.getLocationURLs(element);
+ for (int j = 0; j < dirURLs.length; ++j) {
+ if (launchDiag) { System.out.println("adding library JAR: " + dirURLs[j]);}
+ libPathURLs.add(dirURLs[j]);
+ }
+ }
+
+ final URL url = Locator.fileToURL(element);
+ if (launchDiag) {
+ System.out.println("adding library URL: " + url);
+ }
+ libPathURLs.add(url);
+ }
+ }
+
+ /**
+ * Run the launcher to launch Ant.
+ *
+ * @param args the command line arguments
+ * @return an exit code. As the normal ant main calls exit when it ends,
+ * this is for handling failures at bind-time
+ * @throws MalformedURLException if the URLs required for the classloader
+ * cannot be created.
+ * @throws LaunchException for launching problems
+ */
+ private int run(final String[] args)
+ throws LaunchException, MalformedURLException {
+ final String antHomeProperty = System.getProperty(ANTHOME_PROPERTY);
+ File antHome = null;
+
+ final File sourceJar = Locator.getClassSource(getClass());
+ final File jarDir = sourceJar.getParentFile();
+ String mainClassname = MAIN_CLASS;
+
+ if (antHomeProperty != null) {
+ antHome = new File(antHomeProperty);
+ }
+
+ if (antHome == null || !antHome.exists()) {
+ antHome = jarDir.getParentFile();
+ setProperty(ANTHOME_PROPERTY, antHome.getAbsolutePath());
+ }
+
+ if (!antHome.exists()) {
+ throw new LaunchException("Ant home is set incorrectly or "
+ + "ant could not be located (estimated value="+antHome.getAbsolutePath()+")");
+ }
+
+ final List<String> libPaths = new ArrayList<String>();
+ String cpString = null;
+ final List<String> argList = new ArrayList<String>();
+ String[] newArgs;
+ boolean noUserLib = false;
+ boolean noClassPath = false;
+
+ for (int i = 0; i < args.length; ++i) {
+ if (args[i].equals("-lib")) {
+ if (i == args.length - 1) {
+ throw new LaunchException("The -lib argument must "
+ + "be followed by a library location");
+ }
+ libPaths.add(args[++i]);
+ } else if (args[i].equals("-cp")) {
+ if (i == args.length - 1) {
+ throw new LaunchException("The -cp argument must "
+ + "be followed by a classpath expression");
+ }
+ if (cpString != null) {
+ throw new LaunchException("The -cp argument must "
+ + "not be repeated");
+ }
+ cpString = args[++i];
+ } else if (args[i].equals("--nouserlib") || args[i].equals("-nouserlib")) {
+ noUserLib = true;
+ } else if (args[i].equals("--launchdiag")) {
+ launchDiag = true;
+ } else if (args[i].equals("--noclasspath") || args[i].equals("-noclasspath")) {
+ noClassPath = true;
+ } else if (args[i].equals("-main")) {
+ if (i == args.length - 1) {
+ throw new LaunchException("The -main argument must "
+ + "be followed by a library location");
+ }
+ mainClassname = args[++i];
+ } else {
+ argList.add(args[i]);
+ }
+ }
+
+ logPath("Launcher JAR",sourceJar);
+ logPath("Launcher JAR directory", sourceJar.getParentFile());
+ logPath("java.home", new File(System.getProperty("java.home")));
+
+ //decide whether to copy the existing arg set, or
+ //build a new one from the list of all args excluding the special
+ //operations that only we handle
+ if (argList.size() == args.length) {
+ newArgs = args;
+ } else {
+ newArgs = argList.toArray(new String[argList.size()]);
+ }
+
+ final URL[] libURLs = getLibPathURLs(
+ noClassPath ? null : cpString, libPaths);
+ final URL[] systemURLs = getSystemURLs(jarDir);
+ final URL[] userURLs = noUserLib ? new URL[0] : getUserURLs();
+
+ final File toolsJAR = Locator.getToolsJar();
+ logPath("tools.jar",toolsJAR);
+ final URL[] jars = getJarArray(
+ libURLs, userURLs, systemURLs, toolsJAR);
+
+ // now update the class.path property
+ final StringBuffer baseClassPath
+ = new StringBuffer(System.getProperty(JAVA_CLASS_PATH));
+ if (baseClassPath.charAt(baseClassPath.length() - 1)
+ == File.pathSeparatorChar) {
+ baseClassPath.setLength(baseClassPath.length() - 1);
+ }
+
+ for (int i = 0; i < jars.length; ++i) {
+ baseClassPath.append(File.pathSeparatorChar);
+ baseClassPath.append(Locator.fromURI(jars[i].toString()));
+ }
+
+ setProperty(JAVA_CLASS_PATH, baseClassPath.toString());
+
+ final URLClassLoader loader = new URLClassLoader(jars, Launcher.class.getClassLoader());
+ Thread.currentThread().setContextClassLoader(loader);
+ Class<?> mainClass = null;
+ int exitCode = 0;
+ Throwable thrown=null;
+ try {
+ mainClass = loader.loadClass(mainClassname);
+ final AntMain main = (AntMain) mainClass.newInstance();
+ main.startAnt(newArgs, null, null);
+ } catch (final InstantiationException ex) {
+ System.err.println(
+ "Incompatible version of " + mainClassname + " detected");
+ final File mainJar = Locator.getClassSource(mainClass);
+ System.err.println(
+ "Location of this class " + mainJar);
+ thrown = ex;
+ } catch (final ClassNotFoundException cnfe) {
+ System.err.println(
+ "Failed to locate" + mainClassname);
+ thrown = cnfe;
+ } catch (final Throwable t) {
+ t.printStackTrace(System.err);
+ thrown=t;
+ }
+ if(thrown!=null) {
+ System.err.println(ANTHOME_PROPERTY+": "+antHome.getAbsolutePath());
+ System.err.println("Classpath: " + baseClassPath.toString());
+ System.err.println("Launcher JAR: " + sourceJar.getAbsolutePath());
+ System.err.println("Launcher Directory: " + jarDir.getAbsolutePath());
+ exitCode = EXIT_CODE_ERROR;
+ }
+ return exitCode;
+ }
+
+ /**
+ * Get the list of -lib entries and -cp entry into
+ * a URL array.
+ * @param cpString the classpath string
+ * @param libPaths the list of -lib entries.
+ * @return an array of URLs.
+ * @throws MalformedURLException if the URLs cannot be created.
+ */
+ private URL[] getLibPathURLs(final String cpString, final List<String> libPaths)
+ throws MalformedURLException {
+ final List<URL> libPathURLs = new ArrayList<URL>();
+
+ if (cpString != null) {
+ addPath(cpString, false, libPathURLs);
+ }
+
+ for (final String libPath : libPaths) {
+ addPath(libPath, true, libPathURLs);
+ }
+
+ return libPathURLs.toArray(new URL[libPathURLs.size()]);
+ }
+
+ /**
+ * Get the jar files in ANT_HOME/lib.
+ * determine ant library directory for system jars: use property
+ * or default using location of ant-launcher.jar
+ * @param antLauncherDir the dir that ant-launcher ran from
+ * @return the URLs
+ * @throws MalformedURLException if the URLs cannot be created.
+ */
+ private URL[] getSystemURLs(final File antLauncherDir) throws MalformedURLException {
+ File antLibDir = null;
+ final String antLibDirProperty = System.getProperty(ANTLIBDIR_PROPERTY);
+ if (antLibDirProperty != null) {
+ antLibDir = new File(antLibDirProperty);
+ }
+ if ((antLibDir == null) || !antLibDir.exists()) {
+ antLibDir = antLauncherDir;
+ setProperty(ANTLIBDIR_PROPERTY, antLibDir.getAbsolutePath());
+ }
+ return Locator.getLocationURLs(antLibDir);
+ }
+
+ /**
+ * Get the jar files in user.home/.ant/lib
+ * @return the URLS from the user's lib dir
+ * @throws MalformedURLException if the URLs cannot be created.
+ */
+ private URL[] getUserURLs() throws MalformedURLException {
+ final File userLibDir
+ = new File(System.getProperty(USER_HOMEDIR), USER_LIBDIR);
+
+ return Locator.getLocationURLs(userLibDir);
+ }
+
+ /**
+ * Combine the various jar sources into a single array of jars.
+ * @param libJars the jars specified in -lib command line options
+ * @param userJars the jars in ~/.ant/lib
+ * @param systemJars the jars in $ANT_HOME/lib
+ * @param toolsJar the tools.jar file
+ * @return a combined array
+ * @throws MalformedURLException if there is a problem.
+ */
+ private URL[] getJarArray (
+ final URL[] libJars, final URL[] userJars, final URL[] systemJars, final File toolsJar)
+ throws MalformedURLException {
+ int numJars = libJars.length + userJars.length + systemJars.length;
+ if (toolsJar != null) {
+ numJars++;
+ }
+ final URL[] jars = new URL[numJars];
+ System.arraycopy(libJars, 0, jars, 0, libJars.length);
+ System.arraycopy(userJars, 0, jars, libJars.length, userJars.length);
+ System.arraycopy(systemJars, 0, jars, userJars.length + libJars.length,
+ systemJars.length);
+
+ if (toolsJar != null) {
+ jars[jars.length - 1] = Locator.fileToURL(toolsJar);
+ }
+ return jars;
+ }
+
+ /**
+ * set a system property, optionally log what is going on
+ * @param name property name
+ * @param value value
+ */
+ private void setProperty(final String name, final String value) {
+ if (launchDiag) {
+ System.out.println("Setting \"" + name + "\" to \"" + value + "\"");
+ }
+ System.setProperty(name, value);
+ }
+
+ private void logPath(final String name,final File path) {
+ if(launchDiag) {
+ System.out.println(name+"= \""+path+"\"");
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/Locator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/Locator.java
new file mode 100644
index 00000000..4640e700
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/launch/Locator.java
@@ -0,0 +1,528 @@
+/*
+ * 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.tools.ant.launch;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.CharacterIterator;
+import java.text.StringCharacterIterator;
+import java.util.Locale;
+
+// CheckStyle:LineLengthCheck OFF - urls are long!
+/**
+ * The Locator is a utility class which is used to find certain items
+ * in the environment.
+ * <p>
+ * It is used at boot time in the launcher, and cannot make use of any of Ant's other classes.
+ * <p>
+ * This is a surprisingly brittle piece of code, and has had lots of bugs filed against it:
+ * <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=42275">running ant off a network share can cause Ant to fail</a>,
+ * <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=8031">use File.toURI().toURL().toExternalForm()</a>,
+ * <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=42222">Locator implementation not encoding URI strings properly: spaces in paths</a>.
+ * It also breaks Eclipse 3.3 Betas:
+ * <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=183283">Exception if installation path has spaces</a>.
+ * <p>
+ * Be very careful when making changes to this class, as a break will upset a lot of people.
+ * @since Ant 1.6
+ */
+// CheckStyle:LineLengthCheck ON - urls are long!
+public final class Locator {
+
+ private static final int NIBBLE = 4;
+ private static final int NIBBLE_MASK = 0xF;
+
+ private static final int ASCII_SIZE = 128;
+
+ private static final int BYTE_SIZE = 256;
+
+ private static final int WORD = 16;
+
+ private static final int SPACE = 0x20;
+ private static final int DEL = 0x7F;
+
+ /**
+ * encoding used to represent URIs
+ */
+ public static final String URI_ENCODING = "UTF-8";
+ // stolen from org.apache.xerces.impl.XMLEntityManager#getUserDir()
+ // of the Xerces-J team
+ // which ASCII characters need to be escaped
+ private static boolean[] gNeedEscaping = new boolean[ASCII_SIZE];
+ // the first hex character if a character needs to be escaped
+ private static char[] gAfterEscaping1 = new char[ASCII_SIZE];
+ // the second hex character if a character needs to be escaped
+ private static char[] gAfterEscaping2 = new char[ASCII_SIZE];
+ private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+ /** Error string used when an invalid uri is seen */
+ public static final String ERROR_NOT_FILE_URI
+ = "Can only handle valid file: URIs, not ";
+
+ // initialize the above 3 arrays
+ static {
+ for (int i = 0; i < SPACE; i++) {
+ gNeedEscaping[i] = true;
+ gAfterEscaping1[i] = gHexChs[i >> NIBBLE];
+ gAfterEscaping2[i] = gHexChs[i & NIBBLE_MASK];
+ }
+ gNeedEscaping[DEL] = true;
+ gAfterEscaping1[DEL] = '7';
+ gAfterEscaping2[DEL] = 'F';
+ char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}',
+ '|', '\\', '^', '~', '[', ']', '`'};
+ int len = escChs.length;
+ char ch;
+ for (int i = 0; i < len; i++) {
+ ch = escChs[i];
+ gNeedEscaping[ch] = true;
+ gAfterEscaping1[ch] = gHexChs[ch >> NIBBLE];
+ gAfterEscaping2[ch] = gHexChs[ch & NIBBLE_MASK];
+ }
+ }
+ /**
+ * Not instantiable
+ */
+ private Locator() {
+ }
+
+ /**
+ * Find the directory or jar file the class has been loaded from.
+ *
+ * @param c the class whose location is required.
+ * @return the file or jar with the class or null if we cannot
+ * determine the location.
+ *
+ * @since Ant 1.6
+ */
+ public static File getClassSource(Class<?> c) {
+ String classResource = c.getName().replace('.', '/') + ".class";
+ return getResourceSource(c.getClassLoader(), classResource);
+ }
+
+ /**
+ * Find the directory or jar a given resource has been loaded from.
+ *
+ * @param c the classloader to be consulted for the source.
+ * @param resource the resource whose location is required.
+ *
+ * @return the file with the resource source or null if
+ * we cannot determine the location.
+ *
+ * @since Ant 1.6
+ */
+ public static File getResourceSource(ClassLoader c, String resource) {
+ if (c == null) {
+ c = Locator.class.getClassLoader();
+ }
+ URL url = null;
+ if (c == null) {
+ url = ClassLoader.getSystemResource(resource);
+ } else {
+ url = c.getResource(resource);
+ }
+ if (url != null) {
+ String u = url.toString();
+ try {
+ if (u.startsWith("jar:file:")) {
+ return new File(fromJarURI(u));
+ } else if (u.startsWith("file:")) {
+ int tail = u.indexOf(resource);
+ String dirName = u.substring(0, tail);
+ return new File(fromURI(dirName));
+ }
+ } catch (IllegalArgumentException e) {
+ //unable to determine the URI for reasons unknown.
+ return null;
+ }
+ }
+ return null;
+ }
+
+
+
+ /**
+ * Constructs a file path from a <code>file:</code> URI.
+ *
+ * <p>Will be an absolute path if the given URI is absolute.</p>
+ *
+ * <p>Prior to Java 1.4,<!-- TODO is JDK version actually relevant? -->
+ * swallows '%' that are not followed by two characters.</p>
+ *
+ * See <a href="http://www.w3.org/TR/xml11/#dt-sysid">dt-sysid</a>
+ * which makes some mention of how
+ * characters not supported by URI Reference syntax should be escaped.
+ *
+ * @param uri the URI designating a file in the local filesystem.
+ * @return the local file system path for the file.
+ * @throws IllegalArgumentException if the URI is malformed or not a legal file: URL
+ * @since Ant 1.6
+ */
+ public static String fromURI(String uri) {
+ return fromURIJava13(uri);
+ // #buzilla8031: first try Java 1.4.
+ // TODO should use java.net.URI now that we can rely on 1.4...
+ // but check for UNC-related regressions, e.g. #42275
+ // (and remember that \\server\share\file -> file:////server/share/file
+ // rather than -> file://server/share/file as it should;
+ // fixed only in JDK 7's java.nio.file.Path.toUri)
+ // return fromUriJava14(uri);
+ }
+
+ /**
+ * Java1.4+ code to extract the path from the URI.
+ * @param uri
+ * @return null if a conversion was not possible
+ */
+ /* currently unused:
+ private static String fromUriJava14(String uri) {
+ // Also check for properly formed URIs. Ant formerly recommended using
+ // nonsense URIs such as "file:./foo.xml" in XML includes. You shouldn't
+ // do that (just "foo.xml" is correct) but for compatibility we special-case
+ // things when the path is not absolute, and fall back to the old parsing behavior.
+ if (uri.startsWith("file:/")) {
+ try {
+ File f = new File(URI.create(encodeURI(uri)));
+ //bug #42227 forgot to decode before returning
+ return decodeUri(f.getAbsolutePath());
+ } catch (IllegalArgumentException e) {
+ // Bad URI, pass this on.
+ // no, this is downgraded to a warning after various
+ // JRE bugs surfaced. Hand off
+ // to our built in code on a failure
+ //throw new IllegalArgumentException(
+ // "Bad URI " + uri + ":" + e.getMessage(), e);
+ e.printStackTrace();
+ } catch (Exception e) {
+ // Unexpected exception? Should not happen.
+ e.printStackTrace();
+ }
+ }
+ return null;
+ }
+ */
+
+ /**
+ * @param uri uri to expand
+ * @return the decoded URI
+ * @since Ant1.7.1
+ */
+ private static String fromURIJava13(String uri) {
+ // Fallback method for Java 1.3 or earlier.
+
+ URL url = null;
+ try {
+ url = new URL(uri);
+ } catch (MalformedURLException emYouEarlEx) {
+ // Ignore malformed exception
+ }
+ if (url == null || !("file".equals(url.getProtocol()))) {
+ throw new IllegalArgumentException(ERROR_NOT_FILE_URI + uri);
+ }
+ StringBuffer buf = new StringBuffer(url.getHost());
+ if (buf.length() > 0) {
+ buf.insert(0, File.separatorChar).insert(0, File.separatorChar);
+ }
+ String file = url.getFile();
+ int queryPos = file.indexOf('?');
+ buf.append((queryPos < 0) ? file : file.substring(0, queryPos));
+
+ uri = buf.toString().replace('/', File.separatorChar);
+
+ if (File.pathSeparatorChar == ';' && uri.startsWith("\\") && uri.length() > 2
+ && Character.isLetter(uri.charAt(1)) && uri.lastIndexOf(':') > -1) {
+ uri = uri.substring(1);
+ }
+ String path = null;
+ try {
+ path = decodeUri(uri);
+ //consider adding the current directory. This is not done when
+ //the path is a UNC name
+ String cwd = System.getProperty("user.dir");
+ int posi = cwd.indexOf(':');
+ boolean pathStartsWithFileSeparator = path.startsWith(File.separator);
+ boolean pathStartsWithUNC = path.startsWith("" + File.separator + File.separator);
+ if ((posi > 0) && pathStartsWithFileSeparator && !pathStartsWithUNC) {
+ path = cwd.substring(0, posi + 1) + path;
+ }
+ } catch (UnsupportedEncodingException exc) {
+ // not sure whether this is clean, but this method is
+ // declared not to throw exceptions.
+ throw new IllegalStateException(
+ "Could not convert URI " + uri + " to path: "
+ + exc.getMessage());
+ }
+ return path;
+ }
+
+ /**
+ * Crack a JAR URI.
+ * This method is public for testing; we may delete it without any warning -it is not part of Ant's stable API.
+ * @param uri uri to expand; contains jar: somewhere in it
+ * @return the decoded URI
+ * @since Ant1.7.1
+ */
+ public static String fromJarURI(String uri) {
+ int pling = uri.indexOf("!/");
+ String jarName = uri.substring("jar:".length(), pling);
+ return fromURI(jarName);
+ }
+
+ /**
+ * Decodes an Uri with % characters.
+ * The URI is escaped
+ * @param uri String with the uri possibly containing % characters.
+ * @return The decoded Uri
+ * @throws UnsupportedEncodingException if UTF-8 is not available
+ * @since Ant 1.7
+ */
+ public static String decodeUri(String uri) throws UnsupportedEncodingException {
+ if (uri.indexOf('%') == -1) {
+ return uri;
+ }
+ ByteArrayOutputStream sb = new ByteArrayOutputStream(uri.length());
+ CharacterIterator iter = new StringCharacterIterator(uri);
+ for (char c = iter.first(); c != CharacterIterator.DONE;
+ c = iter.next()) {
+ if (c == '%') {
+ char c1 = iter.next();
+ if (c1 != CharacterIterator.DONE) {
+ int i1 = Character.digit(c1, WORD);
+ char c2 = iter.next();
+ if (c2 != CharacterIterator.DONE) {
+ int i2 = Character.digit(c2, WORD);
+ sb.write((char) ((i1 << NIBBLE) + i2));
+ }
+ }
+ } else if (c >= 0x0000 && c < 0x0080) {
+ sb.write(c);
+ } else { // #50543
+ byte[] bytes = String.valueOf(c).getBytes(URI_ENCODING);
+ sb.write(bytes, 0, bytes.length);
+ }
+ }
+ return sb.toString(URI_ENCODING);
+ }
+
+ /**
+ * Encodes an Uri with % characters.
+ * The URI is escaped
+ * @param path String to encode.
+ * @return The encoded string, according to URI norms
+ * @throws UnsupportedEncodingException if UTF-8 is not available
+ * @since Ant 1.7
+ */
+ public static String encodeURI(String path) throws UnsupportedEncodingException {
+ int i = 0;
+ int len = path.length();
+ int ch = 0;
+ StringBuffer sb = null;
+ for (; i < len; i++) {
+ ch = path.charAt(i);
+ // if it's not an ASCII character, break here, and use UTF-8 encoding
+ if (ch >= ASCII_SIZE) {
+ break;
+ }
+ if (gNeedEscaping[ch]) {
+ if (sb == null) {
+ sb = new StringBuffer(path.substring(0, i));
+ }
+ sb.append('%');
+ sb.append(gAfterEscaping1[ch]);
+ sb.append(gAfterEscaping2[ch]);
+ // record the fact that it's escaped
+ } else if (sb != null) {
+ sb.append((char) ch);
+ }
+ }
+
+ // we saw some non-ascii character
+ if (i < len) {
+ if (sb == null) {
+ sb = new StringBuffer(path.substring(0, i));
+ }
+ // get UTF-8 bytes for the remaining sub-string
+ byte[] bytes = null;
+ byte b;
+ bytes = path.substring(i).getBytes(URI_ENCODING);
+ len = bytes.length;
+
+ // for each byte
+ for (i = 0; i < len; i++) {
+ b = bytes[i];
+ // for non-ascii character: make it positive, then escape
+ if (b < 0) {
+ ch = b + BYTE_SIZE;
+ sb.append('%');
+ sb.append(gHexChs[ch >> NIBBLE]);
+ sb.append(gHexChs[ch & NIBBLE_MASK]);
+ } else if (gNeedEscaping[b]) {
+ sb.append('%');
+ sb.append(gAfterEscaping1[b]);
+ sb.append(gAfterEscaping2[b]);
+ } else {
+ sb.append((char) b);
+ }
+ }
+ }
+ return sb == null ? path : sb.toString();
+ }
+
+ /**
+ * Convert a File to a URL.
+ * File.toURL() does not encode characters like #.
+ * File.toURI() has been introduced in java 1.4, so
+ * Ant cannot use it (except by reflection) <!-- TODO no longer true -->
+ * FileUtils.toURI() cannot be used by Locator.java
+ * Implemented this way.
+ * File.toURL() adds file: and changes '\' to '/' for dos OSes
+ * encodeURI converts characters like ' ' and '#' to %DD
+ * @param file the file to convert
+ * @return URL the converted File
+ * @throws MalformedURLException on error
+ * @deprecated since 1.9, use {@link FileUtils#getFileURL(File)}
+ */
+ @Deprecated
+ public static URL fileToURL(File file)
+ throws MalformedURLException {
+ return new URL(file.toURI().toASCIIString());
+ }
+
+ /**
+ * Get the File necessary to load the Sun compiler tools. If the classes
+ * are available to this class, then no additional URL is required and
+ * null is returned. This may be because the classes are explicitly in the
+ * class path or provided by the JVM directly.
+ *
+ * @return the tools jar as a File if required, null otherwise.
+ */
+ public static File getToolsJar() {
+ // firstly check if the tools jar is already in the classpath
+ boolean toolsJarAvailable = false;
+ try {
+ // just check whether this throws an exception
+ Class.forName("com.sun.tools.javac.Main");
+ toolsJarAvailable = true;
+ } catch (Exception e) {
+ try {
+ Class.forName("sun.tools.javac.Main");
+ toolsJarAvailable = true;
+ } catch (Exception e2) {
+ // ignore
+ }
+ }
+ if (toolsJarAvailable) {
+ return null;
+ }
+ // couldn't find compiler - try to find tools.jar
+ // based on java.home setting
+ String libToolsJar
+ = File.separator + "lib" + File.separator + "tools.jar";
+ String javaHome = System.getProperty("java.home");
+ File toolsJar = new File(javaHome + libToolsJar);
+ if (toolsJar.exists()) {
+ // Found in java.home as given
+ return toolsJar;
+ }
+ if (javaHome.toLowerCase(Locale.ENGLISH).endsWith(File.separator + "jre")) {
+ javaHome = javaHome.substring(
+ 0, javaHome.length() - "/jre".length());
+ toolsJar = new File(javaHome + libToolsJar);
+ }
+ if (!toolsJar.exists()) {
+ System.out.println("Unable to locate tools.jar. "
+ + "Expected to find it in " + toolsJar.getPath());
+ return null;
+ }
+ return toolsJar;
+ }
+
+ /**
+ * Get an array of URLs representing all of the jar files in the
+ * given location. If the location is a file, it is returned as the only
+ * element of the array. If the location is a directory, it is scanned for
+ * jar files.
+ *
+ * @param location the location to scan for Jars.
+ *
+ * @return an array of URLs for all jars in the given location.
+ *
+ * @exception MalformedURLException if the URLs for the jars cannot be
+ * formed.
+ */
+ public static URL[] getLocationURLs(File location)
+ throws MalformedURLException {
+ return getLocationURLs(location, new String[]{".jar"});
+ }
+
+ /**
+ * Get an array of URLs representing all of the files of a given set of
+ * extensions in the given location. If the location is a file, it is
+ * returned as the only element of the array. If the location is a
+ * directory, it is scanned for matching files.
+ *
+ * @param location the location to scan for files.
+ * @param extensions an array of extension that are to match in the
+ * directory search.
+ *
+ * @return an array of URLs of matching files.
+ * @exception MalformedURLException if the URLs for the files cannot be
+ * formed.
+ */
+ public static URL[] getLocationURLs(File location,
+ final String[] extensions)
+ throws MalformedURLException {
+ URL[] urls = new URL[0];
+
+ if (!location.exists()) {
+ return urls;
+ }
+ if (!location.isDirectory()) {
+ urls = new URL[1];
+ String path = location.getPath();
+ String littlePath = path.toLowerCase(Locale.ENGLISH);
+ for (int i = 0; i < extensions.length; ++i) {
+ if (littlePath.endsWith(extensions[i])) {
+ urls[0] = fileToURL(location);
+ break;
+ }
+ }
+ return urls;
+ }
+ File[] matches = location.listFiles(
+ new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ String littleName = name.toLowerCase(Locale.ENGLISH);
+ for (int i = 0; i < extensions.length; ++i) {
+ if (littleName.endsWith(extensions[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+ });
+ urls = new URL[matches.length];
+ for (int i = 0; i < matches.length; ++i) {
+ urls[i] = fileToURL(matches[i]);
+ }
+ return urls;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/AnsiColorLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/AnsiColorLogger.java
new file mode 100644
index 00000000..2e695005
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/AnsiColorLogger.java
@@ -0,0 +1,249 @@
+/*
+ * 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.tools.ant.listener;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Properties;
+
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Project;
+
+/**
+ * Uses ANSI Color Code Sequences to colorize messages
+ * sent to the console.
+ *
+ * <p>If used with the -logfile option, the output file
+ * will contain all the necessary escape codes to
+ * display the text in colorized mode when displayed
+ * in the console using applications like cat, more,
+ * etc.</p>
+ *
+ * <p>This is designed to work on terminals that support ANSI
+ * color codes. It works on XTerm, ETerm, Mindterm, etc.
+ * It also works on Win9x (with ANSI.SYS loaded.)</p>
+ *
+ * <p>NOTE:
+ * It doesn't work on WinNT's COMMAND.COM even with
+ * ANSI.SYS loaded.</p>
+ *
+ * <p>The default colors used for differentiating
+ * the message levels can be changed by editing the
+ * /org/apache/tools/ant/listener/defaults.properties
+ * file.
+ * This file contains 5 key/value pairs:</p>
+ * <pre>
+ * AnsiColorLogger.ERROR_COLOR=2;31
+ * AnsiColorLogger.WARNING_COLOR=2;35
+ * AnsiColorLogger.INFO_COLOR=2;36
+ * AnsiColorLogger.VERBOSE_COLOR=2;32
+ * AnsiColorLogger.DEBUG_COLOR=2;34
+ * </pre>
+ *
+ * <p>Another option is to pass a system variable named
+ * ant.logger.defaults, with value set to the path of
+ * the file that contains user defined Ansi Color
+ * Codes, to the <B>java</B> command using -D option.</p>
+ *
+ * To change these colors use the following chart:
+ *
+ * <h2>ANSI COLOR LOGGER CONFIGURATION</h2>
+ *
+ * Format for AnsiColorLogger.*=
+ * Attribute;Foreground;Background
+ *
+ * Attribute is one of the following: <pre>
+ * 0 -&gt; Reset All Attributes (return to normal mode)
+ * 1 -&gt; Bright (Usually turns on BOLD)
+ * 2 -&gt; Dim
+ * 3 -&gt; Underline
+ * 5 -&gt; link
+ * 7 -&gt; Reverse
+ * 8 -&gt; Hidden
+ * </pre>
+ *
+ * Foreground is one of the following:<pre>
+ * 30 -&gt; Black
+ * 31 -&gt; Red
+ * 32 -&gt; Green
+ * 33 -&gt; Yellow
+ * 34 -&gt; Blue
+ * 35 -&gt; Magenta
+ * 36 -&gt; Cyan
+ * 37 -&gt; White
+ * </pre>
+ *
+ * Background is one of the following:<pre>
+ * 40 -&gt; Black
+ * 41 -&gt; Red
+ * 42 -&gt; Green
+ * 43 -&gt; Yellow
+ * 44 -&gt; Blue
+ * 45 -&gt; Magenta
+ * 46 -&gt; Cyan
+ * 47 -&gt; White
+ * </pre>
+ */
+public class AnsiColorLogger extends DefaultLogger {
+ // private static final int ATTR_NORMAL = 0;
+ // private static final int ATTR_BRIGHT = 1;
+ private static final int ATTR_DIM = 2;
+ // private static final int ATTR_UNDERLINE = 3;
+ // private static final int ATTR_BLINK = 5;
+ // private static final int ATTR_REVERSE = 7;
+ // private static final int ATTR_HIDDEN = 8;
+
+ // private static final int FG_BLACK = 30;
+ private static final int FG_RED = 31;
+ private static final int FG_GREEN = 32;
+ // private static final int FG_YELLOW = 33;
+ private static final int FG_BLUE = 34;
+ private static final int FG_MAGENTA = 35;
+ private static final int FG_CYAN = 36;
+ // private static final int FG_WHITE = 37;
+
+ // private static final int BG_BLACK = 40;
+ // private static final int BG_RED = 41;
+ // private static final int BG_GREEN = 42;
+ // private static final int BG_YELLOW = 44;
+ // private static final int BG_BLUE = 44;
+ // private static final int BG_MAGENTA = 45;
+ // private static final int BG_CYAN = 46;
+ // private static final int BG_WHITE = 47;
+
+ private static final String PREFIX = "\u001b[";
+ private static final String SUFFIX = "m";
+ private static final char SEPARATOR = ';';
+ private static final String END_COLOR = PREFIX + SUFFIX;
+
+ private String errColor
+ = PREFIX + ATTR_DIM + SEPARATOR + FG_RED + SUFFIX;
+ private String warnColor
+ = PREFIX + ATTR_DIM + SEPARATOR + FG_MAGENTA + SUFFIX;
+ private String infoColor
+ = PREFIX + ATTR_DIM + SEPARATOR + FG_CYAN + SUFFIX;
+ private String verboseColor
+ = PREFIX + ATTR_DIM + SEPARATOR + FG_GREEN + SUFFIX;
+ private String debugColor
+ = PREFIX + ATTR_DIM + SEPARATOR + FG_BLUE + SUFFIX;
+
+ private boolean colorsSet = false;
+
+ /**
+ * Set the colors to use from a property file specified by the
+ * special ant property ant.logger.defaults
+ */
+ private void setColors() {
+ String userColorFile = System.getProperty("ant.logger.defaults");
+ String systemColorFile =
+ "/org/apache/tools/ant/listener/defaults.properties";
+
+ InputStream in = null;
+
+ try {
+ Properties prop = new Properties();
+
+ if (userColorFile != null) {
+ in = new FileInputStream(userColorFile);
+ } else {
+ in = getClass().getResourceAsStream(systemColorFile);
+ }
+
+ if (in != null) {
+ prop.load(in);
+ }
+
+ String errC = prop.getProperty("AnsiColorLogger.ERROR_COLOR");
+ String warn = prop.getProperty("AnsiColorLogger.WARNING_COLOR");
+ String info = prop.getProperty("AnsiColorLogger.INFO_COLOR");
+ String verbose = prop.getProperty("AnsiColorLogger.VERBOSE_COLOR");
+ String debug = prop.getProperty("AnsiColorLogger.DEBUG_COLOR");
+ if (errC != null) {
+ errColor = PREFIX + errC + SUFFIX;
+ }
+ if (warn != null) {
+ warnColor = PREFIX + warn + SUFFIX;
+ }
+ if (info != null) {
+ infoColor = PREFIX + info + SUFFIX;
+ }
+ if (verbose != null) {
+ verboseColor = PREFIX + verbose + SUFFIX;
+ }
+ if (debug != null) {
+ debugColor = PREFIX + debug + SUFFIX;
+ }
+ } catch (IOException ioe) {
+ //Ignore - we will use the defaults.
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ //Ignore - We do not want this to stop the build.
+ }
+ }
+ }
+ }
+
+ /**
+ * @see DefaultLogger#printMessage
+ */
+ /** {@inheritDoc}. */
+ @Override
+ protected void printMessage(final String message,
+ final PrintStream stream,
+ final int priority) {
+ if (message != null && stream != null) {
+ if (!colorsSet) {
+ setColors();
+ colorsSet = true;
+ }
+
+ final StringBuffer msg = new StringBuffer(message);
+ switch (priority) {
+ case Project.MSG_ERR:
+ msg.insert(0, errColor);
+ msg.append(END_COLOR);
+ break;
+ case Project.MSG_WARN:
+ msg.insert(0, warnColor);
+ msg.append(END_COLOR);
+ break;
+ case Project.MSG_INFO:
+ msg.insert(0, infoColor);
+ msg.append(END_COLOR);
+ break;
+ case Project.MSG_VERBOSE:
+ msg.insert(0, verboseColor);
+ msg.append(END_COLOR);
+ break;
+ case Project.MSG_DEBUG:
+ // Fall through
+ default:
+ msg.insert(0, debugColor);
+ msg.append(END_COLOR);
+ break;
+ }
+ final String strmessage = msg.toString();
+ stream.println(strmessage);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/BigProjectLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/BigProjectLogger.java
new file mode 100644
index 00000000..865127d2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/BigProjectLogger.java
@@ -0,0 +1,194 @@
+/*
+ * 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.tools.ant.listener;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.SubBuildListener;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * This is a special logger that is designed to make it easier to work
+ * with big projects, those that use imports and
+ * subant to build complex systems.
+ *
+ * @since Ant1.7.1
+ */
+
+public class BigProjectLogger extends SimpleBigProjectLogger
+ implements SubBuildListener {
+
+ private volatile boolean subBuildStartedRaised = false;
+ private final Object subBuildLock = new Object();
+
+ /**
+ * Header string for the log.
+ * {@value}
+ */
+ public static final String HEADER
+ = "======================================================================";
+ /**
+ * Footer string for the log.
+ * {@value}
+ */
+ public static final String FOOTER = HEADER;
+
+ /**
+ * This is an override point: the message that indicates whether
+ * a build failed. Subclasses can change/enhance the
+ * message.
+ *
+ * @return The classic "BUILD FAILED" plus a timestamp
+ */
+ protected String getBuildFailedMessage() {
+ return super.getBuildFailedMessage() + TimestampedLogger.SPACER + getTimestamp();
+ }
+
+ /**
+ * This is an override point: the message that indicates that
+ * a build succeeded. Subclasses can change/enhance the
+ * message.
+ *
+ * @return The classic "BUILD SUCCESSFUL" plus a timestamp
+ */
+ protected String getBuildSuccessfulMessage() {
+ return super.getBuildSuccessfulMessage() + TimestampedLogger.SPACER + getTimestamp();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param event
+ */
+ public void targetStarted(BuildEvent event) {
+ maybeRaiseSubBuildStarted(event);
+ super.targetStarted(event);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param event
+ */
+ public void taskStarted(BuildEvent event) {
+ maybeRaiseSubBuildStarted(event);
+ super.taskStarted(event);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param event
+ */
+ public void buildFinished(BuildEvent event) {
+ maybeRaiseSubBuildStarted(event);
+ subBuildFinished(event);
+ super.buildFinished(event);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param event
+ */
+ public void messageLogged(BuildEvent event) {
+ maybeRaiseSubBuildStarted(event);
+ super.messageLogged(event);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param event An event with any relevant extra information. Must not be <code>null</code>.
+ */
+ public void subBuildStarted(BuildEvent event) {
+ String name = extractNameOrDefault(event);
+ Project project = event.getProject();
+
+ File base = project == null ? null : project.getBaseDir();
+ String path =
+ (base == null)
+ ? "With no base directory"
+ : "In " + base.getAbsolutePath();
+ printMessage(StringUtils.LINE_SEP + getHeader()
+ + StringUtils.LINE_SEP + "Entering project " + name
+ + StringUtils.LINE_SEP + path
+ + StringUtils.LINE_SEP + getFooter(),
+ out,
+ event.getPriority());
+ }
+
+ /**
+ * Get the name of an event
+ *
+ * @param event the event name
+ * @return the name or a default string
+ */
+ protected String extractNameOrDefault(BuildEvent event) {
+ String name = extractProjectName(event);
+ if (name == null) {
+ name = "";
+ } else {
+ name = '"' + name + '"';
+ }
+ return name;
+ }
+
+ /** {@inheritDoc} */
+ public void subBuildFinished(BuildEvent event) {
+ String name = extractNameOrDefault(event);
+ String failed = event.getException() != null ? "failing " : "";
+ printMessage(StringUtils.LINE_SEP + getHeader()
+ + StringUtils.LINE_SEP + "Exiting " + failed + "project "
+ + name
+ + StringUtils.LINE_SEP + getFooter(),
+ out,
+ event.getPriority());
+ }
+
+ /**
+ * Override point: return the header string for the entry/exit message
+ * @return the header string
+ */
+ protected String getHeader() {
+ return HEADER;
+ }
+
+ /**
+ * Override point: return the footer string for the entry/exit message
+ * @return the footer string
+ */
+ protected String getFooter() {
+ return FOOTER;
+ }
+
+ private void maybeRaiseSubBuildStarted(BuildEvent event) {
+ // double checked locking should be OK since the flag is write-once
+ if (!subBuildStartedRaised) {
+ synchronized (subBuildLock) {
+ if (!subBuildStartedRaised) {
+ subBuildStartedRaised = true;
+ subBuildStarted(event);
+ }
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/CommonsLoggingListener.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/CommonsLoggingListener.java
new file mode 100644
index 00000000..32474eea
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/CommonsLoggingListener.java
@@ -0,0 +1,332 @@
+/*
+ * 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.tools.ant.listener;
+
+import java.io.PrintStream;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogConfigurationException;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.BuildLogger;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.UnknownElement;
+
+
+/**
+ * Jakarta Commons Logging listener.
+ * Note: do not use the SimpleLog as your logger implementation as it
+ * causes an infinite loop since it writes to System.err, which Ant traps
+ * and reroutes to the logger/listener layer.
+ *
+ * The following names are used for the log:
+ * org.apache.tools.ant.Project.PROJECT_NAME - for project events
+ * org.apache.tools.ant.Target.TARGET_NAME - for target events
+ * TASK_CLASS_NAME.TARGET_NAME - for events in individual targets.
+ *
+ * In all target and project names we replace "." and " " with "-".
+ *
+ * TODO: we should use the advanced context logging features (and expose them
+ * in c-l first :-)
+ * TODO: this is _very_ inefficient. Switching the out and tracking the logs
+ * can be optimized a lot - but may require few more changes to the core.
+ *
+ * @since Ant 1.5
+ */
+public class CommonsLoggingListener implements BuildListener, BuildLogger {
+
+ /** Indicates if the listener was initialized. */
+ private boolean initialized = false;
+
+ private LogFactory logFactory;
+
+ /**
+ * name of the category under which target events are logged
+ */
+ public static final String TARGET_LOG = "org.apache.tools.ant.Target";
+ /**
+ * name of the category under which project events are logged
+ */
+ public static final String PROJECT_LOG = "org.apache.tools.ant.Project";
+
+ /**
+ * Construct the listener and make sure that a LogFactory
+ * can be obtained.
+ */
+ public CommonsLoggingListener() {
+ }
+
+ private Log getLog(String cat, String suffix) {
+ if (suffix != null) {
+ suffix = suffix.replace('.', '-');
+ suffix = suffix.replace(' ', '-');
+ cat = cat + "." + suffix;
+ }
+ final PrintStream tmpOut = System.out;
+ final PrintStream tmpErr = System.err;
+ System.setOut(out);
+ System.setErr(err);
+
+ if (!initialized) {
+ try {
+ logFactory = LogFactory.getFactory();
+ } catch (final LogConfigurationException e) {
+ e.printStackTrace(System.err);
+ return null;
+ }
+ }
+
+ initialized = true;
+ final Log log = logFactory.getInstance(cat);
+ System.setOut(tmpOut);
+ System.setErr(tmpErr);
+ return log;
+ }
+
+ /** {@inheritDoc}. */
+ public void buildStarted(final BuildEvent event) {
+ final String categoryString = PROJECT_LOG;
+ final Log log = getLog(categoryString, null);
+
+ if (initialized) {
+ realLog(log, "Build started.", Project.MSG_INFO, null);
+ }
+ }
+
+ /** {@inheritDoc}. */
+ public void buildFinished(final BuildEvent event) {
+ if (initialized) {
+ final String categoryString = PROJECT_LOG;
+ final Log log = getLog(categoryString, event.getProject().getName());
+
+ if (event.getException() == null) {
+ realLog(log, "Build finished.", Project.MSG_INFO, null);
+ } else {
+ realLog(log, "Build finished with error.", Project.MSG_ERR,
+ event.getException());
+ }
+ }
+ }
+
+ /**
+ * @see BuildListener#targetStarted
+ */
+ /** {@inheritDoc}. */
+ public void targetStarted(final BuildEvent event) {
+ if (initialized) {
+ final Log log = getLog(TARGET_LOG,
+ event.getTarget().getName());
+ // Since task log category includes target, we don't really
+ // need this message
+ realLog(log, "Start: " + event.getTarget().getName(),
+ Project.MSG_VERBOSE, null);
+ }
+ }
+
+ /**
+ * @see BuildListener#targetFinished
+ */
+ /** {@inheritDoc}. */
+ public void targetFinished(final BuildEvent event) {
+ if (initialized) {
+ final String targetName = event.getTarget().getName();
+ final Log log = getLog(TARGET_LOG,
+ event.getTarget().getName());
+ if (event.getException() == null) {
+ realLog(log, "Target end: " + targetName, Project.MSG_DEBUG, null);
+ } else {
+ realLog(log, "Target \"" + targetName
+ + "\" finished with error.", Project.MSG_ERR,
+ event.getException());
+ }
+ }
+ }
+
+ /**
+ * @see BuildListener#taskStarted
+ */
+ /** {@inheritDoc}. */
+ public void taskStarted(final BuildEvent event) {
+ if (initialized) {
+ final Task task = event.getTask();
+ Object real = task;
+ if (task instanceof UnknownElement) {
+ final Object realObj = ((UnknownElement) task).getTask();
+ if (realObj != null) {
+ real = realObj;
+ }
+ }
+ final Log log = getLog(real.getClass().getName(), null);
+ if (log.isTraceEnabled()) {
+ realLog(log, "Task \"" + task.getTaskName() + "\" started ",
+ Project.MSG_VERBOSE, null);
+ }
+ }
+ }
+
+ /**
+ * @see BuildListener#taskFinished
+ */
+ /** {@inheritDoc}. */
+ public void taskFinished(final BuildEvent event) {
+ if (initialized) {
+ final Task task = event.getTask();
+ Object real = task;
+ if (task instanceof UnknownElement) {
+ final Object realObj = ((UnknownElement) task).getTask();
+ if (realObj != null) {
+ real = realObj;
+ }
+ }
+ final Log log = getLog(real.getClass().getName(), null);
+ if (event.getException() == null) {
+ if (log.isTraceEnabled()) {
+ realLog(log, "Task \"" + task.getTaskName() + "\" finished.",
+ Project.MSG_VERBOSE, null);
+ }
+ } else {
+ realLog(log, "Task \"" + task.getTaskName()
+ + "\" finished with error.", Project.MSG_ERR,
+ event.getException());
+ }
+ }
+ }
+
+
+ /**
+ * @see BuildListener#messageLogged
+ */
+ /** {@inheritDoc}. */
+ public void messageLogged(final BuildEvent event) {
+ if (initialized) {
+ Object categoryObject = event.getTask();
+ String categoryString = null;
+ String categoryDetail = null;
+
+ if (categoryObject == null) {
+ categoryObject = event.getTarget();
+ if (categoryObject == null) {
+ categoryObject = event.getProject();
+ categoryString = PROJECT_LOG;
+ categoryDetail = event.getProject().getName();
+ } else {
+ categoryString = TARGET_LOG;
+ categoryDetail = event.getTarget().getName();
+ }
+ } else {
+ // It's a task - append the target
+ if (event.getTarget() != null) {
+ categoryString = categoryObject.getClass().getName();
+ categoryDetail = event.getTarget().getName();
+ } else {
+ categoryString = categoryObject.getClass().getName();
+ }
+
+ }
+
+ final Log log = getLog(categoryString, categoryDetail);
+ final int priority = event.getPriority();
+ final String message = event.getMessage();
+ realLog(log, message, priority , null);
+ }
+ }
+
+ private void realLog(final Log log, final String message, final int priority, final Throwable t) {
+ final PrintStream tmpOut = System.out;
+ final PrintStream tmpErr = System.err;
+ System.setOut(out);
+ System.setErr(err);
+ switch (priority) {
+ case Project.MSG_ERR:
+ if (t == null) {
+ log.error(message);
+ } else {
+ log.error(message, t);
+ }
+ break;
+ case Project.MSG_WARN:
+ if (t == null) {
+ log.warn(message);
+ } else {
+ log.warn(message, t);
+ }
+ break;
+ case Project.MSG_INFO:
+ if (t == null) {
+ log.info(message);
+ } else {
+ log.info(message, t);
+ }
+ break;
+ case Project.MSG_VERBOSE:
+ log.debug(message);
+ break;
+ case Project.MSG_DEBUG:
+ log.debug(message);
+ break;
+ default:
+ log.error(message);
+ break;
+ }
+ System.setOut(tmpOut);
+ System.setErr(tmpErr);
+ }
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ PrintStream out = System.out;
+ PrintStream err = System.err;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Set the the output level.
+ * This is not used, the logger config is used instead.
+ * @param level ignored
+ */
+ public void setMessageOutputLevel(final int level) {
+ // Use the logger config
+ }
+
+ /**
+ * Set the output print stream.
+ * @param output the output stream
+ */
+ public void setOutputPrintStream(final PrintStream output) {
+ this.out = output;
+ }
+
+ /**
+ * Set emacs mode.
+ * This is ignored.
+ * @param emacsMode ignored
+ */
+ public void setEmacsMode(final boolean emacsMode) {
+ // Doesn't make sense for c-l. Use the logger config
+ }
+
+ /**
+ * Set the error print stream.
+ * @param err the error stream
+ */
+ public void setErrorPrintStream(final PrintStream err) {
+ this.err = err;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/Log4jListener.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/Log4jListener.java
new file mode 100644
index 00000000..829f1183
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/Log4jListener.java
@@ -0,0 +1,177 @@
+/*
+ * 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.tools.ant.listener;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.NullEnumeration;
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+
+
+/**
+ * Listener which sends events to Log4j logging system
+ *
+ */
+public class Log4jListener implements BuildListener {
+
+ /** Indicates if the listener was initialized. */
+ private final boolean initialized;
+
+ /**
+ * log category we log into
+ */
+ public static final String LOG_ANT = "org.apache.tools.ant";
+
+ /**
+ * Construct the listener and make sure there is a valid appender.
+ */
+ public Log4jListener() {
+ final Logger log = Logger.getLogger(LOG_ANT);
+ final Logger rootLog = Logger.getRootLogger();
+ initialized = !(rootLog.getAllAppenders() instanceof NullEnumeration);
+ if (!initialized) {
+ log.error("No log4j.properties in build area");
+ }
+ }
+
+ /**
+ * @see BuildListener#buildStarted
+ */
+ /** {@inheritDoc}. */
+ public void buildStarted(final BuildEvent event) {
+ if (initialized) {
+ final Logger log = Logger.getLogger(Project.class.getName());
+ log.info("Build started.");
+ }
+ }
+
+ /**
+ * @see BuildListener#buildFinished
+ */
+ /** {@inheritDoc}. */
+ public void buildFinished(final BuildEvent event) {
+ if (initialized) {
+ final Logger log = Logger.getLogger(Project.class.getName());
+ if (event.getException() == null) {
+ log.info("Build finished.");
+ } else {
+ log.error("Build finished with error.", event.getException());
+ }
+ }
+ }
+
+ /**
+ * @see BuildListener#targetStarted
+ */
+ /** {@inheritDoc}. */
+ public void targetStarted(final BuildEvent event) {
+ if (initialized) {
+ final Logger log = Logger.getLogger(Target.class.getName());
+ log.info("Target \"" + event.getTarget().getName() + "\" started.");
+ }
+ }
+
+ /**
+ * @see BuildListener#targetFinished
+ */
+ /** {@inheritDoc}. */
+ public void targetFinished(final BuildEvent event) {
+ if (initialized) {
+ final String targetName = event.getTarget().getName();
+ final Logger cat = Logger.getLogger(Target.class.getName());
+ if (event.getException() == null) {
+ cat.info("Target \"" + targetName + "\" finished.");
+ } else {
+ cat.error("Target \"" + targetName
+ + "\" finished with error.", event.getException());
+ }
+ }
+ }
+
+ /**
+ * @see BuildListener#taskStarted
+ */
+ /** {@inheritDoc}. */
+ public void taskStarted(final BuildEvent event) {
+ if (initialized) {
+ final Task task = event.getTask();
+ final Logger log = Logger.getLogger(task.getClass().getName());
+ log.info("Task \"" + task.getTaskName() + "\" started.");
+ }
+ }
+
+ /**
+ * @see BuildListener#taskFinished
+ */
+ /** {@inheritDoc}. */
+ public void taskFinished(final BuildEvent event) {
+ if (initialized) {
+ final Task task = event.getTask();
+ final Logger log = Logger.getLogger(task.getClass().getName());
+ if (event.getException() == null) {
+ log.info("Task \"" + task.getTaskName() + "\" finished.");
+ } else {
+ log.error("Task \"" + task.getTaskName()
+ + "\" finished with error.", event.getException());
+ }
+ }
+ }
+
+ /**
+ * @see BuildListener#messageLogged
+ */
+ /** {@inheritDoc}. */
+ public void messageLogged(final BuildEvent event) {
+ if (initialized) {
+ Object categoryObject = event.getTask();
+ if (categoryObject == null) {
+ categoryObject = event.getTarget();
+ if (categoryObject == null) {
+ categoryObject = event.getProject();
+ }
+ }
+
+ final Logger log
+ = Logger.getLogger(categoryObject.getClass().getName());
+ switch (event.getPriority()) {
+ case Project.MSG_ERR:
+ log.error(event.getMessage());
+ break;
+ case Project.MSG_WARN:
+ log.warn(event.getMessage());
+ break;
+ case Project.MSG_INFO:
+ log.info(event.getMessage());
+ break;
+ case Project.MSG_VERBOSE:
+ log.debug(event.getMessage());
+ break;
+ case Project.MSG_DEBUG:
+ log.debug(event.getMessage());
+ break;
+ default:
+ log.error(event.getMessage());
+ break;
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/MailLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/MailLogger.java
new file mode 100644
index 00000000..4b50547f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/MailLogger.java
@@ -0,0 +1,440 @@
+/*
+ * 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.tools.ant.listener;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.email.EmailAddress;
+import org.apache.tools.ant.taskdefs.email.Header;
+import org.apache.tools.ant.taskdefs.email.Mailer;
+import org.apache.tools.ant.taskdefs.email.Message;
+import org.apache.tools.ant.util.ClasspathUtils;
+import org.apache.tools.ant.util.DateUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.mail.MailMessage;
+
+/**
+ * Buffers log messages from DefaultLogger, and sends an e-mail with the
+ * results. The following Project properties are used to send the mail.
+ * <ul>
+ * <li> MailLogger.mailhost [default: localhost] - Mail server to use</li>
+ * <li> MailLogger.port [default: 25] - Default port for SMTP </li>
+ * <li> Maillogger.user [no default] - user name for SMTP auth
+ * (requires JavaMail)</li>
+ * <li> Maillogger.password [no default] - password for SMTP auth
+ * (requires JavaMail)</li>
+ * <li> Maillogger.ssl [default: false] - on or true if ssl is
+ * needed (requires JavaMail)</li>
+ * <li> MailLogger.from [required] - Mail "from" address</li>
+ * <li> MailLogger.from [no default] - Mail "replyto" address(es),
+ * comma-separated</li>
+ * <li> MailLogger.failure.notify [default: true] - Send build failure
+ * e-mails?</li>
+ * <li> MailLogger.success.notify [default: true] - Send build success
+ * e-mails?</li>
+ * <li> MailLogger.failure.to [required if failure mail to be sent] - Address
+ * to send failure messages to</li>
+ * <li> MailLogger.success.to [required if success mail to be sent] - Address
+ * to send success messages to</li>
+ * <li> MailLogger.failure.cc [no default] - Address
+ * to send failure messages to carbon copy (cc)</li>
+ * <li> MailLogger.success.to [no default] - Address
+ * to send success messages to carbon copy (cc)</li>
+ * <li> MailLogger.failure.bcc [no default] - Address
+ * to send failure messages to blind carbon copy (bcc)</li>
+ * <li> MailLogger.success.bcc [no default] - Address
+ * to send success messages to blind carbon copy (bcc)</li>
+ * <li> MailLogger.failure.subject [default: "Build Failure"] - Subject of
+ * failed build</li>
+ * <li> MailLogger.success.subject [default: "Build Success"] - Subject of
+ * successful build</li>
+ * <li> MailLogger.failure.body [default: none] - fixed text of
+ * mail body for a failed build, default is to send the logfile</li>
+ * <li> MailLogger.success.body [default: none] - fixed text of
+ * mail body for a successful build, default is to send the logfile</li>
+ * <li> MailLogger.mimeType [default: text/plain] - MIME-Type of email</li>
+ * <li> MailLogger.charset [no default] - character set of email</li>
+ * <li> Maillogger.starttls.enable [default: false] - on or true if
+ * STARTTLS should be supported (requires JavaMail)</li>
+ * <li> MailLogger.properties.file [no default] - Filename of
+ * properties file that will override other values.</li>
+ * </ul>
+ * These properties are set using standard Ant property setting mechanisms
+ * (&lt;property&gt;, command-line -D, etc). Ant properties can be overridden
+ * by specifying the filename of a properties file in the <i>
+ * MailLogger.properties.file property</i> . Any properties defined in that
+ * file will override Ant properties.
+ *
+ */
+public class MailLogger extends DefaultLogger {
+ /** Buffer in which the message is constructed prior to sending */
+ private StringBuffer buffer = new StringBuffer();
+
+ private static final String DEFAULT_MIME_TYPE = "text/plain";
+
+ /**
+ * Sends an e-mail with the log results.
+ *
+ * @param event the build finished event
+ */
+ public void buildFinished(BuildEvent event) {
+ super.buildFinished(event);
+
+ Project project = event.getProject();
+ Hashtable<String, Object> properties = project.getProperties();
+
+ // overlay specified properties file (if any), which overrides project
+ // settings
+ Properties fileProperties = new Properties();
+ String filename = (String) properties.get("MailLogger.properties.file");
+ if (filename != null) {
+ InputStream is = null;
+ try {
+ is = new FileInputStream(filename);
+ fileProperties.load(is);
+ } catch (IOException ioe) {
+ // ignore because properties file is not required
+ } finally {
+ FileUtils.close(is);
+ }
+ }
+
+ for (Enumeration<?> e = fileProperties.keys(); e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ String value = fileProperties.getProperty(key);
+ properties.put(key, project.replaceProperties(value));
+ }
+
+ boolean success = (event.getException() == null);
+ String prefix = success ? "success" : "failure";
+
+ try {
+ boolean notify = Project.toBoolean(getValue(properties,
+ prefix + ".notify", "on"));
+
+ if (!notify) {
+ return;
+ }
+ Values values = new Values()
+ .mailhost(getValue(properties, "mailhost", "localhost"))
+ .port(Integer.parseInt(
+ getValue(
+ properties, "port",
+ String.valueOf(MailMessage.DEFAULT_PORT))))
+ .user(getValue(properties, "user", ""))
+ .password(getValue(properties, "password", ""))
+ .ssl(Project.toBoolean(getValue(properties,
+ "ssl", "off")))
+ .starttls(Project.toBoolean(getValue(properties,
+ "starttls.enable", "off")))
+ .from(getValue(properties, "from", null))
+ .replytoList(getValue(properties, "replyto", ""))
+ .toList(getValue(properties, prefix + ".to", null))
+ .toCcList(getValue(properties, prefix + ".cc", ""))
+ .toBccList(getValue(properties, prefix + ".bcc", ""))
+ .mimeType(getValue(properties, "mimeType", DEFAULT_MIME_TYPE))
+ .charset(getValue(properties, "charset", ""))
+ .body(getValue(properties, prefix + ".body", ""))
+ .subject(getValue(
+ properties, prefix + ".subject",
+ (success) ? "Build Success" : "Build Failure"));
+ if (values.user().equals("")
+ && values.password().equals("")
+ && !values.ssl() && !values.starttls()) {
+ sendMail(values, buffer.substring(0));
+ } else {
+ sendMimeMail(
+ event.getProject(), values, buffer.substring(0));
+ }
+ } catch (Exception e) {
+ System.out.println("MailLogger failed to send e-mail!");
+ e.printStackTrace(System.err);
+ }
+ }
+
+ private static class Values {
+ private String mailhost;
+ public String mailhost() {
+ return mailhost;
+ }
+ public Values mailhost(String mailhost) {
+ this.mailhost = mailhost;
+ return this;
+ }
+ private int port;
+ public int port() {
+ return port;
+ }
+ public Values port(int port) {
+ this.port = port;
+ return this;
+ }
+ private String user;
+ public String user() {
+ return user;
+ }
+ public Values user(String user) {
+ this.user = user;
+ return this;
+ }
+ private String password;
+ public String password() {
+ return password;
+ }
+ public Values password(String password) {
+ this.password = password;
+ return this;
+ }
+ private boolean ssl;
+ public boolean ssl() {
+ return ssl;
+ }
+ public Values ssl(boolean ssl) {
+ this.ssl = ssl;
+ return this;
+ }
+ private String from;
+ public String from() {
+ return from;
+ }
+ public Values from(String from) {
+ this.from = from;
+ return this;
+ }
+ private String replytoList;
+ public String replytoList() {
+ return replytoList;
+ }
+ public Values replytoList(String replytoList) {
+ this.replytoList = replytoList;
+ return this;
+ }
+ private String toList;
+ public String toList() {
+ return toList;
+ }
+ public Values toList(String toList) {
+ this.toList = toList;
+ return this;
+ }
+ private String toCcList;
+ public String toCcList() {
+ return toCcList;
+ }
+ public Values toCcList(String toCcList) {
+ this.toCcList = toCcList;
+ return this;
+ }
+ private String toBccList;
+ public String toBccList() {
+ return toBccList;
+ }
+ public Values toBccList(String toBccList) {
+ this.toBccList = toBccList;
+ return this;
+ }
+ private String subject;
+ public String subject() {
+ return subject;
+ }
+ public Values subject(String subject) {
+ this.subject = subject;
+ return this;
+ }
+ private String charset;
+ public String charset() {
+ return charset;
+ }
+ public Values charset(String charset) {
+ this.charset = charset;
+ return this;
+ }
+ private String mimeType;
+ public String mimeType() {
+ return mimeType;
+ }
+ public Values mimeType(String mimeType) {
+ this.mimeType = mimeType;
+ return this;
+ }
+ private String body;
+ public String body() {
+ return body;
+ }
+ public Values body(String body) {
+ this.body = body;
+ return this;
+ }
+ private boolean starttls;
+ public boolean starttls() {
+ return starttls;
+ }
+ public Values starttls(boolean starttls) {
+ this.starttls = starttls;
+ return this;
+ }
+ }
+
+ /**
+ * Receives and buffers log messages.
+ *
+ * @param message the message being logger
+ */
+ protected void log(String message) {
+ buffer.append(message).append(StringUtils.LINE_SEP);
+ }
+
+
+ /**
+ * Gets the value of a property.
+ *
+ * @param properties Properties to obtain value from
+ * @param name suffix of property name. "MailLogger." will be
+ * prepended internally.
+ * @param defaultValue value returned if not present in the properties.
+ * Set to null to make required.
+ * @return The value of the property, or default value.
+ * @exception Exception thrown if no default value is specified and the
+ * property is not present in properties.
+ */
+ private String getValue(Hashtable<String, Object> properties, String name,
+ String defaultValue) throws Exception {
+ String propertyName = "MailLogger." + name;
+ String value = (String) properties.get(propertyName);
+
+ if (value == null) {
+ value = defaultValue;
+ }
+
+ if (value == null) {
+ throw new Exception("Missing required parameter: " + propertyName);
+ }
+
+ return value;
+ }
+
+
+ /**
+ * Send the mail
+ * @param values the various values.
+ * @param message mail body
+ * @exception IOException thrown if sending message fails
+ */
+ private void sendMail(Values values, String message) throws IOException {
+ MailMessage mailMessage = new MailMessage(
+ values.mailhost(), values.port());
+ mailMessage.setHeader("Date", DateUtils.getDateForHeader());
+
+ mailMessage.from(values.from());
+ if (!values.replytoList().equals("")) {
+ StringTokenizer t = new StringTokenizer(
+ values.replytoList(), ", ", false);
+ while (t.hasMoreTokens()) {
+ mailMessage.replyto(t.nextToken());
+ }
+ }
+ StringTokenizer t = new StringTokenizer(values.toList(), ", ", false);
+ while (t.hasMoreTokens()) {
+ mailMessage.to(t.nextToken());
+ }
+
+ mailMessage.setSubject(values.subject());
+
+ if (values.charset().length() > 0) {
+ mailMessage.setHeader("Content-Type", values.mimeType()
+ + "; charset=\"" + values.charset() + "\"");
+ } else {
+ mailMessage.setHeader("Content-Type", values.mimeType());
+ }
+
+ PrintStream ps = mailMessage.getPrintStream();
+ ps.println(values.body().length() > 0 ? values.body() : message);
+
+ mailMessage.sendAndClose();
+ }
+ /**
+ * Send the mail (MimeMail)
+ * @param project current ant project
+ * @param values various values
+ * @param message mail body
+ */
+ private void sendMimeMail(Project project, Values values, String message) {
+ Mailer mailer = null;
+ try {
+ mailer = (Mailer) ClasspathUtils.newInstance(
+ "org.apache.tools.ant.taskdefs.email.MimeMailer",
+ MailLogger.class.getClassLoader(), Mailer.class);
+ } catch (BuildException e) {
+ Throwable t = e.getCause() == null ? e : e.getCause();
+ log("Failed to initialise MIME mail: " + t.getMessage());
+ return;
+ }
+ // convert the replyTo string into a vector of emailaddresses
+ Vector<EmailAddress> replyToList = vectorizeEmailAddresses(values.replytoList());
+ mailer.setHost(values.mailhost());
+ mailer.setPort(values.port());
+ mailer.setUser(values.user());
+ mailer.setPassword(values.password());
+ mailer.setSSL(values.ssl());
+ mailer.setEnableStartTLS(values.starttls());
+ Message mymessage =
+ new Message(values.body().length() > 0 ? values.body() : message);
+ mymessage.setProject(project);
+ mymessage.setMimeType(values.mimeType());
+ if (values.charset().length() > 0) {
+ mymessage.setCharset(values.charset());
+ }
+ mailer.setMessage(mymessage);
+ mailer.setFrom(new EmailAddress(values.from()));
+ mailer.setReplyToList(replyToList);
+ Vector<EmailAddress> toList = vectorizeEmailAddresses(values.toList());
+ mailer.setToList(toList);
+ Vector<EmailAddress> toCcList = vectorizeEmailAddresses(values.toCcList());
+ mailer.setCcList(toCcList);
+ Vector<EmailAddress> toBccList = vectorizeEmailAddresses(values.toBccList());
+ mailer.setBccList(toBccList);
+ mailer.setFiles(new Vector<File>());
+ mailer.setSubject(values.subject());
+ mailer.setHeaders(new Vector<Header>());
+ mailer.send();
+ }
+ private Vector<EmailAddress> vectorizeEmailAddresses(String listString) {
+ Vector<EmailAddress> emailList = new Vector<EmailAddress>();
+ StringTokenizer tokens = new StringTokenizer(listString, ",");
+ while (tokens.hasMoreTokens()) {
+ emailList.addElement(new EmailAddress(tokens.nextToken()));
+ }
+ return emailList;
+ }
+}
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/ProfileLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/ProfileLogger.java
new file mode 100644
index 00000000..bbf5bb4f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/ProfileLogger.java
@@ -0,0 +1,112 @@
+/*
+ * 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.tools.ant.listener;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * This is a special logger that is designed to profile builds.
+ *
+ * @since Ant1.8
+ */
+public class ProfileLogger extends DefaultLogger {
+
+ private Map<Object, Date> profileData = new ConcurrentHashMap<Object, Date>();
+
+ /**
+ * Logs a message to say that the target has started.
+ *
+ * @param event
+ * An event with any relevant extra information. Must not be
+ * <code>null</code>.
+ */
+ public void targetStarted(BuildEvent event) {
+ Date now = new Date();
+ String name = "Target " + event.getTarget().getName();
+ logStart(event, now, name);
+ profileData.put(event.getTarget(), now);
+ }
+
+ /**
+ * Logs a message to say that the target has finished.
+ *
+ * @param event
+ * An event with any relevant extra information. Must not be
+ * <code>null</code>.
+ */
+ public void targetFinished(BuildEvent event) {
+ Date start = (Date) profileData.remove(event.getTarget());
+ String name = "Target " + event.getTarget().getName();
+ logFinish(event, start, name);
+ }
+
+ /**
+ * Logs a message to say that the task has started.
+ *
+ * @param event
+ * An event with any relevant extra information. Must not be
+ * <code>null</code>.
+ */
+ public void taskStarted(BuildEvent event) {
+ String name = event.getTask().getTaskName();
+ Date now = new Date();
+ logStart(event, now, name);
+ profileData.put(event.getTask(), now);
+ }
+
+ /**
+ * Logs a message to say that the task has finished.
+ *
+ * @param event
+ * An event with any relevant extra information. Must not be
+ * <code>null</code>.
+ */
+ public void taskFinished(BuildEvent event) {
+ Date start = (Date) profileData.remove(event.getTask());
+ String name = event.getTask().getTaskName();
+ logFinish(event, start, name);
+ }
+
+ private void logFinish(BuildEvent event, Date start, String name) {
+ Date now = new Date();
+ String msg = null;
+ if (start != null) {
+ long diff = now.getTime() - start.getTime();
+ msg = StringUtils.LINE_SEP + name + ": finished " + now + " ("
+ + diff + "ms)";
+ } else {
+ msg = StringUtils.LINE_SEP + name + ": finished " + now
+ + " (unknown duration, start not detected)";
+ }
+ printMessage(msg, out, event.getPriority());
+ log(msg);
+ }
+
+ private void logStart(BuildEvent event, Date start, String name) {
+ String msg = StringUtils.LINE_SEP + name + ": started " + start;
+ printMessage(msg, out, event.getPriority());
+ log(msg);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/SilentLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/SilentLogger.java
new file mode 100644
index 00000000..6ddfd7f9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/SilentLogger.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.tools.ant.listener;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.DefaultLogger;
+
+/**
+ * A logger which logs nothing but build failure and what task might output
+ *
+ * @since 1.9.0
+ */
+public class SilentLogger extends DefaultLogger {
+
+ @Override
+ public void buildStarted(BuildEvent event) {
+ // log nothing
+ }
+
+ @Override
+ public void buildFinished(BuildEvent event) {
+ if (event.getException() != null) {
+ super.buildFinished(event);
+ }
+ }
+
+ @Override
+ public void targetStarted(BuildEvent event) {
+ // log nothing
+ }
+
+ @Override
+ public void targetFinished(BuildEvent event) {
+ // log nothing
+ }
+
+ @Override
+ public void taskStarted(BuildEvent event) {
+ // log nothing
+ }
+
+ @Override
+ public void taskFinished(BuildEvent event) {
+ // log nothing
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/SimpleBigProjectLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/SimpleBigProjectLogger.java
new file mode 100644
index 00000000..18f8dc68
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/SimpleBigProjectLogger.java
@@ -0,0 +1,46 @@
+/*
+ * 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.tools.ant.listener;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.NoBannerLogger;
+
+/**
+ * Displays subproject names like {@link BigProjectLogger}
+ * but is otherwise as quiet as {@link NoBannerLogger}.
+ * @since Ant1.8.1
+ */
+public class SimpleBigProjectLogger extends NoBannerLogger {
+
+ /**
+ * Override point, extract the target name
+ *
+ * @param event the event to work on
+ * @return the target name -including the owning project name (if non-null)
+ */
+ protected String extractTargetName(BuildEvent event) {
+ String targetName = super.extractTargetName(event);
+ String projectName = extractProjectName(event);
+ if (projectName != null && targetName != null) {
+ return projectName + '.' + targetName;
+ } else {
+ return targetName;
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/TimestampedLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/TimestampedLogger.java
new file mode 100644
index 00000000..91296e32
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/TimestampedLogger.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.tools.ant.listener;
+
+import org.apache.tools.ant.DefaultLogger;
+
+/**
+ * Like a normal logger, except with timed outputs
+ */
+public class TimestampedLogger extends DefaultLogger {
+
+ /**
+ * what appears between the old message and the new
+ */
+ public static final String SPACER = " - at ";
+
+
+ /**
+ * This is an override point: the message that indicates whether a build failed.
+ * Subclasses can change/enhance the message.
+ *
+ * @return The classic "BUILD FAILED" plus a timestamp
+ */
+ protected String getBuildFailedMessage() {
+ return super.getBuildFailedMessage() + SPACER + getTimestamp();
+ }
+
+ /**
+ * This is an override point: the message that indicates that a build succeeded.
+ * Subclasses can change/enhance the message.
+ *
+ * @return The classic "BUILD SUCCESSFUL" plus a timestamp
+ */
+ protected String getBuildSuccessfulMessage() {
+ return super.getBuildSuccessfulMessage() + SPACER + getTimestamp();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/defaults.properties b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/defaults.properties
new file mode 100644
index 00000000..2994382e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/listener/defaults.properties
@@ -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.
+#
+####################################################
+#
+# ANSI COLOR LOGGER CONFIGURATION
+#
+# Format for AnsiColorLogger.*=
+# Attribute;Foreground;Background
+#
+# Attribute is one of the following:
+# 0 -> Reset All Attributes (return to normal mode)
+# 1 -> Bright (Usually turns on BOLD)
+# 2 -> Dim
+# 3 -> Underline
+# 5 -> link
+# 7 -> Reverse
+# 8 -> Hidden
+#
+# Foreground is one of the following:
+# 30 -> Black
+# 31 -> Red
+# 32 -> Green
+# 33 -> Yellow
+# 34 -> Blue
+# 35 -> Magenta
+# 36 -> Cyan
+# 37 -> White
+#
+# Background is one of the following:
+# 40 -> Black
+# 41 -> Red
+# 42 -> Green
+# 43 -> Yellow
+# 44 -> Blue
+# 45 -> Magenta
+# 46 -> Cyan
+# 47 -> White
+#
+####################################################
+
+AnsiColorLogger.ERROR_COLOR=2;31
+AnsiColorLogger.WARNING_COLOR=2;35
+AnsiColorLogger.INFO_COLOR=2;36
+AnsiColorLogger.VERBOSE_COLOR=2;32
+AnsiColorLogger.DEBUG_COLOR=2;34
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/loader/AntClassLoader2.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/loader/AntClassLoader2.java
new file mode 100644
index 00000000..1a4cac62
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/loader/AntClassLoader2.java
@@ -0,0 +1,31 @@
+/*
+ * 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.tools.ant.loader;
+
+import org.apache.tools.ant.AntClassLoader;
+
+/**
+ * @deprecated since 1.7
+ * Just use {@link AntClassLoader} itself.
+ */
+public class AntClassLoader2 extends AntClassLoader {
+ /** No args constructor. */
+ public AntClassLoader2() {
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/loader/AntClassLoader5.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/loader/AntClassLoader5.java
new file mode 100644
index 00000000..a91ed41e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/loader/AntClassLoader5.java
@@ -0,0 +1,65 @@
+/*
+ * 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.tools.ant.loader;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Overrides getResources which became non-final in Java5 and
+ * implements Closeable
+ */
+public class AntClassLoader5 extends AntClassLoader implements Closeable {
+ /**
+ * Creates a classloader for the given project using the classpath given.
+ *
+ * @param parent The parent classloader to which unsatisfied loading
+ * attempts are delegated. May be <code>null</code>,
+ * in which case the classloader which loaded this
+ * class is used as the parent.
+ * @param project The project to which this classloader is to belong.
+ * Must not be <code>null</code>.
+ * @param classpath the classpath to use to load the classes.
+ * May be <code>null</code>, in which case no path
+ * elements are set up to start with.
+ * @param parentFirst If <code>true</code>, indicates that the parent
+ * classloader should be consulted before trying to
+ * load the a class through this loader.
+ */
+ public AntClassLoader5(ClassLoader parent, Project project,
+ Path classpath, boolean parentFirst) {
+ super(parent, project, classpath, parentFirst);
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration<URL> getResources(String name) throws IOException {
+ return getNamedResources(name);
+ }
+
+ /** {@inheritDoc} */
+ public void close() {
+ cleanup();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/GetProperty.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/GetProperty.java
new file mode 100644
index 00000000..c08808aa
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/GetProperty.java
@@ -0,0 +1,31 @@
+/*
+ * 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.tools.ant.property;
+
+/**
+ * Interface to a class (normally PropertyHelper) to get a property.
+ * @since Ant 1.8.0
+ */
+public interface GetProperty {
+ /**
+ * Returns the value of a property if it is set.
+ * @param name name of the property.
+ * @return the property value, or null for no match or for name being null.
+ */
+ Object getProperty(String name);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/LocalProperties.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/LocalProperties.java
new file mode 100644
index 00000000..c9ce3af0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/LocalProperties.java
@@ -0,0 +1,152 @@
+/*
+ * 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.tools.ant.property;
+
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+
+/**
+ * Thread local class containing local properties.
+ * @since Ant 1.8.0
+ */
+public class LocalProperties
+ extends InheritableThreadLocal<LocalPropertyStack>
+ implements PropertyHelper.PropertyEvaluator,
+ PropertyHelper.PropertySetter {
+
+ /**
+ * Get a localproperties for the given project.
+ * @param project the project to retrieve the localproperties for.
+ * @return the localproperties.
+ */
+ public static synchronized LocalProperties get(Project project) {
+ LocalProperties l = (LocalProperties) project.getReference(
+ MagicNames.REFID_LOCAL_PROPERTIES);
+ if (l == null) {
+ l = new LocalProperties();
+ project.addReference(MagicNames.REFID_LOCAL_PROPERTIES, l);
+ PropertyHelper.getPropertyHelper(project).add(l);
+ }
+ return l;
+ }
+
+ // --------------------------------------------------
+ //
+ // Thread stuff
+ //
+ // --------------------------------------------------
+
+ /**
+ * Construct a new LocalProperties object.
+ */
+ private LocalProperties() {
+ }
+
+ /**
+ * Get the initial value.
+ * @return a new localproperties stack.
+ */
+ protected synchronized LocalPropertyStack initialValue() {
+ return new LocalPropertyStack();
+ }
+
+ private LocalPropertyStack current() {
+ return (LocalPropertyStack) get();
+ }
+
+ // --------------------------------------------------
+ //
+ // Local property adding and scoping
+ //
+ // --------------------------------------------------
+
+ /**
+ * Add a local property to the current scope.
+ * @param property the property name to add.
+ */
+ public void addLocal(String property) {
+ current().addLocal(property);
+ }
+
+ /** enter the scope */
+ public void enterScope() {
+ current().enterScope();
+ }
+
+ /** exit the scope */
+ public void exitScope() {
+ current().exitScope();
+ }
+
+ // --------------------------------------------------
+ //
+ // Copy - used in parallel to make a new stack
+ //
+ // --------------------------------------------------
+
+ /**
+ * Copy the stack for a parallel thread.
+ * To be called from the parallel thread itself.
+ */
+ public void copy() {
+ set(current().copy());
+ }
+
+ // --------------------------------------------------
+ //
+ // PropertyHelper delegate methods
+ //
+ // --------------------------------------------------
+
+ /**
+ * Evaluate a property.
+ * @param property the property's String "identifier".
+ * @param helper the invoking PropertyHelper.
+ * @return Object value.
+ */
+ public Object evaluate(String property, PropertyHelper helper) {
+ return current().evaluate(property, helper);
+ }
+
+ /**
+ * Set a *new" property.
+ * @param property the property's String "identifier".
+ * @param value the value to set.
+ * @param propertyHelper the invoking PropertyHelper.
+ * @return true if this entity 'owns' the property.
+ */
+ public boolean setNew(
+ String property, Object value, PropertyHelper propertyHelper) {
+ return current().setNew(property, value, propertyHelper);
+ }
+
+ /**
+ * Set a property.
+ * @param property the property's String "identifier".
+ * @param value the value to set.
+ * @param propertyHelper the invoking PropertyHelper.
+ * @return true if this entity 'owns' the property.
+ */
+ public boolean set(
+ String property, Object value, PropertyHelper propertyHelper) {
+ return current().set(property, value, propertyHelper);
+ }
+}
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/LocalPropertyStack.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/LocalPropertyStack.java
new file mode 100644
index 00000000..482f28cd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/LocalPropertyStack.java
@@ -0,0 +1,161 @@
+/*
+ * 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.tools.ant.property;
+
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.tools.ant.PropertyHelper;
+
+/**
+ * A stack of local property maps.
+ * There is a map for each scope (target, sequential, macro).
+ * @since Ant 1.8.0
+ */
+public class LocalPropertyStack {
+ private final LinkedList<Map<String, Object>> stack = new LinkedList<Map<String, Object>>();
+ private final Object LOCK = new Object();
+
+ // --------------------------------------------------
+ //
+ // Local property adding and scoping
+ //
+ // --------------------------------------------------
+
+ /**
+ * Add a local property.
+ * @param property the name of the local property.
+ */
+ public void addLocal(String property) {
+ synchronized (LOCK) {
+ Map<String, Object> map = stack.peek();
+ if (map != null) {
+ map.put(property, NullReturn.NULL);
+ }
+ }
+ }
+
+ /**
+ * Enter the local scope.
+ */
+ public void enterScope() {
+ synchronized (LOCK) {
+ stack.addFirst(new ConcurrentHashMap<String, Object>());
+ }
+ }
+
+ /**
+ * Exit the local scope.
+ */
+ public void exitScope() {
+ synchronized (LOCK) {
+ stack.removeFirst().clear();
+ }
+ }
+
+ // --------------------------------------------------
+ //
+ // Copy - used in parallel to make a new stack
+ //
+ // --------------------------------------------------
+
+ /**
+ * Copy the stack for a parallel thread.
+ * @return a copy.
+ */
+ public LocalPropertyStack copy() {
+ synchronized (LOCK) {
+ LocalPropertyStack ret = new LocalPropertyStack();
+ ret.stack.addAll(stack);
+ return ret;
+ }
+ }
+
+ // --------------------------------------------------
+ //
+ // PropertyHelper delegate methods
+ //
+ // --------------------------------------------------
+
+ /**
+ * Evaluate a property.
+ * @param property the property's String "identifier".
+ * @param helper the invoking PropertyHelper.
+ * @return Object value.
+ */
+ public Object evaluate(String property, PropertyHelper helper) {
+ synchronized (LOCK) {
+ for (Map<String, Object> map : stack) {
+ Object ret = map.get(property);
+ if (ret != null) {
+ return ret;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Set a *new" property.
+ * @param property the property's String "identifier".
+ * @param value the value to set.
+ * @param propertyHelper the invoking PropertyHelper.
+ * @return true if this entity 'owns' the property.
+ */
+ public boolean setNew(
+ String property, Object value, PropertyHelper propertyHelper) {
+ Map<String, Object> map = getMapForProperty(property);
+ if (map == null) {
+ return false;
+ }
+ Object currValue = map.get(property);
+ if (currValue == NullReturn.NULL) {
+ map.put(property, value);
+ }
+ return true;
+ }
+
+ /**
+ * Set a property.
+ * @param property the property's String "identifier".
+ * @param value the value to set.
+ * @param propertyHelper the invoking PropertyHelper.
+ * @return true if this entity 'owns' the property.
+ */
+ public boolean set(String property, Object value, PropertyHelper propertyHelper) {
+ Map<String, Object> map = getMapForProperty(property);
+ if (map == null) {
+ return false;
+ }
+ map.put(property, value);
+ return true;
+ }
+
+ private Map<String, Object> getMapForProperty(String property) {
+ synchronized (LOCK) {
+ for (Map<String, Object> map : stack) {
+ if (map.get(property) != null) {
+ return map;
+ }
+ }
+ }
+ return null;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/NullReturn.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/NullReturn.java
new file mode 100644
index 00000000..067aa9f1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/NullReturn.java
@@ -0,0 +1,38 @@
+/*
+ * 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.tools.ant.property;
+
+/**
+ * Class to represent a null and to stop the chain of lookups.
+ * @since Ant 1.8.0
+ */
+public final class NullReturn {
+ /** a value to use in a property helper to stop looking properties */
+ public static final NullReturn NULL = new NullReturn();
+
+ /** Private constructor */
+ private NullReturn() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return "null";
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/ParseNextProperty.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/ParseNextProperty.java
new file mode 100644
index 00000000..5e5dfc07
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/ParseNextProperty.java
@@ -0,0 +1,44 @@
+/*
+ * 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.tools.ant.property;
+
+import java.text.ParsePosition;
+
+import org.apache.tools.ant.Project;
+
+/**
+ * Helper for {@link PropertyExpander PropertyExpander} that can be
+ * used to expand property references to values.
+ * @since Ant 1.8.0
+ */
+public interface ParseNextProperty {
+ /**
+ * Get the current project.
+ * @return the current ant project.
+ */
+ Project getProject();
+
+ /**
+ * Return any property that can be parsed from the specified position
+ * in the specified String.
+ * @param value String to parse
+ * @param pos ParsePosition
+ * @return Object or null if no property is at the current location.
+ */
+ Object parseNextProperty(String value, ParsePosition pos);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/ParseProperties.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/ParseProperties.java
new file mode 100644
index 00000000..f03f966e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/ParseProperties.java
@@ -0,0 +1,199 @@
+/*
+ * 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.tools.ant.property;
+
+import java.text.ParsePosition;
+import java.util.Collection;
+
+import org.apache.tools.ant.Project;
+
+/**
+ * Parse properties using a collection of expanders.
+ *
+ * @since Ant 1.8.0
+ */
+public class ParseProperties implements ParseNextProperty {
+
+ private final Project project;
+ private final GetProperty getProperty;
+ private final Collection<PropertyExpander> expanders;
+
+ /**
+ * Constructor with a getProperty.
+ * @param project the current Ant project.
+ * @param expanders a sequence of expanders
+ * @param getProperty property resolver.
+ */
+ public ParseProperties(Project project, Collection<PropertyExpander> expanders, GetProperty getProperty) {
+ this.project = project;
+ this.expanders = expanders;
+ this.getProperty = getProperty;
+ }
+
+ /**
+ * Get the project.
+ * @return the current Ant project.
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * Decode properties from a String representation.
+ *
+ * <ul>
+ *
+ * <li>This implementation starts parsing the <code>value</code>
+ * parameter (unsurprisingly) at the beginning and asks each
+ * {@link PropertyExpander PropertyExpander} whether there is a
+ * property reference at that point. PropertyExpanders return
+ * the name of a property they may find and may advance the parse
+ * position.</li>
+ *
+ * <li>If the PropertyExpander returns <code>null</code> the
+ * method continues with the next PropertyExpander, otherwise it
+ * tries to look up the property's value using the configured
+ * {@link GetProperty GetProperty} instance.</li>
+ *
+ * <li>Once all PropertyExpanders have been consulted, the parse
+ * position is advanced by one character and the process repeated
+ * until <code>value</code> is exhausted.</li>
+ *
+ * </ul>
+ *
+ * <p>If the entire contents of <code>value</code> resolves to a
+ * single property, the looked up property value is returned.
+ * Otherwise a String is returned that concatenates the
+ * non-property parts of <code>value</code> and the expanded
+ * values of the properties that have been found.</p>
+ *
+ * @param value The string to be scanned for property references.
+ * May be <code>null</code>, in which case this
+ * method returns immediately with no effect.
+ *
+ * @return the original string with the properties replaced, or
+ * <code>null</code> if the original string is <code>null</code>.
+ */
+ public Object parseProperties(String value) {
+ if (value == null || "".equals(value)) {
+ return value;
+ }
+ final int len = value.length();
+ ParsePosition pos = new ParsePosition(0);
+ Object o = parseNextProperty(value, pos);
+ if (o != null && pos.getIndex() >= len) {
+ return o;
+ }
+ StringBuffer sb = new StringBuffer(len * 2);
+ if (o == null) {
+ sb.append(value.charAt(pos.getIndex()));
+ pos.setIndex(pos.getIndex() + 1);
+ } else {
+ sb.append(o);
+ }
+ while (pos.getIndex() < len) {
+ o = parseNextProperty(value, pos);
+ if (o == null) {
+ sb.append(value.charAt(pos.getIndex()));
+ pos.setIndex(pos.getIndex() + 1);
+ } else {
+ sb.append(o);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Learn whether a String contains replaceable properties.
+ *
+ * <p>Uses the configured {@link PropertyExpander
+ * PropertyExpanders} and scans through the string. Returns true
+ * as soon as any expander finds a property.</p>
+ *
+ * @param value the String to check.
+ * @return <code>true</code> if <code>value</code> contains property notation.
+ */
+ public boolean containsProperties(String value) {
+ if (value == null) {
+ return false;
+ }
+ final int len = value.length();
+ for (ParsePosition pos = new ParsePosition(0); pos.getIndex() < len;) {
+ if (parsePropertyName(value, pos) != null) {
+ return true;
+ }
+ pos.setIndex(pos.getIndex() + 1);
+ }
+ return false;
+ }
+
+ /**
+ * Return any property that can be parsed from the specified position
+ * in the specified String.
+ *
+ * <p>Uses the configured {@link PropertyExpander
+ * PropertyExpanders} and {@link GetProperty GetProperty}
+ * instance .</p>
+ *
+ * @param value String to parse
+ * @param pos ParsePosition
+ * @return Object or null if no property is at the current
+ * location. If a property reference has been found but the
+ * property doesn't expand to a value, the property's name is
+ * returned.
+ */
+ public Object parseNextProperty(String value, ParsePosition pos) {
+ final int start = pos.getIndex();
+
+ if (start > value.length()) {
+ // early exit, can't find any property here, no need to
+ // consult all the delegates.
+ return null;
+ }
+
+ String propertyName = parsePropertyName(value, pos);
+ if (propertyName != null) {
+ Object result = getProperty(propertyName);
+ if (result != null) {
+ return result;
+ }
+ if (project != null) {
+ project.log(
+ "Property \"" + propertyName
+ + "\" has not been set", Project.MSG_VERBOSE);
+ }
+ return value.substring(start, pos.getIndex());
+ }
+ return null;
+ }
+
+ private String parsePropertyName(String value, ParsePosition pos) {
+ for (PropertyExpander propertyExpander : expanders) {
+ String propertyName = propertyExpander.parsePropertyName(value, pos, this);
+ if (propertyName == null) {
+ continue;
+ }
+ return propertyName;
+ }
+ return null;
+ }
+
+ private Object getProperty(String propertyName) {
+ return getProperty.getProperty(propertyName);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/PropertyExpander.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/PropertyExpander.java
new file mode 100644
index 00000000..a2b4d633
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/PropertyExpander.java
@@ -0,0 +1,51 @@
+/*
+ * 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.tools.ant.property;
+
+import java.text.ParsePosition;
+
+import org.apache.tools.ant.PropertyHelper;
+
+/**
+ * Responsible for locating a property reference inside a String.
+ * @since Ant 1.8.0
+ */
+public interface PropertyExpander extends PropertyHelper.Delegate {
+
+ /**
+ * Determine whether there is a property reference at the current
+ * ParsePosition and return its name (or null if there is none).
+ *
+ * <p>Implementations should advance the ParsePosition to the last
+ * character that makes up the property reference. E.g. the
+ * default implementation would return <code>"foo"</code> for
+ * <code>${foo}</code> and advance the ParsePosition to the
+ * <code>}</code> character.</p>
+ *
+ * @param s the String to parse.
+ * @param pos the ParsePosition in use, the location is expected
+ * to be modified if a property reference has been found (and may
+ * even be modified if no reference has been found).
+ * @param parseNextProperty provides access to the Project and may
+ * be used to look up property values.
+ * @return property name if any, else <code>null</code>.
+ */
+ String parsePropertyName(String s, ParsePosition pos,
+ ParseNextProperty parseNextProperty);
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/ResolvePropertyMap.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/ResolvePropertyMap.java
new file mode 100644
index 00000000..5bdd3545
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/ResolvePropertyMap.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.tools.ant.property;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Class to resolve properties in a map. This class is explicitly not threadsafe.
+ * @since Ant 1.8.0
+ */
+public class ResolvePropertyMap implements GetProperty {
+ private final Set<String> seen = new HashSet<String>();
+ private final ParseProperties parseProperties;
+ private final GetProperty master;
+ private Map<String, Object> map;
+ private String prefix;
+ // whether properties of the value side of the map should be
+ // expanded
+ private boolean prefixValues = false;
+ // whether the current getProperty call is expanding the key side
+ // of the map
+ private boolean expandingLHS = true;
+
+ /**
+ * Constructor with a master getproperty and a collection of expanders.
+ * @param project the current ant project.
+ * @param master the master property holder (usually PropertyHelper)
+ * @param expanders a collection of expanders (usually from PropertyHelper).
+ */
+ public ResolvePropertyMap(Project project, GetProperty master, Collection<PropertyExpander> expanders) {
+ this.master = master;
+ this.parseProperties = new ParseProperties(project, expanders, this);
+ }
+
+ /**
+ * Returns the value of a property if it is set.
+ * @param name name of the property.
+ * @return the property value, or null for no match or for name being null.
+ */
+ public Object getProperty(String name) {
+ if (seen.contains(name)) {
+ throw new BuildException(
+ "Property " + name + " was circularly " + "defined.");
+ }
+
+ try {
+
+ // If the property we are looking up is a key in the map
+ // (first call into this method from resolveAllProperties)
+ // or we've been asked to prefix the value side (later
+ // recursive calls via the GetProperty interface) the
+ // prefix must be prepended when looking up the property
+ // outside of the map.
+ String fullKey = name;
+ if (prefix != null && (expandingLHS || prefixValues)) {
+ fullKey = prefix + name;
+ }
+
+ Object masterValue = master.getProperty(fullKey);
+ if (masterValue != null) {
+ // If the property already has a value outside of the
+ // map, use that value to enforce property
+ // immutability.
+
+ return masterValue;
+ }
+
+ seen.add(name);
+
+ String recursiveCallKey = name;
+ if (prefix != null && !expandingLHS && !prefixValues) {
+ // only look up unprefixed properties inside the map
+ // if prefixValues is true or we are expanding the key
+ // itself
+ recursiveCallKey = prefix + name;
+ }
+
+ expandingLHS = false;
+ // will recurse into this method for each property
+ // reference found in the map's value
+ return parseProperties.parseProperties((String) map.get(recursiveCallKey));
+ } finally {
+ seen.remove(name);
+ }
+ }
+
+ /**
+ * The action method - resolves all the properties in a map.
+ * @param map the map to resolve properties in.
+ * @deprecated since Ant 1.8.2, use the three-arg method instead.
+ */
+ public void resolveAllProperties(Map<String, Object> map) {
+ resolveAllProperties(map, null, false);
+ }
+
+ /**
+ * The action method - resolves all the properties in a map.
+ * @param map the map to resolve properties in.
+ * @param prefix the prefix the properties defined inside the map
+ * will finally receive - may be null.
+ * @deprecated since Ant 1.8.2, use the three-arg method instead.
+ */
+ public void resolveAllProperties(Map<String, Object> map, String prefix) {
+ resolveAllProperties(map, null, false);
+ }
+
+ /**
+ * The action method - resolves all the properties in a map.
+ * @param map the map to resolve properties in.
+ * @param prefix the prefix the properties defined inside the map
+ * will finally receive - may be null.
+ * @param prefixValues - whether the prefix will be applied
+ * to properties on the value side of the map as well.
+ */
+ public void resolveAllProperties(Map<String, Object> map, String prefix,
+ boolean prefixValues) {
+ // The map, prefix and prefixValues flag get used in the
+ // getProperty callback
+ this.map = map;
+ this.prefix = prefix;
+ this.prefixValues = prefixValues;
+
+ for (String key : map.keySet()) {
+ expandingLHS = true;
+ Object result = getProperty(key);
+ String value = result == null ? "" : result.toString();
+ map.put(key, value);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/package.html b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/package.html
new file mode 100644
index 00000000..7a497ecf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/property/package.html
@@ -0,0 +1,19 @@
+<!--
+ 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.
+-->
+<body>
+ Contains helper classes for ant properties.
+</body>
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java
new file mode 100644
index 00000000..1ce81166
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java
@@ -0,0 +1,873 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * original Cvs.java 1.20
+ *
+ * NOTE: This implementation has been moved here from Cvs.java with
+ * the addition of some accessors for extensibility. Another task
+ * can extend this with some customized output processing.
+ *
+ * @since Ant 1.5
+ */
+public abstract class AbstractCvsTask extends Task {
+ /**
+ * Default compression level to use, if compression is enabled via
+ * setCompression( true ).
+ */
+ public static final int DEFAULT_COMPRESSION_LEVEL = 3;
+ private static final int MAXIMUM_COMRESSION_LEVEL = 9;
+
+ private Commandline cmd = new Commandline();
+
+ private ArrayList<Module> modules = new ArrayList<Module>();
+
+ /** list of Commandline children */
+ private Vector<Commandline> vecCommandlines = new Vector<Commandline>();
+
+ /**
+ * the CVSROOT variable.
+ */
+ private String cvsRoot;
+
+ /**
+ * the CVS_RSH variable.
+ */
+ private String cvsRsh;
+
+ /**
+ * the package/module to check out.
+ */
+ private String cvsPackage;
+ /**
+ * the tag
+ */
+ private String tag;
+ /**
+ * the default command.
+ */
+ private static final String DEFAULT_COMMAND = "checkout";
+ /**
+ * the CVS command to execute.
+ */
+ private String command = null;
+
+ /**
+ * suppress information messages.
+ */
+ private boolean quiet = false;
+
+ /**
+ * suppress all messages.
+ */
+ private boolean reallyquiet = false;
+
+ /**
+ * compression level to use.
+ */
+ private int compression = 0;
+
+ /**
+ * report only, don't change any files.
+ */
+ private boolean noexec = false;
+
+ /**
+ * CVS port
+ */
+ private int port = 0;
+
+ /**
+ * CVS password file
+ */
+ private File passFile = null;
+
+ /**
+ * the directory where the checked out files should be placed.
+ */
+ private File dest;
+
+ /** whether or not to append stdout/stderr to existing files */
+ private boolean append = false;
+
+ /**
+ * the file to direct standard output from the command.
+ */
+ private File output;
+
+ /**
+ * the file to direct standard error from the command.
+ */
+ private File error;
+
+ /**
+ * If true it will stop the build if cvs exits with error.
+ * Default is false. (Iulian)
+ */
+ private boolean failOnError = false;
+
+ /**
+ * Create accessors for the following, to allow different handling of
+ * the output.
+ */
+ private ExecuteStreamHandler executeStreamHandler;
+ private OutputStream outputStream;
+ private OutputStream errorStream;
+
+ /** empty no-arg constructor*/
+ public AbstractCvsTask() {
+ super();
+ }
+
+ /**
+ * sets the handler
+ * @param handler a handler able of processing the output and error streams from the cvs exe
+ */
+ public void setExecuteStreamHandler(ExecuteStreamHandler handler) {
+ this.executeStreamHandler = handler;
+ }
+
+ /**
+ * find the handler and instantiate it if it does not exist yet
+ * @return handler for output and error streams
+ */
+ protected ExecuteStreamHandler getExecuteStreamHandler() {
+
+ if (this.executeStreamHandler == null) {
+ setExecuteStreamHandler(new PumpStreamHandler(getOutputStream(),
+ getErrorStream()));
+ }
+
+ return this.executeStreamHandler;
+ }
+
+ /**
+ * sets a stream to which the output from the cvs executable should be sent
+ * @param outputStream stream to which the stdout from cvs should go
+ */
+ protected void setOutputStream(OutputStream outputStream) {
+
+ this.outputStream = outputStream;
+ }
+
+ /**
+ * access the stream to which the stdout from cvs should go
+ * if this stream has already been set, it will be returned
+ * if the stream has not yet been set, if the attribute output
+ * has been set, the output stream will go to the output file
+ * otherwise the output will go to ant's logging system
+ * @return output stream to which cvs' stdout should go to
+ */
+ protected OutputStream getOutputStream() {
+
+ if (this.outputStream == null) {
+
+ if (output != null) {
+ try {
+ setOutputStream(new PrintStream(
+ new BufferedOutputStream(
+ new FileOutputStream(output
+ .getPath(),
+ append))));
+ } catch (IOException e) {
+ throw new BuildException(e, getLocation());
+ }
+ } else {
+ setOutputStream(new LogOutputStream(this, Project.MSG_INFO));
+ }
+ }
+
+ return this.outputStream;
+ }
+
+ /**
+ * sets a stream to which the stderr from the cvs exe should go
+ * @param errorStream an output stream willing to process stderr
+ */
+ protected void setErrorStream(OutputStream errorStream) {
+
+ this.errorStream = errorStream;
+ }
+
+ /**
+ * access the stream to which the stderr from cvs should go
+ * if this stream has already been set, it will be returned
+ * if the stream has not yet been set, if the attribute error
+ * has been set, the output stream will go to the file denoted by the error attribute
+ * otherwise the stderr output will go to ant's logging system
+ * @return output stream to which cvs' stderr should go to
+ */
+ protected OutputStream getErrorStream() {
+
+ if (this.errorStream == null) {
+
+ if (error != null) {
+
+ try {
+ setErrorStream(new PrintStream(
+ new BufferedOutputStream(
+ new FileOutputStream(error.getPath(),
+ append))));
+ } catch (IOException e) {
+ throw new BuildException(e, getLocation());
+ }
+ } else {
+ setErrorStream(new LogOutputStream(this, Project.MSG_WARN));
+ }
+ }
+
+ return this.errorStream;
+ }
+
+ /**
+ * Sets up the environment for toExecute and then runs it.
+ * @param toExecute the command line to execute
+ * @throws BuildException if failonError is set to true and the cvs command fails
+ */
+ protected void runCommand(Commandline toExecute) throws BuildException {
+ // TODO: we should use JCVS (www.ice.com/JCVS) instead of
+ // command line execution so that we don't rely on having
+ // native CVS stuff around (SM)
+
+ // We can't do it ourselves as jCVS is GPLed, a third party task
+ // outside of Apache repositories would be possible though (SB).
+
+ Environment env = new Environment();
+
+ if (port > 0) {
+ Environment.Variable var = new Environment.Variable();
+ var.setKey("CVS_CLIENT_PORT");
+ var.setValue(String.valueOf(port));
+ env.addVariable(var);
+
+ // non-standard environment variable used by CVSNT, WinCVS
+ // and others
+ var = new Environment.Variable();
+ var.setKey("CVS_PSERVER_PORT");
+ var.setValue(String.valueOf(port));
+ env.addVariable(var);
+ }
+
+ /**
+ * Need a better cross platform integration with <cvspass>, so
+ * use the same filename.
+ */
+ if (passFile == null) {
+
+ File defaultPassFile = new File(
+ System.getProperty("cygwin.user.home",
+ System.getProperty("user.home"))
+ + File.separatorChar + ".cvspass");
+
+ if (defaultPassFile.exists()) {
+ this.setPassfile(defaultPassFile);
+ }
+ }
+
+ if (passFile != null) {
+ if (passFile.isFile() && passFile.canRead()) {
+ Environment.Variable var = new Environment.Variable();
+ var.setKey("CVS_PASSFILE");
+ var.setValue(String.valueOf(passFile));
+ env.addVariable(var);
+ log("Using cvs passfile: " + String.valueOf(passFile),
+ Project.MSG_VERBOSE);
+ } else if (!passFile.canRead()) {
+ log("cvs passfile: " + String.valueOf(passFile)
+ + " ignored as it is not readable",
+ Project.MSG_WARN);
+ } else {
+ log("cvs passfile: " + String.valueOf(passFile)
+ + " ignored as it is not a file",
+ Project.MSG_WARN);
+ }
+ }
+
+ if (cvsRsh != null) {
+ Environment.Variable var = new Environment.Variable();
+ var.setKey("CVS_RSH");
+ var.setValue(String.valueOf(cvsRsh));
+ env.addVariable(var);
+ }
+
+ //
+ // Just call the getExecuteStreamHandler() and let it handle
+ // the semantics of instantiation or retrieval.
+ //
+ Execute exe = new Execute(getExecuteStreamHandler(), null);
+
+ exe.setAntRun(getProject());
+ if (dest == null) {
+ dest = getProject().getBaseDir();
+ }
+
+ if (!dest.exists()) {
+ dest.mkdirs();
+ }
+
+ exe.setWorkingDirectory(dest);
+ exe.setCommandline(toExecute.getCommandline());
+ exe.setEnvironment(env.getVariables());
+
+ try {
+ String actualCommandLine = executeToString(exe);
+
+ log(actualCommandLine, Project.MSG_VERBOSE);
+ int retCode = exe.execute();
+ log("retCode=" + retCode, Project.MSG_DEBUG);
+
+ if (failOnError && Execute.isFailure(retCode)) {
+ throw new BuildException("cvs exited with error code "
+ + retCode
+ + StringUtils.LINE_SEP
+ + "Command line was ["
+ + actualCommandLine + "]",
+ getLocation());
+ }
+ } catch (IOException e) {
+ if (failOnError) {
+ throw new BuildException(e, getLocation());
+ }
+ log("Caught exception: " + e.getMessage(), Project.MSG_WARN);
+ } catch (BuildException e) {
+ if (failOnError) {
+ throw(e);
+ }
+ Throwable t = e.getCause();
+ if (t == null) {
+ t = e;
+ }
+ log("Caught exception: " + t.getMessage(), Project.MSG_WARN);
+ } catch (Exception e) {
+ if (failOnError) {
+ throw new BuildException(e, getLocation());
+ }
+ log("Caught exception: " + e.getMessage(), Project.MSG_WARN);
+ }
+ }
+
+ /**
+ * do the work
+ * @throws BuildException if failonerror is set to true and the
+ * cvs command fails.
+ */
+ public void execute() throws BuildException {
+
+ String savedCommand = getCommand();
+
+ if (this.getCommand() == null && vecCommandlines.size() == 0) {
+ // re-implement legacy behaviour:
+ this.setCommand(AbstractCvsTask.DEFAULT_COMMAND);
+ }
+
+ String c = this.getCommand();
+ Commandline cloned = null;
+ if (c != null) {
+ cloned = (Commandline) cmd.clone();
+ cloned.createArgument(true).setLine(c);
+ this.addConfiguredCommandline(cloned, true);
+ }
+
+ try {
+ final int size = vecCommandlines.size();
+ for (int i = 0; i < size; i++) {
+ this.runCommand((Commandline) vecCommandlines.elementAt(i));
+ }
+ } finally {
+ if (cloned != null) {
+ removeCommandline(cloned);
+ }
+ setCommand(savedCommand);
+ FileUtils.close(outputStream);
+ FileUtils.close(errorStream);
+ }
+ }
+
+ private String executeToString(Execute execute) {
+
+ String cmdLine = Commandline.describeCommand(execute
+ .getCommandline());
+ StringBuffer stringBuffer = removeCvsPassword(cmdLine);
+
+ String newLine = StringUtils.LINE_SEP;
+ String[] variableArray = execute.getEnvironment();
+
+ if (variableArray != null) {
+ stringBuffer.append(newLine);
+ stringBuffer.append(newLine);
+ stringBuffer.append("environment:");
+ stringBuffer.append(newLine);
+ for (int z = 0; z < variableArray.length; z++) {
+ stringBuffer.append(newLine);
+ stringBuffer.append("\t");
+ stringBuffer.append(variableArray[z]);
+ }
+ }
+
+ return stringBuffer.toString();
+ }
+
+ /**
+ * Removes the cvs password from the command line, if given on the command
+ * line. This password can be given on the command line in the cvsRoot
+ * -d:pserver:user:password@server:path
+ * It has to be noted that the password may be omitted altogether.
+ * @param cmdLine the CVS command line
+ * @return a StringBuffer where the password has been removed (if available)
+ */
+ private StringBuffer removeCvsPassword(String cmdLine) {
+ StringBuffer stringBuffer = new StringBuffer(cmdLine);
+
+ int start = cmdLine.indexOf("-d:");
+
+ if (start >= 0) {
+ int stop = cmdLine.indexOf("@", start);
+ int startproto = cmdLine.indexOf(":", start);
+ int startuser = cmdLine.indexOf(":", startproto + 1);
+ int startpass = cmdLine.indexOf(":", startuser + 1);
+ stop = cmdLine.indexOf("@", start);
+ if (stop >= 0 && startpass > startproto && startpass < stop) {
+ for (int i = startpass + 1; i < stop; i++) {
+ stringBuffer.replace(i, i + 1, "*");
+ }
+ }
+ }
+ return stringBuffer;
+ }
+
+ /**
+ * The CVSROOT variable.
+ *
+ * @param root
+ * the CVSROOT variable
+ */
+ public void setCvsRoot(String root) {
+
+ // Check if not real cvsroot => set it to null
+ if (root != null) {
+ if (root.trim().equals("")) {
+ root = null;
+ }
+ }
+
+ this.cvsRoot = root;
+ }
+
+ /**
+ * access the CVSROOT variable
+ * @return CVSROOT
+ */
+ public String getCvsRoot() {
+
+ return this.cvsRoot;
+ }
+
+ /**
+ * The CVS_RSH variable.
+ *
+ * @param rsh the CVS_RSH variable
+ */
+ public void setCvsRsh(String rsh) {
+ // Check if not real cvsrsh => set it to null
+ if (rsh != null) {
+ if (rsh.trim().equals("")) {
+ rsh = null;
+ }
+ }
+
+ this.cvsRsh = rsh;
+ }
+
+ /**
+ * access the CVS_RSH variable
+ * @return the CVS_RSH variable
+ */
+ public String getCvsRsh() {
+
+ return this.cvsRsh;
+ }
+
+ /**
+ * Port used by CVS to communicate with the server.
+ *
+ * @param port port of CVS
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ * access the port of CVS
+ * @return the port of CVS
+ */
+ public int getPort() {
+
+ return this.port;
+ }
+
+ /**
+ * Password file to read passwords from.
+ *
+ * @param passFile password file to read passwords from
+ */
+ public void setPassfile(File passFile) {
+ this.passFile = passFile;
+ }
+
+ /**
+ * find the password file
+ * @return password file
+ */
+ public File getPassFile() {
+
+ return this.passFile;
+ }
+
+ /**
+ * The directory where the checked out files should be placed.
+ *
+ * <p>Note that this is different from CVS's -d command line
+ * switch as Ant will never shorten pathnames to avoid empty
+ * directories.</p>
+ *
+ * @param dest directory where the checked out files should be placed
+ */
+ public void setDest(File dest) {
+ this.dest = dest;
+ }
+
+ /**
+ * get the file where the checked out files should be placed
+ *
+ * @return directory where the checked out files should be placed
+ */
+ public File getDest() {
+
+ return this.dest;
+ }
+
+ /**
+ * The package/module to operate upon.
+ *
+ * @param p package or module to operate upon
+ */
+ public void setPackage(String p) {
+ this.cvsPackage = p;
+ }
+
+ /**
+ * access the package or module to operate upon
+ *
+ * @return package/module
+ */
+ public String getPackage() {
+
+ return this.cvsPackage;
+ }
+ /**
+ * tag or branch
+ * @return tag or branch
+ * @since ant 1.6.1
+ */
+ public String getTag() {
+ return tag;
+ }
+
+ /**
+ * The tag of the package/module to operate upon.
+ * @param p tag
+ */
+ public void setTag(String p) {
+ // Check if not real tag => set it to null
+ if (p != null && p.trim().length() > 0) {
+ tag = p;
+ addCommandArgument("-r" + p);
+ }
+ }
+
+ /**
+ * This needs to be public to allow configuration
+ * of commands externally.
+ * @param arg command argument
+ */
+ public void addCommandArgument(String arg) {
+ this.addCommandArgument(cmd, arg);
+ }
+
+ /**
+ * This method adds a command line argument to an external command.
+ *
+ * I do not understand what this method does in this class ???
+ * particularly not why it is public ????
+ * AntoineLL July 23d 2003
+ *
+ * @param c command line to which one argument should be added
+ * @param arg argument to add
+ */
+ public void addCommandArgument(Commandline c, String arg) {
+ c.createArgument().setValue(arg);
+ }
+
+
+ /**
+ * Use the most recent revision no later than the given date.
+ * @param p a date as string in a format that the CVS executable
+ * can understand see man cvs
+ */
+ public void setDate(String p) {
+ if (p != null && p.trim().length() > 0) {
+ addCommandArgument("-D");
+ addCommandArgument(p);
+ }
+ }
+
+ /**
+ * The CVS command to execute.
+ *
+ * This should be deprecated, it is better to use the Commandline class ?
+ * AntoineLL July 23d 2003
+ *
+ * @param c a command as string
+ */
+ public void setCommand(String c) {
+ this.command = c;
+ }
+ /**
+ * accessor to a command line as string
+ *
+ * This should be deprecated
+ * AntoineLL July 23d 2003
+ *
+ * @return command line as string
+ */
+ public String getCommand() {
+ return this.command;
+ }
+
+ /**
+ * If true, suppress informational messages.
+ * @param q if true, suppress informational messages
+ */
+ public void setQuiet(boolean q) {
+ quiet = q;
+ }
+
+ /**
+ * If true, suppress all messages.
+ * @param q if true, suppress all messages
+ * @since Ant 1.6
+ */
+ public void setReallyquiet(boolean q) {
+ reallyquiet = q;
+ }
+
+
+ /**
+ * If true, report only and don't change any files.
+ *
+ * @param ne if true, report only and do not change any files.
+ */
+ public void setNoexec(boolean ne) {
+ noexec = ne;
+ }
+
+ /**
+ * The file to direct standard output from the command.
+ * @param output a file to which stdout should go
+ */
+ public void setOutput(File output) {
+ this.output = output;
+ }
+
+ /**
+ * The file to direct standard error from the command.
+ *
+ * @param error a file to which stderr should go
+ */
+ public void setError(File error) {
+ this.error = error;
+ }
+
+ /**
+ * Whether to append output/error when redirecting to a file.
+ * @param value true indicated you want to append
+ */
+ public void setAppend(boolean value) {
+ this.append = value;
+ }
+
+ /**
+ * Stop the build process if the command exits with
+ * a return code other than 0.
+ * Defaults to false.
+ * @param failOnError stop the build process if the command exits with
+ * a return code other than 0
+ */
+ public void setFailOnError(boolean failOnError) {
+ this.failOnError = failOnError;
+ }
+
+ /**
+ * Configure a commandline element for things like cvsRoot, quiet, etc.
+ * @param c the command line which will be configured
+ * if the commandline is initially null, the function is a noop
+ * otherwise the function append to the commandline arguments concerning
+ * <ul>
+ * <li>
+ * cvs package
+ * </li>
+ * <li>
+ * compression
+ * </li>
+ * <li>
+ * quiet or reallyquiet
+ * </li>
+ * <li>cvsroot</li>
+ * <li>noexec</li>
+ * </ul>
+ */
+ protected void configureCommandline(Commandline c) {
+ if (c == null) {
+ return;
+ }
+ c.setExecutable("cvs");
+ if (cvsPackage != null) {
+ c.createArgument().setLine(cvsPackage);
+ }
+ for (Module m : modules) {
+ c.createArgument().setValue(m.getName());
+ }
+ if (this.compression > 0
+ && this.compression <= MAXIMUM_COMRESSION_LEVEL) {
+ c.createArgument(true).setValue("-z" + this.compression);
+ }
+ if (quiet && !reallyquiet) {
+ c.createArgument(true).setValue("-q");
+ }
+ if (reallyquiet) {
+ c.createArgument(true).setValue("-Q");
+ }
+ if (noexec) {
+ c.createArgument(true).setValue("-n");
+ }
+ if (cvsRoot != null) {
+ c.createArgument(true).setLine("-d" + cvsRoot);
+ }
+ }
+
+ /**
+ * remove a particular command from a vector of command lines
+ * @param c command line which should be removed
+ */
+ protected void removeCommandline(Commandline c) {
+ vecCommandlines.removeElement(c);
+ }
+
+ /**
+ * Adds direct command-line to execute.
+ * @param c command line to execute
+ */
+ public void addConfiguredCommandline(Commandline c) {
+ this.addConfiguredCommandline(c, false);
+ }
+
+ /**
+ * Configures and adds the given Commandline.
+ * @param c commandline to insert
+ * @param insertAtStart If true, c is
+ * inserted at the beginning of the vector of command lines
+ */
+ public void addConfiguredCommandline(Commandline c,
+ boolean insertAtStart) {
+ if (c == null) {
+ return;
+ }
+ this.configureCommandline(c);
+ if (insertAtStart) {
+ vecCommandlines.insertElementAt(c, 0);
+ } else {
+ vecCommandlines.addElement(c);
+ }
+ }
+
+ /**
+ * If set to a value 1-9 it adds -zN to the cvs command line, else
+ * it disables compression.
+ * @param level compression level 1 to 9
+ */
+ public void setCompressionLevel(int level) {
+ this.compression = level;
+ }
+
+ /**
+ * If true, this is the same as compressionlevel="3".
+ *
+ * @param usecomp If true, turns on compression using default
+ * level, AbstractCvsTask.DEFAULT_COMPRESSION_LEVEL.
+ */
+ public void setCompression(boolean usecomp) {
+ setCompressionLevel(usecomp
+ ? AbstractCvsTask.DEFAULT_COMPRESSION_LEVEL : 0);
+ }
+
+ /**
+ * add a named module/package.
+ *
+ * @since Ant 1.8.0
+ */
+ public void addModule(Module m) {
+ modules.add(m);
+ }
+
+ protected List<Module> getModules() {
+ @SuppressWarnings("unchecked")
+ final List<Module> clone = (List<Module>) modules.clone();
+ return clone;
+ }
+
+ public static final class Module {
+ private String name;
+ public void setName(String s) {
+ name = s;
+ }
+ public String getName() {
+ return name;
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AbstractJarSignerTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AbstractJarSignerTask.java
new file mode 100644
index 00000000..bc8c0319
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AbstractJarSignerTask.java
@@ -0,0 +1,423 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.filters.LineContainsRegExp;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.RedirectorElement;
+import org.apache.tools.ant.types.RegularExpression;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * This is factored out from {@link SignJar}; a base class that can be used
+ * for both signing and verifying JAR files using jarsigner
+ */
+
+public abstract class AbstractJarSignerTask extends Task {
+ // CheckStyle:VisibilityModifier OFF - bc
+ /**
+ * The name of the jar file.
+ */
+ protected File jar;
+ /**
+ * The alias of signer.
+ */
+ protected String alias;
+ /**
+ * The url or path of keystore file.
+ */
+ protected String keystore;
+ /**
+ * password for the store
+ */
+ protected String storepass;
+ /**
+ * type of store,-storetype param
+ */
+ protected String storetype;
+ /**
+ * password for the key in the store
+ */
+ protected String keypass;
+ /**
+ * verbose output
+ */
+ protected boolean verbose;
+ /**
+ * strict checking
+ * @since Ant 1.9.1
+ */
+ protected boolean strict = false;
+ /**
+ * The maximum amount of memory to use for Jar signer
+ */
+ protected String maxMemory;
+ /**
+ * the filesets of the jars to sign
+ */
+ protected Vector<FileSet> filesets = new Vector<FileSet>();
+ /**
+ * name of JDK program we are looking for
+ */
+ protected static final String JARSIGNER_COMMAND = "jarsigner";
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * redirector used to talk to the jarsigner program
+ */
+ private RedirectorElement redirector;
+
+ /**
+ * Java declarations -J-Dname=value
+ */
+ private Environment sysProperties = new Environment();
+
+ /**
+ * error string for unit test verification: {@value}
+ */
+ public static final String ERROR_NO_SOURCE = "jar must be set through jar attribute "
+ + "or nested filesets";
+
+ /**
+ * Path holding all non-filesets of filesystem resources we want to sign.
+ *
+ * @since Ant 1.7
+ */
+ private Path path = null;
+
+ /**
+ * The executable to use instead of jarsigner.
+ *
+ * @since Ant 1.8.0
+ */
+ private String executable;
+
+ /**
+ * Set the maximum memory to be used by the jarsigner process
+ *
+ * @param max a string indicating the maximum memory according to the JVM
+ * conventions (e.g. 128m is 128 Megabytes)
+ */
+ public void setMaxmemory(String max) {
+ maxMemory = max;
+ }
+
+ /**
+ * the jar file to sign; required
+ *
+ * @param jar the jar file to sign
+ */
+ public void setJar(final File jar) {
+ this.jar = jar;
+ }
+
+ /**
+ * the alias to sign under; required
+ *
+ * @param alias the alias to sign under
+ */
+ public void setAlias(final String alias) {
+ this.alias = alias;
+ }
+
+ /**
+ * keystore location; required
+ *
+ * @param keystore the keystore location
+ */
+ public void setKeystore(final String keystore) {
+ this.keystore = keystore;
+ }
+
+ /**
+ * password for keystore integrity; required
+ *
+ * @param storepass the password for the keystore
+ */
+ public void setStorepass(final String storepass) {
+ this.storepass = storepass;
+ }
+
+ /**
+ * keystore type; optional
+ *
+ * @param storetype the keystore type
+ */
+ public void setStoretype(final String storetype) {
+ this.storetype = storetype;
+ }
+
+ /**
+ * password for private key (if different); optional
+ *
+ * @param keypass the password for the key (if different)
+ */
+ public void setKeypass(final String keypass) {
+ this.keypass = keypass;
+ }
+
+ /**
+ * Enable verbose output when signing ; optional: default false
+ *
+ * @param verbose if true enable verbose output
+ */
+ public void setVerbose(final boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ /**
+ * do strict checking
+ * @since Ant 1.9.1
+ * @param strict
+ */
+ public void setStrict(boolean strict) {
+ this.strict = strict;
+ }
+
+ /**
+ * Adds a set of files to sign
+ *
+ * @param set a set of files to sign
+ * @since Ant 1.4
+ */
+ public void addFileset(final FileSet set) {
+ filesets.addElement(set);
+ }
+
+ /**
+ * Add a system property.
+ *
+ * @param sysp system property.
+ */
+ public void addSysproperty(Environment.Variable sysp) {
+ sysProperties.addVariable(sysp);
+ }
+
+ /**
+ * Adds a path of files to sign.
+ *
+ * @return a path of files to sign.
+ * @since Ant 1.7
+ */
+ public Path createPath() {
+ if (path == null) {
+ path = new Path(getProject());
+ }
+ return path.createPath();
+ }
+
+ /**
+ * init processing logic; this is retained through our execution(s)
+ */
+ protected void beginExecution() {
+
+ redirector = createRedirector();
+ }
+
+ /**
+ * any cleanup logic
+ */
+ protected void endExecution() {
+ redirector = null;
+ }
+
+ /**
+ * Create the redirector to use, if any.
+ *
+ * @return a configured RedirectorElement.
+ */
+ private RedirectorElement createRedirector() {
+ RedirectorElement result = new RedirectorElement();
+ if (storepass != null) {
+ StringBuffer input = new StringBuffer(storepass).append('\n');
+ if (keypass != null) {
+ input.append(keypass).append('\n');
+ }
+ result.setInputString(input.toString());
+ result.setLogInputString(false);
+ // Try to avoid showing password prompts on log output, as they would be confusing.
+ LineContainsRegExp filter = new LineContainsRegExp();
+ RegularExpression rx = new RegularExpression();
+ // TODO only handles English locale, not ja or zh_CN
+ rx.setPattern("^(Enter Passphrase for keystore: |Enter key password for .+: )$");
+ filter.addConfiguredRegexp(rx);
+ filter.setNegate(true);
+ result.createErrorFilterChain().addLineContainsRegExp(filter);
+ }
+ return result;
+ }
+
+ /**
+ * get the redirector. Non-null between invocations of
+ * {@link #beginExecution()} and {@link #endExecution()}
+ * @return a redirector or null
+ */
+ public RedirectorElement getRedirector() {
+ return redirector;
+ }
+
+ /**
+ * Sets the actual executable command to invoke, instead of the binary
+ * <code>jarsigner</code> found in Ant's JDK.
+ * @param executable the command to invoke.
+ * @since Ant 1.8.0
+ */
+ public void setExecutable(String executable) {
+ this.executable = executable;
+ }
+
+ /**
+ * these are options common to signing and verifying
+ * @param cmd command to configure
+ */
+ protected void setCommonOptions(final ExecTask cmd) {
+ if (maxMemory != null) {
+ addValue(cmd, "-J-Xmx" + maxMemory);
+ }
+
+ if (verbose) {
+ addValue(cmd, "-verbose");
+ }
+
+ if (strict) {
+ addValue(cmd, "-strict");
+ }
+
+ //now patch in all system properties
+ for (Environment.Variable variable : sysProperties.getVariablesVector()) {
+ declareSysProperty(cmd, variable);
+ }
+ }
+
+ /**
+ *
+ * @param cmd command to configure
+ * @param property property to set
+ * @throws BuildException if the property is not correctly defined.
+ */
+ protected void declareSysProperty(
+ ExecTask cmd, Environment.Variable property) throws BuildException {
+ addValue(cmd, "-J-D" + property.getContent());
+ }
+
+
+ /**
+ * bind to a keystore if the attributes are there
+ * @param cmd command to configure
+ */
+ protected void bindToKeystore(final ExecTask cmd) {
+ if (null != keystore) {
+ // is the keystore a file
+ addValue(cmd, "-keystore");
+ String loc;
+ File keystoreFile = getProject().resolveFile(keystore);
+ if (keystoreFile.exists()) {
+ loc = keystoreFile.getPath();
+ } else {
+ // must be a URL - just pass as is
+ loc = keystore;
+ }
+ addValue(cmd, loc);
+ }
+ if (null != storetype) {
+ addValue(cmd, "-storetype");
+ addValue(cmd, storetype);
+ }
+ }
+
+ /**
+ * create the jarsigner executable task
+ * @return a task set up with the executable of jarsigner, failonerror=true
+ * and bound to our redirector
+ */
+ protected ExecTask createJarSigner() {
+ final ExecTask cmd = new ExecTask(this);
+ if (executable == null) {
+ cmd.setExecutable(JavaEnvUtils.getJdkExecutable(JARSIGNER_COMMAND));
+ } else {
+ cmd.setExecutable(executable);
+ }
+ cmd.setTaskType(JARSIGNER_COMMAND);
+ cmd.setFailonerror(true);
+ cmd.addConfiguredRedirector(redirector);
+ return cmd;
+ }
+
+ /**
+ * clone our filesets vector, and patch in the jar attribute as a new
+ * fileset, if is defined
+ * @return a vector of FileSet instances
+ */
+ protected Vector<FileSet> createUnifiedSources() {
+ @SuppressWarnings("unchecked")
+ Vector<FileSet> sources = (Vector<FileSet>) filesets.clone();
+ if (jar != null) {
+ //we create a fileset with the source file.
+ //this lets us combine our logic for handling output directories,
+ //mapping etc.
+ FileSet sourceJar = new FileSet();
+ sourceJar.setProject(getProject());
+ sourceJar.setFile(jar);
+ sourceJar.setDir(jar.getParentFile());
+ sources.add(sourceJar);
+ }
+ return sources;
+ }
+
+ /**
+ * clone our path and add all explicitly specified FileSets as
+ * well, patch in the jar attribute as a new fileset if it is
+ * defined.
+ * @return a path that contains all files to sign
+ * @since Ant 1.7
+ */
+ protected Path createUnifiedSourcePath() {
+ Path p = path == null ? new Path(getProject()) : (Path) path.clone();
+ for (FileSet fileSet : createUnifiedSources()) {
+ p.add(fileSet);
+ }
+ return p;
+ }
+
+ /**
+ * Has either a path or a fileset been specified?
+ * @return true if a path or fileset has been specified.
+ * @since Ant 1.7
+ */
+ protected boolean hasResources() {
+ return path != null || filesets.size() > 0;
+ }
+
+ /**
+ * add a value argument to a command
+ * @param cmd command to manipulate
+ * @param value value to add
+ */
+ protected void addValue(final ExecTask cmd, String value) {
+ cmd.createArg().setValue(value);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Ant.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Ant.java
new file mode 100644
index 00000000..eba4731a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Ant.java
@@ -0,0 +1,836 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Main;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.PropertySet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.VectorSet;
+
+/**
+ * Build a sub-project.
+ *
+ * <pre>
+ * &lt;target name=&quot;foo&quot; depends=&quot;init&quot;&gt;
+ * &lt;ant antfile=&quot;build.xml&quot; target=&quot;bar&quot; &gt;
+ * &lt;property name=&quot;property1&quot; value=&quot;aaaaa&quot; /&gt;
+ * &lt;property name=&quot;foo&quot; value=&quot;baz&quot; /&gt;
+ * &lt;/ant&gt;
+ * &lt;/target&gt;
+ *
+ * &lt;target name=&quot;bar&quot; depends=&quot;init&quot;&gt;
+ * &lt;echo message=&quot;prop is ${property1} ${foo}&quot; /&gt;
+ * &lt;/target&gt;
+ * </pre>
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="control"
+ */
+public class Ant extends Task {
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** the basedir where is executed the build file */
+ private File dir = null;
+
+ /**
+ * the build.xml file (can be absolute) in this case dir will be
+ * ignored
+ */
+ private String antFile = null;
+
+ /** the output */
+ private String output = null;
+
+ /** should we inherit properties from the parent ? */
+ private boolean inheritAll = true;
+
+ /** should we inherit references from the parent ? */
+ private boolean inheritRefs = false;
+
+ /** the properties to pass to the new project */
+ private Vector<Property> properties = new Vector<Property>();
+
+ /** the references to pass to the new project */
+ private Vector<Reference> references = new Vector<Reference>();
+
+ /** the temporary project created to run the build file */
+ private Project newProject;
+
+ /** The stream to which output is to be written. */
+ private PrintStream out = null;
+
+ /** the sets of properties to pass to the new project */
+ private Vector<PropertySet> propertySets = new Vector<PropertySet>();
+
+ /** the targets to call on the new project */
+ private Vector<String> targets = new Vector<String>();
+
+ /** whether the target attribute was specified **/
+ private boolean targetAttributeSet = false;
+
+ /**
+ * Whether the basedir of the new project should be the same one
+ * as it would be when running the build file directly -
+ * independent of dir and/or inheritAll settings.
+ *
+ * @since Ant 1.8.0
+ */
+ private boolean useNativeBasedir = false;
+
+ /**
+ * simple constructor
+ */
+ public Ant() {
+ //default
+ }
+
+ /**
+ * create a task bound to its creator
+ * @param owner owning task
+ */
+ public Ant(Task owner) {
+ bindToOwner(owner);
+ }
+
+ /**
+ * Whether the basedir of the new project should be the same one
+ * as it would be when running the build file directly -
+ * independent of dir and/or inheritAll settings.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setUseNativeBasedir(boolean b) {
+ useNativeBasedir = b;
+ }
+
+ /**
+ * If true, pass all properties to the new Ant project.
+ * Defaults to true.
+ * @param value if true pass all properties to the new Ant project.
+ */
+ public void setInheritAll(boolean value) {
+ inheritAll = value;
+ }
+
+ /**
+ * If true, pass all references to the new Ant project.
+ * Defaults to false.
+ * @param value if true, pass all references to the new Ant project
+ */
+ public void setInheritRefs(boolean value) {
+ inheritRefs = value;
+ }
+
+ /**
+ * Creates a Project instance for the project to call.
+ */
+ public void init() {
+ newProject = getProject().createSubProject();
+ newProject.setJavaVersionProperty();
+ }
+
+ /**
+ * Called in execute or createProperty (via getNewProject())
+ * if newProject is null.
+ *
+ * <p>This can happen if the same instance of this task is run
+ * twice as newProject is set to null at the end of execute (to
+ * save memory and help the GC).</p>
+ * <p>calls init() again</p>
+ *
+ */
+ private void reinit() {
+ init();
+ }
+
+ /**
+ * Attaches the build listeners of the current project to the new
+ * project, configures a possible logfile, transfers task and
+ * data-type definitions, transfers properties (either all or just
+ * the ones specified as user properties to the current project,
+ * depending on inheritall), transfers the input handler.
+ */
+ private void initializeProject() {
+ newProject.setInputHandler(getProject().getInputHandler());
+
+ Iterator<BuildListener> iter = getBuildListeners();
+ while (iter.hasNext()) {
+ newProject.addBuildListener((BuildListener) iter.next());
+ }
+
+ if (output != null) {
+ File outfile = null;
+ if (dir != null) {
+ outfile = FILE_UTILS.resolveFile(dir, output);
+ } else {
+ outfile = getProject().resolveFile(output);
+ }
+ try {
+ out = new PrintStream(new FileOutputStream(outfile));
+ DefaultLogger logger = new DefaultLogger();
+ logger.setMessageOutputLevel(Project.MSG_INFO);
+ logger.setOutputPrintStream(out);
+ logger.setErrorPrintStream(out);
+ newProject.addBuildListener(logger);
+ } catch (IOException ex) {
+ log("Ant: Can't set output to " + output);
+ }
+ }
+ // set user-defined properties
+ if (useNativeBasedir) {
+ addAlmostAll(getProject().getUserProperties(), PropertyType.USER);
+ } else {
+ getProject().copyUserProperties(newProject);
+ }
+
+ if (!inheritAll) {
+ // set Ant's built-in properties separately,
+ // because they are not being inherited.
+ newProject.initProperties();
+
+ } else {
+ // set all properties from calling project
+ addAlmostAll(getProject().getProperties(), PropertyType.PLAIN);
+ }
+
+ for (PropertySet ps : propertySets) {
+ addAlmostAll(ps.getProperties(), PropertyType.PLAIN);
+ }
+ }
+
+ /**
+ * Handles output.
+ * Send it the the new project if is present, otherwise
+ * call the super class.
+ * @param outputToHandle The string output to output.
+ * @see Task#handleOutput(String)
+ * @since Ant 1.5
+ */
+ public void handleOutput(String outputToHandle) {
+ if (newProject != null) {
+ newProject.demuxOutput(outputToHandle, false);
+ } else {
+ super.handleOutput(outputToHandle);
+ }
+ }
+
+ /**
+ * Handles input.
+ * Delegate to the created project, if present, otherwise
+ * call the super class.
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception IOException if the data cannot be read.
+ * @see Task#handleInput(byte[], int, int)
+ * @since Ant 1.6
+ */
+ public int handleInput(byte[] buffer, int offset, int length)
+ throws IOException {
+ if (newProject != null) {
+ return newProject.demuxInput(buffer, offset, length);
+ }
+ return super.handleInput(buffer, offset, length);
+ }
+
+ /**
+ * Handles output.
+ * Send it the the new project if is present, otherwise
+ * call the super class.
+ * @param toFlush The string to output.
+ * @see Task#handleFlush(String)
+ * @since Ant 1.5.2
+ */
+ public void handleFlush(String toFlush) {
+ if (newProject != null) {
+ newProject.demuxFlush(toFlush, false);
+ } else {
+ super.handleFlush(toFlush);
+ }
+ }
+
+ /**
+ * Handle error output.
+ * Send it the the new project if is present, otherwise
+ * call the super class.
+ * @param errorOutputToHandle The string to output.
+ *
+ * @see Task#handleErrorOutput(String)
+ * @since Ant 1.5
+ */
+ public void handleErrorOutput(String errorOutputToHandle) {
+ if (newProject != null) {
+ newProject.demuxOutput(errorOutputToHandle, true);
+ } else {
+ super.handleErrorOutput(errorOutputToHandle);
+ }
+ }
+
+ /**
+ * Handle error output.
+ * Send it the the new project if is present, otherwise
+ * call the super class.
+ * @param errorOutputToFlush The string to output.
+ * @see Task#handleErrorFlush(String)
+ * @since Ant 1.5.2
+ */
+ public void handleErrorFlush(String errorOutputToFlush) {
+ if (newProject != null) {
+ newProject.demuxFlush(errorOutputToFlush, true);
+ } else {
+ super.handleErrorFlush(errorOutputToFlush);
+ }
+ }
+
+ /**
+ * Do the execution.
+ * @throws BuildException if a target tries to call itself;
+ * probably also if a BuildException is thrown by the new project.
+ */
+ public void execute() throws BuildException {
+ File savedDir = dir;
+ String savedAntFile = antFile;
+ Vector<String> locals = new VectorSet<String>(targets);
+ try {
+ getNewProject();
+
+ if (dir == null && inheritAll) {
+ dir = getProject().getBaseDir();
+ }
+
+ initializeProject();
+
+ if (dir != null) {
+ if (!useNativeBasedir) {
+ newProject.setBaseDir(dir);
+ if (savedDir != null) {
+ // has been set explicitly
+ newProject.setInheritedProperty(MagicNames.PROJECT_BASEDIR,
+ dir.getAbsolutePath());
+ }
+ }
+ } else {
+ dir = getProject().getBaseDir();
+ }
+
+ overrideProperties();
+
+ if (antFile == null) {
+ antFile = getDefaultBuildFile();
+ }
+
+ File file = FILE_UTILS.resolveFile(dir, antFile);
+ antFile = file.getAbsolutePath();
+
+ log("calling target(s) "
+ + ((locals.size() > 0) ? locals.toString() : "[default]")
+ + " in build file " + antFile, Project.MSG_VERBOSE);
+ newProject.setUserProperty(MagicNames.ANT_FILE , antFile);
+
+ String thisAntFile = getProject().getProperty(MagicNames.ANT_FILE);
+ // Are we trying to call the target in which we are defined (or
+ // the build file if this is a top level task)?
+ if (thisAntFile != null
+ && file.equals(getProject().resolveFile(thisAntFile))
+ && getOwningTarget() != null) {
+
+ if (getOwningTarget().getName().equals("")) {
+ if (getTaskName().equals("antcall")) {
+ throw new BuildException("antcall must not be used at"
+ + " the top level.");
+ }
+ throw new BuildException(getTaskName() + " task at the"
+ + " top level must not invoke"
+ + " its own build file.");
+ }
+ }
+
+ try {
+ ProjectHelper.configureProject(newProject, file);
+ } catch (BuildException ex) {
+ throw ProjectHelper.addLocationToBuildException(
+ ex, getLocation());
+ }
+
+ if (locals.size() == 0) {
+ String defaultTarget = newProject.getDefaultTarget();
+ if (defaultTarget != null) {
+ locals.add(defaultTarget);
+ }
+ }
+
+ if (newProject.getProperty(MagicNames.ANT_FILE)
+ .equals(getProject().getProperty(MagicNames.ANT_FILE))
+ && getOwningTarget() != null) {
+
+ String owningTargetName = getOwningTarget().getName();
+
+ if (locals.contains(owningTargetName)) {
+ throw new BuildException(getTaskName() + " task calling "
+ + "its own parent target.");
+ }
+ boolean circular = false;
+ for (Iterator<String> it = locals.iterator();
+ !circular && it.hasNext();) {
+ Target other =
+ getProject().getTargets().get(it.next());
+ circular |= (other != null
+ && other.dependsOn(owningTargetName));
+ }
+ if (circular) {
+ throw new BuildException(getTaskName()
+ + " task calling a target"
+ + " that depends on"
+ + " its parent target \'"
+ + owningTargetName
+ + "\'.");
+ }
+ }
+
+ addReferences();
+
+ if (locals.size() > 0 && !(locals.size() == 1
+ && "".equals(locals.get(0)))) {
+ BuildException be = null;
+ try {
+ log("Entering " + antFile + "...", Project.MSG_VERBOSE);
+ newProject.fireSubBuildStarted();
+ newProject.executeTargets(locals);
+ } catch (BuildException ex) {
+ be = ProjectHelper
+ .addLocationToBuildException(ex, getLocation());
+ throw be;
+ } finally {
+ log("Exiting " + antFile + ".", Project.MSG_VERBOSE);
+ newProject.fireSubBuildFinished(be);
+ }
+ }
+ } finally {
+ // help the gc
+ newProject = null;
+ for (Property p : properties) {
+ p.setProject(null);
+ }
+
+ if (output != null && out != null) {
+ try {
+ out.close();
+ } catch (final Exception ex) {
+ //ignore
+ }
+ }
+ dir = savedDir;
+ antFile = savedAntFile;
+ }
+ }
+
+ /**
+ * Get the default build file name to use when launching the task.
+ * <p>
+ * This function may be overrided by providers of custom ProjectHelper so they can implement easily their sub
+ * launcher.
+ *
+ * @return the name of the default file
+ * @since Ant 1.8.0
+ */
+ protected String getDefaultBuildFile() {
+ return Main.DEFAULT_BUILD_FILENAME;
+ }
+
+ /**
+ * Override the properties in the new project with the one
+ * explicitly defined as nested elements here.
+ * @throws BuildException under unknown circumstances.
+ */
+ private void overrideProperties() throws BuildException {
+ // remove duplicate properties - last property wins
+ // Needed for backward compatibility
+ Set<String> set = new HashSet<String>();
+ for (int i = properties.size() - 1; i >= 0; --i) {
+ Property p = properties.get(i);
+ if (p.getName() != null && !p.getName().equals("")) {
+ if (set.contains(p.getName())) {
+ properties.remove(i);
+ } else {
+ set.add(p.getName());
+ }
+ }
+ }
+ Enumeration<Property> e = properties.elements();
+ while (e.hasMoreElements()) {
+ Property p = e.nextElement();
+ p.setProject(newProject);
+ p.execute();
+ }
+ if (useNativeBasedir) {
+ addAlmostAll(getProject().getInheritedProperties(),
+ PropertyType.INHERITED);
+ } else {
+ getProject().copyInheritedProperties(newProject);
+ }
+ }
+
+ /**
+ * Add the references explicitly defined as nested elements to the
+ * new project. Also copy over all references that don't override
+ * existing references in the new project if inheritrefs has been
+ * requested.
+ * @throws BuildException if a reference does not have a refid.
+ */
+ private void addReferences() throws BuildException {
+ @SuppressWarnings("unchecked")
+ Hashtable<String, Object> thisReferences
+ = (Hashtable<String, Object>) getProject().getReferences().clone();
+ for (Reference ref : references) {
+ String refid = ref.getRefId();
+ if (refid == null) {
+ throw new BuildException("the refid attribute is required"
+ + " for reference elements");
+ }
+ if (!thisReferences.containsKey(refid)) {
+ log("Parent project doesn't contain any reference '"
+ + refid + "'",
+ Project.MSG_WARN);
+ continue;
+ }
+
+ thisReferences.remove(refid);
+ String toRefid = ref.getToRefid();
+ if (toRefid == null) {
+ toRefid = refid;
+ }
+ copyReference(refid, toRefid);
+ }
+
+ // Now add all references that are not defined in the
+ // subproject, if inheritRefs is true
+ if (inheritRefs) {
+ Hashtable<String, Object> newReferences = newProject.getReferences();
+ for (String key : thisReferences.keySet()) {
+ if (newReferences.containsKey(key)) {
+ continue;
+ }
+ copyReference(key, key);
+ newProject.inheritIDReferences(getProject());
+ }
+ }
+ }
+
+ /**
+ * Try to clone and reconfigure the object referenced by oldkey in
+ * the parent project and add it to the new project with the key newkey.
+ *
+ * <p>If we cannot clone it, copy the referenced object itself and
+ * keep our fingers crossed.</p>
+ * @param oldKey the reference id in the current project.
+ * @param newKey the reference id in the new project.
+ */
+ private void copyReference(String oldKey, String newKey) {
+ Object orig = getProject().getReference(oldKey);
+ if (orig == null) {
+ log("No object referenced by " + oldKey + ". Can't copy to "
+ + newKey,
+ Project.MSG_WARN);
+ return;
+ }
+
+ Class<?> c = orig.getClass();
+ Object copy = orig;
+ try {
+ Method cloneM = c.getMethod("clone", new Class[0]);
+ if (cloneM != null) {
+ copy = cloneM.invoke(orig, new Object[0]);
+ log("Adding clone of reference " + oldKey, Project.MSG_DEBUG);
+ }
+ } catch (Exception e) {
+ // not Clonable
+ }
+
+
+ if (copy instanceof ProjectComponent) {
+ ((ProjectComponent) copy).setProject(newProject);
+ } else {
+ try {
+ Method setProjectM =
+ c.getMethod("setProject", new Class[] {Project.class});
+ if (setProjectM != null) {
+ setProjectM.invoke(copy, new Object[] {newProject});
+ }
+ } catch (NoSuchMethodException e) {
+ // ignore this if the class being referenced does not have
+ // a set project method.
+ } catch (Exception e2) {
+ String msg = "Error setting new project instance for "
+ + "reference with id " + oldKey;
+ throw new BuildException(msg, e2, getLocation());
+ }
+ }
+ newProject.addReference(newKey, copy);
+ }
+
+ /**
+ * Copies all properties from the given table to the new project -
+ * omitting those that have already been set in the new project as
+ * well as properties named basedir or ant.file.
+ * @param props properties <code>Hashtable</code> to copy to the
+ * new project.
+ * @param the type of property to set (a plain Ant property, a
+ * user property or an inherited property).
+ * @since Ant 1.8.0
+ */
+ private void addAlmostAll(Hashtable<?, ?> props, PropertyType type) {
+ Enumeration<?> e = props.keys();
+ while (e.hasMoreElements()) {
+ String key = e.nextElement().toString();
+ if (MagicNames.PROJECT_BASEDIR.equals(key)
+ || MagicNames.ANT_FILE.equals(key)) {
+ // basedir and ant.file get special treatment in execute()
+ continue;
+ }
+
+ String value = props.get(key).toString();
+ if (type == PropertyType.PLAIN) {
+ // don't re-set user properties, avoid the warning message
+ if (newProject.getProperty(key) == null) {
+ // no user property
+ newProject.setNewProperty(key, value);
+ }
+ } else if (type == PropertyType.USER) {
+ newProject.setUserProperty(key, value);
+ } else if (type == PropertyType.INHERITED) {
+ newProject.setInheritedProperty(key, value);
+ }
+ }
+ }
+
+ /**
+ * The directory to use as a base directory for the new Ant project.
+ * Defaults to the current project's basedir, unless inheritall
+ * has been set to false, in which case it doesn't have a default
+ * value. This will override the basedir setting of the called project.
+ * @param dir new directory as <code>File</code>.
+ */
+ public void setDir(File dir) {
+ this.dir = dir;
+ }
+
+ /**
+ * The build file to use. Defaults to "build.xml". This file is expected
+ * to be a filename relative to the dir attribute given.
+ * @param antFile the <code>String</code> build file name.
+ */
+ public void setAntfile(String antFile) {
+ // @note: it is a string and not a file to handle relative/absolute
+ // otherwise a relative file will be resolved based on the current
+ // basedir.
+ this.antFile = antFile;
+ }
+
+ /**
+ * The target of the new Ant project to execute.
+ * Defaults to the new project's default target.
+ * @param targetToAdd the name of the target to invoke.
+ */
+ public void setTarget(String targetToAdd) {
+ if (targetToAdd.equals("")) {
+ throw new BuildException("target attribute must not be empty");
+ }
+ targets.add(targetToAdd);
+ targetAttributeSet = true;
+ }
+
+ /**
+ * Set the filename to write the output to. This is relative to the value
+ * of the dir attribute if it has been set or to the base directory of the
+ * current project otherwise.
+ * @param outputFile the name of the file to which the output should go.
+ */
+ public void setOutput(String outputFile) {
+ this.output = outputFile;
+ }
+
+ /**
+ * Property to pass to the new project.
+ * The property is passed as a 'user property'.
+ * @return the created <code>Property</code> object.
+ */
+ public Property createProperty() {
+ Property p = new Property(true, getProject());
+ p.setProject(getNewProject());
+ p.setTaskName("property");
+ properties.addElement(p);
+ return p;
+ }
+
+ /**
+ * Add a Reference element identifying a data type to carry
+ * over to the new project.
+ * @param ref <code>Reference</code> to add.
+ */
+ public void addReference(Reference ref) {
+ references.addElement(ref);
+ }
+
+ /**
+ * Add a target to this Ant invocation.
+ * @param t the <code>TargetElement</code> to add.
+ * @since Ant 1.6.3
+ */
+ public void addConfiguredTarget(TargetElement t) {
+ if (targetAttributeSet) {
+ throw new BuildException(
+ "nested target is incompatible with the target attribute");
+ }
+ String name = t.getName();
+ if (name.equals("")) {
+ throw new BuildException("target name must not be empty");
+ }
+ targets.add(name);
+ }
+
+ /**
+ * Add a set of properties to pass to the new project.
+ *
+ * @param ps <code>PropertySet</code> to add.
+ * @since Ant 1.6
+ */
+ public void addPropertyset(PropertySet ps) {
+ propertySets.addElement(ps);
+ }
+
+ /**
+ * Get the (sub)-Project instance currently in use.
+ * @return Project
+ * @since Ant 1.7
+ */
+ protected Project getNewProject() {
+ if (newProject == null) {
+ reinit();
+ }
+ return newProject;
+ }
+
+ /**
+ * @since Ant 1.6.2
+ */
+ private Iterator<BuildListener> getBuildListeners() {
+ return getProject().getBuildListeners().iterator();
+ }
+
+ /**
+ * Helper class that implements the nested &lt;reference&gt;
+ * element of &lt;ant&gt; and &lt;antcall&gt;.
+ */
+ public static class Reference
+ extends org.apache.tools.ant.types.Reference {
+
+ /** Creates a reference to be configured by Ant. */
+ public Reference() {
+ super();
+ }
+
+ private String targetid = null;
+
+ /**
+ * Set the id that this reference to be stored under in the
+ * new project.
+ *
+ * @param targetid the id under which this reference will be passed to
+ * the new project. */
+ public void setToRefid(String targetid) {
+ this.targetid = targetid;
+ }
+
+ /**
+ * Get the id under which this reference will be stored in the new
+ * project.
+ *
+ * @return the id of the reference in the new project.
+ */
+ public String getToRefid() {
+ return targetid;
+ }
+ }
+
+ /**
+ * Helper class that implements the nested &lt;target&gt;
+ * element of &lt;ant&gt; and &lt;antcall&gt;.
+ * @since Ant 1.6.3
+ */
+ public static class TargetElement {
+ private String name;
+
+ /**
+ * Default constructor.
+ */
+ public TargetElement() {
+ //default
+ }
+
+ /**
+ * Set the name of this TargetElement.
+ * @param name the <code>String</code> target name.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the name of this TargetElement.
+ * @return <code>String</code>.
+ */
+ public String getName() {
+ return name;
+ }
+ }
+
+ private static final class PropertyType {
+ private PropertyType() {}
+ private static final PropertyType PLAIN = new PropertyType();
+ private static final PropertyType INHERITED = new PropertyType();
+ private static final PropertyType USER = new PropertyType();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AntStructure.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AntStructure.java
new file mode 100644
index 00000000..20f811a8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AntStructure.java
@@ -0,0 +1,485 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.IntrospectionHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Creates a partial DTD for Ant from the currently known tasks.
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="xml"
+ */
+public class AntStructure extends Task {
+
+ private static final String LINE_SEP
+ = System.getProperty("line.separator");
+
+ private File output;
+ private StructurePrinter printer = new DTDPrinter();
+
+ /**
+ * The output file.
+ * @param output the output file
+ */
+ public void setOutput(final File output) {
+ this.output = output;
+ }
+
+ /**
+ * The StructurePrinter to use.
+ * @param p the printer to use.
+ * @since Ant 1.7
+ */
+ public void add(final StructurePrinter p) {
+ printer = p;
+ }
+
+ /**
+ * Build the antstructure DTD.
+ *
+ * @exception BuildException if the DTD cannot be written.
+ */
+ @Override
+ public void execute() throws BuildException {
+
+ if (output == null) {
+ throw new BuildException("output attribute is required", getLocation());
+ }
+
+ PrintWriter out = null;
+ try {
+ try {
+ out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(output), "UTF8"));
+ } catch (final UnsupportedEncodingException ue) {
+ /*
+ * Plain impossible with UTF8, see
+ * http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html
+ *
+ * fallback to platform specific anyway.
+ */
+ out = new PrintWriter(new FileWriter(output));
+ }
+
+ printer.printHead(out, getProject(),
+ new Hashtable<String, Class<?>>(getProject().getTaskDefinitions()),
+ new Hashtable<String, Class<?>>(getProject().getDataTypeDefinitions()));
+
+ printer.printTargetDecl(out);
+
+ for (final String typeName : getProject().getCopyOfDataTypeDefinitions()
+ .keySet()) {
+ printer.printElementDecl(
+ out, getProject(), typeName,
+ getProject().getDataTypeDefinitions().get(typeName));
+ }
+
+ for (final String tName : getProject().getCopyOfTaskDefinitions().keySet()) {
+ printer.printElementDecl(out, getProject(), tName,
+ getProject().getTaskDefinitions().get(tName));
+ }
+
+ printer.printTail(out);
+
+ if (out.checkError()) {
+ throw new IOException("Encountered an error writing Ant"
+ + " structure");
+ }
+ } catch (final IOException ioe) {
+ throw new BuildException("Error writing "
+ + output.getAbsolutePath(), ioe, getLocation());
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ }
+ }
+
+ /**
+ * Writes the actual structure information.
+ *
+ * <p>{@link #printHead}, {@link #printTargetDecl} and {@link #printTail}
+ * are called exactly once, {@link #printElementDecl} once for
+ * each declared task and type.</p>
+ */
+ public interface StructurePrinter {
+ /**
+ * Prints the header of the generated output.
+ *
+ * @param out PrintWriter to write to.
+ * @param p Project instance for the current task
+ * @param tasks map (name to implementing class)
+ * @param types map (name to implementing class)
+ * data types.
+ */
+ void printHead(PrintWriter out, Project p, Hashtable<String, Class<?>> tasks,
+ Hashtable<String, Class<?>> types);
+
+ /**
+ * Prints the definition for the target element.
+ * @param out PrintWriter to write to.
+ */
+ void printTargetDecl(PrintWriter out);
+
+ /**
+ * Print the definition for a given element.
+ *
+ * @param out PrintWriter to write to.
+ * @param p Project instance for the current task
+ * @param name element name.
+ * @param element class of the defined element.
+ */
+ void printElementDecl(PrintWriter out, Project p, String name,
+ Class<?> element);
+
+ /**
+ * Prints the trailer.
+ * @param out PrintWriter to write to.
+ */
+ void printTail(PrintWriter out);
+ }
+
+ private static class DTDPrinter implements StructurePrinter {
+
+ private static final String BOOLEAN = "%boolean;";
+ private static final String TASKS = "%tasks;";
+ private static final String TYPES = "%types;";
+
+ private final Hashtable<String, String> visited = new Hashtable<String, String>();
+
+ public void printTail(final PrintWriter out) {
+ visited.clear();
+ }
+
+ public void printHead(final PrintWriter out, final Project p, final Hashtable<String, Class<?>> tasks,
+ final Hashtable<String, Class<?>> types) {
+ printHead(out, tasks.keys(), types.keys());
+ }
+
+
+ /**
+ * Prints the header of the generated output.
+ *
+ * <p>Basically this prints the XML declaration, defines some
+ * entities and the project element.</p>
+ */
+ private void printHead(final PrintWriter out, final Enumeration<String> tasks,
+ final Enumeration<String> types) {
+ out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
+ out.println("<!ENTITY % boolean \"(true|false|on|off|yes|no)\">");
+ out.print("<!ENTITY % tasks \"");
+ boolean first = true;
+ while (tasks.hasMoreElements()) {
+ final String tName = tasks.nextElement();
+ if (!first) {
+ out.print(" | ");
+ } else {
+ first = false;
+ }
+ out.print(tName);
+ }
+ out.println("\">");
+ out.print("<!ENTITY % types \"");
+ first = true;
+ while (types.hasMoreElements()) {
+ final String typeName = types.nextElement();
+ if (!first) {
+ out.print(" | ");
+ } else {
+ first = false;
+ }
+ out.print(typeName);
+ }
+ out.println("\">");
+
+ out.println("");
+
+ out.print("<!ELEMENT project (target | extension-point | ");
+ out.print(TASKS);
+ out.print(" | ");
+ out.print(TYPES);
+ out.println(")*>");
+ out.println("<!ATTLIST project");
+ out.println(" name CDATA #IMPLIED");
+ out.println(" default CDATA #IMPLIED");
+ out.println(" basedir CDATA #IMPLIED>");
+ out.println("");
+ }
+
+ /**
+ * Prints the definition for the target element.
+ */
+ public void printTargetDecl(final PrintWriter out) {
+ out.print("<!ELEMENT target (");
+ out.print(TASKS);
+ out.print(" | ");
+ out.print(TYPES);
+ out.println(")*>");
+ out.println("");
+ printTargetAttrs(out, "target");
+ out.println("<!ELEMENT extension-point EMPTY>");
+ out.println("");
+ printTargetAttrs(out, "extension-point");
+ }
+
+ /**
+ * Prints the definition for the target element.
+ */
+ private void printTargetAttrs(final PrintWriter out, final String tag) {
+ out.print("<!ATTLIST ");
+ out.println(tag);
+ out.println(" id ID #IMPLIED");
+ out.println(" name CDATA #REQUIRED");
+ out.println(" if CDATA #IMPLIED");
+ out.println(" unless CDATA #IMPLIED");
+ out.println(" depends CDATA #IMPLIED");
+ out.println(" extensionOf CDATA #IMPLIED");
+ out.println(" onMissingExtensionPoint CDATA #IMPLIED");
+ out.println(" description CDATA #IMPLIED>");
+ out.println("");
+ }
+
+ /**
+ * Print the definition for a given element.
+ */
+ public void printElementDecl(final PrintWriter out, final Project p,
+ final String name, final Class<?> element) {
+
+ if (visited.containsKey(name)) {
+ return;
+ }
+ visited.put(name, "");
+
+ IntrospectionHelper ih = null;
+ try {
+ ih = IntrospectionHelper.getHelper(p, element);
+ } catch (final Throwable t) {
+ /*
+ * TODO - failed to load the class properly.
+ *
+ * should we print a warning here?
+ */
+ return;
+ }
+
+ StringBuffer sb = new StringBuffer("<!ELEMENT ");
+ sb.append(name).append(" ");
+
+ if (org.apache.tools.ant.types.Reference.class.equals(element)) {
+ sb.append("EMPTY>").append(LINE_SEP);
+ sb.append("<!ATTLIST ").append(name);
+ sb.append(LINE_SEP).append(" id ID #IMPLIED");
+ sb.append(LINE_SEP).append(" refid IDREF #IMPLIED");
+ sb.append(">").append(LINE_SEP);
+ out.println(sb);
+ return;
+ }
+
+ final Vector<String> v = new Vector<String>();
+ if (ih.supportsCharacters()) {
+ v.addElement("#PCDATA");
+ }
+
+ if (TaskContainer.class.isAssignableFrom(element)) {
+ v.addElement(TASKS);
+ }
+
+ Enumeration<String> e = ih.getNestedElements();
+ while (e.hasMoreElements()) {
+ v.addElement(e.nextElement());
+ }
+
+ if (v.isEmpty()) {
+ sb.append("EMPTY");
+ } else {
+ sb.append("(");
+ final int count = v.size();
+ for (int i = 0; i < count; i++) {
+ if (i != 0) {
+ sb.append(" | ");
+ }
+ sb.append(v.elementAt(i));
+ }
+ sb.append(")");
+ if (count > 1 || !v.elementAt(0).equals("#PCDATA")) {
+ sb.append("*");
+ }
+ }
+ sb.append(">");
+ out.println(sb);
+
+ sb = new StringBuffer("<!ATTLIST ");
+ sb.append(name);
+ sb.append(LINE_SEP).append(" id ID #IMPLIED");
+
+ e = ih.getAttributes();
+ while (e.hasMoreElements()) {
+ final String attrName = e.nextElement();
+ if ("id".equals(attrName)) {
+ continue;
+ }
+
+ sb.append(LINE_SEP).append(" ")
+ .append(attrName).append(" ");
+ final Class<?> type = ih.getAttributeType(attrName);
+ if (type.equals(java.lang.Boolean.class)
+ || type.equals(java.lang.Boolean.TYPE)) {
+ sb.append(BOOLEAN).append(" ");
+ } else if (Reference.class.isAssignableFrom(type)) {
+ sb.append("IDREF ");
+ } else if (EnumeratedAttribute.class.isAssignableFrom(type)) {
+ try {
+ final EnumeratedAttribute ea =
+ (EnumeratedAttribute) type.newInstance();
+ final String[] values = ea.getValues();
+ if (values == null
+ || values.length == 0
+ || !areNmtokens(values)) {
+ sb.append("CDATA ");
+ } else {
+ sb.append("(");
+ for (int i = 0; i < values.length; i++) {
+ if (i != 0) {
+ sb.append(" | ");
+ }
+ sb.append(values[i]);
+ }
+ sb.append(") ");
+ }
+ } catch (final InstantiationException ie) {
+ sb.append("CDATA ");
+ } catch (final IllegalAccessException ie) {
+ sb.append("CDATA ");
+ }
+ } else if (type.getSuperclass() != null
+ && type.getSuperclass().getName().equals("java.lang.Enum")) {
+ try {
+ final Object[] values = (Object[]) type.getMethod("values", (Class[]) null)
+ .invoke(null, (Object[]) null);
+ if (values.length == 0) {
+ sb.append("CDATA ");
+ } else {
+ sb.append('(');
+ for (int i = 0; i < values.length; i++) {
+ if (i != 0) {
+ sb.append(" | ");
+ }
+ sb.append(type.getMethod("name", (Class[]) null)
+ .invoke(values[i], (Object[]) null));
+ }
+ sb.append(") ");
+ }
+ } catch (final Exception x) {
+ sb.append("CDATA ");
+ }
+ } else {
+ sb.append("CDATA ");
+ }
+ sb.append("#IMPLIED");
+ }
+ sb.append(">").append(LINE_SEP);
+ out.println(sb);
+
+ final int count = v.size();
+ for (int i = 0; i < count; i++) {
+ final String nestedName = v.elementAt(i);
+ if (!"#PCDATA".equals(nestedName)
+ && !TASKS.equals(nestedName)
+ && !TYPES.equals(nestedName)) {
+ printElementDecl(out, p, nestedName, ih.getElementType(nestedName));
+ }
+ }
+ }
+
+ /**
+ * Does this String match the XML-NMTOKEN production?
+ * @param s the string to test
+ * @return true if the string matches the XML-NMTOKEN
+ */
+ public static final boolean isNmtoken(final String s) {
+ final int length = s.length();
+ for (int i = 0; i < length; i++) {
+ final char c = s.charAt(i);
+ // TODO - we are committing CombiningChar and Extender here
+ if (!Character.isLetterOrDigit(c)
+ && c != '.' && c != '-' && c != '_' && c != ':') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Do the Strings all match the XML-NMTOKEN production?
+ *
+ * <p>Otherwise they are not suitable as an enumerated attribute,
+ * for example.</p>
+ * @param s the array of string to test
+ * @return true if all the strings in the array math XML-NMTOKEN
+ */
+ public static final boolean areNmtokens(final String[] s) {
+ for (int i = 0; i < s.length; i++) {
+ if (!isNmtoken(s[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Does this String match the XML-NMTOKEN production?
+ * @param s the string to test
+ * @return true if the string matches the XML-NMTOKEN
+ */
+ protected boolean isNmtoken(final String s) {
+ return DTDPrinter.isNmtoken(s);
+ }
+
+ /**
+ * Do the Strings all match the XML-NMTOKEN production?
+ *
+ * <p>Otherwise they are not suitable as an enumerated attribute,
+ * for example.</p>
+ * @param s the array of string to test
+ * @return true if all the strings in the array math XML-NMTOKEN
+ */
+ protected boolean areNmtokens(final String[] s) {
+ return DTDPrinter.areNmtokens(s);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Antlib.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Antlib.java
new file mode 100644
index 00000000..8ff836a3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Antlib.java
@@ -0,0 +1,184 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.ProjectHelperRepository;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.UnknownElement;
+import org.apache.tools.ant.types.resources.URLResource;
+
+
+/**
+ * Antlib task. It does not
+ * occur in an ant build file. It is the root element
+ * an antlib xml file.
+ *
+ * @since Ant 1.6
+ */
+public class Antlib extends Task implements TaskContainer {
+ //
+ // Static
+ //
+
+ /** The name of this task */
+ public static final String TAG = "antlib";
+
+ /**
+ * Static method to read an ant lib definition from
+ * a url.
+ *
+ * @param project the current project
+ * @param antlibUrl the url to read the definitions from
+ * @param uri the uri that the antlib is to be placed in
+ * @return the ant lib task
+ */
+ public static Antlib createAntlib(Project project, URL antlibUrl,
+ String uri) {
+ // Check if we can contact the URL
+ try {
+ URLConnection conn = antlibUrl.openConnection();
+ conn.setUseCaches(false);
+ conn.connect();
+ } catch (IOException ex) {
+ throw new BuildException(
+ "Unable to find " + antlibUrl, ex);
+ }
+ ComponentHelper helper =
+ ComponentHelper.getComponentHelper(project);
+ helper.enterAntLib(uri);
+ URLResource antlibResource = new URLResource(antlibUrl);
+ try {
+ // Should be safe to parse
+ ProjectHelper parser = null;
+ Object p =
+ project.getReference(ProjectHelper.PROJECTHELPER_REFERENCE);
+ if (p instanceof ProjectHelper) {
+ parser = (ProjectHelper) p;
+ if (!parser.canParseAntlibDescriptor(antlibResource)) {
+ parser = null;
+ }
+ }
+ if (parser == null) {
+ ProjectHelperRepository helperRepository =
+ ProjectHelperRepository.getInstance();
+ parser = helperRepository.getProjectHelperForAntlib(antlibResource);
+ }
+ UnknownElement ue =
+ parser.parseAntlibDescriptor(project, antlibResource);
+ // Check name is "antlib"
+ if (!(ue.getTag().equals(TAG))) {
+ throw new BuildException(
+ "Unexpected tag " + ue.getTag() + " expecting "
+ + TAG, ue.getLocation());
+ }
+ Antlib antlib = new Antlib();
+ antlib.setProject(project);
+ antlib.setLocation(ue.getLocation());
+ antlib.setTaskName("antlib");
+ antlib.init();
+ ue.configure(antlib);
+ return antlib;
+ } finally {
+ helper.exitAntLib();
+ }
+ }
+
+ //
+ // Instance
+ //
+ private ClassLoader classLoader;
+ private String uri = "";
+ private List<Object> tasks = new ArrayList<Object>();
+
+ /**
+ * Set the class loader for this antlib.
+ * This class loader is used for any tasks that
+ * derive from Definer.
+ *
+ * @param classLoader the class loader
+ */
+ protected void setClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * Set the URI for this antlib.
+ * @param uri the namespace uri
+ */
+ protected void setURI(String uri) {
+ this.uri = uri;
+ }
+
+ private ClassLoader getClassLoader() {
+ if (classLoader == null) {
+ classLoader = Antlib.class.getClassLoader();
+ }
+ return classLoader;
+ }
+
+ /**
+ * add a task to the list of tasks
+ *
+ * @param nestedTask Nested task to execute in antlib
+ */
+ public void addTask(Task nestedTask) {
+ tasks.add(nestedTask);
+ }
+
+ /**
+ * Execute the nested tasks, setting the classloader for
+ * any tasks that derive from Definer.
+ */
+ public void execute() {
+ //TODO handle tasks added via #addTask()
+ for (Iterator<Object> i = tasks.iterator(); i.hasNext();) {
+ UnknownElement ue = (UnknownElement) i.next();
+ setLocation(ue.getLocation());
+ ue.maybeConfigure();
+ Object configuredObject = ue.getRealThing();
+ if (configuredObject == null) {
+ continue;
+ }
+ if (!(configuredObject instanceof AntlibDefinition)) {
+ throw new BuildException(
+ "Invalid task in antlib " + ue.getTag()
+ + " " + configuredObject.getClass() + " does not "
+ + "extend org.apache.tools.ant.taskdefs.AntlibDefinition");
+ }
+ AntlibDefinition def = (AntlibDefinition) configuredObject;
+ def.setURI(uri);
+ def.setAntlibClassLoader(getClassLoader());
+ def.init();
+ def.execute();
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AntlibDefinition.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AntlibDefinition.java
new file mode 100644
index 00000000..eef33345
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AntlibDefinition.java
@@ -0,0 +1,81 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Task;
+
+/**
+ * Base class for tasks that that can be used in antlibs.
+ * For handling uri and class loading.
+ *
+ * @since Ant 1.6
+ */
+public class AntlibDefinition extends Task {
+
+ private String uri = "";
+ private ClassLoader antlibClassLoader;
+
+ /**
+ * The URI for this definition.
+ * If the URI is "antlib:org.apache.tools.ant",
+ * (this is the default uri)
+ * the uri will be set to "".
+ * URIs that start with "ant:" are reserved
+ * and are not allowed in this context.
+ * @param uri the namespace URI
+ * @throws BuildException if a reserved URI is used
+ */
+ public void setURI(String uri) throws BuildException {
+ if (uri.equals(ProjectHelper.ANT_CORE_URI)) {
+ uri = "";
+ }
+ if (uri.startsWith("ant:")) {
+ throw new BuildException("Attempt to use a reserved URI " + uri);
+ }
+ this.uri = uri;
+ }
+
+ /**
+ * The URI for this definition.
+ * @return The URI for this definition.
+ */
+ public String getURI() {
+ return uri;
+ }
+
+ /**
+ * Set the class loader of the loading object
+ *
+ * @param classLoader a <code>ClassLoader</code> value
+ */
+ public void setAntlibClassLoader(ClassLoader classLoader) {
+ this.antlibClassLoader = classLoader;
+ }
+
+ /**
+ * The current antlib classloader
+ * @return the antlib classloader for the definition, this
+ * is null if the definition is not used in an antlib.
+ */
+ public ClassLoader getAntlibClassLoader() {
+ return antlibClassLoader;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Apt.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Apt.java
new file mode 100644
index 00000000..52154a8c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Apt.java
@@ -0,0 +1,270 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.compilers.AptExternalCompilerAdapter;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Apt Task for running the Annotation processing tool for JDK 1.5. It derives
+ * from the existing Javac task, and forces the compiler based on whether we're
+ * executing internally, or externally.
+ *
+ * @since Ant 1.7
+ */
+
+
+public class Apt
+ extends Javac {
+ private boolean compile = true;
+ private String factory;
+ private Path factoryPath;
+ private Vector<Option> options = new Vector<Option>();
+ private File preprocessDir;
+ /** The name of the apt tool. */
+ public static final String EXECUTABLE_NAME = "apt";
+ /** An warning message when ignoring compiler attribute. */
+ public static final String ERROR_IGNORING_COMPILER_OPTION
+ = "Ignoring compiler attribute for the APT task, as it is fixed";
+ /** A warning message if used with java &lt; 1.5. */
+ public static final String ERROR_WRONG_JAVA_VERSION
+ = "Apt task requires Java 1.5+";
+
+ /**
+ * exposed for debug messages
+ */
+ public static final String WARNING_IGNORING_FORK =
+ "Apt only runs in its own JVM; fork=false option ignored";
+
+ /**
+ * The nested option element.
+ */
+ public static final class Option {
+ private String name;
+ private String value;
+
+ /** Constructor for Option */
+ public Option() {
+ //default
+ }
+
+ /**
+ * Get the name attribute.
+ * @return the name attribute.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name attribute.
+ * @param name the name of the option.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the value attribute.
+ * @return the value attribute.
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * Set the value attribute.
+ * @param value the value of the option.
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+ }
+
+ /**
+ * Constructor for Apt task.
+ * This sets the apt compiler adapter as the compiler in the super class.
+ */
+ public Apt() {
+ super();
+ super.setCompiler(AptExternalCompilerAdapter.class.getName());
+ super.setFork(true);
+ }
+
+ /**
+ * Get the name of the apt executable.
+ *
+ * @return the name of the executable.
+ */
+ public String getAptExecutable() {
+ String exe = getExecutable();
+ return exe != null ? exe :
+ JavaEnvUtils.getJdkExecutable(EXECUTABLE_NAME);
+ }
+
+ /**
+ * Set the compiler.
+ * This is not allowed and a warning log message is made.
+ * @param compiler not used.
+ */
+ public void setCompiler(String compiler) {
+ log(ERROR_IGNORING_COMPILER_OPTION, Project.MSG_WARN);
+ }
+
+ /**
+ * Set the fork attribute.
+ * Non-forking APT is highly classpath dependent and appears to be too
+ * brittle to work. The sole reason this attribute is retained
+ * is the superclass does it
+ * @param fork if false; warn the option is ignored.
+ */
+ public void setFork(boolean fork) {
+ if (!fork) {
+ log(WARNING_IGNORING_FORK, Project.MSG_WARN);
+ }
+ }
+
+ /**
+ * Get the compiler class name.
+ * @return the compiler class name.
+ */
+ public String getCompiler() {
+ return super.getCompiler();
+ }
+
+ /**
+ * Get the compile option for the apt compiler.
+ * If this is false the "-nocompile" argument will be used.
+ * @return the value of the compile option.
+ */
+ public boolean isCompile() {
+ return compile;
+ }
+
+ /**
+ * Set the compile option for the apt compiler.
+ * Default value is true.
+ * @param compile if true set the compile option.
+ */
+ public void setCompile(boolean compile) {
+ this.compile = compile;
+ }
+
+ /**
+ * Get the factory option for the apt compiler.
+ * If this is non-null the "-factory" argument will be used.
+ * @return the value of the factory option.
+ */
+ public String getFactory() {
+ return factory;
+ }
+
+ /**
+ * Set the factory option for the apt compiler.
+ * Default value is null.
+ * @param factory the classname of the factory.
+ */
+ public void setFactory(String factory) {
+ this.factory = factory;
+ }
+
+ /**
+ * Add a reference to a path to the factoryPath attribute.
+ * @param ref a reference to a path.
+ */
+ public void setFactoryPathRef(Reference ref) {
+ createFactoryPath().setRefid(ref);
+ }
+
+ /**
+ * Add a path to the factoryPath attribute.
+ * @return a path to be configured.
+ */
+ public Path createFactoryPath() {
+ if (factoryPath == null) {
+ factoryPath = new Path(getProject());
+ }
+ return factoryPath.createPath();
+ }
+
+ /**
+ * Get the factory path attribute.
+ * If this is not null, the "-factorypath" argument will be used.
+ * The default value is null.
+ * @return the factory path attribute.
+ */
+ public Path getFactoryPath() {
+ return factoryPath;
+ }
+
+ /**
+ * Create a nested option.
+ * @return an option to be configured.
+ */
+ public Option createOption() {
+ Option opt = new Option();
+ options.add(opt);
+ return opt;
+ }
+
+ /**
+ * Get the options to the compiler.
+ * Each option will use '"-E" name ["=" value]' argument.
+ * @return the options.
+ */
+ public Vector<Option> getOptions() {
+ return options;
+ }
+
+ /**
+ * Get the preprocessdir attribute.
+ * This corresponds to the "-s" argument.
+ * The default value is null.
+ * @return the preprocessdir attribute.
+ */
+ public File getPreprocessDir() {
+ return preprocessDir;
+ }
+
+ /**
+ * Set the preprocessdir attribute.
+ * @param preprocessDir where to place processor generated source files.
+ */
+ public void setPreprocessDir(File preprocessDir) {
+ this.preprocessDir = preprocessDir;
+ }
+
+ /**
+ * Do the compilation.
+ * @throws BuildException on error.
+ */
+ public void execute()
+ throws BuildException {
+ if (JavaEnvUtils.getJavaVersionNumber() >= 18) {
+ throw new BuildException("apt does not exist under Java 1.8 and higher");
+ }
+ super.execute();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AttributeNamespaceDef.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AttributeNamespaceDef.java
new file mode 100644
index 00000000..150b7288
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AttributeNamespaceDef.java
@@ -0,0 +1,51 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.AntTypeDefinition;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.attribute.AttributeNamespace;
+
+/**
+ * Definition to allow the URI to be considered for
+ * Ant attributes.
+ *
+ * @since Ant 1.9.1
+ */
+public final class AttributeNamespaceDef extends AntlibDefinition {
+
+ /**
+ * Run the definition.
+ * This registers the XML namespace (URI) as a namepace for
+ * attributes.
+ */
+ public void execute() {
+ String componentName = ProjectHelper.nsToComponentName(
+ getURI());
+ AntTypeDefinition def = new AntTypeDefinition();
+ def.setName(componentName);
+ def.setClassName(AttributeNamespace.class.getName());
+ def.setClass(AttributeNamespace.class);
+ def.setRestrict(true);
+ def.setClassLoader(AttributeNamespace.class.getClassLoader());
+ ComponentHelper.getComponentHelper(getProject())
+ .addDataTypeDefinition(def);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AugmentReference.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AugmentReference.java
new file mode 100644
index 00000000..637795f3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/AugmentReference.java
@@ -0,0 +1,96 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.RuntimeConfigurable;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TypeAdapter;
+
+/**
+ * Ant task to dynamically augment a previously declared reference.
+ * @since Ant 1.8.1
+ */
+public class AugmentReference extends Task implements TypeAdapter {
+ private String id;
+
+ /**
+ * {@inheritDoc}
+ */
+ public void checkProxyClass(Class<?> proxyClass) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public synchronized Object getProxy() {
+ if (getProject() == null) {
+ throw new IllegalStateException(getTaskName() + "Project owner unset");
+ }
+ hijackId();
+ if (getProject().hasReference(id)) {
+ Object result = getProject().getReference(id);
+ log("project reference " + id + "=" + String.valueOf(result), Project.MSG_DEBUG);
+ return result;
+ }
+ throw new IllegalStateException("Unknown reference \"" + id + "\"");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setProxy(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ private synchronized void hijackId() {
+ if (id == null) {
+ RuntimeConfigurable wrapper = getWrapper();
+ id = wrapper.getId();
+ if (id == null) {
+ throw new IllegalStateException(getTaskName() + " attribute 'id' unset");
+ }
+ wrapper.setAttribute("id", null);
+ wrapper.removeAttribute("id");
+ wrapper.setElementTag("augmented reference \"" + id + "\"");
+ }
+ }
+
+ /**
+ * Overridden to restore the wrapper once it is no longer needed.
+ * @since Ant 1.8.3
+ */
+ public void execute() {
+ restoreWrapperId();
+ }
+
+ /**
+ * Needed if two different targets reuse the same instance.
+ * @see "https://issues.apache.org/bugzilla/show_bug.cgi?id=50894"
+ */
+ private synchronized void restoreWrapperId() {
+ if (id != null) {
+ log("restoring augment wrapper " + id, Project.MSG_DEBUG);
+ RuntimeConfigurable wrapper = getWrapper();
+ wrapper.setAttribute("id", id);
+ wrapper.setElementTag(getTaskName());
+ id = null;
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Available.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Available.java
new file mode 100644
index 00000000..816568e0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Available.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.tools.ant.taskdefs;
+
+import java.io.File;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Will set the given property if the requested resource is available at
+ * runtime. This task may also be used as a condition by the condition task.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="control"
+ */
+public class Available extends Task implements Condition {
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private String property;
+ private String classname;
+ private String filename;
+ private File file;
+ private Path filepath;
+ private String resource;
+ private FileDir type;
+ private Path classpath;
+ private AntClassLoader loader;
+ private Object value = "true";
+ private boolean isTask = false;
+ private boolean ignoreSystemclasses = false;
+ private boolean searchParents = false;
+
+ /**
+ * Set the searchParents attribute.
+ * This controls the behaviour of the the "file" type.
+ * If true, the path, parent path and grandparent path are
+ * searched for the file. If false, only the path is searched.
+ * The default value is false.
+ * @param searchParents the value to set.
+ */
+ public void setSearchParents(boolean searchParents) {
+ this.searchParents = searchParents;
+ }
+
+ /**
+ * Set the classpath to be used when searching for classes and resources.
+ *
+ * @param classpath an Ant Path object containing the search path.
+ */
+ public void setClasspath(Path classpath) {
+ createClasspath().append(classpath);
+ }
+
+ /**
+ * Classpath to be used when searching for classes and resources.
+ *
+ * @return an empty Path instance to be configured by Ant.
+ */
+ public Path createClasspath() {
+ if (this.classpath == null) {
+ this.classpath = new Path(getProject());
+ }
+ return this.classpath.createPath();
+ }
+
+ /**
+ * Set the classpath by reference.
+ *
+ * @param r a Reference to a Path instance to be used as the classpath
+ * value.
+ */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * Set the path to use when looking for a file.
+ *
+ * @param filepath a Path instance containing the search path for files.
+ */
+ public void setFilepath(Path filepath) {
+ createFilepath().append(filepath);
+ }
+
+ /**
+ * Path to search for file resources.
+ *
+ * @return a new Path instance which Ant will configure with a file search
+ * path.
+ */
+ public Path createFilepath() {
+ if (this.filepath == null) {
+ this.filepath = new Path(getProject());
+ }
+ return this.filepath.createPath();
+ }
+
+ /**
+ * Set the name of the property which will be set if the particular resource
+ * is available.
+ *
+ * @param property the name of the property to set.
+ */
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ /**
+ * Set the value to be given to the property if the desired resource is
+ * available.
+ *
+ * @param value the value to be given.
+ */
+ public void setValue(Object value) {
+ this.value = value;
+ }
+
+ /**
+ * Set the value to be given to the property if the desired resource is
+ * available.
+ *
+ * @param value the value to be given.
+ */
+ public void setValue(String value) {
+ setValue((Object) value);
+ }
+
+ /**
+ * Set a classname of a class which must be available to set the given
+ * property.
+ *
+ * @param classname the name of the class required.
+ */
+ public void setClassname(String classname) {
+ if (!"".equals(classname)) {
+ this.classname = classname;
+ }
+ }
+
+ /**
+ * Set the file which must be present in the file system to set the given
+ * property.
+ *
+ * @param file the name of the file which is required.
+ */
+ public void setFile(File file) {
+ this.file = file;
+ this.filename = FILE_UTILS.removeLeadingPath(getProject().getBaseDir(), file);
+ }
+
+ /**
+ * Set the name of a Java resource which is required to set the property.
+ *
+ * @param resource the name of a resource which is required to be available.
+ */
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ /**
+ * @deprecated since 1.5.x.
+ * setType(String) is deprecated and is replaced with
+ * setType(Available.FileDir) to make Ant's Introspection
+ * mechanism do the work and also to encapsulate operations on
+ * the type in its own class.
+ * @param type the type of resource
+ */
+ public void setType(String type) {
+ log("DEPRECATED - The setType(String) method has been deprecated."
+ + " Use setType(Available.FileDir) instead.",
+ Project.MSG_WARN);
+ this.type = new FileDir();
+ this.type.setValue(type);
+ }
+
+ /**
+ * Set what type of file is required - either directory or file.
+ *
+ * @param type an instance of the FileDir enumeratedAttribute indicating
+ * whether the file required is to be a directory or a plain
+ * file.
+ */
+ public void setType(FileDir type) {
+ this.type = type;
+ }
+
+ /**
+ * Set whether the search for classes should ignore the runtime classes and
+ * just use the given classpath.
+ *
+ * @param ignore true if system classes are to be ignored.
+ */
+ public void setIgnoresystemclasses(boolean ignore) {
+ this.ignoreSystemclasses = ignore;
+ }
+
+ /**
+ * Entry point when operating as a task.
+ *
+ * @exception BuildException if the task is not configured correctly.
+ */
+ public void execute() throws BuildException {
+ if (property == null) {
+ throw new BuildException("property attribute is required",
+ getLocation());
+ }
+
+ isTask = true;
+ try {
+ if (eval()) {
+ PropertyHelper ph = PropertyHelper.getPropertyHelper(getProject());
+ Object oldvalue = ph.getProperty(property);
+ if (null != oldvalue && !oldvalue.equals(value)) {
+ log("DEPRECATED - <available> used to override an existing"
+ + " property."
+ + StringUtils.LINE_SEP
+ + " Build file should not reuse the same property"
+ + " name for different values.",
+ Project.MSG_WARN);
+ }
+ // NB: this makes use of Project#setProperty rather than Project#setNewProperty
+ // due to backwards compatibility reasons
+ ph.setProperty(property, value, true);
+ }
+ } finally {
+ isTask = false;
+ }
+ }
+
+ /**
+ * Evaluate the availability of a resource.
+ *
+ * @return boolean is the resource is available.
+ * @exception BuildException if the condition is not configured correctly
+ */
+ public boolean eval() throws BuildException {
+ try {
+ if (classname == null && file == null && resource == null) {
+ throw new BuildException("At least one of (classname|file|"
+ + "resource) is required", getLocation());
+ }
+ if (type != null) {
+ if (file == null) {
+ throw new BuildException("The type attribute is only valid "
+ + "when specifying the file "
+ + "attribute.", getLocation());
+ }
+ }
+ if (classpath != null) {
+ classpath.setProject(getProject());
+ this.loader = getProject().createClassLoader(classpath);
+ }
+ String appendix = "";
+ if (isTask) {
+ appendix = " to set property " + property;
+ } else {
+ setTaskName("available");
+ }
+ if ((classname != null) && !checkClass(classname)) {
+ log("Unable to load class " + classname + appendix,
+ Project.MSG_VERBOSE);
+ return false;
+ }
+ if ((file != null) && !checkFile()) {
+ StringBuffer buf = new StringBuffer("Unable to find ");
+ if (type != null) {
+ buf.append(type).append(' ');
+ }
+ buf.append(filename).append(appendix);
+ log(buf.toString(), Project.MSG_VERBOSE);
+ return false;
+ }
+ if ((resource != null) && !checkResource(resource)) {
+ log("Unable to load resource " + resource + appendix,
+ Project.MSG_VERBOSE);
+ return false;
+ }
+ } finally {
+ if (loader != null) {
+ loader.cleanup();
+ loader = null;
+ }
+ if (!isTask) {
+ setTaskName(null);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Search for file/directory either relative to project's
+ * basedir or in the path given as filepath.
+ *
+ * <p>filepath can be a list of directory and/or file names (gen'd
+ * via <fileset>)</p>
+ *
+ * <p>look for:</p><ul>
+ * <li>full-pathname specified == path in list</li>
+ * <li>full-pathname specified == parent dir of path in list</li>
+ * <li>simple name specified == path in list</li>
+ * <li>simple name specified == path in list + name</li>
+ * <li>simple name specified == parent dir + name</li>
+ * <li>simple name specified == parent of parent dir + name</li>
+ * </ul>
+ */
+ private boolean checkFile() {
+ if (filepath == null) {
+ return checkFile(file, filename);
+ } else {
+ String[] paths = filepath.list();
+ for (int i = 0; i < paths.length; ++i) {
+ log("Searching " + paths[i], Project.MSG_VERBOSE);
+ File path = new File(paths[i]);
+
+ // ** full-pathname specified == path in list
+ // ** simple name specified == path in list
+ if (path.exists()
+ && (filename.equals(paths[i])
+ || filename.equals(path.getName()))) {
+ if (type == null) {
+ log("Found: " + path, Project.MSG_VERBOSE);
+ return true;
+ } else if (type.isDir()
+ && path.isDirectory()) {
+ log("Found directory: " + path, Project.MSG_VERBOSE);
+ return true;
+ } else if (type.isFile()
+ && path.isFile()) {
+ log("Found file: " + path, Project.MSG_VERBOSE);
+ return true;
+ }
+ // not the requested type
+ return false;
+ }
+ File parent = path.getParentFile();
+ // ** full-pathname specified == parent dir of path in list
+ if (parent != null && parent.exists()
+ && filename.equals(parent.getAbsolutePath())) {
+ if (type == null) {
+ log("Found: " + parent, Project.MSG_VERBOSE);
+ return true;
+ } else if (type.isDir()) {
+ log("Found directory: " + parent, Project.MSG_VERBOSE);
+ return true;
+ }
+ // not the requested type
+ return false;
+ }
+ // ** simple name specified == path in list + name
+ if (path.exists() && path.isDirectory()) {
+ if (checkFile(new File(path, filename),
+ filename + " in " + path)) {
+ return true;
+ }
+ }
+
+ // ** simple name specified == parent dir + name
+ while (searchParents && parent != null && parent.exists()) {
+ if (checkFile(new File(parent, filename),
+ filename + " in " + parent)) {
+ return true;
+ }
+ parent = parent.getParentFile();
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if a given file exists and matches the required type.
+ */
+ private boolean checkFile(File f, String text) {
+ if (type != null) {
+ if (type.isDir()) {
+ if (f.isDirectory()) {
+ log("Found directory: " + text, Project.MSG_VERBOSE);
+ }
+ return f.isDirectory();
+ } else if (type.isFile()) {
+ if (f.isFile()) {
+ log("Found file: " + text, Project.MSG_VERBOSE);
+ }
+ return f.isFile();
+ }
+ }
+ if (f.exists()) {
+ log("Found: " + text, Project.MSG_VERBOSE);
+ }
+ return f.exists();
+ }
+
+ /**
+ * Check if a given resource can be loaded.
+ */
+ private boolean checkResource(String resource) {
+ if (loader != null) {
+ return (loader.getResourceAsStream(resource) != null);
+ } else {
+ ClassLoader cL = this.getClass().getClassLoader();
+ if (cL != null) {
+ return (cL.getResourceAsStream(resource) != null);
+ } else {
+ return
+ (ClassLoader.getSystemResourceAsStream(resource) != null);
+ }
+ }
+ }
+
+ /**
+ * Check if a given class can be loaded.
+ */
+ private boolean checkClass(String classname) {
+ try {
+ if (ignoreSystemclasses) {
+ loader = getProject().createClassLoader(classpath);
+ loader.setParentFirst(false);
+ loader.addJavaLibraries();
+ try {
+ loader.findClass(classname);
+ } catch (SecurityException se) {
+ // class found but restricted name; this is
+ // actually the case we're looking for in JDK 1.3+,
+ // so catch the exception and return
+ return true;
+ }
+ } else if (loader != null) {
+ loader.loadClass(classname);
+ } else {
+ ClassLoader l = this.getClass().getClassLoader();
+ // Can return null to represent the bootstrap class loader.
+ // see API docs of Class.getClassLoader.
+ if (l != null) {
+ Class.forName(classname, true, l);
+ } else {
+ Class.forName(classname);
+ }
+ }
+ return true;
+ } catch (ClassNotFoundException e) {
+ log("class \"" + classname + "\" was not found",
+ Project.MSG_DEBUG);
+ return false;
+ } catch (NoClassDefFoundError e) {
+ log("Could not load dependent class \"" + e.getMessage()
+ + "\" for class \"" + classname + "\"",
+ Project.MSG_DEBUG);
+ return false;
+ }
+ }
+
+ /**
+ * EnumeratedAttribute covering the file types to be checked for, either
+ * file or dir.
+ */
+ public static class FileDir extends EnumeratedAttribute {
+
+ private static final String[] VALUES = {"file", "dir"};
+
+ /**
+ * @see EnumeratedAttribute#getValues
+ */
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return VALUES;
+ }
+
+ /**
+ * Indicate if the value specifies a directory.
+ *
+ * @return true if the value specifies a directory.
+ */
+ public boolean isDir() {
+ return "dir".equalsIgnoreCase(getValue());
+ }
+
+ /**
+ * Indicate if the value specifies a file.
+ *
+ * @return true if the value specifies a file.
+ */
+ public boolean isFile() {
+ return "file".equalsIgnoreCase(getValue());
+ }
+
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BUnzip2.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BUnzip2.java
new file mode 100644
index 00000000..323b738c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BUnzip2.java
@@ -0,0 +1,112 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+
+import java.io.BufferedInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.bzip2.CBZip2InputStream;
+
+/**
+ * Expands a file that has been compressed with the BZIP2
+ * algorithm. Normally used to compress non-compressed archives such
+ * as TAR files.
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="packaging"
+ */
+
+public class BUnzip2 extends Unpack {
+
+ private static final int BUFFER_SIZE = 8 * 1024;
+
+ private static final String DEFAULT_EXTENSION = ".bz2";
+
+ /**
+ * Get the default extension.
+ * @return the string ".bz2"
+ */
+ protected String getDefaultExtension() {
+ return DEFAULT_EXTENSION;
+ }
+
+ /**
+ * Do the unbzipping.
+ */
+ protected void extract() {
+ if (source.lastModified() > dest.lastModified()) {
+ log("Expanding " + source.getAbsolutePath() + " to "
+ + dest.getAbsolutePath());
+
+ FileOutputStream out = null;
+ CBZip2InputStream zIn = null;
+ InputStream fis = null;
+ BufferedInputStream bis = null;
+ try {
+ out = new FileOutputStream(dest);
+ fis = srcResource.getInputStream();
+ bis = new BufferedInputStream(fis);
+ int b = bis.read();
+ if (b != 'B') {
+ throw new BuildException("Invalid bz2 file.", getLocation());
+ }
+ b = bis.read();
+ if (b != 'Z') {
+ throw new BuildException("Invalid bz2 file.", getLocation());
+ }
+ zIn = new CBZip2InputStream(bis, true);
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int count = 0;
+ do {
+ out.write(buffer, 0, count);
+ count = zIn.read(buffer, 0, buffer.length);
+ } while (count != -1);
+ } catch (IOException ioe) {
+ String msg = "Problem expanding bzip2 " + ioe.getMessage();
+ throw new BuildException(msg, ioe, getLocation());
+ } finally {
+ FileUtils.close(bis);
+ FileUtils.close(fis);
+ FileUtils.close(out);
+ FileUtils.close(zIn);
+ }
+ }
+ }
+
+ /**
+ * Whether this task can deal with non-file resources.
+ *
+ * <p>This implementation returns true only if this task is
+ * &lt;gunzip&gt;. Any subclass of this class that also wants to
+ * support non-file resources needs to override this method. We
+ * need to do so for backwards compatibility reasons since we
+ * can't expect subclasses to support resources.</p>
+ * @return true if this class supports non file resources.
+ * @since Ant 1.7
+ */
+ protected boolean supportsNonFileResources() {
+ return getClass().equals(BUnzip2.class);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BZip2.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BZip2.java
new file mode 100644
index 00000000..f5944df5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BZip2.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.tools.ant.taskdefs;
+
+
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.bzip2.CBZip2OutputStream;
+
+/**
+ * Compresses a file with the BZIP2 algorithm. Normally used to compress
+ * non-compressed archives such as TAR files.
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="packaging"
+ */
+
+public class BZip2 extends Pack {
+ /**
+ * Compress the zipFile.
+ */
+ protected void pack() {
+ CBZip2OutputStream zOut = null;
+ try {
+ BufferedOutputStream bos =
+ new BufferedOutputStream(new FileOutputStream(zipFile));
+ bos.write('B');
+ bos.write('Z');
+ zOut = new CBZip2OutputStream(bos);
+ zipResource(getSrcResource(), zOut);
+ } catch (IOException ioe) {
+ String msg = "Problem creating bzip2 " + ioe.getMessage();
+ throw new BuildException(msg, ioe, getLocation());
+ } finally {
+ FileUtils.close(zOut);
+ }
+ }
+
+ /**
+ * Whether this task can deal with non-file resources.
+ *
+ * <p>This implementation returns true only if this task is
+ * &lt;bzip2&gt;. Any subclass of this class that also wants to
+ * support non-file resources needs to override this method. We
+ * need to do so for backwards compatibility reasons since we
+ * can't expect subclasses to support resources.</p>
+ * @return true if this task support non file resources.
+ * @since Ant 1.7
+ */
+ protected boolean supportsNonFileResources() {
+ return getClass().equals(BZip2.class);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Basename.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Basename.java
new file mode 100644
index 00000000..0415af70
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Basename.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.tools.ant.taskdefs;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Sets a property to the base name of a specified file, optionally minus a
+ * suffix.
+ *
+ * This task can accept the following attributes:
+ * <ul>
+ * <li>file
+ * <li>property
+ * <li>suffix
+ * </ul>
+ * The <b>file</b> and <b>property</b> attributes are required. The
+ * <b>suffix</b> attribute can be specified either with or without
+ * the &quot;.&quot;, and the result will be the same (ie., the
+ * returned file name will be minus the .suffix).
+ * <p>
+ * When this task executes, it will set the specified property to the
+ * value of the last element in the specified file. If file is a
+ * directory, the basename will be the last directory element. If file
+ * is a full-path filename, the basename will be the simple file name.
+ * If a suffix is specified, and the specified file ends in that suffix,
+ * the basename will be the simple file name without the suffix.
+ *
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="property"
+ */
+
+public class Basename extends Task {
+ private File file;
+ private String property;
+ private String suffix;
+
+ /**
+ * file or directory to get base name from
+ * @param file file or directory to get base name from
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * Property to set base name to.
+ * @param property name of property
+ */
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ /**
+ * Optional suffix to remove from base name.
+ * @param suffix suffix to remove from base name
+ */
+ public void setSuffix(String suffix) {
+ this.suffix = suffix;
+ }
+
+ /**
+ * do the work
+ * @throws BuildException if required attributes are not supplied
+ * property and attribute are required attributes
+ */
+ public void execute() throws BuildException {
+ if (property == null) {
+ throw new BuildException("property attribute required", getLocation());
+ }
+ if (file == null) {
+ throw new BuildException("file attribute required", getLocation());
+ }
+ String value = file.getName();
+ if (suffix != null && value.endsWith(suffix)) {
+ // if the suffix does not starts with a '.' and the
+ // char preceding the suffix is a '.', we assume the user
+ // wants to remove the '.' as well (see docs)
+ int pos = value.length() - suffix.length();
+ if (pos > 0 && suffix.charAt(0) != '.'
+ && value.charAt(pos - 1) == '.') {
+ pos--;
+ }
+ value = value.substring(0, pos);
+ }
+ getProject().setNewProperty(property, value);
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BindTargets.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BindTargets.java
new file mode 100644
index 00000000..45ad9ae7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BindTargets.java
@@ -0,0 +1,91 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.ProjectHelper.OnMissingExtensionPoint;
+import org.apache.tools.ant.Task;
+
+/**
+ * Simple task which bind some targets to some defined extension point
+ */
+public class BindTargets extends Task {
+
+ private String extensionPoint;
+
+ private final List<String> targets = new ArrayList<String>();
+
+ private OnMissingExtensionPoint onMissingExtensionPoint;
+
+ public void setExtensionPoint(final String extensionPoint) {
+ this.extensionPoint = extensionPoint;
+ }
+
+ public void setOnMissingExtensionPoint(final String onMissingExtensionPoint) {
+ try {
+ this.onMissingExtensionPoint = OnMissingExtensionPoint.valueOf(onMissingExtensionPoint);
+ } catch (final IllegalArgumentException e) {
+ throw new BuildException("Invalid onMissingExtensionPoint: " + onMissingExtensionPoint);
+ }
+ }
+
+ public void setOnMissingExtensionPoint(final OnMissingExtensionPoint onMissingExtensionPoint) {
+ this.onMissingExtensionPoint = onMissingExtensionPoint;
+ }
+
+ public void setTargets(final String target) {
+ final String[] inputs = target.split(",");
+ for (int i = 0; i < inputs.length; i++) {
+ final String input = inputs[i].trim();
+ if (input.length() > 0) {
+ targets.add(input);
+ }
+ }
+ }
+
+ @Override
+ public void execute() throws BuildException {
+ if (extensionPoint == null) {
+ throw new BuildException("extensionPoint required", getLocation());
+ }
+
+ if (getOwningTarget() == null
+ || !"".equals(getOwningTarget().getName())) {
+ throw new BuildException(
+ "bindtargets only allowed as a top-level task");
+ }
+
+ if (onMissingExtensionPoint == null) {
+ onMissingExtensionPoint = OnMissingExtensionPoint.FAIL;
+ }
+ final ProjectHelper helper = (ProjectHelper) getProject().getReference(
+ ProjectHelper.PROJECTHELPER_REFERENCE);
+
+ for (final Iterator<String> itTarget = targets.iterator(); itTarget.hasNext();) {
+ helper.getExtensionStack().add(
+ new String[] {extensionPoint, itTarget.next(),
+ onMissingExtensionPoint.name()});
+ }
+
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BuildNumber.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BuildNumber.java
new file mode 100644
index 00000000..aee071d3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/BuildNumber.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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Read, increment, and write a build number in a file
+ * It will first
+ * attempt to read a build number from a file, then set the property
+ * "build.number" to the value that was read in (or 0 if no such value). Then
+ * it will increment the build number by one and write it back out into the
+ * file.
+ *
+ * @since Ant 1.5
+ * @ant.task name="buildnumber"
+ */
+public class BuildNumber
+ extends Task {
+ /**
+ * The name of the property in which the build number is stored.
+ */
+ private static final String DEFAULT_PROPERTY_NAME = "build.number";
+
+ /** The default filename to use if no file specified. */
+ private static final String DEFAULT_FILENAME = DEFAULT_PROPERTY_NAME;
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** The File in which the build number is stored. */
+ private File myFile;
+
+
+ /**
+ * The file in which the build number is stored. Defaults to
+ * "build.number" if not specified.
+ *
+ * @param file the file in which build number is stored.
+ */
+ public void setFile(final File file) {
+ myFile = file;
+ }
+
+
+ /**
+ * Run task.
+ *
+ * @exception BuildException if an error occurs
+ */
+ public void execute()
+ throws BuildException {
+ File savedFile = myFile; // may be altered in validate
+
+ validate();
+
+ final Properties properties = loadProperties();
+ final int buildNumber = getBuildNumber(properties);
+
+ properties.put(DEFAULT_PROPERTY_NAME,
+ String.valueOf(buildNumber + 1));
+
+ // Write the properties file back out
+ FileOutputStream output = null;
+
+ try {
+ output = new FileOutputStream(myFile);
+
+ final String header = "Build Number for ANT. Do not edit!";
+
+ properties.store(output, header);
+ } catch (final IOException ioe) {
+ final String message = "Error while writing " + myFile;
+
+ throw new BuildException(message, ioe);
+ } finally {
+ if (null != output) {
+ try {
+ output.close();
+ } catch (final IOException ioe) {
+ log("error closing output stream " + ioe, Project.MSG_ERR);
+ }
+ }
+ myFile = savedFile;
+ }
+
+ //Finally set the property
+ getProject().setNewProperty(DEFAULT_PROPERTY_NAME,
+ String.valueOf(buildNumber));
+ }
+
+
+ /**
+ * Utility method to retrieve build number from properties object.
+ *
+ * @param properties the properties to retrieve build number from
+ * @return the build number or if no number in properties object
+ * @throws BuildException if build.number property is not an integer
+ */
+ private int getBuildNumber(final Properties properties)
+ throws BuildException {
+ final String buildNumber =
+ properties.getProperty(DEFAULT_PROPERTY_NAME, "0").trim();
+
+ // Try parsing the line into an integer.
+ try {
+ return Integer.parseInt(buildNumber);
+ } catch (final NumberFormatException nfe) {
+ final String message =
+ myFile + " contains a non integer build number: " + buildNumber;
+ throw new BuildException(message, nfe);
+ }
+ }
+
+
+ /**
+ * Utility method to load properties from file.
+ *
+ * @return the loaded properties
+ * @throws BuildException
+ */
+ private Properties loadProperties()
+ throws BuildException {
+ FileInputStream input = null;
+
+ try {
+ final Properties properties = new Properties();
+
+ input = new FileInputStream(myFile);
+ properties.load(input);
+ return properties;
+ } catch (final IOException ioe) {
+ throw new BuildException(ioe);
+ } finally {
+ if (null != input) {
+ try {
+ input.close();
+ } catch (final IOException ioe) {
+ log("error closing input stream " + ioe, Project.MSG_ERR);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Validate that the task parameters are valid.
+ *
+ * @throws BuildException if parameters are invalid
+ */
+ private void validate()
+ throws BuildException {
+ if (null == myFile) {
+ myFile = FILE_UTILS.resolveFile(getProject().getBaseDir(), DEFAULT_FILENAME);
+ }
+
+ if (!myFile.exists()) {
+ try {
+ FILE_UTILS.createNewFile(myFile);
+ } catch (final IOException ioe) {
+ final String message =
+ myFile + " doesn't exist and new file can't be created.";
+ throw new BuildException(message, ioe);
+ }
+ }
+
+ if (!myFile.canRead()) {
+ final String message = "Unable to read from " + myFile + ".";
+ throw new BuildException(message);
+ }
+
+ if (!myFile.canWrite()) {
+ final String message = "Unable to write to " + myFile + ".";
+ throw new BuildException(message);
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CVSPass.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CVSPass.java
new file mode 100644
index 00000000..53f5d3e0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CVSPass.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.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Adds an new entry to a CVS password file.
+ *
+ *
+ * @since Ant 1.4
+ *
+ * @ant.task category="scm"
+ */
+public class CVSPass extends Task {
+ /** CVS Root */
+ private String cvsRoot = null;
+ /** Password file to add password to */
+ private File passFile = null;
+ /** Password to add to file */
+ private String password = null;
+
+ /** Array contain char conversion data */
+ private final char[] shifts = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 114, 120, 53, 79, 96, 109, 72, 108, 70, 64, 76, 67, 116, 74, 68, 87,
+ 111, 52, 75, 119, 49, 34, 82, 81, 95, 65, 112, 86, 118, 110, 122, 105,
+ 41, 57, 83, 43, 46, 102, 40, 89, 38, 103, 45, 50, 42, 123, 91, 35,
+ 125, 55, 54, 66, 124, 126, 59, 47, 92, 71, 115, 78, 88, 107, 106, 56,
+ 36, 121, 117, 104, 101, 100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
+ 58, 113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85, 223,
+ 225, 216, 187, 166, 229, 189, 222, 188, 141, 249, 148, 200, 184, 136, 248, 190,
+ 199, 170, 181, 204, 138, 232, 218, 183, 255, 234, 220, 247, 213, 203, 226, 193,
+ 174, 172, 228, 252, 217, 201, 131, 230, 197, 211, 145, 238, 161, 179, 160, 212,
+ 207, 221, 254, 173, 202, 146, 224, 151, 140, 196, 205, 130, 135, 133, 143, 246,
+ 192, 159, 244, 239, 185, 168, 215, 144, 139, 165, 180, 157, 147, 186, 214, 176,
+ 227, 231, 219, 169, 175, 156, 206, 198, 129, 164, 150, 210, 154, 177, 134, 127,
+ 182, 128, 158, 208, 162, 132, 167, 209, 149, 241, 153, 251, 237, 236, 171, 195,
+ 243, 233, 253, 240, 194, 250, 191, 155, 142, 137, 245, 235, 163, 242, 178, 152
+ };
+
+ /**
+ * Create a CVS task using the default cvspass file location.
+ */
+ public CVSPass() {
+ passFile = new File(
+ System.getProperty("cygwin.user.home",
+ System.getProperty("user.home"))
+ + File.separatorChar + ".cvspass");
+ }
+
+ /**
+ * Does the work.
+ *
+ * @exception BuildException if something goes wrong with the build
+ */
+ public final void execute() throws BuildException {
+ if (cvsRoot == null) {
+ throw new BuildException("cvsroot is required");
+ }
+ if (password == null) {
+ throw new BuildException("password is required");
+ }
+
+ log("cvsRoot: " + cvsRoot, Project.MSG_DEBUG);
+ log("password: " + password, Project.MSG_DEBUG);
+ log("passFile: " + passFile, Project.MSG_DEBUG);
+
+ BufferedReader reader = null;
+ BufferedWriter writer = null;
+ try {
+ StringBuffer buf = new StringBuffer();
+
+ if (passFile.exists()) {
+ reader = new BufferedReader(new FileReader(passFile));
+
+ String line = null;
+
+ while ((line = reader.readLine()) != null) {
+ if (!line.startsWith(cvsRoot)) {
+ buf.append(line).append(StringUtils.LINE_SEP);
+ }
+ }
+ }
+
+ String pwdfile = buf.toString() + cvsRoot + " A"
+ + mangle(password);
+
+ log("Writing -> " + pwdfile , Project.MSG_DEBUG);
+
+ writer = new BufferedWriter(new FileWriter(passFile));
+
+ writer.write(pwdfile);
+ writer.newLine();
+ } catch (IOException e) {
+ throw new BuildException(e);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ FileUtils.close(writer);
+ }
+ }
+
+ private final String mangle(String password) {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < password.length(); i++) {
+ buf.append(shifts[password.charAt(i)]);
+ }
+ return buf.toString();
+ }
+
+ /**
+ * The CVS repository to add an entry for.
+ *
+ * @param cvsRoot the CVS repository
+ */
+ public void setCvsroot(String cvsRoot) {
+ this.cvsRoot = cvsRoot;
+ }
+
+ /**
+ * Password file to add the entry to.
+ *
+ * @param passFile the password file.
+ */
+ public void setPassfile(File passFile) {
+ this.passFile = passFile;
+ }
+
+ /**
+ * Password to be added to the password file.
+ *
+ * @param password the password.
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CallTarget.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CallTarget.java
new file mode 100644
index 00000000..d8a0e8c8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CallTarget.java
@@ -0,0 +1,255 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.PropertySet;
+
+/**
+ * Call another target in the same project.
+ *
+ * <pre>
+ * &lt;target name="foo"&gt;
+ * &lt;antcall target="bar"&gt;
+ * &lt;param name="property1" value="aaaaa" /&gt;
+ * &lt;param name="foo" value="baz" /&gt;
+ * &lt;/antcall&gt;
+ * &lt;/target&gt;
+ *
+ * &lt;target name="bar" depends="init"&gt;
+ * &lt;echo message="prop is ${property1} ${foo}" /&gt;
+ * &lt;/target&gt;
+ * </pre>
+ *
+ * <p>This only works as expected if neither property1 nor foo are
+ * defined in the project itself.
+ *
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task name="antcall" category="control"
+ */
+public class CallTarget extends Task {
+
+ private Ant callee;
+ // must match the default value of Ant#inheritAll
+ private boolean inheritAll = true;
+ // must match the default value of Ant#inheritRefs
+ private boolean inheritRefs = false;
+
+ private boolean targetSet = false;
+
+ /**
+ * If true, pass all properties to the new Ant project.
+ * Defaults to true.
+ * @param inherit <code>boolean</code> flag.
+ */
+ public void setInheritAll(boolean inherit) {
+ inheritAll = inherit;
+ }
+
+ /**
+ * If true, pass all references to the new Ant project.
+ * Defaults to false.
+ * @param inheritRefs <code>boolean</code> flag.
+ */
+ public void setInheritRefs(boolean inheritRefs) {
+ this.inheritRefs = inheritRefs;
+ }
+
+ /**
+ * Initialize this task by creating new instance of the ant task and
+ * configuring it by calling its own init method.
+ */
+ public void init() {
+ callee = new Ant(this);
+ callee.init();
+ }
+
+ /**
+ * Delegate the work to the ant task instance, after setting it up.
+ * @throws BuildException on validation failure or if the target didn't
+ * execute.
+ */
+ public void execute() throws BuildException {
+ if (callee == null) {
+ init();
+ }
+ if (!targetSet) {
+ throw new BuildException(
+ "Attribute target or at least one nested target is required.",
+ getLocation());
+ }
+ callee.setAntfile(getProject().getProperty("ant.file"));
+ callee.setInheritAll(inheritAll);
+ callee.setInheritRefs(inheritRefs);
+ callee.execute();
+ }
+
+ /**
+ * Create a new Property to pass to the invoked target(s).
+ * @return a <code>Property</code> object.
+ */
+ public Property createParam() {
+ if (callee == null) {
+ init();
+ }
+ return callee.createProperty();
+ }
+
+ /**
+ * Reference element identifying a data type to carry
+ * over to the invoked target.
+ * @param r the specified <code>Ant.Reference</code>.
+ * @since Ant 1.5
+ */
+ public void addReference(Ant.Reference r) {
+ if (callee == null) {
+ init();
+ }
+ callee.addReference(r);
+ }
+
+ /**
+ * Set of properties to pass to the new project.
+ * @param ps the <code>PropertySet</code> to pass.
+ * @since Ant 1.6
+ */
+ public void addPropertyset(PropertySet ps) {
+ if (callee == null) {
+ init();
+ }
+ callee.addPropertyset(ps);
+ }
+
+ /**
+ * Set target to execute.
+ * @param target the name of the target to execute.
+ */
+ public void setTarget(String target) {
+ if (callee == null) {
+ init();
+ }
+ callee.setTarget(target);
+ targetSet = true;
+ }
+
+ /**
+ * Add a target to the list of targets to invoke.
+ * @param t <code>Ant.TargetElement</code> representing the target.
+ * @since Ant 1.6.3
+ */
+ public void addConfiguredTarget(Ant.TargetElement t) {
+ if (callee == null) {
+ init();
+ }
+ callee.addConfiguredTarget(t);
+ targetSet = true;
+ }
+
+ /**
+ * Handles output.
+ * Send it the the new project if is present, otherwise
+ * call the super class.
+ * @param output The string output to output.
+ * @see Task#handleOutput(String)
+ * @since Ant 1.5
+ */
+ public void handleOutput(String output) {
+ if (callee != null) {
+ callee.handleOutput(output);
+ } else {
+ super.handleOutput(output);
+ }
+ }
+
+ /**
+ * Handles input.
+ * Delegate to the created project, if present, otherwise
+ * call the super class.
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception IOException if the data cannot be read.
+ * @see Task#handleInput(byte[], int, int)
+ * @since Ant 1.6
+ */
+ public int handleInput(byte[] buffer, int offset, int length)
+ throws IOException {
+ if (callee != null) {
+ return callee.handleInput(buffer, offset, length);
+ }
+ return super.handleInput(buffer, offset, length);
+ }
+
+ /**
+ * Handles output.
+ * Send it the the new project if is present, otherwise
+ * call the super class.
+ * @param output The string to output.
+ * @see Task#handleFlush(String)
+ * @since Ant 1.5.2
+ */
+ public void handleFlush(String output) {
+ if (callee != null) {
+ callee.handleFlush(output);
+ } else {
+ super.handleFlush(output);
+ }
+ }
+
+ /**
+ * Handle error output.
+ * Send it the the new project if is present, otherwise
+ * call the super class.
+ * @param output The string to output.
+ *
+ * @see Task#handleErrorOutput(String)
+ * @since Ant 1.5
+ */
+ public void handleErrorOutput(String output) {
+ if (callee != null) {
+ callee.handleErrorOutput(output);
+ } else {
+ super.handleErrorOutput(output);
+ }
+ }
+
+ /**
+ * Handle error output.
+ * Send it the the new project if is present, otherwise
+ * call the super class.
+ * @param output The string to output.
+ * @see Task#handleErrorFlush(String)
+ * @since Ant 1.5.2
+ */
+ public void handleErrorFlush(String output) {
+ if (callee != null) {
+ callee.handleErrorFlush(output);
+ } else {
+ super.handleErrorFlush(output);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Checksum.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Checksum.java
new file mode 100644
index 00000000..7a94ca09
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Checksum.java
@@ -0,0 +1,712 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.text.MessageFormat;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.Restrict;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.types.resources.selectors.Type;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Used to create or verify file checksums.
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="control"
+ */
+public class Checksum extends MatchingTask implements Condition {
+
+ private static final int NIBBLE = 4;
+ private static final int WORD = 16;
+ private static final int BUFFER_SIZE = 8 * 1024;
+ private static final int BYTE_MASK = 0xFF;
+
+ private static class FileUnion extends Restrict {
+ private Union u;
+ FileUnion() {
+ u = new Union();
+ super.add(u);
+ super.add(Type.FILE);
+ }
+ public void add(ResourceCollection rc) {
+ u.add(rc);
+ }
+ }
+
+ /**
+ * File for which checksum is to be calculated.
+ */
+ private File file = null;
+
+ /**
+ * Root directory in which the checksum files will be written.
+ * If not specified, the checksum files will be written
+ * in the same directory as each file.
+ */
+ private File todir;
+
+ /**
+ * MessageDigest algorithm to be used.
+ */
+ private String algorithm = "MD5";
+ /**
+ * MessageDigest Algorithm provider
+ */
+ private String provider = null;
+ /**
+ * File Extension that is be to used to create or identify
+ * destination file
+ */
+ private String fileext;
+ /**
+ * Holds generated checksum and gets set as a Project Property.
+ */
+ private String property;
+ /**
+ * Holds checksums for all files (both calculated and cached on disk).
+ * Key: java.util.File (source file)
+ * Value: java.lang.String (digest)
+ */
+ private Map<File, byte[]> allDigests = new HashMap<File, byte[]>();
+ /**
+ * Holds relative file names for all files (always with a forward slash).
+ * This is used to calculate the total hash.
+ * Key: java.util.File (source file)
+ * Value: java.lang.String (relative file name)
+ */
+ private Map<File, String> relativeFilePaths = new HashMap<File, String>();
+ /**
+ * Property where totalChecksum gets set.
+ */
+ private String totalproperty;
+ /**
+ * Whether or not to create a new file.
+ * Defaults to <code>false</code>.
+ */
+ private boolean forceOverwrite;
+ /**
+ * Contains the result of a checksum verification. ("true" or "false")
+ */
+ private String verifyProperty;
+ /**
+ * Resource Collection.
+ */
+ private FileUnion resources = null;
+ /**
+ * Stores SourceFile, DestFile pairs and SourceFile, Property String pairs.
+ */
+ private Hashtable<File, Object> includeFileMap = new Hashtable<File, Object>();
+ /**
+ * Message Digest instance
+ */
+ private MessageDigest messageDigest;
+ /**
+ * is this task being used as a nested condition element?
+ */
+ private boolean isCondition;
+ /**
+ * Size of the read buffer to use.
+ */
+ private int readBufferSize = BUFFER_SIZE;
+
+ /**
+ * Formater for the checksum file.
+ */
+ private MessageFormat format = FormatElement.getDefault().getFormat();
+
+ /**
+ * Sets the file for which the checksum is to be calculated.
+ * @param file a <code>File</code> value
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * Sets the root directory where checksum files will be
+ * written/read
+ * @param todir the directory to write to
+ * @since Ant 1.6
+ */
+ public void setTodir(File todir) {
+ this.todir = todir;
+ }
+
+ /**
+ * Specifies the algorithm to be used to compute the checksum.
+ * Defaults to "MD5". Other popular algorithms like "SHA" may be used as well.
+ * @param algorithm a <code>String</code> value
+ */
+ public void setAlgorithm(String algorithm) {
+ this.algorithm = algorithm;
+ }
+
+ /**
+ * Sets the MessageDigest algorithm provider to be used
+ * to calculate the checksum.
+ * @param provider a <code>String</code> value
+ */
+ public void setProvider(String provider) {
+ this.provider = provider;
+ }
+
+ /**
+ * Sets the file extension that is be to used to
+ * create or identify destination file.
+ * @param fileext a <code>String</code> value
+ */
+ public void setFileext(String fileext) {
+ this.fileext = fileext;
+ }
+
+ /**
+ * Sets the property to hold the generated checksum.
+ * @param property a <code>String</code> value
+ */
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ /**
+ * Sets the property to hold the generated total checksum
+ * for all files.
+ * @param totalproperty a <code>String</code> value
+ *
+ * @since Ant 1.6
+ */
+ public void setTotalproperty(String totalproperty) {
+ this.totalproperty = totalproperty;
+ }
+
+ /**
+ * Sets the verify property. This project property holds
+ * the result of a checksum verification - "true" or "false"
+ * @param verifyProperty a <code>String</code> value
+ */
+ public void setVerifyproperty(String verifyProperty) {
+ this.verifyProperty = verifyProperty;
+ }
+
+ /**
+ * Whether or not to overwrite existing file irrespective of
+ * whether it is newer than
+ * the source file. Defaults to false.
+ * @param forceOverwrite a <code>boolean</code> value
+ */
+ public void setForceOverwrite(boolean forceOverwrite) {
+ this.forceOverwrite = forceOverwrite;
+ }
+
+ /**
+ * The size of the read buffer to use.
+ * @param size an <code>int</code> value
+ */
+ public void setReadBufferSize(int size) {
+ this.readBufferSize = size;
+ }
+
+ /**
+ * Select the in/output pattern via a well know format name.
+ * @param e an <code>enumerated</code> value
+ *
+ * @since 1.7.0
+ */
+ public void setFormat(FormatElement e) {
+ format = e.getFormat();
+ }
+
+ /**
+ * Specify the pattern to use as a MessageFormat pattern.
+ *
+ * <p>{0} gets replaced by the checksum, {1} by the filename.</p>
+ * @param p a <code>String</code> value
+ *
+ * @since 1.7.0
+ */
+ public void setPattern(String p) {
+ format = new MessageFormat(p);
+ }
+
+ /**
+ * Files to generate checksums for.
+ * @param set a fileset of files to generate checksums for.
+ */
+ public void addFileset(FileSet set) {
+ add(set);
+ }
+
+ /**
+ * Add a resource collection.
+ * @param rc the ResourceCollection to add.
+ */
+ public void add(ResourceCollection rc) {
+ if (rc == null) {
+ return;
+ }
+ resources = (resources == null) ? new FileUnion() : resources;
+ resources.add(rc);
+ }
+
+ /**
+ * Calculate the checksum(s).
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ isCondition = false;
+ boolean value = validateAndExecute();
+ if (verifyProperty != null) {
+ getProject().setNewProperty(
+ verifyProperty,
+ (value ? Boolean.TRUE.toString() : Boolean.FALSE.toString()));
+ }
+ }
+
+ /**
+ * Calculate the checksum(s)
+ *
+ * @return Returns true if the checksum verification test passed,
+ * false otherwise.
+ * @throws BuildException on error
+ */
+ public boolean eval() throws BuildException {
+ isCondition = true;
+ return validateAndExecute();
+ }
+
+ /**
+ * Validate attributes and get down to business.
+ */
+ private boolean validateAndExecute() throws BuildException {
+ String savedFileExt = fileext;
+
+ if (file == null && (resources == null || resources.size() == 0)) {
+ throw new BuildException(
+ "Specify at least one source - a file or a resource collection.");
+ }
+ if (!(resources == null || resources.isFilesystemOnly())) {
+ throw new BuildException("Can only calculate checksums for file-based resources.");
+ }
+ if (file != null && file.exists() && file.isDirectory()) {
+ throw new BuildException("Checksum cannot be generated for directories");
+ }
+ if (file != null && totalproperty != null) {
+ throw new BuildException("File and Totalproperty cannot co-exist.");
+ }
+ if (property != null && fileext != null) {
+ throw new BuildException("Property and FileExt cannot co-exist.");
+ }
+ if (property != null) {
+ if (forceOverwrite) {
+ throw new BuildException(
+ "ForceOverwrite cannot be used when Property is specified");
+ }
+ int ct = 0;
+ if (resources != null) {
+ ct += resources.size();
+ }
+ if (file != null) {
+ ct++;
+ }
+ if (ct > 1) {
+ throw new BuildException(
+ "Multiple files cannot be used when Property is specified");
+ }
+ }
+ if (verifyProperty != null) {
+ isCondition = true;
+ }
+ if (verifyProperty != null && forceOverwrite) {
+ throw new BuildException("VerifyProperty and ForceOverwrite cannot co-exist.");
+ }
+ if (isCondition && forceOverwrite) {
+ throw new BuildException(
+ "ForceOverwrite cannot be used when conditions are being used.");
+ }
+ messageDigest = null;
+ if (provider != null) {
+ try {
+ messageDigest = MessageDigest.getInstance(algorithm, provider);
+ } catch (NoSuchAlgorithmException noalgo) {
+ throw new BuildException(noalgo, getLocation());
+ } catch (NoSuchProviderException noprovider) {
+ throw new BuildException(noprovider, getLocation());
+ }
+ } else {
+ try {
+ messageDigest = MessageDigest.getInstance(algorithm);
+ } catch (NoSuchAlgorithmException noalgo) {
+ throw new BuildException(noalgo, getLocation());
+ }
+ }
+ if (messageDigest == null) {
+ throw new BuildException("Unable to create Message Digest", getLocation());
+ }
+ if (fileext == null) {
+ fileext = "." + algorithm;
+ } else if (fileext.trim().length() == 0) {
+ throw new BuildException("File extension when specified must not be an empty string");
+ }
+ try {
+ if (resources != null) {
+ for (Resource r : resources) {
+ File src = r.as(FileProvider.class)
+ .getFile();
+ if (totalproperty != null || todir != null) {
+ // Use '/' to calculate digest based on file name.
+ // This is required in order to get the same result
+ // on different platforms.
+ relativeFilePaths.put(src, r.getName().replace(File.separatorChar, '/'));
+ }
+ addToIncludeFileMap(src);
+ }
+ }
+ if (file != null) {
+ if (totalproperty != null || todir != null) {
+ relativeFilePaths.put(
+ file, file.getName().replace(File.separatorChar, '/'));
+ }
+ addToIncludeFileMap(file);
+ }
+ return generateChecksums();
+ } finally {
+ fileext = savedFileExt;
+ includeFileMap.clear();
+ }
+ }
+
+ /**
+ * Add key-value pair to the hashtable upon which
+ * to later operate upon.
+ */
+ private void addToIncludeFileMap(File file) throws BuildException {
+ if (file.exists()) {
+ if (property == null) {
+ File checksumFile = getChecksumFile(file);
+ if (forceOverwrite || isCondition
+ || (file.lastModified() > checksumFile.lastModified())) {
+ includeFileMap.put(file, checksumFile);
+ } else {
+ log(file + " omitted as " + checksumFile + " is up to date.",
+ Project.MSG_VERBOSE);
+ if (totalproperty != null) {
+ // Read the checksum from disk.
+ String checksum = readChecksum(checksumFile);
+ byte[] digest = decodeHex(checksum.toCharArray());
+ allDigests.put(file, digest);
+ }
+ }
+ } else {
+ includeFileMap.put(file, property);
+ }
+ } else {
+ String message = "Could not find file "
+ + file.getAbsolutePath()
+ + " to generate checksum for.";
+ log(message);
+ throw new BuildException(message, getLocation());
+ }
+ }
+
+ private File getChecksumFile(File file) {
+ File directory;
+ if (todir != null) {
+ // A separate directory was explicitly declared
+ String path = getRelativeFilePath(file);
+ directory = new File(todir, path).getParentFile();
+ // Create the directory, as it might not exist.
+ directory.mkdirs();
+ } else {
+ // Just use the same directory as the file itself.
+ // This directory will exist
+ directory = file.getParentFile();
+ }
+ File checksumFile = new File(directory, file.getName() + fileext);
+ return checksumFile;
+ }
+
+ /**
+ * Generate checksum(s) using the message digest created earlier.
+ */
+ private boolean generateChecksums() throws BuildException {
+ boolean checksumMatches = true;
+ FileInputStream fis = null;
+ FileOutputStream fos = null;
+ byte[] buf = new byte[readBufferSize];
+ try {
+ for (Map.Entry<File, Object> e : includeFileMap.entrySet()) {
+ messageDigest.reset();
+ File src = e.getKey();
+ if (!isCondition) {
+ log("Calculating " + algorithm + " checksum for " + src, Project.MSG_VERBOSE);
+ }
+ fis = new FileInputStream(src);
+ DigestInputStream dis = new DigestInputStream(fis,
+ messageDigest);
+ while (dis.read(buf, 0, readBufferSize) != -1) {
+ // Empty statement
+ }
+ dis.close();
+ fis.close();
+ fis = null;
+ byte[] fileDigest = messageDigest.digest ();
+ if (totalproperty != null) {
+ allDigests.put(src, fileDigest);
+ }
+ String checksum = createDigestString(fileDigest);
+ //can either be a property name string or a file
+ Object destination = e.getValue();
+ if (destination instanceof java.lang.String) {
+ String prop = (String) destination;
+ if (isCondition) {
+ checksumMatches
+ = checksumMatches && checksum.equals(property);
+ } else {
+ getProject().setNewProperty(prop, checksum);
+ }
+ } else if (destination instanceof java.io.File) {
+ if (isCondition) {
+ File existingFile = (File) destination;
+ if (existingFile.exists()) {
+ try {
+ String suppliedChecksum =
+ readChecksum(existingFile);
+ checksumMatches = checksumMatches
+ && checksum.equals(suppliedChecksum);
+ } catch (BuildException be) {
+ // file is on wrong format, swallow
+ checksumMatches = false;
+ }
+ } else {
+ checksumMatches = false;
+ }
+ } else {
+ File dest = (File) destination;
+ fos = new FileOutputStream(dest);
+ fos.write(format.format(new Object[] {
+ checksum,
+ src.getName(),
+ FileUtils
+ .getRelativePath(dest
+ .getParentFile(),
+ src),
+ FileUtils
+ .getRelativePath(getProject()
+ .getBaseDir(),
+ src),
+ src.getAbsolutePath()
+ }).getBytes());
+ fos.write(StringUtils.LINE_SEP.getBytes());
+ fos.close();
+ fos = null;
+ }
+ }
+ }
+ if (totalproperty != null) {
+ // Calculate the total checksum
+ // Convert the keys (source files) into a sorted array.
+ File[] keyArray = allDigests.keySet().toArray(new File[allDigests.size()]);
+ // File is Comparable, but sort-order is platform
+ // dependent (case-insensitive on Windows)
+ Arrays.sort(keyArray, new Comparator<File>() {
+ public int compare(File f1, File f2) {
+ return f1 == null ? (f2 == null ? 0 : -1)
+ : (f2 == null ? 1
+ : getRelativeFilePath(f1)
+ .compareTo(getRelativeFilePath(f2)));
+ }
+ });
+ // Loop over the checksums and generate a total hash.
+ messageDigest.reset();
+ for (File src : keyArray) {
+ // Add the digest for the file content
+ byte[] digest = allDigests.get(src);
+ messageDigest.update(digest);
+
+ // Add the file path
+ String fileName = getRelativeFilePath(src);
+ messageDigest.update(fileName.getBytes());
+ }
+ String totalChecksum = createDigestString(messageDigest.digest());
+ getProject().setNewProperty(totalproperty, totalChecksum);
+ }
+ } catch (Exception e) {
+ throw new BuildException(e, getLocation());
+ } finally {
+ FileUtils.close(fis);
+ FileUtils.close(fos);
+ }
+ return checksumMatches;
+ }
+
+ private String createDigestString(byte[] fileDigest) {
+ StringBuffer checksumSb = new StringBuffer();
+ for (int i = 0; i < fileDigest.length; i++) {
+ String hexStr = Integer.toHexString(BYTE_MASK & fileDigest[i]);
+ if (hexStr.length() < 2) {
+ checksumSb.append("0");
+ }
+ checksumSb.append(hexStr);
+ }
+ return checksumSb.toString();
+ }
+
+ /**
+ * Converts an array of characters representing hexadecimal values into an
+ * array of bytes of those same values. The returned array will be half the
+ * length of the passed array, as it takes two characters to represent any
+ * given byte. An exception is thrown if the passed char array has an odd
+ * number of elements.
+ *
+ * NOTE: This code is copied from jakarta-commons codec.
+ * @param data an array of characters representing hexadecimal values
+ * @return the converted array of bytes
+ * @throws BuildException on error
+ */
+ public static byte[] decodeHex(char[] data) throws BuildException {
+ int l = data.length;
+
+ if ((l & 0x01) != 0) {
+ throw new BuildException("odd number of characters.");
+ }
+
+ byte[] out = new byte[l >> 1];
+
+ // two characters form the hex value.
+ for (int i = 0, j = 0; j < l; i++) {
+ int f = Character.digit(data[j++], WORD) << NIBBLE;
+ f = f | Character.digit(data[j++], WORD);
+ out[i] = (byte) (f & BYTE_MASK);
+ }
+
+ return out;
+ }
+
+ /**
+ * reads the checksum from a file using the specified format.
+ *
+ * @since 1.7
+ */
+ private String readChecksum(File f) {
+ BufferedReader diskChecksumReader = null;
+ try {
+ diskChecksumReader = new BufferedReader(new FileReader(f));
+ Object[] result = format.parse(diskChecksumReader.readLine());
+ if (result == null || result.length == 0 || result[0] == null) {
+ throw new BuildException("failed to find a checksum");
+ }
+ return (String) result[0];
+ } catch (IOException e) {
+ throw new BuildException("Couldn't read checksum file " + f, e);
+ } catch (ParseException e) {
+ throw new BuildException("Couldn't read checksum file " + f, e);
+ } finally {
+ FileUtils.close(diskChecksumReader);
+ }
+ }
+
+ /**
+ * @since Ant 1.8.2
+ */
+ private String getRelativeFilePath(File f) {
+ String path = (String) relativeFilePaths.get(f);
+ if (path == null) {
+ //bug 37386. this should not occur, but it has, once.
+ throw new BuildException("Internal error: "
+ + "relativeFilePaths could not match file "
+ + f + "\n"
+ + "please file a bug report on this");
+ }
+ return path;
+ }
+
+ /**
+ * Helper class for the format attribute.
+ *
+ * @since 1.7
+ */
+ public static class FormatElement extends EnumeratedAttribute {
+ private static HashMap<String, MessageFormat> formatMap = new HashMap<String, MessageFormat>();
+ private static final String CHECKSUM = "CHECKSUM";
+ private static final String MD5SUM = "MD5SUM";
+ private static final String SVF = "SVF";
+
+ static {
+ formatMap.put(CHECKSUM, new MessageFormat("{0}"));
+ formatMap.put(MD5SUM, new MessageFormat("{0} *{1}"));
+ formatMap.put(SVF, new MessageFormat("MD5 ({1}) = {0}"));
+ }
+
+ /** Constructor for FormatElement */
+ public FormatElement() {
+ super();
+ }
+
+ /**
+ * Get the default value - CHECKSUM.
+ * @return the defaul value.
+ */
+ public static FormatElement getDefault() {
+ FormatElement e = new FormatElement();
+ e.setValue(CHECKSUM);
+ return e;
+ }
+
+ /**
+ * Convert this enumerated type to a <code>MessageFormat</code>.
+ * @return a <code>MessageFormat</code> object.
+ */
+ public MessageFormat getFormat() {
+ return (MessageFormat) formatMap.get(getValue());
+ }
+
+ /**
+ * Get the valid values.
+ * @return an array of values.
+ */
+ public String[] getValues() {
+ return new String[] {CHECKSUM, MD5SUM, SVF};
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Chmod.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Chmod.java
new file mode 100644
index 00000000..ac0c3d8d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Chmod.java
@@ -0,0 +1,261 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.PatternSet;
+
+/**
+ * Chmod equivalent for unix-like environments.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="filesystem"
+ * @todo Refactor so it does not extend from ExecuteOn and then turn around
+ * and unsupport several attributes.
+ */
+public class Chmod extends ExecuteOn {
+
+ private FileSet defaultSet = new FileSet();
+ private boolean defaultSetDefined = false;
+ private boolean havePerm = false;
+
+ /**
+ * Chmod task for setting file and directory permissions.
+ */
+ public Chmod() {
+ super.setExecutable("chmod");
+ super.setParallel(true);
+ super.setSkipEmptyFilesets(true);
+ }
+
+ /**
+ * Set the project of this task.
+ * Calls the super class and sets the project on dhe default FileSet.
+ * @param project the project for this task.
+ * @see org.apache.tools.ant.ProjectComponent#setProject
+ */
+ public void setProject(Project project) {
+ super.setProject(project);
+ defaultSet.setProject(project);
+ }
+
+ /**
+ * The file or single directory of which the permissions must be changed.
+ * @param src the source file or directory.
+ */
+ public void setFile(File src) {
+ FileSet fs = new FileSet();
+ fs.setFile(src);
+ addFileset(fs);
+ }
+
+ /**
+ * The directory which holds the files whose permissions must be changed.
+ * @param src the directory.
+ */
+ public void setDir(File src) {
+ defaultSet.setDir(src);
+ }
+
+ /**
+ * Set the new permissions.
+ * @param perm the new permissions.
+ */
+ public void setPerm(String perm) {
+ createArg().setValue(perm);
+ havePerm = true;
+ }
+
+ /**
+ * Add a name entry on the include list.
+ * @return a NameEntry to be configured.
+ */
+ public PatternSet.NameEntry createInclude() {
+ defaultSetDefined = true;
+ return defaultSet.createInclude();
+ }
+
+ /**
+ * Add a name entry on the exclude list.
+ * @return a nameentry to be configured.
+ */
+ public PatternSet.NameEntry createExclude() {
+ defaultSetDefined = true;
+ return defaultSet.createExclude();
+ }
+
+ /**
+ * Add a set of patterns.
+ * @return a patternset to be configured.
+ */
+ public PatternSet createPatternSet() {
+ defaultSetDefined = true;
+ return defaultSet.createPatternSet();
+ }
+
+ /**
+ * Sets the set of include patterns. Patterns may be separated by a comma
+ * or a space.
+ *
+ * @param includes the string containing the include patterns.
+ */
+ public void setIncludes(String includes) {
+ defaultSetDefined = true;
+ defaultSet.setIncludes(includes);
+ }
+
+ /**
+ * Sets the set of exclude patterns. Patterns may be separated by a comma
+ * or a space.
+ *
+ * @param excludes the string containing the exclude patterns.
+ */
+ public void setExcludes(String excludes) {
+ defaultSetDefined = true;
+ defaultSet.setExcludes(excludes);
+ }
+
+ /**
+ * Sets whether default exclusions should be used or not.
+ *
+ * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
+ * should be used, "false"|"off"|"no" when they
+ * shouldn't be used.
+ */
+ public void setDefaultexcludes(boolean useDefaultExcludes) {
+ defaultSetDefined = true;
+ defaultSet.setDefaultexcludes(useDefaultExcludes);
+ }
+
+ /**
+ * Check the attributes and nested elements.
+ */
+ protected void checkConfiguration() {
+ if (!havePerm) {
+ throw new BuildException("Required attribute perm not set in chmod",
+ getLocation());
+ }
+
+ if (defaultSetDefined && defaultSet.getDir(getProject()) != null) {
+ addFileset(defaultSet);
+ }
+ super.checkConfiguration();
+ }
+
+ /**
+ * Carry out the chmoding.
+ * @throws BuildException on error.
+ */
+ public void execute() throws BuildException {
+ /*
+ * In Ant 1.1, <chmod dir="foo" /> means, change the permissions
+ * of directory foo, not anything inside of it. This is the case the
+ * second branch of the if statement below catches for backwards
+ * compatibility.
+ */
+ if (defaultSetDefined || defaultSet.getDir(getProject()) == null) {
+ try {
+ super.execute();
+ } finally {
+ if (defaultSetDefined && defaultSet.getDir(getProject()) != null) {
+ filesets.removeElement(defaultSet);
+ }
+ }
+ } else if (isValidOs()) {
+ // we are chmodding the given directory
+ Execute execute = prepareExec();
+ Commandline cloned = (Commandline) cmdl.clone();
+ cloned.createArgument().setValue(defaultSet.getDir(getProject())
+ .getPath());
+ try {
+ execute.setCommandline(cloned.getCommandline());
+ runExecute(execute);
+ } catch (IOException e) {
+ throw new BuildException("Execute failed: " + e, e, getLocation());
+ } finally {
+ // close the output file if required
+ logFlush();
+ }
+ }
+ }
+
+ /**
+ * Set the executable.
+ * This is not allowed for Chmod.
+ * @param e ignored.
+ * @throws BuildException always.
+ * @ant.attribute ignore="true"
+ */
+ public void setExecutable(String e) {
+ throw new BuildException(getTaskType()
+ + " doesn\'t support the executable attribute", getLocation());
+ }
+
+ /**
+ * Set the command.
+ * This is not allowed for Chmod.
+ * @param cmdl ignored.
+ * @throws BuildException always.
+ * @ant.attribute ignore="true"
+ */
+ public void setCommand(Commandline cmdl) {
+ throw new BuildException(getTaskType()
+ + " doesn\'t support the command attribute", getLocation());
+ }
+
+ /**
+ * This is not allowed for Chmod.
+ * @param skip ignored.
+ * @throws BuildException always.
+ * @ant.attribute ignore="true"
+ */
+ public void setSkipEmptyFilesets(boolean skip) {
+ throw new BuildException(getTaskType()
+ + " doesn\'t support the skipemptyfileset attribute", getLocation());
+ }
+
+ /**
+ * This is not allowed for Chmod.
+ * @param b ignored.
+ * @throws BuildException always.
+ * @ant.attribute ignore="true"
+ */
+ public void setAddsourcefile(boolean b) {
+ throw new BuildException(getTaskType()
+ + " doesn\'t support the addsourcefile attribute", getLocation());
+ }
+
+ /**
+ * Check if the os is valid.
+ * Always include unix.
+ * @return true if the os is valid.
+ */
+ protected boolean isValidOs() {
+ return getOs() == null && getOsFamily() == null
+ ? Os.isFamily(Os.FAMILY_UNIX) : super.isValidOs();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Classloader.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Classloader.java
new file mode 100644
index 00000000..8a5967c7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Classloader.java
@@ -0,0 +1,244 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * EXPERIMENTAL
+ * Create or modifies ClassLoader. The required pathRef parameter
+ * will be used to add classpath elements.
+ *
+ * The classpath is a regular path. Currently only file components are
+ * supported (future extensions may allow URLs).
+ *
+ * You can modify the core loader by not specifying any name or using
+ * "ant.coreLoader". (the core loader is used to load system ant
+ * tasks and for taskdefs that don't specify an explicit path).
+ *
+ * Taskdef and typedef can use the loader you create if the name follows
+ * the "ant.loader.NAME" pattern. NAME will be used as a pathref when
+ * calling taskdef.
+ *
+ * This tasks will not modify the core loader if "build.sysclasspath=only"
+ *
+ * The typical use is:
+ * <pre>
+ * &lt;path id="ant.deps" &gt;
+ * &lt;fileset dir="myDir" &gt;
+ * &lt;include name="junit.jar, bsf.jar, js.jar, etc"/&gt;
+ * &lt;/fileset&gt;
+ * &lt;/path&gt;
+ *
+ * &lt;classloader pathRef="ant.deps" /&gt;
+ *
+ * </pre>
+ *
+ */
+public class Classloader extends Task {
+ /** @see MagicNames#SYSTEM_LOADER_REF */
+ public static final String SYSTEM_LOADER_REF = MagicNames.SYSTEM_LOADER_REF;
+
+ private String name = null;
+ private Path classpath;
+ private boolean reset = false;
+ private boolean parentFirst = true;
+ private String parentName = null;
+
+ /**
+ * Default constructor
+ */
+ public Classloader() {
+ }
+
+ /** Name of the loader. If none, the default loader will be modified
+ *
+ * @param name the name of this loader
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Reset the classloader, if it already exists. A new loader will
+ * be created and all the references to the old one will be removed.
+ * (it is not possible to remove paths from a loader). The new
+ * path will be used.
+ *
+ * @param b true if the loader is to be reset.
+ */
+ public void setReset(boolean b) {
+ this.reset = b;
+ }
+
+ /**
+ * Set reverse attribute.
+ * @param b if true reverse the normal classloader lookup.
+ * @deprecated use setParentFirst with a negated argument instead
+ */
+ public void setReverse(boolean b) {
+ this.parentFirst = !b;
+ }
+
+ /**
+ * Set reverse attribute.
+ * @param b if true reverse the normal classloader lookup.
+ */
+ public void setParentFirst(boolean b) {
+ this.parentFirst = b;
+ }
+
+ /**
+ * Set the name of the parent.
+ * @param name the parent name.
+ */
+ public void setParentName(String name) {
+ this.parentName = name;
+ }
+
+
+ /** Specify which path will be used. If the loader already exists
+ * and is an AntClassLoader (or any other loader we can extend),
+ * the path will be added to the loader.
+ * @param pathRef a reference to a path.
+ * @throws BuildException if there is a problem.
+ */
+ public void setClasspathRef(Reference pathRef) throws BuildException {
+ classpath = (Path) pathRef.getReferencedObject(getProject());
+ }
+
+ /**
+ * Set the classpath to be used when searching for component being defined
+ *
+ * @param classpath an Ant Path object containing the classpath.
+ */
+ public void setClasspath(Path classpath) {
+ if (this.classpath == null) {
+ this.classpath = classpath;
+ } else {
+ this.classpath.append(classpath);
+ }
+ }
+
+ /**
+ * Create a classpath.
+ * @return a path for configuration.
+ */
+ public Path createClasspath() {
+ if (this.classpath == null) {
+ this.classpath = new Path(null);
+ }
+ return this.classpath.createPath();
+ }
+
+
+ /**
+ * do the classloader manipulation.
+ */
+ public void execute() {
+ try {
+ // Gump friendly - don't mess with the core loader if only classpath
+ if ("only".equals(getProject().getProperty("build.sysclasspath"))
+ && (name == null || SYSTEM_LOADER_REF.equals(name))) {
+ log("Changing the system loader is disabled "
+ + "by build.sysclasspath=only", Project.MSG_WARN);
+ return;
+ }
+
+ String loaderName = (name == null) ? SYSTEM_LOADER_REF : name;
+
+ Object obj = getProject().getReference(loaderName);
+ if (reset) {
+ // Are any other references held ? Can we 'close' the loader
+ // so it removes the locks on jars ?
+ obj = null; // a new one will be created.
+ }
+
+ // TODO maybe use reflection to addPathElement (other patterns ?)
+ if (obj != null && !(obj instanceof AntClassLoader)) {
+ log("Referenced object is not an AntClassLoader",
+ Project.MSG_ERR);
+ return;
+ }
+
+ AntClassLoader acl = (AntClassLoader) obj;
+ boolean existingLoader = acl != null;
+
+ if (acl == null) {
+ // Construct a new class loader
+ Object parent = null;
+ if (parentName != null) {
+ parent = getProject().getReference(parentName);
+ if (!(parent instanceof ClassLoader)) {
+ parent = null;
+ }
+ }
+ // TODO: allow user to request the system or no parent
+ if (parent == null) {
+ parent = this.getClass().getClassLoader();
+ }
+
+ if (name == null) {
+ // The core loader must be reverse
+ //reverse=true;
+ }
+ getProject().log("Setting parent loader " + name + " "
+ + parent + " " + parentFirst, Project.MSG_DEBUG);
+
+ // The param is "parentFirst"
+ acl = AntClassLoader.newAntClassLoader((ClassLoader) parent,
+ getProject(), classpath, parentFirst);
+
+ getProject().addReference(loaderName, acl);
+
+ if (name == null) {
+ // This allows the core loader to load optional tasks
+ // without delegating
+ acl.addLoaderPackageRoot("org.apache.tools.ant.taskdefs.optional");
+ getProject().setCoreLoader(acl);
+ }
+ }
+
+ if (existingLoader && classpath != null) {
+ String[] list = classpath.list();
+ for (int i = 0; i < list.length; i++) {
+ File f = new File(list[i]);
+ if (f.exists()) {
+ log("Adding to class loader " + acl + " " + f.getAbsolutePath(),
+ Project.MSG_DEBUG);
+ acl.addPathElement(f.getAbsolutePath());
+ }
+ }
+ }
+
+ // TODO add exceptions
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CloseResources.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CloseResources.java
new file mode 100644
index 00000000..71a94559
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CloseResources.java
@@ -0,0 +1,59 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.URLProvider;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Not a real task but used during tests.
+ *
+ * Closes the resources associated with an URL. In particular this is
+ * going to close the jars associated with a jar:file: URL - and it
+ * does so in a way that the Java VM still thinks it is open, so use
+ * it at your own risk.
+ */
+public class CloseResources extends Task {
+ private Union resources = new Union();
+
+ public void add(ResourceCollection rc) {
+ resources.add(rc);
+ }
+
+ public void execute() {
+ for (Resource r : resources) {
+ URLProvider up = r.as(URLProvider.class);
+ if (up != null) {
+ URL u = up.getURL();
+ try {
+ FileUtils.close(u.openConnection());
+ } catch (IOException ex) {
+ // ignore
+ }
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CommandLauncherTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CommandLauncherTask.java
new file mode 100644
index 00000000..1e1bf948
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CommandLauncherTask.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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.launcher.CommandLauncher;
+
+/**
+ * Task that configures the {@link
+ * org.apache.tools.ant.taskdefs.launcher.CommandLauncher} to used
+ * when starting external processes.
+ * @since Ant 1.9.0
+ */
+public class CommandLauncherTask extends Task {
+ private boolean vmLauncher;
+ private CommandLauncher commandLauncher;
+
+ public synchronized void addConfigured(CommandLauncher commandLauncher) {
+ if (this.commandLauncher != null) {
+ throw new BuildException("Only one CommandLauncher can be installed");
+ }
+ this.commandLauncher = commandLauncher;
+ }
+
+ @Override
+ public void execute() {
+ if (commandLauncher != null) {
+ if (vmLauncher) {
+ CommandLauncher.setVMLauncher(getProject(), commandLauncher);
+ } else {
+ CommandLauncher.setShellLauncher(getProject(), commandLauncher);
+ }
+ }
+ }
+
+ public void setVmLauncher(boolean vmLauncher) {
+ this.vmLauncher = vmLauncher;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Componentdef.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Componentdef.java
new file mode 100644
index 00000000..1c5590cd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Componentdef.java
@@ -0,0 +1,40 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+/**
+ * Adds a component definition to the current project.
+ * <p>Used in the current project two attributes are needed, the name that identifies
+ * this component uniquely, and the full name of the class (including the packages) that
+ * implements this component.</p>
+ *
+ * @since Ant 1.8
+ * @ant.task category="internal"
+ */
+public class Componentdef extends Definer {
+
+ /**
+ * Default constructor.
+ * Creates a new Componentdef instance.
+ * Sets the restrict attribute to true.
+ */
+ public Componentdef() {
+ setRestrict(true);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Concat.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Concat.java
new file mode 100644
index 00000000..1338f2a4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Concat.java
@@ -0,0 +1,955 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.types.FileList;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.Intersect;
+import org.apache.tools.ant.types.resources.LogOutputResource;
+import org.apache.tools.ant.types.resources.Resources;
+import org.apache.tools.ant.types.resources.Restrict;
+import org.apache.tools.ant.types.resources.StringResource;
+import org.apache.tools.ant.types.resources.selectors.Exists;
+import org.apache.tools.ant.types.resources.selectors.Not;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+import org.apache.tools.ant.util.ConcatResourceInputStream;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.ReaderInputStream;
+import org.apache.tools.ant.util.ResourceUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * This class contains the 'concat' task, used to concatenate a series
+ * of files into a single stream. The destination of this stream may
+ * be the system console, or a file. The following is a sample
+ * invocation:
+ *
+ * <pre>
+ * &lt;concat destfile=&quot;${build.dir}/index.xml&quot;
+ * append=&quot;false&quot;&gt;
+ *
+ * &lt;fileset dir=&quot;${xml.root.dir}&quot;
+ * includes=&quot;*.xml&quot; /&gt;
+ *
+ * &lt;/concat&gt;
+ * </pre>
+ *
+ */
+public class Concat extends Task implements ResourceCollection {
+
+ // The size of buffers to be used
+ private static final int BUFFER_SIZE = 8192;
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private static final ResourceSelector EXISTS = new Exists();
+ private static final ResourceSelector NOT_EXISTS = new Not(EXISTS);
+
+ /**
+ * sub element points to a file or contains text
+ */
+ public static class TextElement extends ProjectComponent {
+ private String value = "";
+ private boolean trimLeading = false;
+ private boolean trim = false;
+ private boolean filtering = true;
+ private String encoding = null;
+
+ /**
+ * whether to filter the text in this element
+ * or not.
+ *
+ * @param filtering true if the text should be filtered.
+ * the default value is true.
+ */
+ public void setFiltering(boolean filtering) {
+ this.filtering = filtering;
+ }
+
+ /** return the filtering attribute */
+ private boolean getFiltering() {
+ return filtering;
+ }
+
+ /**
+ * The encoding of the text element
+ *
+ * @param encoding the name of the charset used to encode
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * set the text using a file
+ * @param file the file to use
+ * @throws BuildException if the file does not exist, or cannot be
+ * read
+ */
+ public void setFile(File file) throws BuildException {
+ // non-existing files are not allowed
+ if (!file.exists()) {
+ throw new BuildException("File " + file + " does not exist.");
+ }
+
+ BufferedReader reader = null;
+ try {
+ if (this.encoding == null) {
+ reader = new BufferedReader(new FileReader(file));
+ } else {
+ reader = new BufferedReader(
+ new InputStreamReader(new FileInputStream(file),
+ this.encoding));
+ }
+ value = FileUtils.safeReadFully(reader);
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ } finally {
+ FileUtils.close(reader);
+ }
+ }
+
+ /**
+ * set the text using inline
+ * @param value the text to place inline
+ */
+ public void addText(String value) {
+ this.value += getProject().replaceProperties(value);
+ }
+
+ /**
+ * s:^\s*:: on each line of input
+ * @param strip if true do the trim
+ */
+ public void setTrimLeading(boolean strip) {
+ this.trimLeading = strip;
+ }
+
+ /**
+ * whether to call text.trim()
+ * @param trim if true trim the text
+ */
+ public void setTrim(boolean trim) {
+ this.trim = trim;
+ }
+
+ /**
+ * @return the text, after possible trimming
+ */
+ public String getValue() {
+ if (value == null) {
+ value = "";
+ }
+ if (value.trim().length() == 0) {
+ value = "";
+ }
+ if (trimLeading) {
+ char[] current = value.toCharArray();
+ StringBuffer b = new StringBuffer(current.length);
+ boolean startOfLine = true;
+ int pos = 0;
+ while (pos < current.length) {
+ char ch = current[pos++];
+ if (startOfLine) {
+ if (ch == ' ' || ch == '\t') {
+ continue;
+ }
+ startOfLine = false;
+ }
+ b.append(ch);
+ if (ch == '\n' || ch == '\r') {
+ startOfLine = true;
+ }
+ }
+ value = b.toString();
+ }
+ if (trim) {
+ value = value.trim();
+ }
+ return value;
+ }
+ }
+
+ private interface ReaderFactory<S> {
+ Reader getReader(S s) throws IOException;
+ }
+
+ /**
+ * This class reads from each of the source files in turn.
+ * The concatentated result can then be filtered as
+ * a single stream.
+ */
+ private final class MultiReader<S> extends Reader {
+ private Reader reader = null;
+ private int lastPos = 0;
+ private char[] lastChars = new char[eolString.length()];
+ private boolean needAddSeparator = false;
+ private Iterator<S> readerSources;
+ private ReaderFactory<S> factory;
+
+ private MultiReader(Iterator<S> readerSources, ReaderFactory<S> factory) {
+ this.readerSources = readerSources;
+ this.factory = factory;
+ }
+
+ private Reader getReader() throws IOException {
+ if (reader == null && readerSources.hasNext()) {
+ reader = factory.getReader(readerSources.next());
+ Arrays.fill(lastChars, (char) 0);
+ }
+ return reader;
+ }
+
+ private void nextReader() throws IOException {
+ close();
+ reader = null;
+ }
+
+ /**
+ * Read a character from the current reader object. Advance
+ * to the next if the reader is finished.
+ * @return the character read, -1 for EOF on the last reader.
+ * @exception IOException - possibly thrown by the read for a reader
+ * object.
+ */
+ public int read() throws IOException {
+ if (needAddSeparator) {
+ if (lastPos >= eolString.length()) {
+ lastPos = 0;
+ needAddSeparator = false;
+ } else {
+ return eolString.charAt(lastPos++);
+ }
+ }
+ while (getReader() != null) {
+ int ch = getReader().read();
+ if (ch == -1) {
+ nextReader();
+ if (isFixLastLine() && isMissingEndOfLine()) {
+ needAddSeparator = true;
+ lastPos = 1;
+ return eolString.charAt(0);
+ }
+ } else {
+ addLastChar((char) ch);
+ return ch;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Read into the buffer <code>cbuf</code>.
+ * @param cbuf The array to be read into.
+ * @param off The offset.
+ * @param len The length to read.
+ * @exception IOException - possibly thrown by the reads to the
+ * reader objects.
+ */
+ public int read(char[] cbuf, int off, int len)
+ throws IOException {
+
+ int amountRead = 0;
+ while (getReader() != null || needAddSeparator) {
+ if (needAddSeparator) {
+ cbuf[off] = eolString.charAt(lastPos++);
+ if (lastPos >= eolString.length()) {
+ lastPos = 0;
+ needAddSeparator = false;
+ }
+ len--;
+ off++;
+ amountRead++;
+ if (len == 0) {
+ return amountRead;
+ }
+ continue;
+ }
+ int nRead = getReader().read(cbuf, off, len);
+ if (nRead == -1 || nRead == 0) {
+ nextReader();
+ if (isFixLastLine() && isMissingEndOfLine()) {
+ needAddSeparator = true;
+ lastPos = 0;
+ }
+ } else {
+ if (isFixLastLine()) {
+ for (int i = nRead;
+ i > (nRead - lastChars.length);
+ --i) {
+ if (i <= 0) {
+ break;
+ }
+ addLastChar(cbuf[off + i - 1]);
+ }
+ }
+ len -= nRead;
+ off += nRead;
+ amountRead += nRead;
+ if (len == 0) {
+ return amountRead;
+ }
+ }
+ }
+ if (amountRead == 0) {
+ return -1;
+ } else {
+ return amountRead;
+ }
+ }
+
+ /**
+ * Close the current reader
+ */
+ public void close() throws IOException {
+ if (reader != null) {
+ reader.close();
+ }
+ }
+
+ /**
+ * if checking for end of line at end of file
+ * add a character to the lastchars buffer
+ */
+ private void addLastChar(char ch) {
+ for (int i = lastChars.length - 2; i >= 0; --i) {
+ lastChars[i] = lastChars[i + 1];
+ }
+ lastChars[lastChars.length - 1] = ch;
+ }
+
+ /**
+ * return true if the lastchars buffer does
+ * not contain the lineseparator
+ */
+ private boolean isMissingEndOfLine() {
+ for (int i = 0; i < lastChars.length; ++i) {
+ if (lastChars[i] != eolString.charAt(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isFixLastLine() {
+ return fixLastLine && textBuffer == null;
+ }
+ }
+
+ private final class ConcatResource extends Resource {
+ private ResourceCollection c;
+
+ private ConcatResource(ResourceCollection c) {
+ this.c = c;
+ }
+ public InputStream getInputStream() throws IOException {
+ if (binary) {
+ ConcatResourceInputStream result = new ConcatResourceInputStream(c);
+ result.setManagingComponent(this);
+ return result;
+ }
+ Reader resourceReader = getFilteredReader(
+ new MultiReader<Resource>(c.iterator(), resourceReaderFactory));
+ Reader rdr;
+ if (header == null && footer == null) {
+ rdr = resourceReader;
+ } else {
+ int readerCount = 1;
+ if (header != null) {
+ readerCount++;
+ }
+ if (footer != null) {
+ readerCount++;
+ }
+ Reader[] readers = new Reader[readerCount];
+ int pos = 0;
+ if (header != null) {
+ readers[pos] = new StringReader(header.getValue());
+ if (header.getFiltering()) {
+ readers[pos] = getFilteredReader(readers[pos]);
+ }
+ pos++;
+ }
+ readers[pos++] = resourceReader;
+ if (footer != null) {
+ readers[pos] = new StringReader(footer.getValue());
+ if (footer.getFiltering()) {
+ readers[pos] = getFilteredReader(readers[pos]);
+ }
+ }
+ rdr = new MultiReader<Reader>(Arrays.asList(readers).iterator(),
+ identityReaderFactory);
+ }
+ return outputEncoding == null ? new ReaderInputStream(rdr)
+ : new ReaderInputStream(rdr, outputEncoding);
+ }
+ public String getName() {
+ return resourceName == null
+ ? "concat (" + String.valueOf(c) + ")" : resourceName;
+ }
+ }
+
+ // Attributes.
+
+ /**
+ * The destination of the stream. If <code>null</code>, the system
+ * console is used.
+ */
+ private Resource dest;
+
+ /**
+ * Whether or not the stream should be appended if the destination file
+ * exists.
+ * Defaults to <code>false</code>.
+ */
+ private boolean append;
+
+ /**
+ * Stores the input file encoding.
+ */
+ private String encoding;
+
+ /** Stores the output file encoding. */
+ private String outputEncoding;
+
+ /** Stores the binary attribute */
+ private boolean binary;
+
+ // Child elements.
+
+ /**
+ * This buffer stores the text within the 'concat' element.
+ */
+ private StringBuffer textBuffer;
+
+ /**
+ * Stores a collection of file sets and/or file lists, used to
+ * select multiple files for concatenation.
+ */
+ private Resources rc;
+
+ /** for filtering the concatenated */
+ private Vector<FilterChain> filterChains;
+ /** ignore dates on input files */
+ private boolean forceOverwrite = true;
+ /** overwrite read-only files */
+ private boolean force = false;
+ /** String to place at the start of the concatenated stream */
+ private TextElement footer;
+ /** String to place at the end of the concatenated stream */
+ private TextElement header;
+ /** add missing line.separator to files **/
+ private boolean fixLastLine = false;
+ /** endofline for fixlast line */
+ private String eolString;
+ /** outputwriter */
+ private Writer outputWriter = null;
+ /** whether to not create dest if no source files are
+ * available */
+ private boolean ignoreEmpty = true;
+ /** exposed resource name */
+ private String resourceName;
+
+ private ReaderFactory<Resource> resourceReaderFactory = new ReaderFactory<Resource>() {
+ public Reader getReader(Resource o) throws IOException {
+ InputStream is = o.getInputStream();
+ return new BufferedReader(encoding == null
+ ? new InputStreamReader(is)
+ : new InputStreamReader(is, encoding));
+ }
+ };
+
+ private ReaderFactory<Reader> identityReaderFactory = new ReaderFactory<Reader>() {
+ public Reader getReader(Reader o) {
+ return o;
+ }
+ };
+
+ /**
+ * Construct a new Concat task.
+ */
+ public Concat() {
+ reset();
+ }
+
+ /**
+ * Reset state to default.
+ */
+ public void reset() {
+ append = false;
+ forceOverwrite = true;
+ dest = null;
+ encoding = null;
+ outputEncoding = null;
+ fixLastLine = false;
+ filterChains = null;
+ footer = null;
+ header = null;
+ binary = false;
+ outputWriter = null;
+ textBuffer = null;
+ eolString = StringUtils.LINE_SEP;
+ rc = null;
+ ignoreEmpty = true;
+ force = false;
+ }
+
+ // Attribute setters.
+
+ /**
+ * Sets the destination file, or uses the console if not specified.
+ * @param destinationFile the destination file
+ */
+ public void setDestfile(File destinationFile) {
+ setDest(new FileResource(destinationFile));
+ }
+
+ /**
+ * Set the resource to write to.
+ * @param dest the Resource to write to.
+ * @since Ant 1.8
+ */
+ public void setDest(Resource dest) {
+ this.dest = dest;
+ }
+
+ /**
+ * Sets the behavior when the destination exists. If set to
+ * <code>true</code> the task will append the stream data an
+ * {@link Appendable} resource; otherwise existing content will be
+ * overwritten. Defaults to <code>false</code>.
+ * @param append if true append output.
+ */
+ public void setAppend(boolean append) {
+ this.append = append;
+ }
+
+ /**
+ * Sets the character encoding
+ * @param encoding the encoding of the input stream and unless
+ * outputencoding is set, the outputstream.
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ if (outputEncoding == null) {
+ outputEncoding = encoding;
+ }
+ }
+
+ /**
+ * Sets the character encoding for outputting
+ * @param outputEncoding the encoding for the output file
+ * @since Ant 1.6
+ */
+ public void setOutputEncoding(String outputEncoding) {
+ this.outputEncoding = outputEncoding;
+ }
+
+ /**
+ * Force overwrite existing destination file
+ * @param forceOverwrite if true always overwrite, otherwise only
+ * overwrite if the output file is older any of the
+ * input files.
+ * @since Ant 1.6
+ * @deprecated use #setOverwrite instead
+ */
+ public void setForce(boolean forceOverwrite) {
+ this.forceOverwrite = forceOverwrite;
+ }
+
+ /**
+ * Force overwrite existing destination file
+ * @param forceOverwrite if true always overwrite, otherwise only
+ * overwrite if the output file is older any of the
+ * input files.
+ * @since Ant 1.8.2
+ */
+ public void setOverwrite(boolean forceOverwrite) {
+ setForce(forceOverwrite);
+ }
+
+ /**
+ * Whether read-only destinations will be overwritten.
+ *
+ * <p>Defaults to false</p>
+ *
+ * @since Ant 1.8.2
+ */
+ public void setForceReadOnly(boolean f) {
+ force = f;
+ }
+
+ /**
+ * Sets the behavior when no source resource files are available. If set to
+ * <code>false</code> the destination file will always be created.
+ * Defaults to <code>true</code>.
+ * @param ignoreEmpty if false honour destinationfile creation.
+ * @since Ant 1.8.0
+ */
+ public void setIgnoreEmpty(boolean ignoreEmpty) {
+ this.ignoreEmpty = ignoreEmpty;
+ }
+
+ /**
+ * Set the name that will be reported by the exposed {@link Resource}.
+ * @param resourceName to set
+ * @since Ant 1.8.3
+ */
+ public void setResourceName(String resourceName) {
+ this.resourceName = resourceName;
+ }
+
+ // Nested element creators.
+
+ /**
+ * Path of files to concatenate.
+ * @return the path used for concatenating
+ * @since Ant 1.6
+ */
+ public Path createPath() {
+ Path path = new Path(getProject());
+ add(path);
+ return path;
+ }
+
+ /**
+ * Set of files to concatenate.
+ * @param set the set of files
+ */
+ public void addFileset(FileSet set) {
+ add(set);
+ }
+
+ /**
+ * List of files to concatenate.
+ * @param list the list of files
+ */
+ public void addFilelist(FileList list) {
+ add(list);
+ }
+
+ /**
+ * Add an arbitrary ResourceCollection.
+ * @param c the ResourceCollection to add.
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection c) {
+ synchronized (this) {
+ if (rc == null) {
+ rc = new Resources();
+ rc.setProject(getProject());
+ rc.setCache(true);
+ }
+ }
+ rc.add(c);
+ }
+
+ /**
+ * Adds a FilterChain.
+ * @param filterChain a filterchain to filter the concatenated input
+ * @since Ant 1.6
+ */
+ public void addFilterChain(FilterChain filterChain) {
+ if (filterChains == null) {
+ filterChains = new Vector<FilterChain>();
+ }
+ filterChains.addElement(filterChain);
+ }
+
+ /**
+ * This method adds text which appears in the 'concat' element.
+ * @param text the text to be concated.
+ */
+ public void addText(String text) {
+ if (textBuffer == null) {
+ // Initialize to the size of the first text fragment, with
+ // the hopes that it's the only one.
+ textBuffer = new StringBuffer(text.length());
+ }
+
+ // Append the fragment -- we defer property replacement until
+ // later just in case we get a partial property in a fragment.
+ textBuffer.append(text);
+ }
+
+ /**
+ * Add a header to the concatenated output
+ * @param headerToAdd the header
+ * @since Ant 1.6
+ */
+ public void addHeader(TextElement headerToAdd) {
+ this.header = headerToAdd;
+ }
+
+ /**
+ * Add a footer to the concatenated output
+ * @param footerToAdd the footer
+ * @since Ant 1.6
+ */
+ public void addFooter(TextElement footerToAdd) {
+ this.footer = footerToAdd;
+ }
+
+ /**
+ * Append line.separator to files that do not end
+ * with a line.separator, default false.
+ * @param fixLastLine if true make sure each input file has
+ * new line on the concatenated stream
+ * @since Ant 1.6
+ */
+ public void setFixLastLine(boolean fixLastLine) {
+ this.fixLastLine = fixLastLine;
+ }
+
+ /**
+ * Specify the end of line to find and to add if
+ * not present at end of each input file. This attribute
+ * is used in conjunction with fixlastline.
+ * @param crlf the type of new line to add -
+ * cr, mac, lf, unix, crlf, or dos
+ * @since Ant 1.6
+ */
+ public void setEol(FixCRLF.CrLf crlf) {
+ String s = crlf.getValue();
+ if (s.equals("cr") || s.equals("mac")) {
+ eolString = "\r";
+ } else if (s.equals("lf") || s.equals("unix")) {
+ eolString = "\n";
+ } else if (s.equals("crlf") || s.equals("dos")) {
+ eolString = "\r\n";
+ }
+ }
+
+ /**
+ * Set the output writer. This is to allow
+ * concat to be used as a nested element.
+ * @param outputWriter the output writer.
+ * @since Ant 1.6
+ */
+ public void setWriter(Writer outputWriter) {
+ this.outputWriter = outputWriter;
+ }
+
+ /**
+ * Set the binary attribute. If true, concat will concatenate the files
+ * byte for byte. This mode does not allow any filtering or other
+ * modifications to the input streams. The default value is false.
+ * @since Ant 1.6.2
+ * @param binary if true, enable binary mode.
+ */
+ public void setBinary(boolean binary) {
+ this.binary = binary;
+ }
+
+ /**
+ * Execute the concat task.
+ */
+ public void execute() {
+ validate();
+ if (binary && dest == null) {
+ throw new BuildException(
+ "dest|destfile attribute is required for binary concatenation");
+ }
+ ResourceCollection c = getResources();
+ if (isUpToDate(c)) {
+ log(dest + " is up-to-date.", Project.MSG_VERBOSE);
+ return;
+ }
+ if (c.size() == 0 && ignoreEmpty) {
+ return;
+ }
+ try {
+ //most of these are defaulted because the concat-as-a-resource code hijacks a lot:
+ ResourceUtils.copyResource(new ConcatResource(c), dest == null
+ ? new LogOutputResource(this, Project.MSG_WARN)
+ : dest,
+ null, null, true, false, append, null,
+ null, getProject(), force);
+ } catch (IOException e) {
+ throw new BuildException("error concatenating content to " + dest, e);
+ }
+ }
+
+ /**
+ * Implement ResourceCollection.
+ * @return Iterator&lt;Resource&gt;.
+ */
+ public Iterator<Resource> iterator() {
+ validate();
+ return Collections.<Resource>singletonList(new ConcatResource(getResources())).iterator();
+ }
+
+ /**
+ * Implement ResourceCollection.
+ * @return 1.
+ */
+ public int size() {
+ return 1;
+ }
+
+ /**
+ * Implement ResourceCollection.
+ * @return false.
+ */
+ public boolean isFilesystemOnly() {
+ return false;
+ }
+
+ /**
+ * Validate configuration options.
+ */
+ private void validate() {
+
+ // treat empty nested text as no text
+ sanitizeText();
+
+ // if binary check if incompatible attributes are used
+ if (binary) {
+ if (textBuffer != null) {
+ throw new BuildException(
+ "Nested text is incompatible with binary concatenation");
+ }
+ if (encoding != null || outputEncoding != null) {
+ throw new BuildException(
+ "Setting input or output encoding is incompatible with binary"
+ + " concatenation");
+ }
+ if (filterChains != null) {
+ throw new BuildException(
+ "Setting filters is incompatible with binary concatenation");
+ }
+ if (fixLastLine) {
+ throw new BuildException(
+ "Setting fixlastline is incompatible with binary concatenation");
+ }
+ if (header != null || footer != null) {
+ throw new BuildException(
+ "Nested header or footer is incompatible with binary concatenation");
+ }
+ }
+ if (dest != null && outputWriter != null) {
+ throw new BuildException(
+ "Cannot specify both a destination resource and an output writer");
+ }
+ // Sanity check our inputs.
+ if (rc == null && textBuffer == null) {
+ // Nothing to concatenate!
+ throw new BuildException(
+ "At least one resource must be provided, or some text.");
+ }
+ if (rc != null && textBuffer != null) {
+ // If using resources, disallow inline text. This is similar to
+ // using GNU 'cat' with file arguments--stdin is simply ignored.
+ throw new BuildException(
+ "Cannot include inline text when using resources.");
+ }
+ }
+
+ /**
+ * Get the resources to concatenate.
+ */
+ private ResourceCollection getResources() {
+ if (rc == null) {
+ return new StringResource(getProject(), textBuffer.toString());
+ }
+ if (dest != null) {
+ Intersect checkDestNotInSources = new Intersect();
+ checkDestNotInSources.setProject(getProject());
+ checkDestNotInSources.add(rc);
+ checkDestNotInSources.add(dest);
+ if (checkDestNotInSources.size() > 0) {
+ throw new BuildException("Destination resource " + dest
+ + " was specified as an input resource.");
+ }
+ }
+ Restrict noexistRc = new Restrict();
+ noexistRc.add(NOT_EXISTS);
+ noexistRc.add(rc);
+ for (Resource r : noexistRc) {
+ log(r + " does not exist.", Project.MSG_ERR);
+ }
+ Restrict result = new Restrict();
+ result.add(EXISTS);
+ result.add(rc);
+ return result;
+ }
+
+ private boolean isUpToDate(ResourceCollection c) {
+ if (dest == null || forceOverwrite) {
+ return false;
+ }
+ for (Resource r : c) {
+ if (SelectorUtils.isOutOfDate(r, dest, FILE_UTILS.getFileTimestampGranularity())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Treat empty nested text as no text.
+ *
+ * <p>Depending on the XML parser, addText may have been called
+ * for &quot;ignorable whitespace&quot; as well.</p>
+ */
+ private void sanitizeText() {
+ if (textBuffer != null && "".equals(textBuffer.toString().trim())) {
+ textBuffer = null;
+ }
+ }
+
+ private Reader getFilteredReader(Reader r) {
+ if (filterChains == null) {
+ return r;
+ }
+ ChainReaderHelper helper = new ChainReaderHelper();
+ helper.setBufferSize(BUFFER_SIZE);
+ helper.setPrimaryReader(r);
+ helper.setFilterChains(filterChains);
+ helper.setProject(getProject());
+ //used to be a BufferedReader here, but we should be buffering lower:
+ return helper.getAssembledReader();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ConditionTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ConditionTask.java
new file mode 100644
index 00000000..60904e03
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ConditionTask.java
@@ -0,0 +1,130 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.taskdefs.condition.ConditionBase;
+
+/**
+ * Task to set a property conditionally using &lt;uptodate&gt;, &lt;available&gt;,
+ * and many other supported conditions.
+ *
+ * <p>This task supports boolean logic as well as pluggable conditions
+ * to decide, whether a property should be set.</p>
+ *
+ * <p>This task does not extend Task to take advantage of
+ * ConditionBase.</p>
+ *
+ * @since Ant 1.4
+ *
+ * @ant.task category="control"
+ */
+public class ConditionTask extends ConditionBase {
+
+ private String property = null;
+ private Object value = "true";
+ private Object alternative = null;
+
+ /**
+ * Constructor, names this task "condition".
+ */
+ public ConditionTask() {
+ super("condition");
+ }
+
+ /**
+ * The name of the property to set. Required.
+ * @param p the name of the property
+ * @since Ant 1.4
+ */
+ public void setProperty(String p) {
+ property = p;
+ }
+
+ /**
+ * The value for the property to set, if condition evaluates to true.
+ * Defaults to "true".
+ * @param value the (Object) value of the property
+ * @since Ant 1.8
+ */
+ public void setValue(Object value) {
+ this.value = value;
+ }
+
+ /**
+ * The value for the property to set, if condition evaluates to true.
+ * Defaults to "true".
+ * @param v the value of the property
+ * @since Ant 1.4
+ */
+ public void setValue(String v) {
+ setValue((Object) v);
+ }
+
+ /**
+ * The value for the property to set, if condition evaluates to false.
+ * If this attribute is not specified, the property will not be set.
+ * @param alt the alternate value of the property.
+ * @since Ant 1.8
+ */
+ public void setElse(Object alt) {
+ alternative = alt;
+ }
+
+ /**
+ * The value for the property to set, if condition evaluates to false.
+ * If this attribute is not specified, the property will not be set.
+ * @param e the alternate value of the property.
+ * @since Ant 1.6.3
+ */
+ public void setElse(String e) {
+ setElse((Object) e);
+ }
+
+ /**
+ * See whether our nested condition holds and set the property.
+ *
+ * @since Ant 1.4
+ * @exception BuildException if an error occurs
+ */
+ public void execute() throws BuildException {
+ if (countConditions() > 1) {
+ throw new BuildException("You must not nest more than one condition into <"
+ + getTaskName() + ">");
+ }
+ if (countConditions() < 1) {
+ throw new BuildException("You must nest a condition into <" + getTaskName() + ">");
+ }
+ if (property == null) {
+ throw new BuildException("The property attribute is required.");
+ }
+ Condition c = (Condition) getConditions().nextElement();
+ if (c.eval()) {
+ log("Condition true; setting " + property + " to " + value, Project.MSG_DEBUG);
+ PropertyHelper.getPropertyHelper(getProject()).setNewProperty(property, value);
+ } else if (alternative != null) {
+ log("Condition false; setting " + property + " to " + alternative, Project.MSG_DEBUG);
+ PropertyHelper.getPropertyHelper(getProject()).setNewProperty(property, alternative);
+ } else {
+ log("Condition false; not setting " + property, Project.MSG_DEBUG);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copy.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copy.java
new file mode 100644
index 00000000..2394b8f4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copy.java
@@ -0,0 +1,1111 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.types.FilterSet;
+import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.FlatFileNameMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.ant.util.LinkedHashtable;
+import org.apache.tools.ant.util.ResourceUtils;
+import org.apache.tools.ant.util.SourceFileScanner;
+
+/**
+ * <p>Copies a file or directory to a new file
+ * or directory. Files are only copied if the source file is newer
+ * than the destination file, or when the destination file does not
+ * exist. It is possible to explicitly overwrite existing files.</p>
+ *
+ * <p>This implementation is based on Arnout Kuiper's initial design
+ * document, the following mailing list discussions, and the
+ * copyfile/copydir tasks.</p>
+ *
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="filesystem"
+ */
+public class Copy extends Task {
+ private static final String MSG_WHEN_COPYING_EMPTY_RC_TO_FILE =
+ "Cannot perform operation from directory to file.";
+
+ static final File NULL_FILE_PLACEHOLDER = new File("/NULL_FILE");
+ static final String LINE_SEPARATOR = System.getProperty("line.separator");
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected File file = null; // the source file
+ protected File destFile = null; // the destination file
+ protected File destDir = null; // the destination directory
+ protected Vector<ResourceCollection> rcs = new Vector<ResourceCollection>();
+ // here to provide API backwards compatibility
+ protected Vector<ResourceCollection> filesets = rcs;
+
+ private boolean enableMultipleMappings = false;
+ protected boolean filtering = false;
+ protected boolean preserveLastModified = false;
+ protected boolean forceOverwrite = false;
+ protected boolean flatten = false;
+ protected int verbosity = Project.MSG_VERBOSE;
+ protected boolean includeEmpty = true;
+ protected boolean failonerror = true;
+
+ protected Hashtable<String, String[]> fileCopyMap = new LinkedHashtable<String, String[]>();
+ protected Hashtable<String, String[]> dirCopyMap = new LinkedHashtable<String, String[]>();
+ protected Hashtable<File, File> completeDirMap = new LinkedHashtable<File, File>();
+
+ protected Mapper mapperElement = null;
+ protected FileUtils fileUtils;
+ //CheckStyle:VisibilityModifier ON
+ private final Vector<FilterChain> filterChains = new Vector<FilterChain>();
+ private final Vector<FilterSet> filterSets = new Vector<FilterSet>();
+ private String inputEncoding = null;
+ private String outputEncoding = null;
+ private long granularity = 0;
+ private boolean force = false;
+ private boolean quiet = false;
+
+ // used to store the single non-file resource to copy when the
+ // tofile attribute has been used
+ private Resource singleResource = null;
+
+ /**
+ * Copy task constructor.
+ */
+ public Copy() {
+ fileUtils = FileUtils.getFileUtils();
+ granularity = fileUtils.getFileTimestampGranularity();
+ }
+
+ /**
+ * Get the FileUtils for this task.
+ * @return the fileutils object.
+ */
+ protected FileUtils getFileUtils() {
+ return fileUtils;
+ }
+
+ /**
+ * Set a single source file to copy.
+ * @param file the file to copy.
+ */
+ public void setFile(final File file) {
+ this.file = file;
+ }
+
+ /**
+ * Set the destination file.
+ * @param destFile the file to copy to.
+ */
+ public void setTofile(final File destFile) {
+ this.destFile = destFile;
+ }
+
+ /**
+ * Set the destination directory.
+ * @param destDir the destination directory.
+ */
+ public void setTodir(final File destDir) {
+ this.destDir = destDir;
+ }
+
+ /**
+ * Add a FilterChain.
+ * @return a filter chain object.
+ */
+ public FilterChain createFilterChain() {
+ final FilterChain filterChain = new FilterChain();
+ filterChains.addElement(filterChain);
+ return filterChain;
+ }
+
+ /**
+ * Add a filterset.
+ * @return a filter set object.
+ */
+ public FilterSet createFilterSet() {
+ final FilterSet filterSet = new FilterSet();
+ filterSets.addElement(filterSet);
+ return filterSet;
+ }
+
+ /**
+ * Give the copied files the same last modified time as the original files.
+ * @param preserve a boolean string.
+ * @deprecated since 1.5.x.
+ * setPreserveLastModified(String) has been deprecated and
+ * replaced with setPreserveLastModified(boolean) to
+ * consistently let the Introspection mechanism work.
+ */
+ @Deprecated
+ public void setPreserveLastModified(final String preserve) {
+ setPreserveLastModified(Project.toBoolean(preserve));
+ }
+
+ /**
+ * Give the copied files the same last modified time as the original files.
+ * @param preserve if true preserve the modified time; default is false.
+ */
+ public void setPreserveLastModified(final boolean preserve) {
+ preserveLastModified = preserve;
+ }
+
+ /**
+ * Get whether to give the copied files the same last modified time as
+ * the original files.
+ * @return the whether destination files will inherit the modification
+ * times of the corresponding source files.
+ * @since 1.32, Ant 1.5
+ */
+ public boolean getPreserveLastModified() {
+ return preserveLastModified;
+ }
+
+ /**
+ * Get the filtersets being applied to this operation.
+ *
+ * @return a vector of FilterSet objects.
+ */
+ protected Vector<FilterSet> getFilterSets() {
+ return filterSets;
+ }
+
+ /**
+ * Get the filterchains being applied to this operation.
+ *
+ * @return a vector of FilterChain objects.
+ */
+ protected Vector<FilterChain> getFilterChains() {
+ return filterChains;
+ }
+
+ /**
+ * Set filtering mode.
+ * @param filtering if true enable filtering; default is false.
+ */
+ public void setFiltering(final boolean filtering) {
+ this.filtering = filtering;
+ }
+
+ /**
+ * Set overwrite mode regarding existing destination file(s).
+ * @param overwrite if true force overwriting of destination file(s)
+ * even if the destination file(s) are younger than
+ * the corresponding source file. Default is false.
+ */
+ public void setOverwrite(final boolean overwrite) {
+ this.forceOverwrite = overwrite;
+ }
+
+ /**
+ * Whether read-only destinations will be overwritten.
+ *
+ * <p>Defaults to false</p>
+ *
+ * @since Ant 1.8.2
+ */
+ public void setForce(final boolean f) {
+ force = f;
+ }
+
+ /**
+ * Whether read-only destinations will be overwritten.
+ *
+ * @since Ant 1.8.2
+ */
+ public boolean getForce() {
+ return force;
+ }
+
+ /**
+ * Set whether files copied from directory trees will be "flattened"
+ * into a single directory. If there are multiple files with
+ * the same name in the source directory tree, only the first
+ * file will be copied into the "flattened" directory, unless
+ * the forceoverwrite attribute is true.
+ * @param flatten if true flatten the destination directory. Default
+ * is false.
+ */
+ public void setFlatten(final boolean flatten) {
+ this.flatten = flatten;
+ }
+
+ /**
+ * Set verbose mode. Used to force listing of all names of copied files.
+ * @param verbose whether to output the names of copied files.
+ * Default is false.
+ */
+ public void setVerbose(final boolean verbose) {
+ this.verbosity = verbose ? Project.MSG_INFO : Project.MSG_VERBOSE;
+ }
+
+ /**
+ * Set whether to copy empty directories.
+ * @param includeEmpty if true copy empty directories. Default is true.
+ */
+ public void setIncludeEmptyDirs(final boolean includeEmpty) {
+ this.includeEmpty = includeEmpty;
+ }
+
+ /**
+ * Set quiet mode. Used to hide messages when a file or directory to be
+ * copied does not exist.
+ *
+ * @param quiet
+ * whether or not to display error messages when a file or
+ * directory does not exist. Default is false.
+ */
+ public void setQuiet(final boolean quiet) {
+ this.quiet = quiet;
+ }
+
+ /**
+ * Set method of handling mappers that return multiple
+ * mappings for a given source path.
+ * @param enableMultipleMappings If true the task will
+ * copy to all the mappings for a given source path, if
+ * false, only the first file or directory is
+ * processed.
+ * By default, this setting is false to provide backward
+ * compatibility with earlier releases.
+ * @since Ant 1.6
+ */
+ public void setEnableMultipleMappings(final boolean enableMultipleMappings) {
+ this.enableMultipleMappings = enableMultipleMappings;
+ }
+
+ /**
+ * Get whether multiple mapping is enabled.
+ * @return true if multiple mapping is enabled; false otherwise.
+ */
+ public boolean isEnableMultipleMapping() {
+ return enableMultipleMappings;
+ }
+
+ /**
+ * Set whether to fail when errors are encountered. If false, note errors
+ * to the output but keep going. Default is true.
+ * @param failonerror true or false.
+ */
+ public void setFailOnError(final boolean failonerror) {
+ this.failonerror = failonerror;
+ }
+
+ /**
+ * Add a set of files to copy.
+ * @param set a set of files to copy.
+ */
+ public void addFileset(final FileSet set) {
+ add(set);
+ }
+
+ /**
+ * Add a collection of files to copy.
+ * @param res a resource collection to copy.
+ * @since Ant 1.7
+ */
+ public void add(final ResourceCollection res) {
+ rcs.add(res);
+ }
+
+ /**
+ * Define the mapper to map source to destination files.
+ * @return a mapper to be configured.
+ * @exception BuildException if more than one mapper is defined.
+ */
+ public Mapper createMapper() throws BuildException {
+ if (mapperElement != null) {
+ throw new BuildException("Cannot define more than one mapper",
+ getLocation());
+ }
+ mapperElement = new Mapper(getProject());
+ return mapperElement;
+ }
+
+ /**
+ * Add a nested filenamemapper.
+ * @param fileNameMapper the mapper to add.
+ * @since Ant 1.6.3
+ */
+ public void add(final FileNameMapper fileNameMapper) {
+ createMapper().add(fileNameMapper);
+ }
+
+ /**
+ * Set the character encoding.
+ * @param encoding the character encoding.
+ * @since 1.32, Ant 1.5
+ */
+ public void setEncoding(final String encoding) {
+ this.inputEncoding = encoding;
+ if (outputEncoding == null) {
+ outputEncoding = encoding;
+ }
+ }
+
+ /**
+ * Get the character encoding to be used.
+ * @return the character encoding, <code>null</code> if not set.
+ *
+ * @since 1.32, Ant 1.5
+ */
+ public String getEncoding() {
+ return inputEncoding;
+ }
+
+ /**
+ * Set the character encoding for output files.
+ * @param encoding the output character encoding.
+ * @since Ant 1.6
+ */
+ public void setOutputEncoding(final String encoding) {
+ this.outputEncoding = encoding;
+ }
+
+ /**
+ * Get the character encoding for output files.
+ * @return the character encoding for output files,
+ * <code>null</code> if not set.
+ *
+ * @since Ant 1.6
+ */
+ public String getOutputEncoding() {
+ return outputEncoding;
+ }
+
+ /**
+ * Set the number of milliseconds leeway to give before deciding a
+ * target is out of date.
+ *
+ * <p>Default is 1 second, or 2 seconds on DOS systems.</p>
+ * @param granularity the granularity used to decide if a target is out of
+ * date.
+ * @since Ant 1.6.2
+ */
+ public void setGranularity(final long granularity) {
+ this.granularity = granularity;
+ }
+
+ /**
+ * Perform the copy operation.
+ * @exception BuildException if an error occurs.
+ */
+ @Override
+ public void execute() throws BuildException {
+ final File savedFile = file; // may be altered in validateAttributes
+ final File savedDestFile = destFile;
+ final File savedDestDir = destDir;
+ ResourceCollection savedRc = null;
+ if (file == null && destFile != null && rcs.size() == 1) {
+ // will be removed in validateAttributes
+ savedRc = rcs.elementAt(0);
+ }
+
+ try {
+ // make sure we don't have an illegal set of options
+ try {
+ validateAttributes();
+ } catch (final BuildException e) {
+ if (failonerror
+ || !getMessage(e)
+ .equals(MSG_WHEN_COPYING_EMPTY_RC_TO_FILE)) {
+ throw e;
+ } else {
+ log("Warning: " + getMessage(e), Project.MSG_ERR);
+ return;
+ }
+ }
+
+ // deal with the single file
+ copySingleFile();
+
+ // deal with the ResourceCollections
+
+ /* for historical and performance reasons we have to do
+ things in a rather complex way.
+
+ (1) Move is optimized to move directories if a fileset
+ has been included completely, therefore FileSets need a
+ special treatment. This is also required to support
+ the failOnError semantice (skip filesets with broken
+ basedir but handle the remaining collections).
+
+ (2) We carry around a few protected methods that work
+ on basedirs and arrays of names. To optimize stuff, all
+ resources with the same basedir get collected in
+ separate lists and then each list is handled in one go.
+ */
+
+ final HashMap<File, List<String>> filesByBasedir = new HashMap<File, List<String>>();
+ final HashMap<File, List<String>> dirsByBasedir = new HashMap<File, List<String>>();
+ final HashSet<File> baseDirs = new HashSet<File>();
+ final ArrayList<Resource> nonFileResources = new ArrayList<Resource>();
+ final int size = rcs.size();
+ for (int i = 0; i < size; i++) {
+ final ResourceCollection rc = rcs.elementAt(i);
+
+ // Step (1) - beware of the ZipFileSet
+ if (rc instanceof FileSet && rc.isFilesystemOnly()) {
+ final FileSet fs = (FileSet) rc;
+ DirectoryScanner ds = null;
+ try {
+ ds = fs.getDirectoryScanner(getProject());
+ } catch (final BuildException e) {
+ if (failonerror
+ || !getMessage(e).endsWith(DirectoryScanner
+ .DOES_NOT_EXIST_POSTFIX)) {
+ throw e;
+ } else {
+ if (!quiet) {
+ log("Warning: " + getMessage(e), Project.MSG_ERR);
+ }
+ continue;
+ }
+ }
+ final File fromDir = fs.getDir(getProject());
+
+ final String[] srcFiles = ds.getIncludedFiles();
+ final String[] srcDirs = ds.getIncludedDirectories();
+ if (!flatten && mapperElement == null
+ && ds.isEverythingIncluded() && !fs.hasPatterns()) {
+ completeDirMap.put(fromDir, destDir);
+ }
+ add(fromDir, srcFiles, filesByBasedir);
+ add(fromDir, srcDirs, dirsByBasedir);
+ baseDirs.add(fromDir);
+ } else { // not a fileset or contains non-file resources
+
+ if (!rc.isFilesystemOnly() && !supportsNonFileResources()) {
+ throw new BuildException(
+ "Only FileSystem resources are supported.");
+ }
+
+ for (final Resource r : rc) {
+ if (!r.isExists()) {
+ final String message = "Warning: Could not find resource "
+ + r.toLongString() + " to copy.";
+ if (!failonerror) {
+ if (!quiet) {
+ log(message, Project.MSG_ERR);
+ }
+ } else {
+ throw new BuildException(message);
+ }
+ continue;
+ }
+
+ File baseDir = NULL_FILE_PLACEHOLDER;
+ String name = r.getName();
+ final FileProvider fp = r.as(FileProvider.class);
+ if (fp != null) {
+ final FileResource fr = ResourceUtils.asFileResource(fp);
+ baseDir = getKeyFile(fr.getBaseDir());
+ if (fr.getBaseDir() == null) {
+ name = fr.getFile().getAbsolutePath();
+ }
+ }
+
+ // copying of dirs is trivial and can be done
+ // for non-file resources as well as for real
+ // files.
+ if (r.isDirectory() || fp != null) {
+ add(baseDir, name,
+ r.isDirectory() ? dirsByBasedir
+ : filesByBasedir);
+ baseDirs.add(baseDir);
+ } else { // a not-directory file resource
+ // needs special treatment
+ nonFileResources.add(r);
+ }
+ }
+ }
+ }
+
+ iterateOverBaseDirs(baseDirs, dirsByBasedir, filesByBasedir);
+
+ // do all the copy operations now...
+ try {
+ doFileOperations();
+ } catch (final BuildException e) {
+ if (!failonerror) {
+ if (!quiet) {
+ log("Warning: " + getMessage(e), Project.MSG_ERR);
+ }
+ } else {
+ throw e;
+ }
+ }
+
+ if (nonFileResources.size() > 0 || singleResource != null) {
+ final Resource[] nonFiles =
+ nonFileResources.toArray(new Resource[nonFileResources.size()]);
+ // restrict to out-of-date resources
+ final Map<Resource, String[]> map = scan(nonFiles, destDir);
+ if (singleResource != null) {
+ map.put(singleResource,
+ new String[] {destFile.getAbsolutePath()});
+ }
+ try {
+ doResourceOperations(map);
+ } catch (final BuildException e) {
+ if (!failonerror) {
+ if (!quiet) {
+ log("Warning: " + getMessage(e), Project.MSG_ERR);
+ }
+ } else {
+ throw e;
+ }
+ }
+ }
+ } finally {
+ // clean up again, so this instance can be used a second
+ // time
+ singleResource = null;
+ file = savedFile;
+ destFile = savedDestFile;
+ destDir = savedDestDir;
+ if (savedRc != null) {
+ rcs.insertElementAt(savedRc, 0);
+ }
+ fileCopyMap.clear();
+ dirCopyMap.clear();
+ completeDirMap.clear();
+ }
+ }
+
+ /************************************************************************
+ ** protected and private methods
+ ************************************************************************/
+
+ private void copySingleFile() {
+ // deal with the single file
+ if (file != null) {
+ if (file.exists()) {
+ if (destFile == null) {
+ destFile = new File(destDir, file.getName());
+ }
+ if (forceOverwrite || !destFile.exists()
+ || (file.lastModified() - granularity
+ > destFile.lastModified())) {
+ fileCopyMap.put(file.getAbsolutePath(),
+ new String[] {destFile.getAbsolutePath()});
+ } else {
+ log(file + " omitted as " + destFile
+ + " is up to date.", Project.MSG_VERBOSE);
+ }
+ } else {
+ final String message = "Warning: Could not find file "
+ + file.getAbsolutePath() + " to copy.";
+ if (!failonerror) {
+ if (!quiet) {
+ log(message, Project.MSG_ERR);
+ }
+ } else {
+ throw new BuildException(message);
+ }
+ }
+ }
+ }
+
+ private void iterateOverBaseDirs(
+ final HashSet<File> baseDirs, final HashMap<File, List<String>> dirsByBasedir, final HashMap<File, List<String>> filesByBasedir) {
+
+ for (final File f : baseDirs) {
+ final List<String> files = filesByBasedir.get(f);
+ final List<String> dirs = dirsByBasedir.get(f);
+
+ String[] srcFiles = new String[0];
+ if (files != null) {
+ srcFiles = files.toArray(srcFiles);
+ }
+ String[] srcDirs = new String[0];
+ if (dirs != null) {
+ srcDirs = dirs.toArray(srcDirs);
+ }
+ scan(f == NULL_FILE_PLACEHOLDER ? null : f, destDir, srcFiles,
+ srcDirs);
+ }
+ }
+
+ /**
+ * Ensure we have a consistent and legal set of attributes, and set
+ * any internal flags necessary based on different combinations
+ * of attributes.
+ * @exception BuildException if an error occurs.
+ */
+ protected void validateAttributes() throws BuildException {
+ if (file == null && rcs.size() == 0) {
+ throw new BuildException(
+ "Specify at least one source--a file or a resource collection.");
+ }
+ if (destFile != null && destDir != null) {
+ throw new BuildException(
+ "Only one of tofile and todir may be set.");
+ }
+ if (destFile == null && destDir == null) {
+ throw new BuildException("One of tofile or todir must be set.");
+ }
+ if (file != null && file.isDirectory()) {
+ throw new BuildException("Use a resource collection to copy directories.");
+ }
+ if (destFile != null && rcs.size() > 0) {
+ if (rcs.size() > 1) {
+ throw new BuildException(
+ "Cannot concatenate multiple files into a single file.");
+ } else {
+ final ResourceCollection rc = rcs.elementAt(0);
+ if (!rc.isFilesystemOnly() && !supportsNonFileResources()) {
+ throw new BuildException("Only FileSystem resources are"
+ + " supported.");
+ }
+ if (rc.size() == 0) {
+ throw new BuildException(MSG_WHEN_COPYING_EMPTY_RC_TO_FILE);
+ } else if (rc.size() == 1) {
+ final Resource res = rc.iterator().next();
+ final FileProvider r = res.as(FileProvider.class);
+ if (file == null) {
+ if (r != null) {
+ file = r.getFile();
+ } else {
+ singleResource = res;
+ }
+ rcs.removeElementAt(0);
+ } else {
+ throw new BuildException(
+ "Cannot concatenate multiple files into a single file.");
+ }
+ } else {
+ throw new BuildException(
+ "Cannot concatenate multiple files into a single file.");
+ }
+ }
+ }
+ if (destFile != null) {
+ destDir = destFile.getParentFile();
+ }
+ }
+
+ /**
+ * Compares source files to destination files to see if they should be
+ * copied.
+ *
+ * @param fromDir The source directory.
+ * @param toDir The destination directory.
+ * @param files A list of files to copy.
+ * @param dirs A list of directories to copy.
+ */
+ protected void scan(final File fromDir, final File toDir, final String[] files,
+ final String[] dirs) {
+ final FileNameMapper mapper = getMapper();
+ buildMap(fromDir, toDir, files, mapper, fileCopyMap);
+
+ if (includeEmpty) {
+ buildMap(fromDir, toDir, dirs, mapper, dirCopyMap);
+ }
+ }
+
+ /**
+ * Compares source resources to destination files to see if they
+ * should be copied.
+ *
+ * @param fromResources The source resources.
+ * @param toDir The destination directory.
+ *
+ * @return a Map with the out-of-date resources as keys and an
+ * array of target file names as values.
+ *
+ * @since Ant 1.7
+ */
+ protected Map<Resource, String[]> scan(final Resource[] fromResources, final File toDir) {
+ return buildMap(fromResources, toDir, getMapper());
+ }
+
+ /**
+ * Add to a map of files/directories to copy.
+ *
+ * @param fromDir the source directory.
+ * @param toDir the destination directory.
+ * @param names a list of filenames.
+ * @param mapper a <code>FileNameMapper</code> value.
+ * @param map a map of source file to array of destination files.
+ */
+ protected void buildMap(final File fromDir, final File toDir, final String[] names,
+ final FileNameMapper mapper, final Hashtable<String, String[]> map) {
+ String[] toCopy = null;
+ if (forceOverwrite) {
+ final Vector<String> v = new Vector<String>();
+ for (int i = 0; i < names.length; i++) {
+ if (mapper.mapFileName(names[i]) != null) {
+ v.addElement(names[i]);
+ }
+ }
+ toCopy = new String[v.size()];
+ v.copyInto(toCopy);
+ } else {
+ final SourceFileScanner ds = new SourceFileScanner(this);
+ toCopy = ds.restrict(names, fromDir, toDir, mapper, granularity);
+ }
+ for (int i = 0; i < toCopy.length; i++) {
+ final File src = new File(fromDir, toCopy[i]);
+ final String[] mappedFiles = mapper.mapFileName(toCopy[i]);
+
+ if (!enableMultipleMappings) {
+ map.put(src.getAbsolutePath(),
+ new String[] {new File(toDir, mappedFiles[0]).getAbsolutePath()});
+ } else {
+ // reuse the array created by the mapper
+ for (int k = 0; k < mappedFiles.length; k++) {
+ mappedFiles[k] = new File(toDir, mappedFiles[k]).getAbsolutePath();
+ }
+ map.put(src.getAbsolutePath(), mappedFiles);
+ }
+ }
+ }
+
+ /**
+ * Create a map of resources to copy.
+ *
+ * @param fromResources The source resources.
+ * @param toDir the destination directory.
+ * @param mapper a <code>FileNameMapper</code> value.
+ * @return a map of source resource to array of destination files.
+ * @since Ant 1.7
+ */
+ protected Map<Resource, String[]> buildMap(final Resource[] fromResources, final File toDir,
+ final FileNameMapper mapper) {
+ final HashMap<Resource, String[]> map = new HashMap<Resource, String[]>();
+ Resource[] toCopy = null;
+ if (forceOverwrite) {
+ final Vector<Resource> v = new Vector<Resource>();
+ for (int i = 0; i < fromResources.length; i++) {
+ if (mapper.mapFileName(fromResources[i].getName()) != null) {
+ v.addElement(fromResources[i]);
+ }
+ }
+ toCopy = new Resource[v.size()];
+ v.copyInto(toCopy);
+ } else {
+ toCopy = ResourceUtils.selectOutOfDateSources(this, fromResources,
+ mapper,
+ new ResourceFactory() {
+ public Resource getResource(final String name) {
+ return new FileResource(toDir, name);
+ }
+ },
+ granularity);
+ }
+ for (int i = 0; i < toCopy.length; i++) {
+ final String[] mappedFiles = mapper.mapFileName(toCopy[i].getName());
+ for (int j = 0; j < mappedFiles.length; j++) {
+ if (mappedFiles[j] == null) {
+ throw new BuildException("Can't copy a resource without a"
+ + " name if the mapper doesn't"
+ + " provide one.");
+ }
+ }
+
+ if (!enableMultipleMappings) {
+ map.put(toCopy[i],
+ new String[] {new File(toDir, mappedFiles[0]).getAbsolutePath()});
+ } else {
+ // reuse the array created by the mapper
+ for (int k = 0; k < mappedFiles.length; k++) {
+ mappedFiles[k] = new File(toDir, mappedFiles[k]).getAbsolutePath();
+ }
+ map.put(toCopy[i], mappedFiles);
+ }
+ }
+ return map;
+ }
+
+ /**
+ * Actually does the file (and possibly empty directory) copies.
+ * This is a good method for subclasses to override.
+ */
+ protected void doFileOperations() {
+ if (fileCopyMap.size() > 0) {
+ log("Copying " + fileCopyMap.size()
+ + " file" + (fileCopyMap.size() == 1 ? "" : "s")
+ + " to " + destDir.getAbsolutePath());
+
+ for (final Map.Entry<String, String[]> e : fileCopyMap.entrySet()) {
+ final String fromFile = e.getKey();
+ final String[] toFiles = e.getValue();
+
+ for (int i = 0; i < toFiles.length; i++) {
+ final String toFile = toFiles[i];
+
+ if (fromFile.equals(toFile)) {
+ log("Skipping self-copy of " + fromFile, verbosity);
+ continue;
+ }
+ try {
+ log("Copying " + fromFile + " to " + toFile, verbosity);
+
+ final FilterSetCollection executionFilters =
+ new FilterSetCollection();
+ if (filtering) {
+ executionFilters
+ .addFilterSet(getProject().getGlobalFilterSet());
+ }
+ for (final FilterSet filterSet : filterSets) {
+ executionFilters.addFilterSet(filterSet);
+ }
+ fileUtils.copyFile(new File(fromFile), new File(toFile),
+ executionFilters,
+ filterChains, forceOverwrite,
+ preserveLastModified,
+ /* append: */ false, inputEncoding,
+ outputEncoding, getProject(),
+ getForce());
+ } catch (final IOException ioe) {
+ String msg = "Failed to copy " + fromFile + " to " + toFile
+ + " due to " + getDueTo(ioe);
+ final File targetFile = new File(toFile);
+ if (!(ioe instanceof
+ ResourceUtils.ReadOnlyTargetFileException)
+ && targetFile.exists() && !targetFile.delete()) {
+ msg += " and I couldn't delete the corrupt " + toFile;
+ }
+ if (failonerror) {
+ throw new BuildException(msg, ioe, getLocation());
+ }
+ log(msg, Project.MSG_ERR);
+ }
+ }
+ }
+ }
+ if (includeEmpty) {
+ int createCount = 0;
+ for (final String[] dirs : dirCopyMap.values()) {
+ for (int i = 0; i < dirs.length; i++) {
+ final File d = new File(dirs[i]);
+ if (!d.exists()) {
+ if (!(d.mkdirs() || d.isDirectory())) {
+ log("Unable to create directory "
+ + d.getAbsolutePath(), Project.MSG_ERR);
+ } else {
+ createCount++;
+ }
+ }
+ }
+ }
+ if (createCount > 0) {
+ log("Copied " + dirCopyMap.size()
+ + " empty director"
+ + (dirCopyMap.size() == 1 ? "y" : "ies")
+ + " to " + createCount
+ + " empty director"
+ + (createCount == 1 ? "y" : "ies") + " under "
+ + destDir.getAbsolutePath());
+ }
+ }
+ }
+
+ /**
+ * Actually does the resource copies.
+ * This is a good method for subclasses to override.
+ * @param map a map of source resource to array of destination files.
+ * @since Ant 1.7
+ */
+ protected void doResourceOperations(final Map<Resource, String[]> map) {
+ if (map.size() > 0) {
+ log("Copying " + map.size()
+ + " resource" + (map.size() == 1 ? "" : "s")
+ + " to " + destDir.getAbsolutePath());
+
+ for (final Map.Entry<Resource, String[]> e : map.entrySet()) {
+ final Resource fromResource = e.getKey();
+ for (final String toFile : e.getValue()) {
+ try {
+ log("Copying " + fromResource + " to " + toFile,
+ verbosity);
+
+ final FilterSetCollection executionFilters = new FilterSetCollection();
+ if (filtering) {
+ executionFilters
+ .addFilterSet(getProject().getGlobalFilterSet());
+ }
+ for (final FilterSet filterSet : filterSets) {
+ executionFilters.addFilterSet(filterSet);
+ }
+ ResourceUtils.copyResource(fromResource,
+ new FileResource(destDir,
+ toFile),
+ executionFilters,
+ filterChains,
+ forceOverwrite,
+ preserveLastModified,
+ /* append: */ false,
+ inputEncoding,
+ outputEncoding,
+ getProject(),
+ getForce());
+ } catch (final IOException ioe) {
+ String msg = "Failed to copy " + fromResource
+ + " to " + toFile
+ + " due to " + getDueTo(ioe);
+ final File targetFile = new File(toFile);
+ if (!(ioe instanceof
+ ResourceUtils.ReadOnlyTargetFileException)
+ && targetFile.exists() && !targetFile.delete()) {
+ msg += " and I couldn't delete the corrupt " + toFile;
+ }
+ if (failonerror) {
+ throw new BuildException(msg, ioe, getLocation());
+ }
+ log(msg, Project.MSG_ERR);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether this task can deal with non-file resources.
+ *
+ * <p>&lt;copy&gt; can while &lt;move&gt; can't since we don't
+ * know how to remove non-file resources.</p>
+ *
+ * <p>This implementation returns true only if this task is
+ * &lt;copy&gt;. Any subclass of this class that also wants to
+ * support non-file resources needs to override this method. We
+ * need to do so for backwards compatibility reasons since we
+ * can't expect subclasses to support resources.</p>
+ * @return true if this task supports non file resources.
+ * @since Ant 1.7
+ */
+ protected boolean supportsNonFileResources() {
+ return getClass().equals(Copy.class);
+ }
+
+ /**
+ * Adds the given strings to a list contained in the given map.
+ * The file is the key into the map.
+ */
+ private static void add(File baseDir, final String[] names, final Map<File, List<String>> m) {
+ if (names != null) {
+ baseDir = getKeyFile(baseDir);
+ List<String> l = m.get(baseDir);
+ if (l == null) {
+ l = new ArrayList<String>(names.length);
+ m.put(baseDir, l);
+ }
+ l.addAll(java.util.Arrays.asList(names));
+ }
+ }
+
+ /**
+ * Adds the given string to a list contained in the given map.
+ * The file is the key into the map.
+ */
+ private static void add(final File baseDir, final String name, final Map<File, List<String>> m) {
+ if (name != null) {
+ add(baseDir, new String[] {name}, m);
+ }
+ }
+
+ /**
+ * Either returns its argument or a plaeholder if the argument is null.
+ */
+ private static File getKeyFile(final File f) {
+ return f == null ? NULL_FILE_PLACEHOLDER : f;
+ }
+
+ /**
+ * returns the mapper to use based on nested elements or the
+ * flatten attribute.
+ */
+ private FileNameMapper getMapper() {
+ FileNameMapper mapper = null;
+ if (mapperElement != null) {
+ mapper = mapperElement.getImplementation();
+ } else if (flatten) {
+ mapper = new FlatFileNameMapper();
+ } else {
+ mapper = new IdentityMapper();
+ }
+ return mapper;
+ }
+
+ /**
+ * Handle getMessage() for exceptions.
+ * @param ex the exception to handle
+ * @return ex.getMessage() if ex.getMessage() is not null
+ * otherwise return ex.toString()
+ */
+ private String getMessage(final Exception ex) {
+ return ex.getMessage() == null ? ex.toString() : ex.getMessage();
+ }
+
+ /**
+ * Returns a reason for failure based on
+ * the exception thrown.
+ * If the exception is not IOException output the class name,
+ * output the message
+ * if the exception is MalformedInput add a little note.
+ */
+ private String getDueTo(final Exception ex) {
+ final boolean baseIOException = ex.getClass() == IOException.class;
+ final StringBuffer message = new StringBuffer();
+ if (!baseIOException || ex.getMessage() == null) {
+ message.append(ex.getClass().getName());
+ }
+ if (ex.getMessage() != null) {
+ if (!baseIOException) {
+ message.append(" ");
+ }
+ message.append(ex.getMessage());
+ }
+ if (ex.getClass().getName().indexOf("MalformedInput") != -1) {
+ message.append(LINE_SEPARATOR);
+ message.append(
+ "This is normally due to the input file containing invalid");
+ message.append(LINE_SEPARATOR);
+ message.append("bytes for the character encoding used : ");
+ message.append(
+ (inputEncoding == null
+ ? fileUtils.getDefaultEncoding() : inputEncoding));
+ message.append(LINE_SEPARATOR);
+ }
+ return message.toString();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CopyPath.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CopyPath.java
new file mode 100644
index 00000000..53596fd9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/CopyPath.java
@@ -0,0 +1,214 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Copy the contents of a path to a destination, using the mapper of choice
+ *
+ * @since Ant 1.7.0
+ *
+ * @ant.task category="filesystem"
+ * @deprecated this task should have never been released and was
+ * obsoleted by ResourceCollection support in Copy available since Ant
+ * 1.7.0. Don't use it.
+ */
+
+public class CopyPath extends Task {
+
+ // Error messages
+ /** No destdir attribute */
+ public static final String ERROR_NO_DESTDIR = "No destDir specified";
+
+ /** No path */
+ public static final String ERROR_NO_PATH = "No path specified";
+
+ /** No mapper */
+ public static final String ERROR_NO_MAPPER = "No mapper specified";
+
+ // fileutils
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ // --- Fields --
+ private FileNameMapper mapper;
+
+ private Path path;
+
+ private File destDir;
+
+ // TODO not read, yet in a public setter
+ private long granularity = FILE_UTILS.getFileTimestampGranularity();
+
+ private boolean preserveLastModified = false;
+
+ /**
+ * The dest dir attribute.
+ * @param destDir the value of the destdir attribute.
+ */
+ public void setDestDir(File destDir) {
+ this.destDir = destDir;
+ }
+
+ /**
+ * add a mapper
+ *
+ * @param newmapper the mapper to add.
+ */
+ public void add(FileNameMapper newmapper) {
+ if (mapper != null) {
+ throw new BuildException("Only one mapper allowed");
+ }
+ mapper = newmapper;
+ }
+
+ /**
+ * Set the path to be used when running the Java class.
+ *
+ * @param s
+ * an Ant Path object containing the path.
+ */
+ public void setPath(Path s) {
+ createPath().append(s);
+ }
+
+ /**
+ * Set the path to use by reference.
+ *
+ * @param r
+ * a reference to an existing path.
+ */
+ public void setPathRef(Reference r) {
+ createPath().setRefid(r);
+ }
+
+ /**
+ * Create a path.
+ *
+ * @return a path to be configured.
+ */
+ public Path createPath() {
+ if (path == null) {
+ path = new Path(getProject());
+ }
+ return path;
+ }
+
+ /**
+ * Set the number of milliseconds leeway to give before deciding a
+ * target is out of date.
+ * TODO: This is not yet used.
+ * @param granularity the granularity used to decide if a target is out of
+ * date.
+ */
+ public void setGranularity(long granularity) {
+ this.granularity = granularity;
+ }
+
+ /**
+ * Give the copied files the same last modified time as the original files.
+ * @param preserveLastModified if true preserve the modified time;
+ * default is false.
+ */
+ public void setPreserveLastModified(boolean preserveLastModified) {
+ this.preserveLastModified = preserveLastModified;
+ }
+
+ /**
+ * Ensure we have a consistent and legal set of attributes, and set any
+ * internal flags necessary based on different combinations of attributes.
+ *
+ * @throws BuildException
+ * if an error occurs.
+ */
+ protected void validateAttributes() throws BuildException {
+ if (destDir == null) {
+ throw new BuildException(ERROR_NO_DESTDIR);
+ }
+ if (mapper == null) {
+ throw new BuildException(ERROR_NO_MAPPER);
+ }
+ if (path == null) {
+ throw new BuildException(ERROR_NO_PATH);
+ }
+ }
+
+ /**
+ * This is a very minimal derivative of the normal copy logic.
+ *
+ * @throws BuildException
+ * if something goes wrong with the build.
+ */
+ public void execute() throws BuildException {
+ log("This task should have never been released and was"
+ + " obsoleted by ResourceCollection support in <copy> available"
+ + " since Ant 1.7.0. Don't use it.",
+ Project.MSG_ERR);
+
+ validateAttributes();
+ String[] sourceFiles = path.list();
+ if (sourceFiles.length == 0) {
+ log("Path is empty", Project.MSG_VERBOSE);
+ return;
+ }
+
+ for (int sources = 0; sources < sourceFiles.length; sources++) {
+
+ String sourceFileName = sourceFiles[sources];
+ File sourceFile = new File(sourceFileName);
+ String[] toFiles = (String[]) mapper.mapFileName(sourceFileName);
+
+ for (int i = 0; i < toFiles.length; i++) {
+ String destFileName = toFiles[i];
+ File destFile = new File(destDir, destFileName);
+
+ if (sourceFile.equals(destFile)) {
+ log("Skipping self-copy of " + sourceFileName, Project.MSG_VERBOSE);
+ continue;
+ }
+ if (sourceFile.isDirectory()) {
+ log("Skipping directory " + sourceFileName);
+ continue;
+ }
+ try {
+ log("Copying " + sourceFile + " to " + destFile, Project.MSG_VERBOSE);
+
+ FILE_UTILS.copyFile(sourceFile, destFile, null, null, false,
+ preserveLastModified, null, null, getProject());
+ } catch (IOException ioe) {
+ String msg = "Failed to copy " + sourceFile + " to " + destFile + " due to "
+ + ioe.getMessage();
+ if (destFile.exists() && !destFile.delete()) {
+ msg += " and I couldn't delete the corrupt " + destFile;
+ }
+ throw new BuildException(msg, ioe, getLocation());
+ }
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copydir.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copydir.java
new file mode 100644
index 00000000..8b4efc3b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copydir.java
@@ -0,0 +1,163 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+
+/**
+ * Copies a directory.
+ *
+ * @since Ant 1.1
+ *
+ * @deprecated The copydir task is deprecated since Ant 1.2. Use copy instead.
+ */
+
+public class Copydir extends MatchingTask {
+
+ private File srcDir;
+ private File destDir;
+ private boolean filtering = false;
+ private boolean flatten = false;
+ private boolean forceOverwrite = false;
+ private Hashtable<String, String> filecopyList = new Hashtable<String, String>();
+
+ /**
+ * The src attribute
+ *
+ * @param src the source file
+ */
+ public void setSrc(File src) {
+ srcDir = src;
+ }
+
+ /**
+ * The dest attribute
+ *
+ * @param dest the destination file
+ */
+ public void setDest(File dest) {
+ destDir = dest;
+ }
+
+ /**
+ * The filtering attribute.
+ * Default is false.
+ * @param filter if true use filtering
+ */
+ public void setFiltering(boolean filter) {
+ filtering = filter;
+ }
+
+ /**
+ * The flattening attribute.
+ * Default is false.
+ * @param flatten if true use flattening
+ */
+ public void setFlatten(boolean flatten) {
+ this.flatten = flatten;
+ }
+
+ /**
+ * The forceoverwrite attribute.
+ * Default is false.
+ * @param force if true overwrite even if the destination file
+ * is newer that the source file
+ */
+ public void setForceoverwrite(boolean force) {
+ forceOverwrite = force;
+ }
+
+ /**
+ * Execute the task.
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ log("DEPRECATED - The copydir task is deprecated. Use copy instead.");
+
+ if (srcDir == null) {
+ throw new BuildException("src attribute must be set!",
+ getLocation());
+ }
+
+ if (!srcDir.exists()) {
+ throw new BuildException("srcdir " + srcDir.toString()
+ + " does not exist!", getLocation());
+ }
+
+ if (destDir == null) {
+ throw new BuildException("The dest attribute must be set.",
+ getLocation());
+ }
+
+ if (srcDir.equals(destDir)) {
+ log("Warning: src == dest", Project.MSG_WARN);
+ }
+
+ DirectoryScanner ds = super.getDirectoryScanner(srcDir);
+
+ try {
+ String[] files = ds.getIncludedFiles();
+ scanDir(srcDir, destDir, files);
+ if (filecopyList.size() > 0) {
+ log("Copying " + filecopyList.size() + " file"
+ + (filecopyList.size() == 1 ? "" : "s")
+ + " to " + destDir.getAbsolutePath());
+ for (Map.Entry<String, String> e : filecopyList.entrySet()) {
+ String fromFile = e.getKey();
+ String toFile = e.getValue();
+ try {
+ getProject().copyFile(fromFile, toFile, filtering,
+ forceOverwrite);
+ } catch (IOException ioe) {
+ String msg = "Failed to copy " + fromFile + " to "
+ + toFile + " due to " + ioe.getMessage();
+ throw new BuildException(msg, ioe, getLocation());
+ }
+ }
+ }
+ } finally {
+ filecopyList.clear();
+ }
+ }
+
+ private void scanDir(File from, File to, String[] files) {
+ for (int i = 0; i < files.length; i++) {
+ String filename = files[i];
+ File srcFile = new File(from, filename);
+ File destFile;
+ if (flatten) {
+ destFile = new File(to, new File(filename).getName());
+ } else {
+ destFile = new File(to, filename);
+ }
+ if (forceOverwrite
+ || (srcFile.lastModified() > destFile.lastModified())) {
+ filecopyList.put(srcFile.getAbsolutePath(),
+ destFile.getAbsolutePath());
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copyfile.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copyfile.java
new file mode 100644
index 00000000..e9acb1d6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copyfile.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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Copies a file.
+ *
+ * @since Ant 1.1
+ *
+ * @deprecated The copyfile task is deprecated since Ant 1.2. Use
+ * copy instead.
+ */
+
+public class Copyfile extends Task {
+
+ private File srcFile;
+ private File destFile;
+ private boolean filtering = false;
+ private boolean forceOverwrite = false;
+
+ /**
+ * Set the source file.
+ * @param src the source file.
+ */
+ public void setSrc(File src) {
+ srcFile = src;
+ }
+
+ /**
+ * The forceoverwrite attribute.
+ * Default is false.
+ * @param force if true overwrite even if the destination file
+ * is newer that the source file
+ */
+ public void setForceoverwrite(boolean force) {
+ forceOverwrite = force;
+ }
+
+ /**
+ * Set the destination file.
+ * @param dest the destination file.
+ */
+ public void setDest(File dest) {
+ destFile = dest;
+ }
+
+ /**
+ * The filtering attribute.
+ * Default is false.
+ * @param filter if true use filtering
+ */
+ public void setFiltering(String filter) {
+ filtering = Project.toBoolean(filter);
+ }
+
+ /**
+ * Execute the task.
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ log("DEPRECATED - The copyfile task is deprecated. Use copy instead.");
+
+ if (srcFile == null) {
+ throw new BuildException("The src attribute must be present.",
+ getLocation());
+ }
+
+ if (!srcFile.exists()) {
+ throw new BuildException("src " + srcFile.toString()
+ + " does not exist.", getLocation());
+ }
+
+ if (destFile == null) {
+ throw new BuildException("The dest attribute must be present.",
+ getLocation());
+ }
+
+ if (srcFile.equals(destFile)) {
+ log("Warning: src == dest", Project.MSG_WARN);
+ }
+
+ if (forceOverwrite
+ || srcFile.lastModified() > destFile.lastModified()) {
+ try {
+ getProject().copyFile(srcFile, destFile, filtering, forceOverwrite);
+ } catch (IOException ioe) {
+ String msg = "Error copying file: " + srcFile.getAbsolutePath()
+ + " due to " + ioe.getMessage();
+ throw new BuildException(msg);
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Cvs.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Cvs.java
new file mode 100644
index 00000000..822a4ea9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Cvs.java
@@ -0,0 +1,41 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+/**
+ * Performs operations on a CVS repository.
+ *
+ * original 1.20
+ *
+ * NOTE: This implementation has been moved to AbstractCvsTask with
+ * the addition of some accessors for extensibility.
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="scm"
+ */
+public class Cvs extends AbstractCvsTask {
+
+ /**
+ * CVS Task - now implemented by the Abstract CVS Task base class
+ */
+ public Cvs() {
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DefBase.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DefBase.java
new file mode 100644
index 00000000..b52bbb72
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DefBase.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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.ClasspathUtils;
+
+/**
+ * Base class for Definitions handling uri and class loading.
+ * (This was part of Definer)
+ *
+ * @since Ant 1.6
+ */
+public abstract class DefBase extends AntlibDefinition {
+ private ClassLoader createdLoader;
+ private ClasspathUtils.Delegate cpDelegate;
+
+ /**
+ * Check if classpath attributes have been set.
+ * (to be called before getCpDelegate() is used.
+ * @return true if cpDelegate has been created.
+ */
+ protected boolean hasCpDelegate() {
+ return cpDelegate != null;
+ }
+
+ /**
+ * @param reverseLoader if true a delegated loader will take precedence over
+ * the parent
+ * @deprecated since 1.6.x.
+ * stop using this attribute
+ * @ant.attribute ignore="true"
+ */
+ public void setReverseLoader(boolean reverseLoader) {
+ getDelegate().setReverseLoader(reverseLoader);
+ log("The reverseloader attribute is DEPRECATED. It will be removed",
+ Project.MSG_WARN);
+ }
+
+ /**
+ * @return the classpath for this definition
+ */
+ public Path getClasspath() {
+ return getDelegate().getClasspath();
+ }
+
+ /**
+ * @return the reverse loader attribute of the classpath delegate.
+ */
+ public boolean isReverseLoader() {
+ return getDelegate().isReverseLoader();
+ }
+
+ /**
+ * Returns the loader id of the class path Delegate.
+ * @return the loader id
+ */
+ public String getLoaderId() {
+ return getDelegate().getClassLoadId();
+ }
+
+ /**
+ * Returns the class path id of the class path delegate.
+ * @return the class path id
+ */
+ public String getClasspathId() {
+ return getDelegate().getClassLoadId();
+ }
+
+ /**
+ * Set the classpath to be used when searching for component being defined.
+ *
+ * @param classpath an Ant Path object containing the classpath.
+ */
+ public void setClasspath(Path classpath) {
+ getDelegate().setClasspath(classpath);
+ }
+
+ /**
+ * Create the classpath to be used when searching for component being
+ * defined.
+ * @return the classpath of the this definition
+ */
+ public Path createClasspath() {
+ return getDelegate().createClasspath();
+ }
+
+ /**
+ * Set a reference to a classpath to use when loading the files.
+ * To actually share the same loader, set loaderref as well
+ * @param r the reference to the classpath
+ */
+ public void setClasspathRef(Reference r) {
+ getDelegate().setClasspathref(r);
+ }
+
+ /**
+ * Use the reference to locate the loader. If the loader is not
+ * found, the specified classpath will be used and registered
+ * with the specified name.
+ *
+ * This allows multiple taskdef/typedef to use the same class loader,
+ * so they can be used together, eliminating the need to
+ * put them in the CLASSPATH.
+ *
+ * @param r the reference to locate the loader.
+ * @since Ant 1.5
+ */
+ public void setLoaderRef(Reference r) {
+ getDelegate().setLoaderRef(r);
+ }
+
+ /**
+ * create a classloader for this definition
+ * @return the classloader from the cpDelegate
+ */
+ protected ClassLoader createLoader() {
+ if (getAntlibClassLoader() != null && cpDelegate == null) {
+ return getAntlibClassLoader();
+ }
+ if (createdLoader == null) {
+ createdLoader = getDelegate().getClassLoader();
+ // need to load Task via system classloader or the new
+ // task we want to define will never be a Task but always
+ // be wrapped into a TaskAdapter.
+ ((AntClassLoader) createdLoader)
+ .addSystemPackageRoot("org.apache.tools.ant");
+ }
+ return createdLoader;
+ }
+
+ /**
+ * @see org.apache.tools.ant.Task#init()
+ * @throws BuildException on error.
+ * @since Ant 1.6
+ */
+ public void init() throws BuildException {
+ super.init();
+ }
+
+ private ClasspathUtils.Delegate getDelegate() {
+ if (cpDelegate == null) {
+ cpDelegate = ClasspathUtils.getDelegate(this);
+ }
+ return cpDelegate;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DefaultExcludes.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DefaultExcludes.java
new file mode 100644
index 00000000..6b912e2f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DefaultExcludes.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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Alters the default excludes for the <strong>entire</strong> build..
+ *
+ * @since Ant 1.6
+ *
+ * @ant.task category="utility"
+ */
+public class DefaultExcludes extends Task {
+ private String add = "";
+ private String remove = "";
+ private boolean defaultrequested = false;
+ private boolean echo = false;
+
+ // by default, messages are always displayed
+ private int logLevel = Project.MSG_WARN;
+
+ /**
+ * Does the work.
+ *
+ * @exception BuildException if something goes wrong with the build
+ */
+ public void execute() throws BuildException {
+ if (!defaultrequested && add.equals("") && remove.equals("") && !echo) {
+ throw new BuildException("<defaultexcludes> task must set "
+ + "at least one attribute (echo=\"false\""
+ + " doesn't count since that is the default");
+ }
+ if (defaultrequested) {
+ DirectoryScanner.resetDefaultExcludes();
+ }
+ if (!add.equals("")) {
+ DirectoryScanner.addDefaultExclude(add);
+ }
+ if (!remove.equals("")) {
+ DirectoryScanner.removeDefaultExclude(remove);
+ }
+ if (echo) {
+ StringBuffer message
+ = new StringBuffer("Current Default Excludes:");
+ message.append(StringUtils.LINE_SEP);
+ String[] excludes = DirectoryScanner.getDefaultExcludes();
+ for (int i = 0; i < excludes.length; i++) {
+ message.append(" ");
+ message.append(excludes[i]);
+ message.append(StringUtils.LINE_SEP);
+ }
+ log(message.toString(), logLevel);
+ }
+ }
+
+ /**
+ * go back to standard default patterns
+ *
+ * @param def if true go back to default patterns
+ */
+ public void setDefault(boolean def) {
+ defaultrequested = def;
+ }
+ /**
+ * Pattern to add to the default excludes
+ *
+ * @param add Sets the value for the pattern to exclude.
+ */
+ public void setAdd(String add) {
+ this.add = add;
+ }
+
+ /**
+ * Pattern to remove from the default excludes.
+ *
+ * @param remove Sets the value for the pattern that
+ * should no longer be excluded.
+ */
+ public void setRemove(String remove) {
+ this.remove = remove;
+ }
+
+ /**
+ * If true, echo the default excludes.
+ *
+ * @param echo whether or not to echo the contents of
+ * the default excludes.
+ */
+ public void setEcho(boolean echo) {
+ this.echo = echo;
+ }
+
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Definer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Definer.java
new file mode 100644
index 00000000..8196fa51
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Definer.java
@@ -0,0 +1,639 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.tools.ant.AntTypeDefinition;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Base class for Taskdef and Typedef - handles all
+ * the attributes for Typedef. The uri and class
+ * handling is handled by DefBase
+ *
+ * @since Ant 1.4
+ */
+public abstract class Definer extends DefBase {
+
+ /**
+ * the extension of an antlib file for autoloading.
+ * {@value[
+ */
+ private static final String ANTLIB_XML = "/antlib.xml";
+
+ private static final ThreadLocal<Map<URL, Location>> RESOURCE_STACK = new ThreadLocal<Map<URL, Location>>() {
+ protected Map<URL, Location> initialValue() {
+ return new HashMap<URL, Location>();
+ }
+ };
+
+ private String name;
+ private String classname;
+ private File file;
+ private String resource;
+ private boolean restrict = false;
+
+ private int format = Format.PROPERTIES;
+ private boolean definerSet = false;
+ private int onError = OnError.FAIL;
+ private String adapter;
+ private String adaptTo;
+
+ private Class<?> adapterClass;
+ private Class<?> adaptToClass;
+
+ /**
+ * Enumerated type for onError attribute
+ *
+ * @see EnumeratedAttribute
+ */
+ public static class OnError extends EnumeratedAttribute {
+ /** Enumerated values */
+ public static final int FAIL = 0, REPORT = 1, IGNORE = 2, FAIL_ALL = 3;
+
+ /**
+ * text value of onerror option {@value}
+ */
+ public static final String POLICY_FAIL = "fail";
+ /**
+ * text value of onerror option {@value}
+ */
+ public static final String POLICY_REPORT = "report";
+ /**
+ * text value of onerror option {@value}
+ */
+ public static final String POLICY_IGNORE = "ignore";
+ /**
+ * text value of onerror option {@value}
+ */
+ public static final String POLICY_FAILALL = "failall";
+
+ /**
+ * Constructor
+ */
+ public OnError() {
+ super();
+ }
+
+ /**
+ * Constructor using a string.
+ * @param value the value of the attribute
+ */
+ public OnError(String value) {
+ setValue(value);
+ }
+
+ /**
+ * get the values
+ * @return an array of the allowed values for this attribute.
+ */
+ public String[] getValues() {
+ return new String[] {POLICY_FAIL, POLICY_REPORT, POLICY_IGNORE, POLICY_FAILALL};
+ }
+ }
+
+ /**
+ * Enumerated type for format attribute
+ *
+ * @see EnumeratedAttribute
+ */
+ public static class Format extends EnumeratedAttribute {
+ /** Enumerated values */
+ public static final int PROPERTIES = 0, XML = 1;
+
+ /**
+ * get the values
+ * @return an array of the allowed values for this attribute.
+ */
+ public String[] getValues() {
+ return new String[] {"properties", "xml"};
+ }
+ }
+
+ /**
+ * The restrict attribute.
+ * If this is true, only use this definition in add(X).
+ * @param restrict the value to set.
+ */
+ protected void setRestrict(boolean restrict) {
+ this.restrict = restrict;
+ }
+
+
+ /**
+ * What to do if there is an error in loading the class.
+ * <ul>
+ * <li>error - throw build exception</li>
+ * <li>report - output at warning level</li>
+ * <li>ignore - output at debug level</li>
+ * </ul>
+ *
+ * @param onError an <code>OnError</code> value
+ */
+ public void setOnError(OnError onError) {
+ this.onError = onError.getIndex();
+ }
+
+ /**
+ * Sets the format of the file or resource
+ * @param format the enumerated value - xml or properties
+ */
+ public void setFormat(Format format) {
+ this.format = format.getIndex();
+ }
+
+ /**
+ * @return the name for this definition
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return the file containing definitions
+ */
+ public File getFile() {
+ return file;
+ }
+
+ /**
+ * @return the resource containing definitions
+ */
+ public String getResource() {
+ return resource;
+ }
+
+
+ /**
+ * Run the definition.
+ *
+ * @exception BuildException if an error occurs
+ */
+ public void execute() throws BuildException {
+ ClassLoader al = createLoader();
+
+ if (!definerSet) {
+ //we arent fully defined yet. this is an error unless
+ //we are in an antlib, in which case the resource name is determined
+ //automatically.
+ //NB: URIs in the ant core package will be "" at this point.
+ if (getURI() == null) {
+ throw new BuildException(
+ "name, file or resource attribute of "
+ + getTaskName() + " is undefined",
+ getLocation());
+ }
+
+ if (getURI().startsWith(MagicNames.ANTLIB_PREFIX)) {
+ //convert the URI to a resource
+ String uri1 = getURI();
+ setResource(makeResourceFromURI(uri1));
+ } else {
+ throw new BuildException(
+ "Only antlib URIs can be located from the URI alone,"
+ + " not the URI '" + getURI() + "'");
+ }
+ }
+
+ if (name != null) {
+ if (classname == null) {
+ throw new BuildException(
+ "classname attribute of " + getTaskName() + " element "
+ + "is undefined", getLocation());
+ }
+ addDefinition(al, name, classname);
+ } else {
+ if (classname != null) {
+ String msg = "You must not specify classname "
+ + "together with file or resource.";
+ throw new BuildException(msg, getLocation());
+ }
+ final Enumeration<URL> urls;
+ if (file == null) {
+ urls = resourceToURLs(al);
+ } else {
+ final URL url = fileToURL();
+ if (url == null) {
+ return;
+ }
+ urls = Collections.enumeration(Collections.singleton(url));
+ }
+
+ while (urls.hasMoreElements()) {
+ URL url = urls.nextElement();
+
+ int fmt = this.format;
+ if (url.toString().toLowerCase(Locale.ENGLISH).endsWith(".xml")) {
+ fmt = Format.XML;
+ }
+
+ if (fmt == Format.PROPERTIES) {
+ loadProperties(al, url);
+ break;
+ } else {
+ if (RESOURCE_STACK.get().get(url) != null) {
+ log("Warning: Recursive loading of " + url
+ + " ignored"
+ + " at " + getLocation()
+ + " originally loaded at "
+ + RESOURCE_STACK.get().get(url),
+ Project.MSG_WARN);
+ } else {
+ try {
+ RESOURCE_STACK.get().put(url, getLocation());
+ loadAntlib(al, url);
+ } finally {
+ RESOURCE_STACK.get().remove(url);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This is where the logic to map from a URI to an antlib resource
+ * is kept.
+ * @param uri the xml namespace uri that to convert.
+ * @return the name of a resource. It may not exist
+ */
+
+ public static String makeResourceFromURI(String uri) {
+ String path = uri.substring(MagicNames.ANTLIB_PREFIX.length());
+ String resource;
+ if (path.startsWith("//")) {
+ //handle new style full paths to an antlib, in which
+ //all but the forward slashes are allowed.
+ resource = path.substring("//".length());
+ if (!resource.endsWith(".xml")) {
+ //if we haven't already named an XML file, it gets antlib.xml
+ resource = resource + ANTLIB_XML;
+ }
+ } else {
+ //convert from a package to a path
+ resource = path.replace('.', '/') + ANTLIB_XML;
+ }
+ return resource;
+ }
+
+ /**
+ * Convert a file to a file: URL.
+ *
+ * @return the URL, or null if it isn't valid and the active error policy
+ * is not to raise a fault
+ * @throws BuildException if the file is missing/not a file and the
+ * policy requires failure at this point.
+ */
+ private URL fileToURL() {
+ String message = null;
+ if (!(file.exists())) {
+ message = "File " + file + " does not exist";
+ }
+ if (message == null && !(file.isFile())) {
+ message = "File " + file + " is not a file";
+ }
+ if (message == null) {
+ try {
+ return FileUtils.getFileUtils().getFileURL(file);
+ } catch (Exception ex) {
+ message =
+ "File " + file + " cannot use as URL: "
+ + ex.toString();
+ }
+ }
+ // Here if there is an error
+ switch (onError) {
+ case OnError.FAIL_ALL:
+ throw new BuildException(message);
+ case OnError.FAIL:
+ // Fall Through
+ case OnError.REPORT:
+ log(message, Project.MSG_WARN);
+ break;
+ case OnError.IGNORE:
+ // log at a lower level
+ log(message, Project.MSG_VERBOSE);
+ break;
+ default:
+ // Ignore the problem
+ break;
+ }
+ return null;
+ }
+
+ private Enumeration<URL> resourceToURLs(ClassLoader classLoader) {
+ Enumeration<URL> ret;
+ try {
+ ret = classLoader.getResources(resource);
+ } catch (IOException e) {
+ throw new BuildException(
+ "Could not fetch resources named " + resource,
+ e, getLocation());
+ }
+ if (!ret.hasMoreElements()) {
+ String message = "Could not load definitions from resource "
+ + resource + ". It could not be found.";
+ switch (onError) {
+ case OnError.FAIL_ALL:
+ throw new BuildException(message);
+ case OnError.FAIL:
+ case OnError.REPORT:
+ log(message, Project.MSG_WARN);
+ break;
+ case OnError.IGNORE:
+ log(message, Project.MSG_VERBOSE);
+ break;
+ default:
+ // Ignore the problem
+ break;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Load type definitions as properties from a URL.
+ *
+ * @param al the classloader to use
+ * @param url the url to get the definitions from
+ */
+ protected void loadProperties(ClassLoader al, URL url) {
+ InputStream is = null;
+ try {
+ is = url.openStream();
+ if (is == null) {
+ log("Could not load definitions from " + url,
+ Project.MSG_WARN);
+ return;
+ }
+ Properties props = new Properties();
+ props.load(is);
+ Enumeration<?> keys = props.keys();
+ while (keys.hasMoreElements()) {
+ name = ((String) keys.nextElement());
+ classname = props.getProperty(name);
+ addDefinition(al, name, classname);
+ }
+ } catch (IOException ex) {
+ throw new BuildException(ex, getLocation());
+ } finally {
+ FileUtils.close(is);
+ }
+ }
+
+ /**
+ * Load an antlib from a URL.
+ *
+ * @param classLoader the classloader to use.
+ * @param url the url to load the definitions from.
+ */
+ private void loadAntlib(ClassLoader classLoader, URL url) {
+ try {
+ Antlib antlib = Antlib.createAntlib(getProject(), url, getURI());
+ antlib.setClassLoader(classLoader);
+ antlib.setURI(getURI());
+ antlib.execute();
+ } catch (BuildException ex) {
+ throw ProjectHelper.addLocationToBuildException(
+ ex, getLocation());
+ }
+ }
+
+ /**
+ * Name of the property file to load
+ * ant name/classname pairs from.
+ * @param file the file
+ */
+ public void setFile(File file) {
+ if (definerSet) {
+ tooManyDefinitions();
+ }
+ definerSet = true;
+ this.file = file;
+ }
+
+ /**
+ * Name of the property resource to load
+ * ant name/classname pairs from.
+ * @param res the resource to use
+ */
+ public void setResource(String res) {
+ if (definerSet) {
+ tooManyDefinitions();
+ }
+ definerSet = true;
+ this.resource = res;
+ }
+
+ /**
+ * Antlib attribute, sets resource and uri.
+ * uri is set the antlib value and, resource is set
+ * to the antlib.xml resource in the classpath.
+ * For example antlib="antlib:org.acme.bland.cola"
+ * corresponds to uri="antlib:org.acme.bland.cola"
+ * resource="org/acme/bland/cola/antlib.xml".
+ * ASF Bugzilla Bug 31999
+ * @param antlib the value to set.
+ */
+ public void setAntlib(String antlib) {
+ if (definerSet) {
+ tooManyDefinitions();
+ }
+ if (!antlib.startsWith("antlib:")) {
+ throw new BuildException(
+ "Invalid antlib attribute - it must start with antlib:");
+ }
+ setURI(antlib);
+ this.resource = antlib.substring("antlib:".length()).replace('.', '/')
+ + "/antlib.xml";
+ definerSet = true;
+ }
+
+ /**
+ * Name of the definition
+ * @param name the name of the definition
+ */
+ public void setName(String name) {
+ if (definerSet) {
+ tooManyDefinitions();
+ }
+ definerSet = true;
+ this.name = name;
+ }
+
+ /**
+ * Returns the classname of the object we are defining.
+ * May be <code>null</code>.
+ * @return the class name
+ */
+ public String getClassname() {
+ return classname;
+ }
+
+ /**
+ * The full class name of the object being defined.
+ * Required, unless file or resource have
+ * been specified.
+ * @param classname the name of the class
+ */
+ public void setClassname(String classname) {
+ this.classname = classname;
+ }
+
+ /**
+ * Set the class name of the adapter class.
+ * An adapter class is used to proxy the
+ * definition class. It is used if the
+ * definition class is not assignable to
+ * the adaptto class, or if the adaptto
+ * class is not present.
+ *
+ * @param adapter the name of the adapter class
+ */
+
+ public void setAdapter(String adapter) {
+ this.adapter = adapter;
+ }
+
+ /**
+ * Set the adapter class.
+ *
+ * @param adapterClass the class to use to adapt the definition class
+ */
+ protected void setAdapterClass(Class<?> adapterClass) {
+ this.adapterClass = adapterClass;
+ }
+
+ /**
+ * Set the classname of the class that the definition
+ * must be compatible with, either directly or
+ * by use of the adapter class.
+ *
+ * @param adaptTo the name of the adaptto class
+ */
+ public void setAdaptTo(String adaptTo) {
+ this.adaptTo = adaptTo;
+ }
+
+ /**
+ * Set the class for adaptToClass, to be
+ * used by derived classes, used instead of
+ * the adaptTo attribute.
+ *
+ * @param adaptToClass the class for adaptor.
+ */
+ protected void setAdaptToClass(Class<?> adaptToClass) {
+ this.adaptToClass = adaptToClass;
+ }
+
+
+ /**
+ * Add a definition using the attributes of Definer
+ *
+ * @param al the ClassLoader to use
+ * @param name the name of the definition
+ * @param classname the classname of the definition
+ * @exception BuildException if an error occurs
+ */
+ protected void addDefinition(ClassLoader al, String name, String classname)
+ throws BuildException {
+ Class<?> cl = null;
+ try {
+ try {
+ name = ProjectHelper.genComponentName(getURI(), name);
+
+ if (onError != OnError.IGNORE) {
+ cl = Class.forName(classname, true, al);
+ }
+
+ if (adapter != null) {
+ adapterClass = Class.forName(adapter, true, al);
+ }
+
+ if (adaptTo != null) {
+ adaptToClass = Class.forName(adaptTo, true, al);
+ }
+
+ AntTypeDefinition def = new AntTypeDefinition();
+ def.setName(name);
+ def.setClassName(classname);
+ def.setClass(cl);
+ def.setAdapterClass(adapterClass);
+ def.setAdaptToClass(adaptToClass);
+ def.setRestrict(restrict);
+ def.setClassLoader(al);
+ if (cl != null) {
+ def.checkClass(getProject());
+ }
+ ComponentHelper.getComponentHelper(getProject())
+ .addDataTypeDefinition(def);
+ } catch (ClassNotFoundException cnfe) {
+ String msg = getTaskName() + " class " + classname
+ + " cannot be found"
+ + "\n using the classloader " + al;
+ throw new BuildException(msg, cnfe, getLocation());
+ } catch (NoClassDefFoundError ncdfe) {
+ String msg = getTaskName() + " A class needed by class "
+ + classname + " cannot be found: " + ncdfe.getMessage()
+ + "\n using the classloader " + al;
+ throw new BuildException(msg, ncdfe, getLocation());
+ }
+ } catch (BuildException ex) {
+ switch (onError) {
+ case OnError.FAIL_ALL:
+ case OnError.FAIL:
+ throw ex;
+ case OnError.REPORT:
+ log(ex.getLocation() + "Warning: " + ex.getMessage(),
+ Project.MSG_WARN);
+ break;
+ default:
+ log(ex.getLocation() + ex.getMessage(),
+ Project.MSG_DEBUG);
+ }
+ }
+ }
+
+ /**
+ * handle too many definitions by raising an exception.
+ * @throws BuildException always.
+ */
+ private void tooManyDefinitions() {
+ throw new BuildException(
+ "Only one of the attributes name, file and resource"
+ + " can be set", getLocation());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Delete.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Delete.java
new file mode 100644
index 00000000..6c34ccf2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Delete.java
@@ -0,0 +1,836 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResourceIterator;
+import org.apache.tools.ant.types.resources.Resources;
+import org.apache.tools.ant.types.resources.Restrict;
+import org.apache.tools.ant.types.resources.Sort;
+import org.apache.tools.ant.types.resources.comparators.FileSystem;
+import org.apache.tools.ant.types.resources.comparators.ResourceComparator;
+import org.apache.tools.ant.types.resources.comparators.Reverse;
+import org.apache.tools.ant.types.resources.selectors.Exists;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+import org.apache.tools.ant.types.selectors.AndSelector;
+import org.apache.tools.ant.types.selectors.ContainsRegexpSelector;
+import org.apache.tools.ant.types.selectors.ContainsSelector;
+import org.apache.tools.ant.types.selectors.DateSelector;
+import org.apache.tools.ant.types.selectors.DependSelector;
+import org.apache.tools.ant.types.selectors.DepthSelector;
+import org.apache.tools.ant.types.selectors.ExtendSelector;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.FilenameSelector;
+import org.apache.tools.ant.types.selectors.MajoritySelector;
+import org.apache.tools.ant.types.selectors.NoneSelector;
+import org.apache.tools.ant.types.selectors.NotSelector;
+import org.apache.tools.ant.types.selectors.OrSelector;
+import org.apache.tools.ant.types.selectors.PresentSelector;
+import org.apache.tools.ant.types.selectors.SelectSelector;
+import org.apache.tools.ant.types.selectors.SizeSelector;
+import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.SymbolicLinkUtils;
+
+/**
+ * Deletes a file or directory, or set of files defined by a fileset.
+ * The original delete task would delete a file, or a set of files
+ * using the include/exclude syntax. The deltree task would delete a
+ * directory tree. This task combines the functionality of these two
+ * originally distinct tasks.
+ * <p>Currently Delete extends MatchingTask. This is intended <i>only</i>
+ * to provide backwards compatibility for a release. The future position
+ * is to use nested filesets exclusively.</p>
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="filesystem"
+ */
+public class Delete extends MatchingTask {
+ private static final ResourceComparator REVERSE_FILESYSTEM = new Reverse(new FileSystem());
+ private static final ResourceSelector EXISTS = new Exists();
+
+ private static class ReverseDirs implements ResourceCollection {
+ static final Comparator<Comparable<?>> REVERSE = new Comparator<Comparable<?>>() {
+ public int compare(Comparable<?> foo, Comparable<?> bar) {
+ return ((Comparable) foo).compareTo(bar) * -1;
+ }
+ };
+ private Project project;
+ private File basedir;
+ private String[] dirs;
+ ReverseDirs(Project project, File basedir, String[] dirs) {
+ this.project = project;
+ this.basedir = basedir;
+ this.dirs = dirs;
+ Arrays.sort(this.dirs, REVERSE);
+ }
+ public Iterator<Resource> iterator() {
+ return new FileResourceIterator(project, basedir, dirs);
+ }
+ public boolean isFilesystemOnly() { return true; }
+ public int size() { return dirs.length; }
+ }
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected File file = null;
+ protected File dir = null;
+ protected Vector<FileSet> filesets = new Vector<FileSet>();
+ protected boolean usedMatchingTask = false;
+ // by default, remove matching empty dirs
+ protected boolean includeEmpty = false;
+ // CheckStyle:VisibilityModifier ON
+
+ private int verbosity = Project.MSG_VERBOSE;
+ private boolean quiet = false;
+ private boolean failonerror = true;
+ private boolean deleteOnExit = false;
+ private boolean removeNotFollowedSymlinks = false;
+ private Resources rcs = null;
+ private static FileUtils FILE_UTILS = FileUtils.getFileUtils();
+ private static SymbolicLinkUtils SYMLINK_UTILS =
+ SymbolicLinkUtils.getSymbolicLinkUtils();
+ private boolean performGc = Os.isFamily("windows");
+
+ /**
+ * Set the name of a single file to be removed.
+ *
+ * @param file the file to be deleted
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * Set the directory from which files are to be deleted
+ *
+ * @param dir the directory path.
+ */
+ public void setDir(File dir) {
+ this.dir = dir;
+ getImplicitFileSet().setDir(dir);
+ }
+
+ /**
+ * If true, list all names of deleted files.
+ *
+ * @param verbose "true" or "on"
+ */
+ public void setVerbose(boolean verbose) {
+ if (verbose) {
+ this.verbosity = Project.MSG_INFO;
+ } else {
+ this.verbosity = Project.MSG_VERBOSE;
+ }
+ }
+
+ /**
+ * If true and the file does not exist, do not display a diagnostic
+ * message or modify the exit status to reflect an error.
+ * This means that if a file or directory cannot be deleted,
+ * then no error is reported. This setting emulates the
+ * -f option to the Unix &quot;rm&quot; command.
+ * Default is false meaning things are &quot;noisy&quot;
+ * @param quiet "true" or "on"
+ */
+ public void setQuiet(boolean quiet) {
+ this.quiet = quiet;
+ if (quiet) {
+ this.failonerror = false;
+ }
+ }
+
+ /**
+ * If false, note errors but continue.
+ *
+ * @param failonerror true or false
+ */
+ public void setFailOnError(boolean failonerror) {
+ this.failonerror = failonerror;
+ }
+
+ /**
+ * If true, on failure to delete, note the error and set
+ * the deleteonexit flag, and continue
+ *
+ * @param deleteOnExit true or false
+ */
+ public void setDeleteOnExit(boolean deleteOnExit) {
+ this.deleteOnExit = deleteOnExit;
+ }
+
+
+ /**
+ * If true, delete empty directories.
+ * @param includeEmpty if true delete empty directories (only
+ * for filesets). Default is false.
+ */
+ public void setIncludeEmptyDirs(boolean includeEmpty) {
+ this.includeEmpty = includeEmpty;
+ }
+
+ /**
+ * Whether to perform a garbage collection before retrying a failed delete.
+ *
+ * <p>This may be required on Windows (where it is set to true by
+ * default) but also on other operating systems, for example when
+ * deleting directories from an NFS share.</p>
+ *
+ * @since Ant 1.8.3
+ */
+ public void setPerformGcOnFailedDelete(boolean b) {
+ performGc = b;
+ }
+
+ /**
+ * Adds a set of files to be deleted.
+ * @param set the set of files to be deleted
+ */
+ public void addFileset(FileSet set) {
+ filesets.addElement(set);
+ }
+
+ /**
+ * Add an arbitrary ResourceCollection to be deleted.
+ * @param rc the filesystem-only ResourceCollection.
+ */
+ public void add(ResourceCollection rc) {
+ if (rc == null) {
+ return;
+ }
+ if (rcs == null) {
+ rcs = new Resources();
+ rcs.setCache(true);
+ }
+ rcs.add(rc);
+ }
+
+ /**
+ * add a name entry on the include list
+ * @return a NameEntry object to be configured
+ */
+ public PatternSet.NameEntry createInclude() {
+ usedMatchingTask = true;
+ return super.createInclude();
+ }
+
+ /**
+ * add a name entry on the include files list
+ * @return an NameEntry object to be configured
+ */
+ public PatternSet.NameEntry createIncludesFile() {
+ usedMatchingTask = true;
+ return super.createIncludesFile();
+ }
+
+ /**
+ * add a name entry on the exclude list
+ * @return an NameEntry object to be configured
+ */
+ public PatternSet.NameEntry createExclude() {
+ usedMatchingTask = true;
+ return super.createExclude();
+ }
+
+ /**
+ * add a name entry on the include files list
+ * @return an NameEntry object to be configured
+ */
+ public PatternSet.NameEntry createExcludesFile() {
+ usedMatchingTask = true;
+ return super.createExcludesFile();
+ }
+
+ /**
+ * add a set of patterns
+ * @return PatternSet object to be configured
+ */
+ public PatternSet createPatternSet() {
+ usedMatchingTask = true;
+ return super.createPatternSet();
+ }
+
+ /**
+ * Sets the set of include patterns. Patterns may be separated by a comma
+ * or a space.
+ *
+ * @param includes the string containing the include patterns
+ */
+ public void setIncludes(String includes) {
+ usedMatchingTask = true;
+ super.setIncludes(includes);
+ }
+
+ /**
+ * Sets the set of exclude patterns. Patterns may be separated by a comma
+ * or a space.
+ *
+ * @param excludes the string containing the exclude patterns
+ */
+ public void setExcludes(String excludes) {
+ usedMatchingTask = true;
+ super.setExcludes(excludes);
+ }
+
+ /**
+ * Sets whether default exclusions should be used or not.
+ *
+ * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
+ * should be used, "false"|"off"|"no" when they
+ * shouldn't be used.
+ */
+ public void setDefaultexcludes(boolean useDefaultExcludes) {
+ usedMatchingTask = true;
+ super.setDefaultexcludes(useDefaultExcludes);
+ }
+
+ /**
+ * Sets the name of the file containing the includes patterns.
+ *
+ * @param includesfile A string containing the filename to fetch
+ * the include patterns from.
+ */
+ public void setIncludesfile(File includesfile) {
+ usedMatchingTask = true;
+ super.setIncludesfile(includesfile);
+ }
+
+ /**
+ * Sets the name of the file containing the includes patterns.
+ *
+ * @param excludesfile A string containing the filename to fetch
+ * the include patterns from.
+ */
+ public void setExcludesfile(File excludesfile) {
+ usedMatchingTask = true;
+ super.setExcludesfile(excludesfile);
+ }
+
+ /**
+ * Sets case sensitivity of the file system
+ *
+ * @param isCaseSensitive "true"|"on"|"yes" if file system is case
+ * sensitive, "false"|"off"|"no" when not.
+ */
+ public void setCaseSensitive(boolean isCaseSensitive) {
+ usedMatchingTask = true;
+ super.setCaseSensitive(isCaseSensitive);
+ }
+
+ /**
+ * Sets whether or not symbolic links should be followed.
+ *
+ * @param followSymlinks whether or not symbolic links should be followed
+ */
+ public void setFollowSymlinks(boolean followSymlinks) {
+ usedMatchingTask = true;
+ super.setFollowSymlinks(followSymlinks);
+ }
+
+ /**
+ * Sets whether the symbolic links that have not been followed
+ * shall be removed (the links, not the locations they point at).
+ *
+ * @since Ant 1.8.0
+ */
+ public void setRemoveNotFollowedSymlinks(boolean b) {
+ removeNotFollowedSymlinks = b;
+ }
+
+ /**
+ * add a "Select" selector entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addSelector(SelectSelector selector) {
+ usedMatchingTask = true;
+ super.addSelector(selector);
+ }
+
+ /**
+ * add an "And" selector entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addAnd(AndSelector selector) {
+ usedMatchingTask = true;
+ super.addAnd(selector);
+ }
+
+ /**
+ * add an "Or" selector entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addOr(OrSelector selector) {
+ usedMatchingTask = true;
+ super.addOr(selector);
+ }
+
+ /**
+ * add a "Not" selector entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addNot(NotSelector selector) {
+ usedMatchingTask = true;
+ super.addNot(selector);
+ }
+
+ /**
+ * add a "None" selector entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addNone(NoneSelector selector) {
+ usedMatchingTask = true;
+ super.addNone(selector);
+ }
+
+ /**
+ * add a majority selector entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addMajority(MajoritySelector selector) {
+ usedMatchingTask = true;
+ super.addMajority(selector);
+ }
+
+ /**
+ * add a selector date entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addDate(DateSelector selector) {
+ usedMatchingTask = true;
+ super.addDate(selector);
+ }
+
+ /**
+ * add a selector size entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addSize(SizeSelector selector) {
+ usedMatchingTask = true;
+ super.addSize(selector);
+ }
+
+ /**
+ * add a selector filename entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addFilename(FilenameSelector selector) {
+ usedMatchingTask = true;
+ super.addFilename(selector);
+ }
+
+ /**
+ * add an extended selector entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addCustom(ExtendSelector selector) {
+ usedMatchingTask = true;
+ super.addCustom(selector);
+ }
+
+ /**
+ * add a contains selector entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addContains(ContainsSelector selector) {
+ usedMatchingTask = true;
+ super.addContains(selector);
+ }
+
+ /**
+ * add a present selector entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addPresent(PresentSelector selector) {
+ usedMatchingTask = true;
+ super.addPresent(selector);
+ }
+
+ /**
+ * add a depth selector entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addDepth(DepthSelector selector) {
+ usedMatchingTask = true;
+ super.addDepth(selector);
+ }
+
+ /**
+ * add a depends selector entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addDepend(DependSelector selector) {
+ usedMatchingTask = true;
+ super.addDepend(selector);
+ }
+
+ /**
+ * add a regular expression selector entry on the selector list
+ * @param selector the selector to be added
+ */
+ public void addContainsRegexp(ContainsRegexpSelector selector) {
+ usedMatchingTask = true;
+ super.addContainsRegexp(selector);
+ }
+
+ /**
+ * add the modified selector
+ * @param selector the selector to add
+ * @since ant 1.6
+ */
+ public void addModified(ModifiedSelector selector) {
+ usedMatchingTask = true;
+ super.addModified(selector);
+ }
+
+ /**
+ * add an arbitrary selector
+ * @param selector the selector to be added
+ * @since Ant 1.6
+ */
+ public void add(FileSelector selector) {
+ usedMatchingTask = true;
+ super.add(selector);
+ }
+
+ /**
+ * Delete the file(s).
+ * @exception BuildException if an error occurs
+ */
+ public void execute() throws BuildException {
+ if (usedMatchingTask) {
+ log("DEPRECATED - Use of the implicit FileSet is deprecated. "
+ + "Use a nested fileset element instead.", quiet ? Project.MSG_VERBOSE : verbosity);
+ }
+
+ if (file == null && dir == null && filesets.size() == 0 && rcs == null) {
+ throw new BuildException("At least one of the file or dir "
+ + "attributes, or a nested resource collection, "
+ + "must be set.");
+ }
+
+ if (quiet && failonerror) {
+ throw new BuildException("quiet and failonerror cannot both be "
+ + "set to true", getLocation());
+ }
+
+ // delete the single file
+ if (file != null) {
+ if (file.exists()) {
+ if (file.isDirectory()) {
+ log("Directory " + file.getAbsolutePath()
+ + " cannot be removed using the file attribute. "
+ + "Use dir instead.", quiet ? Project.MSG_VERBOSE : verbosity);
+ } else {
+ log("Deleting: " + file.getAbsolutePath());
+
+ if (!delete(file)) {
+ handle("Unable to delete file " + file.getAbsolutePath());
+ }
+ }
+ } else if (isDanglingSymlink(file)) {
+ log("Trying to delete file " + file.getAbsolutePath()
+ + " which looks like a broken symlink.",
+ quiet ? Project.MSG_VERBOSE : verbosity);
+ if (!delete(file)) {
+ handle("Unable to delete file " + file.getAbsolutePath());
+ }
+ } else {
+ log("Could not find file " + file.getAbsolutePath()
+ + " to delete.", quiet ? Project.MSG_VERBOSE : verbosity);
+ }
+ }
+
+ // delete the directory
+ if (dir != null && !usedMatchingTask) {
+ if (dir.exists() && dir.isDirectory()) {
+ /*
+ If verbosity is MSG_VERBOSE, that mean we are doing
+ regular logging (backwards as that sounds). In that
+ case, we want to print one message about deleting the
+ top of the directory tree. Otherwise, the removeDir
+ method will handle messages for _all_ directories.
+ */
+ if (verbosity == Project.MSG_VERBOSE) {
+ log("Deleting directory " + dir.getAbsolutePath());
+ }
+ removeDir(dir);
+ } else if (isDanglingSymlink(dir)) {
+ log("Trying to delete directory " + dir.getAbsolutePath()
+ + " which looks like a broken symlink.",
+ quiet ? Project.MSG_VERBOSE : verbosity);
+ if (!delete(dir)) {
+ handle("Unable to delete directory "
+ + dir.getAbsolutePath());
+ }
+ }
+ }
+ Resources resourcesToDelete = new Resources();
+ resourcesToDelete.setProject(getProject());
+ resourcesToDelete.setCache(true);
+ Resources filesetDirs = new Resources();
+ filesetDirs.setProject(getProject());
+ filesetDirs.setCache(true);
+ FileSet implicit = null;
+ if (usedMatchingTask && dir != null && dir.isDirectory()) {
+ //add the files from the default fileset:
+ implicit = getImplicitFileSet();
+ implicit.setProject(getProject());
+ filesets.add(implicit);
+ }
+
+ final int size = filesets.size();
+ for (int i = 0; i < size; i++) {
+ FileSet fs = (FileSet) filesets.get(i);
+ if (fs.getProject() == null) {
+ log("Deleting fileset with no project specified;"
+ + " assuming executing project", Project.MSG_VERBOSE);
+ fs = (FileSet) fs.clone();
+ fs.setProject(getProject());
+ }
+ final File fsDir = fs.getDir();
+ if (!fs.getErrorOnMissingDir() &&
+ (fsDir == null || !fsDir.exists())) {
+ continue;
+ }
+ if (fsDir == null) {
+ throw new BuildException(
+ "File or Resource without directory or file specified");
+ } else if (!fsDir.isDirectory()) {
+ handle("Directory does not exist: " + fsDir);
+ } else {
+ DirectoryScanner ds = fs.getDirectoryScanner();
+ // the previous line has already scanned the
+ // filesystem, in order to avoid a rescan when later
+ // iterating, capture the results now and store them
+ final String[] files = ds.getIncludedFiles();
+ resourcesToDelete.add(new ResourceCollection() {
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+ public int size() {
+ return files.length;
+ }
+ public Iterator<Resource> iterator() {
+ return new FileResourceIterator(getProject(),
+ fsDir, files);
+ }
+ });
+ if (includeEmpty) {
+ filesetDirs.add(new ReverseDirs(getProject(), fsDir,
+ ds
+ .getIncludedDirectories()));
+ }
+
+ if (removeNotFollowedSymlinks) {
+ String[] n = ds.getNotFollowedSymlinks();
+ if (n.length > 0) {
+ String[] links = new String[n.length];
+ System.arraycopy(n, 0, links, 0, n.length);
+ Arrays.sort(links, ReverseDirs.REVERSE);
+ for (int l = 0; l < links.length; l++) {
+ try {
+ SYMLINK_UTILS
+ .deleteSymbolicLink(new File(links[l]),
+ this);
+ } catch (java.io.IOException ex) {
+ handle(ex);
+ }
+ }
+ }
+ }
+ }
+ }
+ resourcesToDelete.add(filesetDirs);
+ if (rcs != null) {
+ // sort first to files, then dirs
+ Restrict exists = new Restrict();
+ exists.add(EXISTS);
+ exists.add(rcs);
+ Sort s = new Sort();
+ s.add(REVERSE_FILESYSTEM);
+ s.add(exists);
+ resourcesToDelete.add(s);
+ }
+ try {
+ if (resourcesToDelete.isFilesystemOnly()) {
+ for (Resource r : resourcesToDelete) {
+ // nonexistent resources could only occur if we already
+ // deleted something from a fileset:
+ File f = r.as(FileProvider.class)
+ .getFile();
+ if (!f.exists()) {
+ continue;
+ }
+ if (!(f.isDirectory()) || f.list().length == 0) {
+ log("Deleting " + f, verbosity);
+ if (!delete(f) && failonerror) {
+ handle("Unable to delete "
+ + (f.isDirectory() ? "directory " : "file ") + f);
+ }
+ }
+ }
+ } else {
+ handle(getTaskName() + " handles only filesystem resources");
+ }
+ } catch (Exception e) {
+ handle(e);
+ } finally {
+ if (implicit != null) {
+ filesets.remove(implicit);
+ }
+ }
+ }
+
+//************************************************************************
+// protected and private methods
+//************************************************************************
+
+ private void handle(String msg) {
+ handle(new BuildException(msg));
+ }
+
+ private void handle(Exception e) {
+ if (failonerror) {
+ throw (e instanceof BuildException)
+ ? (BuildException) e : new BuildException(e);
+ }
+ log(e, quiet ? Project.MSG_VERBOSE : verbosity);
+ }
+
+ /**
+ * Accommodate Windows bug encountered in both Sun and IBM JDKs.
+ * Others possible. If the delete does not work, call System.gc(),
+ * wait a little and try again.
+ */
+ private boolean delete(File f) {
+ if (!FILE_UTILS.tryHardToDelete(f, performGc)) {
+ if (deleteOnExit) {
+ int level = quiet ? Project.MSG_VERBOSE : Project.MSG_INFO;
+ log("Failed to delete " + f + ", calling deleteOnExit."
+ + " This attempts to delete the file when the Ant jvm"
+ + " has exited and might not succeed.", level);
+ f.deleteOnExit();
+ return true;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Delete a directory
+ *
+ * @param d the directory to delete
+ */
+ protected void removeDir(File d) {
+ String[] list = d.list();
+ if (list == null) {
+ list = new String[0];
+ }
+ for (int i = 0; i < list.length; i++) {
+ String s = list[i];
+ File f = new File(d, s);
+ if (f.isDirectory()) {
+ removeDir(f);
+ } else {
+ log("Deleting " + f.getAbsolutePath(), quiet ? Project.MSG_VERBOSE : verbosity);
+ if (!delete(f)) {
+ handle("Unable to delete file " + f.getAbsolutePath());
+ }
+ }
+ }
+ log("Deleting directory " + d.getAbsolutePath(), verbosity);
+ if (!delete(d)) {
+ handle("Unable to delete directory " + d.getAbsolutePath());
+ }
+ }
+
+ /**
+ * remove an array of files in a directory, and a list of subdirectories
+ * which will only be deleted if 'includeEmpty' is true
+ * @param d directory to work from
+ * @param files array of files to delete; can be of zero length
+ * @param dirs array of directories to delete; can of zero length
+ */
+ protected void removeFiles(File d, String[] files, String[] dirs) {
+ if (files.length > 0) {
+ log("Deleting " + files.length + " files from "
+ + d.getAbsolutePath(), quiet ? Project.MSG_VERBOSE : verbosity);
+ for (int j = 0; j < files.length; j++) {
+ File f = new File(d, files[j]);
+ log("Deleting " + f.getAbsolutePath(),
+ quiet ? Project.MSG_VERBOSE : verbosity);
+ if (!delete(f)) {
+ handle("Unable to delete file " + f.getAbsolutePath());
+ }
+ }
+ }
+
+ if (dirs.length > 0 && includeEmpty) {
+ int dirCount = 0;
+ for (int j = dirs.length - 1; j >= 0; j--) {
+ File currDir = new File(d, dirs[j]);
+ String[] dirFiles = currDir.list();
+ if (dirFiles == null || dirFiles.length == 0) {
+ log("Deleting " + currDir.getAbsolutePath(),
+ quiet ? Project.MSG_VERBOSE : verbosity);
+ if (!delete(currDir)) {
+ handle("Unable to delete directory "
+ + currDir.getAbsolutePath());
+ } else {
+ dirCount++;
+ }
+ }
+ }
+
+ if (dirCount > 0) {
+ log("Deleted "
+ + dirCount
+ + " director" + (dirCount == 1 ? "y" : "ies")
+ + " form " + d.getAbsolutePath(),
+ quiet ? Project.MSG_VERBOSE : verbosity);
+ }
+ }
+ }
+
+ private boolean isDanglingSymlink(File f) {
+ try {
+ return SYMLINK_UTILS.isDanglingSymbolicLink(f);
+ } catch (java.io.IOException e) {
+ log("Error while trying to detect " + f.getAbsolutePath()
+ + " as broken symbolic link. " + e.getMessage(),
+ quiet ? Project.MSG_VERBOSE : verbosity);
+ return false;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Deltree.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Deltree.java
new file mode 100644
index 00000000..098d6e5f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Deltree.java
@@ -0,0 +1,112 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @deprecated The deltree task is deprecated since Ant 1.2. Use
+ * delete instead.
+ */
+
+public class Deltree extends Task {
+
+ private File dir;
+
+ /**
+ * Set the directory to be deleted
+ *
+ * @param dir the root of the tree to be removed.
+ */
+ public void setDir(File dir) {
+ this.dir = dir;
+ }
+
+ /**
+ * Do the work.
+ *
+ * @exception BuildException if the task is not configured correctly or
+ * the tree cannot be removed.
+ */
+ public void execute() throws BuildException {
+ log("DEPRECATED - The deltree task is deprecated. "
+ + "Use delete instead.");
+
+ if (dir == null) {
+ throw new BuildException("dir attribute must be set!", getLocation());
+ }
+
+ if (dir.exists()) {
+ if (!dir.isDirectory()) {
+ if (!dir.delete()) {
+ throw new BuildException("Unable to delete directory "
+ + dir.getAbsolutePath(),
+ getLocation());
+ }
+ return;
+ }
+
+ log("Deleting: " + dir.getAbsolutePath());
+
+ try {
+ removeDir(dir);
+ } catch (IOException ioe) {
+ String msg = "Unable to delete " + dir.getAbsolutePath();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+ }
+
+ private void removeDir(File dir) throws IOException {
+
+ // check to make sure that the given dir isn't a symlink
+ // the comparison of absolute path and canonical path
+ // catches this
+
+ // if (dir.getCanonicalPath().equals(dir.getAbsolutePath())) {
+ // (costin) It will not work if /home/costin is symlink to
+ // /da0/home/costin ( taz for example )
+ String[] list = dir.list();
+ for (int i = 0; i < list.length; i++) {
+ String s = list[i];
+ File f = new File(dir, s);
+ if (f.isDirectory()) {
+ removeDir(f);
+ } else {
+ if (!f.delete()) {
+ throw new BuildException("Unable to delete file "
+ + f.getAbsolutePath());
+ }
+ }
+ }
+ if (!dir.delete()) {
+ throw new BuildException("Unable to delete directory "
+ + dir.getAbsolutePath());
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DependSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DependSet.java
new file mode 100644
index 00000000..dc42beb1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DependSet.java
@@ -0,0 +1,299 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileList;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.TimeComparison;
+import org.apache.tools.ant.types.resources.Resources;
+import org.apache.tools.ant.types.resources.Restrict;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.types.resources.comparators.ResourceComparator;
+import org.apache.tools.ant.types.resources.comparators.Reverse;
+import org.apache.tools.ant.types.resources.selectors.Exists;
+import org.apache.tools.ant.types.resources.selectors.Not;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+
+/**
+ * Examines and removes out of date target files. If any of the target files
+ * are out of date with respect to any of the source files, all target
+ * files are removed. This is useful where dependencies cannot be
+ * computed (for example, dynamically interpreted parameters or files
+ * that need to stay in synch but are not directly linked) or where
+ * the ant task in question could compute them but does not (for
+ * example, the linked DTD for an XML file using the XSLT task).
+ *
+ * nested arguments:
+ * <ul>
+ * <li>sources (resource union describing the source resources to examine)
+ * <li>srcfileset (fileset describing the source files to examine)
+ * <li>srcfilelist (filelist describing the source files to examine)
+ * <li>targets (path describing the target files to examine)
+ * <li>targetfileset (fileset describing the target files to examine)
+ * <li>targetfilelist (filelist describing the target files to examine)
+ * </ul>
+ * At least one of both source and target entities is required.
+ * <p>
+ * This task will examine each of the sources against each of the target files. If
+ * any target files are out of date with respect to any of the sources, all targets
+ * are removed. If any sources or targets do not exist, all targets are removed.
+ * Hint: If missing files should be ignored, specify them as include patterns
+ * in filesets, rather than using filelists.
+ * </p><p>
+ * This task attempts to optimize speed of dependency checking
+ * by comparing only the dates of the oldest target file and the newest source.
+ * </p><p>
+ * Example uses:
+ * <ul><li>
+ * Record the fact that an XML file must be up to date with respect to its XSD
+ * (Schema file), even though the XML file itself includes no reference to its XSD.
+ * </li><li>
+ * Record the fact that an XSL stylesheet includes other sub-stylesheets
+ * </li><li>
+ * Record the fact that java files must be recompiled if the ant build file changes
+ * </li></ul>
+ *
+ * @ant.task category="filesystem"
+ * @since Ant 1.4
+ */
+public class DependSet extends MatchingTask {
+
+ private static final ResourceSelector NOT_EXISTS = new Not(new Exists());
+ private static final ResourceComparator DATE
+ = new org.apache.tools.ant.types.resources.comparators.Date();
+ private static final ResourceComparator REVERSE_DATE = new Reverse(DATE);
+
+ private static final class NonExistent extends Restrict {
+ private NonExistent(ResourceCollection rc) {
+ super.add(rc);
+ super.add(NOT_EXISTS);
+ }
+ }
+
+ private static final class HideMissingBasedir
+ implements ResourceCollection {
+ private FileSet fs;
+
+ private HideMissingBasedir(FileSet fs) {
+ this.fs = fs;
+ }
+ public Iterator<Resource> iterator() {
+ return basedirExists() ? fs.iterator() : Resources.EMPTY_ITERATOR;
+ }
+ public int size() {
+ return basedirExists() ? fs.size() : 0;
+ }
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+ private boolean basedirExists() {
+ File basedir = fs.getDir();
+ //trick to evoke "basedir not set" if null:
+ return basedir == null || basedir.exists();
+ }
+ }
+
+ private Union sources = null;
+ private Path targets = null;
+
+ private boolean verbose;
+
+ /**
+ * Create a nested sources element.
+ * @return a Union instance.
+ */
+ public synchronized Union createSources() {
+ sources = (sources == null) ? new Union() : sources;
+ return sources;
+ }
+
+ /**
+ * Add a set of source files.
+ * @param fs the FileSet to add.
+ */
+ public void addSrcfileset(FileSet fs) {
+ createSources().add(fs);
+ }
+
+ /**
+ * Add a list of source files.
+ * @param fl the FileList to add.
+ */
+ public void addSrcfilelist(FileList fl) {
+ createSources().add(fl);
+ }
+
+ /**
+ * Create a nested targets element.
+ * @return a Union instance.
+ */
+ public synchronized Path createTargets() {
+ targets = (targets == null) ? new Path(getProject()) : targets;
+ return targets;
+ }
+
+ /**
+ * Add a set of target files.
+ * @param fs the FileSet to add.
+ */
+ public void addTargetfileset(FileSet fs) {
+ createTargets().add(new HideMissingBasedir(fs));
+ }
+
+ /**
+ * Add a list of target files.
+ * @param fl the FileList to add.
+ */
+ public void addTargetfilelist(FileList fl) {
+ createTargets().add(fl);
+ }
+
+ /**
+ * In verbose mode missing targets and sources as well as the
+ * modification times of the newest source and latest target will
+ * be logged as info.
+ *
+ * <p>All deleted files will be logged as well.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public void setVerbose(boolean b) {
+ verbose = b;
+ }
+
+ /**
+ * Execute the task.
+ * @throws BuildException if errors occur.
+ */
+ public void execute() throws BuildException {
+ if (sources == null) {
+ throw new BuildException(
+ "At least one set of source resources must be specified");
+ }
+ if (targets == null) {
+ throw new BuildException(
+ "At least one set of target files must be specified");
+ }
+ //no sources = nothing to compare; no targets = nothing to delete:
+ if (sources.size() > 0 && targets.size() > 0 && !uptodate(sources, targets)) {
+ log("Deleting all target files.", Project.MSG_VERBOSE);
+ if (verbose) {
+ String[] t = targets.list();
+ for (int i = 0; i < t.length; i++) {
+ log("Deleting " + t[i]);
+ }
+ }
+ Delete delete = new Delete();
+ delete.bindToOwner(this);
+ delete.add(targets);
+ delete.perform();
+ }
+ }
+
+ private boolean uptodate(ResourceCollection src, ResourceCollection target) {
+ org.apache.tools.ant.types.resources.selectors.Date datesel
+ = new org.apache.tools.ant.types.resources.selectors.Date();
+ datesel.setMillis(System.currentTimeMillis());
+ datesel.setWhen(TimeComparison.AFTER);
+ // don't whine because a file has changed during the last
+ // second (or whathever our current granularity may be)
+ datesel.setGranularity(0);
+ logFuture(targets, datesel);
+
+ NonExistent missingTargets = new NonExistent(targets);
+ int neTargets = missingTargets.size();
+ if (neTargets > 0) {
+ log(neTargets + " nonexistent targets", Project.MSG_VERBOSE);
+ logMissing(missingTargets, "target");
+ return false;
+ }
+ Resource oldestTarget = getOldest(targets);
+ logWithModificationTime(oldestTarget, "oldest target file");
+
+ logFuture(sources, datesel);
+
+ NonExistent missingSources = new NonExistent(sources);
+ int neSources = missingSources.size();
+ if (neSources > 0) {
+ log(neSources + " nonexistent sources", Project.MSG_VERBOSE);
+ logMissing(missingSources, "source");
+ return false;
+ }
+ Resource newestSource = (Resource) getNewest(sources);
+ logWithModificationTime(newestSource, "newest source");
+ return oldestTarget.getLastModified() >= newestSource.getLastModified();
+ }
+
+ private void logFuture(ResourceCollection rc, ResourceSelector rsel) {
+ Restrict r = new Restrict();
+ r.add(rsel);
+ r.add(rc);
+ for (Resource res : r) {
+ log("Warning: " + res + " modified in the future.", Project.MSG_WARN);
+ }
+ }
+
+ private Resource getXest(ResourceCollection rc, ResourceComparator c) {
+ Iterator<Resource> i = rc.iterator();
+ if (!i.hasNext()) {
+ return null;
+
+ }
+ Resource xest = i.next();
+ while (i.hasNext()) {
+ Resource next = i.next();
+ if (c.compare(xest, next) < 0) {
+ xest = next;
+ }
+ }
+ return xest;
+ }
+
+ private Resource getOldest(ResourceCollection rc) {
+ return getXest(rc, REVERSE_DATE);
+ }
+
+ private Resource getNewest(ResourceCollection rc) {
+ return getXest(rc, DATE);
+ }
+
+ private void logWithModificationTime(Resource r, String what) {
+ log(r.toLongString() + " is " + what + ", modified at "
+ + new Date(r.getLastModified()),
+ verbose ? Project.MSG_INFO : Project.MSG_VERBOSE);
+ }
+
+ private void logMissing(ResourceCollection missing, String what) {
+ if (verbose) {
+ for (Resource r : missing) {
+ log("Expected " + what + " " + r.toLongString()
+ + " is missing.");
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DiagnosticsTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DiagnosticsTask.java
new file mode 100644
index 00000000..c745cfb5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DiagnosticsTask.java
@@ -0,0 +1,41 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Diagnostics;
+import org.apache.tools.ant.Task;
+
+/**
+ * This is a task that hands off work to the Diagnostics module.
+ * It lets you run diagnostics in an IDE.
+ */
+public class DiagnosticsTask extends Task {
+
+ private static final String[] ARGS = new String[0];
+
+ /**
+ * Execute the task.
+ * This delegates to the Diagnostics class.
+ * @throws BuildException on error.
+ */
+ public void execute() throws BuildException {
+ Diagnostics.main(ARGS);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Dirname.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Dirname.java
new file mode 100644
index 00000000..c4c5d5f2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Dirname.java
@@ -0,0 +1,82 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Determines the directory name of the specified file.
+ *
+ * This task can accept the following attributes:
+ * <ul>
+ * <li>file
+ * <li>property
+ * </ul>
+ * Both <b>file</b> and <b>property</b> are required.
+ * <p>
+ * When this task executes, it will set the specified property to the
+ * value of the specified file up to, but not including, the last path
+ * element. If file is a file, the directory will be the current
+ * directory.
+ *
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="property"
+ */
+
+public class Dirname extends Task {
+ private File file;
+ private String property;
+
+ /**
+ * Path to take the dirname of.
+ * @param file a <code>File</code> value
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * The name of the property to set.
+ * @param property the name of the property
+ */
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ /**
+ * Execute this task.
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ if (property == null) {
+ throw new BuildException("property attribute required", getLocation());
+ }
+ if (file == null) {
+ throw new BuildException("file attribute required", getLocation());
+ } else {
+ String value = file.getParent();
+ getProject().setNewProperty(property, value);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Ear.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Ear.java
new file mode 100644
index 00000000..6e08ecda
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Ear.java
@@ -0,0 +1,154 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.ZipFileSet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.zip.ZipOutputStream;
+
+/**
+ * Creates a EAR archive. Based on WAR task
+ *
+ * @since Ant 1.4
+ *
+ * @ant.task category="packaging"
+ */
+public class Ear extends Jar {
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private File deploymentDescriptor;
+ private boolean descriptorAdded;
+ private static final String XML_DESCRIPTOR_PATH = "META-INF/application.xml";
+
+ /**
+ * Create an Ear task.
+ */
+ public Ear() {
+ super();
+ archiveType = "ear";
+ emptyBehavior = "create";
+ }
+
+ /**
+ * Set the destination file.
+ * @param earFile the destination file
+ * @deprecated since 1.5.x.
+ * Use setDestFile(destfile) instead.
+ */
+ public void setEarfile(File earFile) {
+ setDestFile(earFile);
+ }
+
+ /**
+ * File to incorporate as application.xml.
+ * @param descr the descriptor file
+ */
+ public void setAppxml(File descr) {
+ deploymentDescriptor = descr;
+ if (!deploymentDescriptor.exists()) {
+ throw new BuildException("Deployment descriptor: "
+ + deploymentDescriptor
+ + " does not exist.");
+ }
+
+ // Create a ZipFileSet for this file, and pass it up.
+ ZipFileSet fs = new ZipFileSet();
+ fs.setFile(deploymentDescriptor);
+ fs.setFullpath(XML_DESCRIPTOR_PATH);
+ super.addFileset(fs);
+ }
+
+
+ /**
+ * Adds zipfileset.
+ *
+ * @param fs zipfileset to add
+ */
+ public void addArchives(ZipFileSet fs) {
+ // We just set the prefix for this fileset, and pass it up.
+ // Do we need to do this? LH
+ fs.setPrefix("/");
+ super.addFileset(fs);
+ }
+
+
+ /**
+ * Initialize the output stream.
+ * @param zOut the zip output stream.
+ * @throws IOException on I/O errors
+ * @throws BuildException on other errors
+ */
+ protected void initZipOutputStream(ZipOutputStream zOut)
+ throws IOException, BuildException {
+ // If no webxml file is specified, it's an error.
+ if (deploymentDescriptor == null && !isInUpdateMode()) {
+ throw new BuildException("appxml attribute is required", getLocation());
+ }
+
+ super.initZipOutputStream(zOut);
+ }
+
+ /**
+ * Overridden from Zip class to deal with application.xml
+ * @param file the file to add to the archive
+ * @param zOut the stream to write to
+ * @param vPath the name this entry shall have in the archive
+ * @param mode the Unix permissions to set.
+ * @throws IOException on error
+ */
+ protected void zipFile(File file, ZipOutputStream zOut, String vPath,
+ int mode)
+ throws IOException {
+ // If the file being added is META-INF/application.xml, we
+ // warn if it's not the one specified in the "appxml"
+ // attribute - or if it's being added twice, meaning the same
+ // file is specified by the "appxml" attribute and in a
+ // <fileset> element.
+ if (XML_DESCRIPTOR_PATH.equalsIgnoreCase(vPath)) {
+ if (deploymentDescriptor == null
+ || !FILE_UTILS.fileNameEquals(deploymentDescriptor, file)
+ || descriptorAdded) {
+ logWhenWriting("Warning: selected " + archiveType
+ + " files include a " + XML_DESCRIPTOR_PATH
+ + " which will"
+ + " be ignored (please use appxml attribute to "
+ + archiveType + " task)",
+ Project.MSG_WARN);
+ } else {
+ super.zipFile(file, zOut, vPath, mode);
+ descriptorAdded = true;
+ }
+ } else {
+ super.zipFile(file, zOut, vPath, mode);
+ }
+ }
+
+ /**
+ * Make sure we don't think we already have a application.xml next
+ * time this task gets executed.
+ */
+ protected void cleanUp() {
+ descriptorAdded = false;
+ super.cleanUp();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Echo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Echo.java
new file mode 100644
index 00000000..ac31ee32
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Echo.java
@@ -0,0 +1,170 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.LogLevel;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.LogOutputResource;
+import org.apache.tools.ant.types.resources.StringResource;
+import org.apache.tools.ant.util.ResourceUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Writes a message to the Ant logging facilities.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="utility"
+ */
+public class Echo extends Task {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected String message = "";
+ protected File file = null;
+ protected boolean append = false;
+ /** encoding; set to null or empty means 'default' */
+ private String encoding = "";
+ private boolean force = false;
+
+ // by default, messages are always displayed
+ protected int logLevel = Project.MSG_WARN;
+ // CheckStyle:VisibilityModifier ON
+
+ private Resource output;
+
+ /**
+ * Does the work.
+ *
+ * @exception BuildException if something goes wrong with the build
+ */
+ public void execute() throws BuildException {
+ final String msg = "".equals(message) ? StringUtils.LINE_SEP : message;
+ try {
+ ResourceUtils
+ .copyResource(new StringResource(msg), output == null
+ ? new LogOutputResource(this, logLevel)
+ : output,
+ null, null, false, false, append, null,
+ "".equals(encoding) ? null : encoding,
+ getProject(), force);
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, getLocation());
+ }
+ }
+
+ /**
+ * Message to write.
+ *
+ * @param msg Sets the value for the message variable.
+ */
+ public void setMessage(String msg) {
+ this.message = msg == null ? "" : msg;
+ }
+
+ /**
+ * File to write to.
+ * @param file the file to write to, if not set, echo to
+ * standard output
+ */
+ public void setFile(File file) {
+ setOutput(new FileResource(getProject(), file));
+ }
+
+ /**
+ * Resource to write to.
+ * @param output the Resource to write to.
+ * @since Ant 1.8
+ */
+ public void setOutput(Resource output) {
+ if (this.output != null) {
+ throw new BuildException("Cannot set > 1 output target");
+ }
+ this.output = output;
+ FileProvider fp = output.as(FileProvider.class);
+ this.file = fp != null ? fp.getFile() : null;
+ }
+
+ /**
+ * If true, append to existing file.
+ * @param append if true, append to existing file, default
+ * is false.
+ */
+ public void setAppend(boolean append) {
+ this.append = append;
+ }
+
+ /**
+ * Set a multiline message.
+ * @param msg the CDATA text to append to the output text
+ */
+ public void addText(String msg) {
+ message += getProject().replaceProperties(msg);
+ }
+
+ /**
+ * Set the logging level. Level should be one of
+ * <ul>
+ * <li>error</li>
+ * <li>warning</li>
+ * <li>info</li>
+ * <li>verbose</li>
+ * <li>debug</li>
+ * </ul>
+ * <p>The default is &quot;warning&quot; to ensure that messages are
+ * displayed by default when using the -quiet command line option.</p>
+ * @param echoLevel the logging level
+ */
+ public void setLevel(EchoLevel echoLevel) {
+ logLevel = echoLevel.getLevel();
+ }
+
+ /**
+ * Declare the encoding to use when outputting to a file;
+ * Use "" for the platform's default encoding.
+ * @param encoding the character encoding to use.
+ * @since 1.7
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Whether read-only destinations will be overwritten.
+ *
+ * <p>Defaults to false</p>
+ *
+ * @since Ant 1.8.2
+ */
+ public void setForce(boolean f) {
+ force = f;
+ }
+
+ /**
+ * The enumerated values for the level attribute.
+ */
+ public static class EchoLevel extends LogLevel {
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/EchoXML.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/EchoXML.java
new file mode 100644
index 00000000..4a4fda29
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/EchoXML.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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.XMLFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Echo XML.
+ *
+ * Known limitations:
+ * <ol>
+ * <li>Processing Instructions get ignored</li>
+ * <li>Encoding is always UTF-8</li>
+ * </ol>
+ *
+ * @since Ant 1.7
+ */
+public class EchoXML extends XMLFragment {
+
+ private File file;
+ private boolean append;
+ private NamespacePolicy namespacePolicy = NamespacePolicy.DEFAULT;
+ private static final String ERROR_NO_XML = "No nested XML specified";
+
+ /**
+ * Set the output file.
+ * @param f the output file.
+ */
+ public void setFile(File f) {
+ file = f;
+ }
+
+ /**
+ * Set the namespace policy for the xml file
+ * @param n namespace policy: "ignore," "elementsOnly," or "all"
+ * @see
+ * org.apache.tools.ant.util.DOMElementWriter.XmlNamespacePolicy
+ */
+ public void setNamespacePolicy(NamespacePolicy n) {
+ namespacePolicy = n;
+ }
+
+ /**
+ * Set whether to append the output file.
+ * @param b boolean append flag.
+ */
+ public void setAppend(boolean b) {
+ append = b;
+ }
+
+ /**
+ * Execute the task.
+ */
+ public void execute() {
+ DOMElementWriter writer =
+ new DOMElementWriter(!append, namespacePolicy.getPolicy());
+ OutputStream os = null;
+ try {
+ if (file != null) {
+ os = new FileOutputStream(file.getAbsolutePath(), append);
+ } else {
+ os = new LogOutputStream(this, Project.MSG_INFO);
+ }
+ Node n = getFragment().getFirstChild();
+ if (n == null) {
+ throw new BuildException(ERROR_NO_XML);
+ }
+ writer.write((Element) n, os);
+ } catch (BuildException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new BuildException(e);
+ } finally {
+ FileUtils.close(os);
+ }
+ }
+
+ public static class NamespacePolicy extends EnumeratedAttribute {
+ private static final String IGNORE = "ignore";
+ private static final String ELEMENTS = "elementsOnly";
+ private static final String ALL = "all";
+
+ public static final NamespacePolicy DEFAULT
+ = new NamespacePolicy(IGNORE);
+
+ public NamespacePolicy() {}
+
+ public NamespacePolicy(String s) {
+ setValue(s);
+ }
+ /** {@inheritDoc}. */
+ @Override
+ public String[] getValues() {
+ return new String[] {IGNORE, ELEMENTS, ALL};
+ }
+
+ public DOMElementWriter.XmlNamespacePolicy getPolicy() {
+ String s = getValue();
+ if (IGNORE.equalsIgnoreCase(s)) {
+ return DOMElementWriter.XmlNamespacePolicy.IGNORE;
+ } else if (ELEMENTS.equalsIgnoreCase(s)) {
+ return
+ DOMElementWriter.XmlNamespacePolicy.ONLY_QUALIFY_ELEMENTS;
+ } else if (ALL.equalsIgnoreCase(s)) {
+ return DOMElementWriter.XmlNamespacePolicy.QUALIFY_ALL;
+ } else {
+ throw new BuildException("Invalid namespace policy: " + s);
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Exec.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Exec.java
new file mode 100644
index 00000000..cfc6b76c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Exec.java
@@ -0,0 +1,281 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.Locale;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Executes a given command if the os platform is appropriate.
+ *
+ * <p><strong>As of Ant 1.2, this class is no longer the
+ * implementation of Ant's &lt;exec&gt; task - it is considered to be
+ * dead code by the Ant developers and is unmaintained. Don't use
+ * it.</strong></p>
+ *
+ * @deprecated since 1.2.
+ * delegate to {@link org.apache.tools.ant.taskdefs.Execute Execute}
+ * instead.
+ */
+public class Exec extends Task {
+ private String os;
+ private String out;
+ private File dir;
+ private String command;
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected PrintWriter fos = null;
+ // CheckStyle:VisibilityModifier ON
+ private boolean failOnError = false;
+
+ /**
+ * Constructor for Exec.
+ * Prints a warning message to std error.
+ */
+ public Exec() {
+ System.err.println("As of Ant 1.2 released in October 2000, "
+ + "the Exec class");
+ System.err.println("is considered to be dead code by the Ant "
+ + "developers and is unmaintained.");
+ System.err.println("Don\'t use it!");
+ }
+
+ /**
+ * Execute the task.
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ run(command);
+ }
+
+ /**
+ * Execute the command.
+ * @param command the command to exec
+ * @return the exit value of the command
+ * @throws BuildException on error
+ */
+ protected int run(String command) throws BuildException {
+
+ int err = -1; // assume the worst
+
+ // test if os match
+ String myos = System.getProperty("os.name");
+ log("Myos = " + myos, Project.MSG_VERBOSE);
+ if ((os != null) && (os.indexOf(myos) < 0)) {
+ // this command will be executed only on the specified OS
+ log("Not found in " + os, Project.MSG_VERBOSE);
+ return 0;
+ }
+
+ // default directory to the project's base directory
+ if (dir == null) {
+ dir = getProject().getBaseDir();
+ }
+
+ if (myos.toLowerCase(Locale.ENGLISH).indexOf("windows") >= 0) {
+ if (!dir.equals(getProject().resolveFile("."))) {
+ if (myos.toLowerCase(Locale.ENGLISH).indexOf("nt") >= 0) {
+ command = "cmd /c cd " + dir + " && " + command;
+ } else {
+ String ant = getProject().getProperty(MagicNames.ANT_HOME);
+ if (ant == null) {
+ throw new BuildException("Property '" + MagicNames.ANT_HOME + "' not "
+ + "found", getLocation());
+ }
+
+ String antRun = getProject().resolveFile(ant + "/bin/antRun.bat").toString();
+ command = antRun + " " + dir + " " + command;
+ }
+ }
+ } else {
+ String ant = getProject().getProperty(MagicNames.ANT_HOME);
+ if (ant == null) {
+ throw new BuildException("Property '" + MagicNames.ANT_HOME + "' not found",
+ getLocation());
+ }
+ String antRun = getProject().resolveFile(ant + "/bin/antRun").toString();
+
+ command = antRun + " " + dir + " " + command;
+ }
+
+ try {
+ // show the command
+ log(command, Project.MSG_VERBOSE);
+
+ // exec command on system runtime
+ Process proc = Runtime.getRuntime().exec(command);
+
+ if (out != null) {
+ fos = new PrintWriter(new FileWriter(out));
+ log("Output redirected to " + out, Project.MSG_VERBOSE);
+ }
+
+ // copy input and error to the output stream
+ StreamPumper inputPumper =
+ new StreamPumper(proc.getInputStream(), Project.MSG_INFO);
+ StreamPumper errorPumper =
+ new StreamPumper(proc.getErrorStream(), Project.MSG_WARN);
+
+ // starts pumping away the generated output/error
+ inputPumper.start();
+ errorPumper.start();
+
+ // Wait for everything to finish
+ proc.waitFor();
+ inputPumper.join();
+ errorPumper.join();
+ proc.destroy();
+
+ // close the output file if required
+ logFlush();
+
+ // check its exit value
+ err = proc.exitValue();
+ if (err != 0) {
+ if (failOnError) {
+ throw new BuildException("Exec returned: " + err, getLocation());
+ } else {
+ log("Result: " + err, Project.MSG_ERR);
+ }
+ }
+ } catch (IOException ioe) {
+ throw new BuildException("Error exec: " + command, ioe, getLocation());
+ } catch (InterruptedException ex) {
+ //ignore
+ }
+
+ return err;
+ }
+
+ /**
+ * Set the directory.
+ * @param d a <code>String</code> value
+ */
+ public void setDir(String d) {
+ this.dir = getProject().resolveFile(d);
+ }
+
+ /**
+ * Set the Operating System that this exec is to run in.
+ * @param os a <code>String</code> value
+ */
+ public void setOs(String os) {
+ this.os = os;
+ }
+
+ /**
+ * Set the command to exec.
+ * @param command a <code>String</code> value
+ */
+ public void setCommand(String command) {
+ this.command = command;
+ }
+
+ /**
+ * Set the output filename.
+ * @param out a <code>String</code> value
+ */
+ public void setOutput(String out) {
+ this.out = out;
+ }
+
+ /**
+ * Set the failOnError attribute.
+ * Default is false.
+ * @param fail a <code>boolean</code> value
+ */
+ public void setFailonerror(boolean fail) {
+ failOnError = fail;
+ }
+
+ /**
+ * Log an output message.
+ * @param line the line to putput
+ * @param messageLevel the level of logging - ignored
+ * if output is going to a file
+ */
+ protected void outputLog(String line, int messageLevel) {
+ if (fos == null) {
+ log(line, messageLevel);
+ } else {
+ fos.println(line);
+ }
+ }
+
+ /**
+ * Close output.
+ */
+ protected void logFlush() {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+
+ // Inner class for continually pumping the input stream during
+ // Process's runtime.
+ class StreamPumper extends Thread {
+ private BufferedReader din;
+ private int messageLevel;
+ private boolean endOfStream = false;
+ private static final int SLEEP_TIME = 5;
+
+ public StreamPumper(InputStream is, int messageLevel) {
+ this.din = new BufferedReader(new InputStreamReader(is));
+ this.messageLevel = messageLevel;
+ }
+
+ public void pumpStream() throws IOException {
+ if (!endOfStream) {
+ String line = din.readLine();
+
+ if (line != null) {
+ outputLog(line, messageLevel);
+ } else {
+ endOfStream = true;
+ }
+ }
+ }
+
+ public void run() {
+ try {
+ try {
+ while (!endOfStream) {
+ pumpStream();
+ sleep(SLEEP_TIME);
+ }
+ } catch (InterruptedException ie) {
+ //ignore
+ }
+ din.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecTask.java
new file mode 100644
index 00000000..dd93978f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecTask.java
@@ -0,0 +1,726 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.RedirectorElement;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Executes a given command if the os platform is appropriate.
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="control"
+ */
+public class ExecTask extends Task {
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private String os;
+ private String osFamily;
+
+ private File dir;
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected boolean failOnError = false;
+ protected boolean newEnvironment = false;
+ private Long timeout = null;
+ private Environment env = new Environment();
+ protected Commandline cmdl = new Commandline();
+ private String resultProperty;
+ private boolean failIfExecFails = true;
+ private String executable;
+ private boolean resolveExecutable = false;
+ private boolean searchPath = false;
+ private boolean spawn = false;
+ private boolean incompatibleWithSpawn = false;
+
+ //include locally for screening purposes
+ private String inputString;
+ private File input;
+ private File output;
+ private File error;
+
+ protected Redirector redirector = new Redirector(this);
+ protected RedirectorElement redirectorElement;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Controls whether the VM (1.3 and above) is used to execute the
+ * command
+ */
+ private boolean vmLauncher = true;
+
+
+ /**
+ * Create an instance.
+ * Needs to be configured by binding to a project.
+ */
+ public ExecTask() {
+ }
+
+ /**
+ * create an instance that is helping another task.
+ * Project, OwningTarget, TaskName and description are all
+ * pulled out
+ * @param owner task that we belong to
+ */
+ public ExecTask(Task owner) {
+ bindToOwner(owner);
+ }
+
+ /**
+ * Set whether or not you want the process to be spawned.
+ * Default is false.
+ * @param spawn if true you do not want Ant to wait for the end of the process.
+ * @since Ant 1.6
+ */
+ public void setSpawn(boolean spawn) {
+ this.spawn = spawn;
+ }
+
+ /**
+ * Set the timeout in milliseconds after which the process will be killed.
+ *
+ * @param value timeout in milliseconds.
+ *
+ * @since Ant 1.5
+ */
+ public void setTimeout(Long value) {
+ timeout = value;
+ incompatibleWithSpawn |= timeout != null;
+ }
+
+ /**
+ * Set the timeout in milliseconds after which the process will be killed.
+ *
+ * @param value timeout in milliseconds.
+ */
+ public void setTimeout(Integer value) {
+ setTimeout(
+ (Long) ((value == null) ? null : new Long(value.intValue())));
+ }
+
+ /**
+ * Set the name of the executable program.
+ * @param value the name of the executable program.
+ */
+ public void setExecutable(String value) {
+ this.executable = value;
+ cmdl.setExecutable(value);
+ }
+
+ /**
+ * Set the working directory of the process.
+ * @param d the working directory of the process.
+ */
+ public void setDir(File d) {
+ this.dir = d;
+ }
+
+ /**
+ * List of operating systems on which the command may be executed.
+ * @param os list of operating systems on which the command may be executed.
+ */
+ public void setOs(String os) {
+ this.os = os;
+ }
+
+ /**
+ * List of operating systems on which the command may be executed.
+ * @since Ant 1.8.0
+ */
+ public final String getOs() {
+ return os;
+ }
+
+ /**
+ * Sets a command line.
+ * @param cmdl command line.
+ * @ant.attribute ignore="true"
+ */
+ public void setCommand(Commandline cmdl) {
+ log("The command attribute is deprecated.\n"
+ + "Please use the executable attribute and nested arg elements.",
+ Project.MSG_WARN);
+ this.cmdl = cmdl;
+ }
+
+ /**
+ * File the output of the process is redirected to. If error is not
+ * redirected, it too will appear in the output.
+ *
+ * @param out name of a file to which output should be sent.
+ */
+ public void setOutput(File out) {
+ this.output = out;
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Set the input file to use for the task.
+ *
+ * @param input name of a file from which to get input.
+ */
+ public void setInput(File input) {
+ if (inputString != null) {
+ throw new BuildException("The \"input\" and \"inputstring\" "
+ + "attributes cannot both be specified");
+ }
+ this.input = input;
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Set the string to use as input.
+ *
+ * @param inputString the string which is used as the input source.
+ */
+ public void setInputString(String inputString) {
+ if (input != null) {
+ throw new BuildException("The \"input\" and \"inputstring\" "
+ + "attributes cannot both be specified");
+ }
+ this.inputString = inputString;
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Controls whether error output of exec is logged. This is only useful when
+ * output is being redirected and error output is desired in the Ant log.
+ *
+ * @param logError set to true to log error output in the normal ant log.
+ */
+ public void setLogError(boolean logError) {
+ redirector.setLogError(logError);
+ incompatibleWithSpawn |= logError;
+ }
+
+ /**
+ * Set the File to which the error stream of the process should be redirected.
+ *
+ * @param error a file to which stderr should be sent.
+ *
+ * @since Ant 1.6
+ */
+ public void setError(File error) {
+ this.error = error;
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Sets the property name whose value should be set to the output of
+ * the process.
+ *
+ * @param outputProp name of property.
+ */
+ public void setOutputproperty(String outputProp) {
+ redirector.setOutputProperty(outputProp);
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Sets the name of the property whose value should be set to the error of
+ * the process.
+ *
+ * @param errorProperty name of property.
+ *
+ * @since Ant 1.6
+ */
+ public void setErrorProperty(String errorProperty) {
+ redirector.setErrorProperty(errorProperty);
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Fail if the command exits with a non-zero return code.
+ *
+ * @param fail if true fail the command on non-zero return code.
+ */
+ public void setFailonerror(boolean fail) {
+ failOnError = fail;
+ incompatibleWithSpawn |= fail;
+ }
+
+ /**
+ * Do not propagate old environment when new environment variables are specified.
+ *
+ * @param newenv if true, do not propagate old environment
+ * when new environment variables are specified.
+ */
+ public void setNewenvironment(boolean newenv) {
+ newEnvironment = newenv;
+ }
+
+ /**
+ * Set whether to attempt to resolve the executable to a file.
+ *
+ * @param resolveExecutable if true, attempt to resolve the
+ * path of the executable.
+ */
+ public void setResolveExecutable(boolean resolveExecutable) {
+ this.resolveExecutable = resolveExecutable;
+ }
+
+ /**
+ * Set whether to search nested, then
+ * system PATH environment variables for the executable.
+ *
+ * @param searchPath if true, search PATHs.
+ */
+ public void setSearchPath(boolean searchPath) {
+ this.searchPath = searchPath;
+ }
+
+ /**
+ * Indicates whether to attempt to resolve the executable to a
+ * file.
+ * @return the resolveExecutable flag
+ *
+ * @since Ant 1.6
+ */
+ public boolean getResolveExecutable() {
+ return resolveExecutable;
+ }
+
+ /**
+ * Add an environment variable to the launched process.
+ *
+ * @param var new environment variable.
+ */
+ public void addEnv(Environment.Variable var) {
+ env.addVariable(var);
+ }
+
+ /**
+ * Adds a command-line argument.
+ *
+ * @return new command line argument created.
+ */
+ public Commandline.Argument createArg() {
+ return cmdl.createArgument();
+ }
+
+ /**
+ * Sets the name of a property in which the return code of the
+ * command should be stored. Only of interest if failonerror=false.
+ *
+ * @since Ant 1.5
+ *
+ * @param resultProperty name of property.
+ */
+ public void setResultProperty(String resultProperty) {
+ this.resultProperty = resultProperty;
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Helper method to set result property to the
+ * passed in value if appropriate.
+ *
+ * @param result value desired for the result property value.
+ */
+ protected void maybeSetResultPropertyValue(int result) {
+ if (resultProperty != null) {
+ String res = Integer.toString(result);
+ getProject().setNewProperty(resultProperty, res);
+ }
+ }
+
+ /**
+ * Set whether to stop the build if program cannot be started.
+ * Defaults to true.
+ *
+ * @param flag stop the build if program cannot be started.
+ *
+ * @since Ant 1.5
+ */
+ public void setFailIfExecutionFails(boolean flag) {
+ failIfExecFails = flag;
+ incompatibleWithSpawn |= flag;
+ }
+
+ /**
+ * Set whether output should be appended to or overwrite an existing file.
+ * Defaults to false.
+ *
+ * @param append if true append is desired.
+ *
+ * @since 1.30, Ant 1.5
+ */
+ public void setAppend(boolean append) {
+ redirector.setAppend(append);
+ incompatibleWithSpawn |= append;
+ }
+
+ /**
+ * Add a <code>RedirectorElement</code> to this task.
+ *
+ * @param redirectorElement <code>RedirectorElement</code>.
+ * @since Ant 1.6.2
+ */
+ public void addConfiguredRedirector(RedirectorElement redirectorElement) {
+ if (this.redirectorElement != null) {
+ throw new BuildException("cannot have > 1 nested <redirector>s");
+ }
+ this.redirectorElement = redirectorElement;
+ incompatibleWithSpawn = true;
+ }
+
+
+ /**
+ * Restrict this execution to a single OS Family
+ * @param osFamily the family to restrict to.
+ */
+ public void setOsFamily(String osFamily) {
+ this.osFamily = osFamily.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Restrict this execution to a single OS Family
+ * @since Ant 1.8.0
+ */
+ public final String getOsFamily() {
+ return osFamily;
+ }
+
+ /**
+ * The method attempts to figure out where the executable is so that we can feed
+ * the full path. We first try basedir, then the exec dir, and then
+ * fallback to the straight executable name (i.e. on the path).
+ *
+ * @param exec the name of the executable.
+ * @param mustSearchPath if true, the executable will be looked up in
+ * the PATH environment and the absolute path is returned.
+ *
+ * @return the executable as a full path if it can be determined.
+ *
+ * @since Ant 1.6
+ */
+ protected String resolveExecutable(String exec, boolean mustSearchPath) {
+ if (!resolveExecutable) {
+ return exec;
+ }
+ // try to find the executable
+ File executableFile = getProject().resolveFile(exec);
+ if (executableFile.exists()) {
+ return executableFile.getAbsolutePath();
+ }
+ // now try to resolve against the dir if given
+ if (dir != null) {
+ executableFile = FILE_UTILS.resolveFile(dir, exec);
+ if (executableFile.exists()) {
+ return executableFile.getAbsolutePath();
+ }
+ }
+ // couldn't find it - must be on path
+ if (mustSearchPath) {
+ Path p = null;
+ String[] environment = env.getVariables();
+ if (environment != null) {
+ for (int i = 0; i < environment.length; i++) {
+ if (isPath(environment[i])) {
+ p = new Path(getProject(), getPath(environment[i]));
+ break;
+ }
+ }
+ }
+ if (p == null) {
+ String path = getPath(Execute.getEnvironmentVariables());
+ if (path != null) {
+ p = new Path(getProject(), path);
+ }
+ }
+ if (p != null) {
+ String[] dirs = p.list();
+ for (int i = 0; i < dirs.length; i++) {
+ executableFile
+ = FILE_UTILS.resolveFile(new File(dirs[i]), exec);
+ if (executableFile.exists()) {
+ return executableFile.getAbsolutePath();
+ }
+ }
+ }
+ }
+ // mustSearchPath is false, or no PATH or not found - keep our
+ // fingers crossed.
+ return exec;
+ }
+
+ /**
+ * Do the work.
+ *
+ * @throws BuildException in a number of circumstances:
+ * <ul>
+ * <li>if failIfExecFails is set to true and the process cannot be started</li>
+ * <li>the java13command launcher can send build exceptions</li>
+ * <li>this list is not exhaustive or limitative</li>
+ * </ul>
+ */
+ public void execute() throws BuildException {
+ // Quick fail if this is not a valid OS for the command
+ if (!isValidOs()) {
+ return;
+ }
+ File savedDir = dir; // possibly altered in prepareExec
+ cmdl.setExecutable(resolveExecutable(executable, searchPath));
+ checkConfiguration();
+ try {
+ runExec(prepareExec());
+ } finally {
+ dir = savedDir;
+ }
+ }
+
+ /**
+ * Has the user set all necessary attributes?
+ * @throws BuildException if there are missing required parameters.
+ */
+ protected void checkConfiguration() throws BuildException {
+ if (cmdl.getExecutable() == null) {
+ throw new BuildException("no executable specified", getLocation());
+ }
+ if (dir != null && !dir.exists()) {
+ throw new BuildException("The directory " + dir + " does not exist");
+ }
+ if (dir != null && !dir.isDirectory()) {
+ throw new BuildException(dir + " is not a directory");
+ }
+ if (spawn && incompatibleWithSpawn) {
+ getProject().log("spawn does not allow attributes related to input, "
+ + "output, error, result", Project.MSG_ERR);
+ getProject().log("spawn also does not allow timeout", Project.MSG_ERR);
+ getProject().log("finally, spawn is not compatible "
+ + "with a nested I/O <redirector>", Project.MSG_ERR);
+ throw new BuildException("You have used an attribute "
+ + "or nested element which is not compatible with spawn");
+ }
+ setupRedirector();
+ }
+
+ /**
+ * Set up properties on the redirector that we needed to store locally.
+ */
+ protected void setupRedirector() {
+ redirector.setInput(input);
+ redirector.setInputString(inputString);
+ redirector.setOutput(output);
+ redirector.setError(error);
+ }
+
+ /**
+ * Is this the OS the user wanted?
+ * @return boolean.
+ * <ul>
+ * <li>
+ * <li><code>true</code> if the os and osfamily attributes are null.</li>
+ * <li><code>true</code> if osfamily is set, and the os family and must match
+ * that of the current OS, according to the logic of
+ * {@link Os#isOs(String, String, String, String)}, and the result of the
+ * <code>os</code> attribute must also evaluate true.
+ * </li>
+ * <li>
+ * <code>true</code> if os is set, and the system.property os.name
+ * is found in the os attribute,</li>
+ * <li><code>false</code> otherwise.</li>
+ * </ul>
+ */
+ protected boolean isValidOs() {
+ //hand osfamily off to Os class, if set
+ if (osFamily != null && !Os.isFamily(osFamily)) {
+ return false;
+ }
+ //the Exec OS check is different from Os.isOs(), which
+ //probes for a specific OS. Instead it searches the os field
+ //for the current os.name
+ String myos = System.getProperty("os.name");
+ log("Current OS is " + myos, Project.MSG_VERBOSE);
+ if ((os != null) && (os.indexOf(myos) < 0)) {
+ // this command will be executed only on the specified OS
+ log("This OS, " + myos
+ + " was not found in the specified list of valid OSes: " + os,
+ Project.MSG_VERBOSE);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Set whether to launch new process with VM, otherwise use the OS's shell.
+ * Default value is true.
+ * @param vmLauncher true if we want to launch new process with VM,
+ * false if we want to use the OS's shell.
+ */
+ public void setVMLauncher(boolean vmLauncher) {
+ this.vmLauncher = vmLauncher;
+ }
+
+ /**
+ * Create an Execute instance with the correct working directory set.
+ *
+ * @return an instance of the Execute class.
+ *
+ * @throws BuildException under unknown circumstances.
+ */
+ protected Execute prepareExec() throws BuildException {
+ // default directory to the project's base directory
+ if (dir == null) {
+ dir = getProject().getBaseDir();
+ }
+ if (redirectorElement != null) {
+ redirectorElement.configure(redirector);
+ }
+ Execute exe = new Execute(createHandler(), createWatchdog());
+ exe.setAntRun(getProject());
+ exe.setWorkingDirectory(dir);
+ exe.setVMLauncher(vmLauncher);
+ String[] environment = env.getVariables();
+ if (environment != null) {
+ for (int i = 0; i < environment.length; i++) {
+ log("Setting environment variable: " + environment[i],
+ Project.MSG_VERBOSE);
+ }
+ }
+ exe.setNewenvironment(newEnvironment);
+ exe.setEnvironment(environment);
+ return exe;
+ }
+
+ /**
+ * A Utility method for this classes and subclasses to run an
+ * Execute instance (an external command).
+ *
+ * @param exe instance of the execute class.
+ *
+ * @throws IOException in case of problem to attach to the stdin/stdout/stderr
+ * streams of the process.
+ */
+ protected final void runExecute(Execute exe) throws IOException {
+ int returnCode = -1; // assume the worst
+
+ if (!spawn) {
+ returnCode = exe.execute();
+
+ //test for and handle a forced process death
+ if (exe.killedProcess()) {
+ String msg = "Timeout: killed the sub-process";
+ if (failOnError) {
+ throw new BuildException(msg);
+ } else {
+ log(msg, Project.MSG_WARN);
+ }
+ }
+ maybeSetResultPropertyValue(returnCode);
+ redirector.complete();
+ if (Execute.isFailure(returnCode)) {
+ if (failOnError) {
+ throw new BuildException(getTaskType() + " returned: "
+ + returnCode, getLocation());
+ } else {
+ log("Result: " + returnCode, Project.MSG_ERR);
+ }
+ }
+ } else {
+ exe.spawn();
+ }
+ }
+
+ /**
+ * Run the command using the given Execute instance. This may be
+ * overridden by subclasses.
+ *
+ * @param exe instance of Execute to run.
+ *
+ * @throws BuildException if the new process could not be started
+ * only if failIfExecFails is set to true (the default).
+ */
+ protected void runExec(Execute exe) throws BuildException {
+ // show the command
+ log(cmdl.describeCommand(), Project.MSG_VERBOSE);
+
+ exe.setCommandline(cmdl.getCommandline());
+ try {
+ runExecute(exe);
+ } catch (IOException e) {
+ if (failIfExecFails) {
+ throw new BuildException("Execute failed: " + e.toString(), e,
+ getLocation());
+ } else {
+ log("Execute failed: " + e.toString(), Project.MSG_ERR);
+ }
+ } finally {
+ // close the output file if required
+ logFlush();
+ }
+ }
+
+ /**
+ * Create the StreamHandler to use with our Execute instance.
+ *
+ * @return instance of ExecuteStreamHandler.
+ *
+ * @throws BuildException under unknown circumstances.
+ */
+ protected ExecuteStreamHandler createHandler() throws BuildException {
+ return redirector.createHandler();
+ }
+
+ /**
+ * Create the Watchdog to kill a runaway process.
+ *
+ * @return instance of ExecuteWatchdog.
+ *
+ * @throws BuildException under unknown circumstances.
+ */
+ protected ExecuteWatchdog createWatchdog() throws BuildException {
+ return (timeout == null)
+ ? null : new ExecuteWatchdog(timeout.longValue());
+ }
+
+ /**
+ * Flush the output stream - if there is one.
+ */
+ protected void logFlush() {
+ }
+
+ private boolean isPath(String line) {
+ return line.startsWith("PATH=")
+ || line.startsWith("Path=");
+ }
+
+ private String getPath(String line) {
+ return line.substring("PATH=".length());
+ }
+
+ private String getPath(Map<String, String> map) {
+ String p = map.get("PATH");
+ return p != null ? p : map.get("Path");
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Execute.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Execute.java
new file mode 100644
index 00000000..9523f453
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Execute.java
@@ -0,0 +1,734 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.taskdefs.launcher.CommandLauncher;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Runs an external program.
+ *
+ * @since Ant 1.2
+ */
+public class Execute {
+
+ private static final int ONE_SECOND = 1000;
+
+ /**
+ * Invalid exit code. set to {@link Integer#MAX_VALUE}
+ */
+ public static final int INVALID = Integer.MAX_VALUE;
+
+ private String[] cmdl = null;
+ private String[] env = null;
+ private int exitValue = INVALID;
+ private ExecuteStreamHandler streamHandler;
+ private final ExecuteWatchdog watchdog;
+ private File workingDirectory = null;
+ private Project project = null;
+ private boolean newEnvironment = false;
+
+ /** Controls whether the VM is used to launch commands, where possible. */
+ private boolean useVMLauncher = true;
+
+ private static String antWorkingDirectory = System.getProperty("user.dir");
+ private static Map<String, String> procEnvironment = null;
+
+ /** Used to destroy processes when the VM exits. */
+ private static ProcessDestroyer processDestroyer = new ProcessDestroyer();
+
+ /** Used for replacing env variables */
+ private static boolean environmentCaseInSensitive = false;
+
+ static {
+ if (Os.isFamily("windows")) {
+ environmentCaseInSensitive = true;
+ }
+ }
+
+ /**
+ * Set whether or not you want the process to be spawned.
+ * Default is not spawned.
+ *
+ * @param spawn if true you do not want Ant
+ * to wait for the end of the process.
+ * Has no influence in here, the calling task contains
+ * and acts accordingly
+ *
+ * @since Ant 1.6
+ * @deprecated
+ */
+ @Deprecated
+ public void setSpawn(boolean spawn) {
+ // Method did not do anything to begin with
+ }
+
+ /**
+ * Find the list of environment variables for this process.
+ *
+ * @return a map containing the environment variables.
+ * @since Ant 1.8.2
+ */
+ public static synchronized Map<String,String> getEnvironmentVariables() {
+ if (procEnvironment != null) {
+ return procEnvironment;
+ }
+ if (!Os.isFamily("openvms")) {
+ try {
+ procEnvironment = System.getenv();
+ return procEnvironment;
+ } catch (Exception x) {
+ x.printStackTrace();
+ }
+ }
+
+ procEnvironment = new LinkedHashMap<String, String>();
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ Execute exe = new Execute(new PumpStreamHandler(out));
+ exe.setCommandline(getProcEnvCommand());
+ // Make sure we do not recurse forever
+ exe.setNewenvironment(true);
+ int retval = exe.execute();
+ if (retval != 0) {
+ // Just try to use what we got
+ }
+ BufferedReader in =
+ new BufferedReader(new StringReader(toString(out)));
+
+ if (Os.isFamily("openvms")) {
+ procEnvironment = getVMSLogicals(in);
+ return procEnvironment;
+ }
+ String var = null;
+ String line, lineSep = StringUtils.LINE_SEP;
+ while ((line = in.readLine()) != null) {
+ if (line.indexOf('=') == -1) {
+ // Chunk part of previous env var (UNIX env vars can
+ // contain embedded new lines).
+ if (var == null) {
+ var = lineSep + line;
+ } else {
+ var += lineSep + line;
+ }
+ } else {
+ // New env var...append the previous one if we have it.
+ if (var != null) {
+ int eq = var.indexOf("=");
+ procEnvironment.put(var.substring(0, eq),
+ var.substring(eq + 1));
+ }
+ var = line;
+ }
+ }
+ // Since we "look ahead" before adding, there's one last env var.
+ if (var != null) {
+ int eq = var.indexOf("=");
+ procEnvironment.put(var.substring(0, eq), var.substring(eq + 1));
+ }
+ } catch (java.io.IOException exc) {
+ exc.printStackTrace();
+ // Just try to see how much we got
+ }
+ return procEnvironment;
+ }
+
+ /**
+ * Find the list of environment variables for this process.
+ *
+ * @return a vector containing the environment variables.
+ * The vector elements are strings formatted like variable = value.
+ * @deprecated use #getEnvironmentVariables instead
+ */
+ @Deprecated
+ public static synchronized Vector<String> getProcEnvironment() {
+ Vector<String> v = new Vector<String>();
+ for (Entry<String, String> entry : getEnvironmentVariables().entrySet()) {
+ v.add(entry.getKey() + "=" + entry.getValue());
+ }
+ return v;
+ }
+
+ /**
+ * This is the operation to get our environment.
+ * It is a notorious troublespot pre-Java1.5, and should be approached
+ * with extreme caution.
+ *
+ * @return command and arguments to get our environment
+ */
+ private static String[] getProcEnvCommand() {
+ if (Os.isFamily("os/2")) {
+ // OS/2 - use same mechanism as Windows 2000
+ return new String[] {"cmd", "/c", "set"};
+ } else if (Os.isFamily("windows")) {
+ // Determine if we're running under XP/2000/NT or 98/95
+ if (Os.isFamily("win9x")) {
+ // Windows 98/95
+ return new String[] {"command.com", "/c", "set"};
+ } else {
+ // Windows XP/2000/NT/2003
+ return new String[] {"cmd", "/c", "set"};
+ }
+ } else if (Os.isFamily("z/os") || Os.isFamily("unix")) {
+ // On most systems one could use: /bin/sh -c env
+
+ // Some systems have /bin/env, others /usr/bin/env, just try
+ String[] cmd = new String[1];
+ if (new File("/bin/env").canRead()) {
+ cmd[0] = "/bin/env";
+ } else if (new File("/usr/bin/env").canRead()) {
+ cmd[0] = "/usr/bin/env";
+ } else {
+ // rely on PATH
+ cmd[0] = "env";
+ }
+ return cmd;
+ } else if (Os.isFamily("netware") || Os.isFamily("os/400")) {
+ // rely on PATH
+ return new String[] {"env"};
+ } else if (Os.isFamily("openvms")) {
+ return new String[] {"show", "logical"};
+ } else {
+ // MAC OS 9 and previous
+ // TODO: I have no idea how to get it, someone must fix it
+ return null;
+ }
+ }
+
+ /**
+ * ByteArrayOutputStream#toString doesn't seem to work reliably on
+ * OS/390, at least not the way we use it in the execution
+ * context.
+ *
+ * @param bos the output stream that one wants to read.
+ * @return the output stream as a string, read with
+ * special encodings in the case of z/os and os/400.
+ * @since Ant 1.5
+ */
+ public static String toString(ByteArrayOutputStream bos) {
+ if (Os.isFamily("z/os")) {
+ try {
+ return bos.toString("Cp1047");
+ } catch (java.io.UnsupportedEncodingException e) {
+ // noop default encoding used
+ }
+ } else if (Os.isFamily("os/400")) {
+ try {
+ return bos.toString("Cp500");
+ } catch (java.io.UnsupportedEncodingException e) {
+ // noop default encoding used
+ }
+ }
+ return bos.toString();
+ }
+
+ /**
+ * Creates a new execute object using <code>PumpStreamHandler</code> for
+ * stream handling.
+ */
+ public Execute() {
+ this(new PumpStreamHandler(), null);
+ }
+
+ /**
+ * Creates a new execute object.
+ *
+ * @param streamHandler the stream handler used to handle the input and
+ * output streams of the subprocess.
+ */
+ public Execute(ExecuteStreamHandler streamHandler) {
+ this(streamHandler, null);
+ }
+
+ /**
+ * Creates a new execute object.
+ *
+ * @param streamHandler the stream handler used to handle the input and
+ * output streams of the subprocess.
+ * @param watchdog a watchdog for the subprocess or <code>null</code>
+ * to disable a timeout for the subprocess.
+ */
+ public Execute(ExecuteStreamHandler streamHandler,
+ ExecuteWatchdog watchdog) {
+ setStreamHandler(streamHandler);
+ this.watchdog = watchdog;
+ // By default, use the shell launcher for VMS
+ //
+ if (Os.isFamily("openvms")) {
+ useVMLauncher = false;
+ }
+ }
+
+ /**
+ * Set the stream handler to use.
+ *
+ * @param streamHandler ExecuteStreamHandler.
+ * @since Ant 1.6
+ */
+ public void setStreamHandler(ExecuteStreamHandler streamHandler) {
+ this.streamHandler = streamHandler;
+ }
+
+ /**
+ * Returns the commandline used to create a subprocess.
+ *
+ * @return the commandline used to create a subprocess.
+ */
+ public String[] getCommandline() {
+ return cmdl;
+ }
+
+ /**
+ * Sets the commandline of the subprocess to launch.
+ *
+ * @param commandline the commandline of the subprocess to launch.
+ */
+ public void setCommandline(String[] commandline) {
+ cmdl = commandline;
+ }
+
+ /**
+ * Set whether to propagate the default environment or not.
+ *
+ * @param newenv whether to propagate the process environment.
+ */
+ public void setNewenvironment(boolean newenv) {
+ newEnvironment = newenv;
+ }
+
+ /**
+ * Returns the environment used to create a subprocess.
+ *
+ * @return the environment used to create a subprocess.
+ */
+ public String[] getEnvironment() {
+ return (env == null || newEnvironment)
+ ? env : patchEnvironment();
+ }
+
+ /**
+ * Sets the environment variables for the subprocess to launch.
+ *
+ * @param env array of Strings, each element of which has
+ * an environment variable settings in format <em>key=value</em>.
+ */
+ public void setEnvironment(String[] env) {
+ this.env = env;
+ }
+
+ /**
+ * Sets the working directory of the process to execute.
+ *
+ * <p>This is emulated using the antRun scripts unless the OS is
+ * Windows NT in which case a cmd.exe is spawned,
+ * or MRJ and setting user.dir works, or JDK 1.3 and there is
+ * official support in java.lang.Runtime.
+ *
+ * @param wd the working directory of the process.
+ */
+ public void setWorkingDirectory(File wd) {
+ workingDirectory =
+ (wd == null || wd.getAbsolutePath().equals(antWorkingDirectory))
+ ? null : wd;
+ }
+
+ /**
+ * Return the working directory.
+ *
+ * @return the directory as a File.
+ * @since Ant 1.7
+ */
+ public File getWorkingDirectory() {
+ return workingDirectory == null ? new File(antWorkingDirectory)
+ : workingDirectory;
+ }
+
+ /**
+ * Set the name of the antRun script using the project's value.
+ *
+ * @param project the current project.
+ * @throws BuildException not clear when it is going to throw an exception, but
+ * it is the method's signature.
+ */
+ public void setAntRun(Project project) throws BuildException {
+ this.project = project;
+ }
+
+ /**
+ * Launch this execution through the VM, where possible, rather than through
+ * the OS's shell. In some cases and operating systems using the shell will
+ * allow the shell to perform additional processing such as associating an
+ * executable with a script, etc.
+ *
+ * @param useVMLauncher true if exec should launch through the VM,
+ * false if the shell should be used to launch the
+ * command.
+ */
+ public void setVMLauncher(boolean useVMLauncher) {
+ this.useVMLauncher = useVMLauncher;
+ }
+
+ /**
+ * Creates a process that runs a command.
+ *
+ * @param project the Project, only used for logging purposes, may be null.
+ * @param command the command to run.
+ * @param env the environment for the command.
+ * @param dir the working directory for the command.
+ * @param useVM use the built-in exec command for JDK 1.3 if available.
+ * @return the process started.
+ * @throws IOException forwarded from the particular launcher used.
+ * @since Ant 1.5
+ */
+ public static Process launch(Project project, String[] command,
+ String[] env, File dir, boolean useVM)
+ throws IOException {
+ if (dir != null && !dir.exists()) {
+ throw new BuildException(dir + " doesn't exist.");
+ }
+
+ CommandLauncher vmLauncher = CommandLauncher.getVMLauncher(project);
+ CommandLauncher launcher = (useVM && vmLauncher != null)
+ ? vmLauncher : CommandLauncher.getShellLauncher(project);
+ return launcher.exec(project, command, env, dir);
+ }
+
+ /**
+ * Runs a process defined by the command line and returns its exit status.
+ *
+ * @return the exit status of the subprocess or <code>INVALID</code>.
+ * @exception java.io.IOException The exception is thrown, if launching
+ * of the subprocess failed.
+ */
+ public int execute() throws IOException {
+ if (workingDirectory != null && !workingDirectory.exists()) {
+ throw new BuildException(workingDirectory + " doesn't exist.");
+ }
+ final Process process = launch(project, getCommandline(),
+ getEnvironment(), workingDirectory,
+ useVMLauncher);
+ try {
+ streamHandler.setProcessInputStream(process.getOutputStream());
+ streamHandler.setProcessOutputStream(process.getInputStream());
+ streamHandler.setProcessErrorStream(process.getErrorStream());
+ } catch (IOException e) {
+ process.destroy();
+ throw e;
+ }
+ streamHandler.start();
+
+ try {
+ // add the process to the list of those to destroy if the VM exits
+ //
+ processDestroyer.add(process);
+
+ if (watchdog != null) {
+ watchdog.start(process);
+ }
+ waitFor(process);
+
+ if (watchdog != null) {
+ watchdog.stop();
+ }
+ streamHandler.stop();
+ closeStreams(process);
+
+ if (watchdog != null) {
+ watchdog.checkException();
+ }
+ return getExitValue();
+ } catch (ThreadDeath t) {
+ // #31928: forcibly kill it before continuing.
+ process.destroy();
+ throw t;
+ } finally {
+ // remove the process to the list of those to destroy if
+ // the VM exits
+ //
+ processDestroyer.remove(process);
+ }
+ }
+
+ /**
+ * Starts a process defined by the command line.
+ * Ant will not wait for this process, nor log its output.
+ *
+ * @throws java.io.IOException The exception is thrown, if launching
+ * of the subprocess failed.
+ * @since Ant 1.6
+ */
+ public void spawn() throws IOException {
+ if (workingDirectory != null && !workingDirectory.exists()) {
+ throw new BuildException(workingDirectory + " doesn't exist.");
+ }
+ final Process process = launch(project, getCommandline(),
+ getEnvironment(), workingDirectory,
+ useVMLauncher);
+ if (Os.isFamily("windows")) {
+ try {
+ Thread.sleep(ONE_SECOND);
+ } catch (InterruptedException e) {
+ project.log("interruption in the sleep after having spawned a"
+ + " process", Project.MSG_VERBOSE);
+ }
+ }
+ OutputStream dummyOut = new OutputStream() {
+ @Override
+ public void write(int b) throws IOException {
+ // Method intended to swallow whatever comes at it
+ }
+ };
+
+ ExecuteStreamHandler handler = new PumpStreamHandler(dummyOut);
+ handler.setProcessErrorStream(process.getErrorStream());
+ handler.setProcessOutputStream(process.getInputStream());
+ handler.start();
+ process.getOutputStream().close();
+
+ project.log("spawned process " + process.toString(),
+ Project.MSG_VERBOSE);
+ }
+
+ /**
+ * Wait for a given process.
+ *
+ * @param process the process one wants to wait for.
+ */
+ protected void waitFor(Process process) {
+ try {
+ process.waitFor();
+ setExitValue(process.exitValue());
+ } catch (InterruptedException e) {
+ process.destroy();
+ }
+ }
+
+ /**
+ * Set the exit value.
+ *
+ * @param value exit value of the process.
+ */
+ protected void setExitValue(int value) {
+ exitValue = value;
+ }
+
+ /**
+ * Query the exit value of the process.
+ *
+ * @return the exit value or Execute.INVALID if no exit value has
+ * been received.
+ */
+ public int getExitValue() {
+ return exitValue;
+ }
+
+ /**
+ * Checks whether <code>exitValue</code> signals a failure on the current
+ * system (OS specific).
+ *
+ * <p><b>Note</b> that this method relies on the conventions of
+ * the OS, it will return false results if the application you are
+ * running doesn't follow these conventions. One notable
+ * exception is the Java VM provided by HP for OpenVMS - it will
+ * return 0 if successful (like on any other platform), but this
+ * signals a failure on OpenVMS. So if you execute a new Java VM
+ * on OpenVMS, you cannot trust this method.</p>
+ *
+ * @param exitValue the exit value (return code) to be checked.
+ * @return <code>true</code> if <code>exitValue</code> signals a failure.
+ */
+ public static boolean isFailure(int exitValue) {
+ // on openvms even exit value signals failure;
+ // for other platforms nonzero exit value signals failure
+ return Os.isFamily("openvms")
+ ? (exitValue % 2 == 0) : (exitValue != 0);
+ }
+
+ /**
+ * Did this execute return in a failure.
+ *
+ * @see #isFailure(int)
+ * @return true if and only if the exit code is interpreted as a failure
+ * @since Ant1.7
+ */
+ public boolean isFailure() {
+ return isFailure(getExitValue());
+ }
+
+ /**
+ * Test for an untimely death of the process.
+ *
+ * @return true if a watchdog had to kill the process.
+ * @since Ant 1.5
+ */
+ public boolean killedProcess() {
+ return watchdog != null && watchdog.killedProcess();
+ }
+
+ /**
+ * Patch the current environment with the new values from the user.
+ *
+ * @return the patched environment.
+ */
+ private String[] patchEnvironment() {
+ // On OpenVMS Runtime#exec() doesn't support the environment array,
+ // so we only return the new values which then will be set in
+ // the generated DCL script, inheriting the parent process environment
+ if (Os.isFamily("openvms")) {
+ return env;
+ }
+ Map<String, String> osEnv =
+ new LinkedHashMap<String, String>(getEnvironmentVariables());
+ for (int i = 0; i < env.length; i++) {
+ String keyValue = env[i];
+ String key = keyValue.substring(0, keyValue.indexOf('='));
+ // Find the key in the current environment copy
+ // and remove it.
+
+ // Try without changing case first
+ if (osEnv.remove(key) == null && environmentCaseInSensitive) {
+ // not found, maybe perform a case insensitive search
+
+ for (String osEnvItem : osEnv.keySet()) {
+ // Nb: using default locale as key is a env name
+ if (osEnvItem.toLowerCase().equals(key.toLowerCase())) {
+ // Use the original casiness of the key
+ key = osEnvItem;
+ break;
+ }
+ }
+ }
+
+ // Add the key to the enviromnent copy
+ osEnv.put(key, keyValue.substring(key.length() + 1));
+ }
+
+ ArrayList<String> l = new ArrayList<String>();
+ for (Entry<String, String> entry : osEnv.entrySet()) {
+ l.add(entry.getKey() + "=" + entry.getValue());
+ }
+ return l.toArray(new String[osEnv.size()]);
+ }
+
+ /**
+ * A utility method that runs an external command. Writes the output and
+ * error streams of the command to the project log.
+ *
+ * @param task The task that the command is part of. Used for logging
+ * @param cmdline The command to execute.
+ * @throws BuildException if the command does not exit successfully.
+ */
+ public static void runCommand(Task task, String[] cmdline)
+ throws BuildException {
+ try {
+ task.log(Commandline.describeCommand(cmdline),
+ Project.MSG_VERBOSE);
+ Execute exe = new Execute(
+ new LogStreamHandler(task, Project.MSG_INFO, Project.MSG_ERR));
+ exe.setAntRun(task.getProject());
+ exe.setCommandline(cmdline);
+ int retval = exe.execute();
+ if (isFailure(retval)) {
+ throw new BuildException(cmdline[0]
+ + " failed with return code " + retval, task.getLocation());
+ }
+ } catch (java.io.IOException exc) {
+ throw new BuildException("Could not launch " + cmdline[0] + ": "
+ + exc, task.getLocation());
+ }
+ }
+
+ /**
+ * Close the streams belonging to the given Process.
+ *
+ * @param process the <code>Process</code>.
+ */
+ public static void closeStreams(Process process) {
+ FileUtils.close(process.getInputStream());
+ FileUtils.close(process.getOutputStream());
+ FileUtils.close(process.getErrorStream());
+ }
+
+ /**
+ * This method is VMS specific and used by getEnvironmentVariables().
+ *
+ * Parses VMS logicals from <code>in</code> and returns them as a Map.
+ * <code>in</code> is expected to be the
+ * output of "SHOW LOGICAL". The method takes care of parsing the output
+ * correctly as well as making sure that a logical defined in multiple
+ * tables only gets added from the highest order table. Logicals with
+ * multiple equivalence names are mapped to a variable with multiple
+ * values separated by a comma (,).
+ */
+ private static Map<String, String> getVMSLogicals(BufferedReader in)
+ throws IOException {
+ HashMap<String, String> logicals = new HashMap<String, String>();
+ String logName = null, logValue = null, newLogName;
+ String line = null;
+ // CheckStyle:MagicNumber OFF
+ while ((line = in.readLine()) != null) {
+ // parse the VMS logicals into required format ("VAR=VAL[,VAL2]")
+ if (line.startsWith("\t=")) {
+ // further equivalence name of previous logical
+ if (logName != null) {
+ logValue += "," + line.substring(4, line.length() - 1);
+ }
+ } else if (line.startsWith(" \"")) {
+ // new logical?
+ if (logName != null) {
+ logicals.put(logName, logValue);
+ }
+ int eqIndex = line.indexOf('=');
+ newLogName = line.substring(3, eqIndex - 2);
+ if (logicals.containsKey(newLogName)) {
+ // already got this logical from a higher order table
+ logName = null;
+ } else {
+ logName = newLogName;
+ logValue = line.substring(eqIndex + 3, line.length() - 1);
+ }
+ }
+ }
+ // CheckStyle:MagicNumber ON
+ // Since we "look ahead" before adding, there's one last env var.
+ if (logName != null) {
+ logicals.put(logName, logValue);
+ }
+ return logicals;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java
new file mode 100644
index 00000000..b78b9a6a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java
@@ -0,0 +1,331 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Permissions;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.TimeoutObserver;
+import org.apache.tools.ant.util.Watchdog;
+
+/**
+ * Execute a Java class.
+ * @since Ant 1.2
+ */
+public class ExecuteJava implements Runnable, TimeoutObserver {
+
+ private Commandline javaCommand = null;
+ private Path classpath = null;
+ private CommandlineJava.SysProperties sysProperties = null;
+ private Permissions perm = null;
+ private Method main = null;
+ private Long timeout = null;
+ private volatile Throwable caught = null;
+ private volatile boolean timedOut = false;
+ private Thread thread = null;
+
+ /**
+ * Set the Java "command" for this ExecuteJava.
+ * @param javaCommand the classname and arguments in a Commandline.
+ */
+ public void setJavaCommand(Commandline javaCommand) {
+ this.javaCommand = javaCommand;
+ }
+
+ /**
+ * Set the classpath to be used when running the Java class.
+ *
+ * @param p an Ant Path object containing the classpath.
+ */
+ public void setClasspath(Path p) {
+ classpath = p;
+ }
+
+ /**
+ * Set the system properties to use when running the Java class.
+ * @param s CommandlineJava system properties.
+ */
+ public void setSystemProperties(CommandlineJava.SysProperties s) {
+ sysProperties = s;
+ }
+
+ /**
+ * Set the permissions for the application run.
+ * @param permissions the Permissions to use.
+ * @since Ant 1.6
+ */
+ public void setPermissions(Permissions permissions) {
+ perm = permissions;
+ }
+
+ /**
+ * Set the stream to which all output (System.out as well as System.err)
+ * will be written.
+ * @param out the PrintStream where output should be sent.
+ * @deprecated since 1.4.x.
+ * manage output at the task level.
+ */
+ public void setOutput(PrintStream out) {
+ }
+
+ /**
+ * Set the timeout for this ExecuteJava.
+ * @param timeout timeout as Long.
+ * @since Ant 1.5
+ */
+ public void setTimeout(Long timeout) {
+ this.timeout = timeout;
+ }
+
+ /**
+ * Execute the Java class against the specified Ant Project.
+ * @param project the Project to use.
+ * @throws BuildException on error.
+ */
+ public void execute(Project project) throws BuildException {
+ final String classname = javaCommand.getExecutable();
+
+ AntClassLoader loader = null;
+ try {
+ if (sysProperties != null) {
+ sysProperties.setSystem();
+ }
+ Class<?> target = null;
+ try {
+ if (classpath == null) {
+ target = Class.forName(classname);
+ } else {
+ loader = project.createClassLoader(classpath);
+ loader.setParent(project.getCoreLoader());
+ loader.setParentFirst(false);
+ loader.addJavaLibraries();
+ loader.setIsolated(true);
+ loader.setThreadContextLoader();
+ loader.forceLoadClass(classname);
+ target = Class.forName(classname, true, loader);
+ }
+ } catch (ClassNotFoundException e) {
+ throw new BuildException("Could not find " + classname + "."
+ + " Make sure you have it in your"
+ + " classpath");
+ }
+ main = target.getMethod("main", new Class[] {String[].class});
+ if (main == null) {
+ throw new BuildException("Could not find main() method in "
+ + classname);
+ }
+ if ((main.getModifiers() & Modifier.STATIC) == 0) {
+ throw new BuildException("main() method in " + classname
+ + " is not declared static");
+ }
+ if (timeout == null) {
+ run();
+ } else {
+ thread = new Thread(this, "ExecuteJava");
+ Task currentThreadTask
+ = project.getThreadTask(Thread.currentThread());
+ // TODO is the following really necessary? it is in the same thread group...
+ project.registerThreadTask(thread, currentThreadTask);
+ // if we run into a timeout, the run-away thread shall not
+ // make the VM run forever - if no timeout occurs, Ant's
+ // main thread will still be there to let the new thread
+ // finish
+ thread.setDaemon(true);
+ Watchdog w = new Watchdog(timeout.longValue());
+ w.addTimeoutObserver(this);
+ synchronized (this) {
+ thread.start();
+ w.start();
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ if (timedOut) {
+ project.log("Timeout: sub-process interrupted",
+ Project.MSG_WARN);
+ } else {
+ thread = null;
+ w.stop();
+ }
+ }
+ }
+ if (caught != null) {
+ throw caught;
+ }
+ } catch (BuildException e) {
+ throw e;
+ } catch (SecurityException e) {
+ throw e;
+ } catch (ThreadDeath e) {
+ // TODO could perhaps also call thread.stop(); not sure if anyone cares
+ throw e;
+ } catch (Throwable e) {
+ throw new BuildException(e);
+ } finally {
+ if (loader != null) {
+ loader.resetThreadContextLoader();
+ loader.cleanup();
+ loader = null;
+ }
+ if (sysProperties != null) {
+ sysProperties.restoreSystem();
+ }
+ }
+ }
+
+ /**
+ * Run this ExecuteJava in a Thread.
+ * @since Ant 1.5
+ */
+ public void run() {
+ final Object[] argument = {javaCommand.getArguments()};
+ try {
+ if (perm != null) {
+ perm.setSecurityManager();
+ }
+ main.invoke(null, argument);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getTargetException();
+ if (!(t instanceof InterruptedException)) {
+ caught = t;
+ } /* else { swallow, probably due to timeout } */
+ } catch (Throwable t) {
+ caught = t;
+ } finally {
+ if (perm != null) {
+ perm.restoreSecurityManager();
+ }
+ synchronized (this) {
+ notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Mark timeout as having occurred.
+ * @param w the responsible Watchdog.
+ * @since Ant 1.5
+ */
+ public synchronized void timeoutOccured(Watchdog w) {
+ if (thread != null) {
+ timedOut = true;
+ thread.interrupt();
+ }
+ notifyAll();
+ }
+
+ /**
+ * Get whether the process was killed.
+ * @return <code>true</code> if the process was killed, false otherwise.
+ * @since 1.19, Ant 1.5
+ */
+ public synchronized boolean killedProcess() {
+ return timedOut;
+ }
+
+ /**
+ * Run the Java command in a separate VM, this does not give you
+ * the full flexibility of the Java task, but may be enough for
+ * simple needs.
+ * @param pc the ProjectComponent to use for logging, etc.
+ * @return the exit status of the subprocess.
+ * @throws BuildException on error.
+ * @since Ant 1.6.3
+ */
+ public int fork(ProjectComponent pc) throws BuildException {
+ CommandlineJava cmdl = new CommandlineJava();
+ cmdl.setClassname(javaCommand.getExecutable());
+ String[] args = javaCommand.getArguments();
+ for (int i = 0; i < args.length; i++) {
+ cmdl.createArgument().setValue(args[i]);
+ }
+ if (classpath != null) {
+ cmdl.createClasspath(pc.getProject()).append(classpath);
+ }
+ if (sysProperties != null) {
+ cmdl.addSysproperties(sysProperties);
+ }
+ Redirector redirector = new Redirector(pc);
+ Execute exe
+ = new Execute(redirector.createHandler(),
+ timeout == null
+ ? null
+ : new ExecuteWatchdog(timeout.longValue()));
+ exe.setAntRun(pc.getProject());
+ if (Os.isFamily("openvms")) {
+ setupCommandLineForVMS(exe, cmdl.getCommandline());
+ } else {
+ exe.setCommandline(cmdl.getCommandline());
+ }
+ try {
+ int rc = exe.execute();
+ redirector.complete();
+ return rc;
+ } catch (IOException e) {
+ throw new BuildException(e);
+ } finally {
+ timedOut = exe.killedProcess();
+ }
+ }
+
+ /**
+ * On VMS platform, we need to create a special java options file
+ * containing the arguments and classpath for the java command.
+ * The special file is supported by the "-V" switch on the VMS JVM.
+ *
+ * @param exe the Execute instance to alter.
+ * @param command the command-line.
+ */
+ public static void setupCommandLineForVMS(Execute exe, String[] command) {
+ //Use the VM launcher instead of shell launcher on VMS
+ exe.setVMLauncher(true);
+ File vmsJavaOptionFile = null;
+ try {
+ String [] args = new String[command.length - 1];
+ System.arraycopy(command, 1, args, 0, command.length - 1);
+ vmsJavaOptionFile = JavaEnvUtils.createVmsJavaOptionFile(args);
+ //we mark the file to be deleted on exit.
+ //the alternative would be to cache the filename and delete
+ //after execution finished, which is much better for long-lived runtimes
+ //though spawning complicates things...
+ vmsJavaOptionFile.deleteOnExit();
+ String [] vmsCmd = {command[0], "-V", vmsJavaOptionFile.getPath()};
+ exe.setCommandline(vmsCmd);
+ } catch (IOException e) {
+ throw new BuildException("Failed to create a temporary file for \"-V\" switch");
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java
new file mode 100644
index 00000000..18cbd29d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java
@@ -0,0 +1,784 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.AbstractFileSet;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.DirSet;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileList;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.ResourceUtils;
+import org.apache.tools.ant.util.SourceFileScanner;
+
+/**
+ * Executes a given command, supplying a set of files as arguments.
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="control" name="apply"
+ */
+public class ExecuteOn extends ExecTask {
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ // filesets has been protected so we need to keep that even after
+ // switching to resource collections. In fact, they will still
+ // get a different treatment form the other resource collections
+ // even in execute since we have some subtle special features like
+ // switching type to "dir" when we encounter a DirSet that would
+ // be more difficult to achieve otherwise.
+
+ protected Vector<AbstractFileSet> filesets = new Vector<AbstractFileSet>(); // contains AbstractFileSet
+ // (both DirSet and FileSet)
+ private Union resources = null;
+ private boolean relative = false;
+ private boolean parallel = false;
+ private boolean forwardSlash = false;
+ protected String type = FileDirBoth.FILE;
+ protected Commandline.Marker srcFilePos = null;
+ private boolean skipEmpty = false;
+ protected Commandline.Marker targetFilePos = null;
+ protected Mapper mapperElement = null;
+ protected FileNameMapper mapper = null;
+ protected File destDir = null;
+ private int maxParallel = -1;
+ private boolean addSourceFile = true;
+ private boolean verbose = false;
+ private boolean ignoreMissing = true;
+ private boolean force = false;
+
+ /**
+ * Has &lt;srcfile&gt; been specified before &lt;targetfile&gt;
+ */
+ protected boolean srcIsFirst = true;
+
+ // CheckStyle:VisibilityModifier ON
+ /**
+ * Add a set of files upon which to operate.
+ * @param set the FileSet to add.
+ */
+ public void addFileset(FileSet set) {
+ filesets.addElement(set);
+ }
+
+ /**
+ * Add a set of directories upon which to operate.
+ *
+ * @param set the DirSet to add.
+ *
+ * @since Ant 1.6
+ */
+ public void addDirset(DirSet set) {
+ filesets.addElement(set);
+ }
+
+ /**
+ * Add a list of source files upon which to operate.
+ * @param list the FileList to add.
+ */
+ public void addFilelist(FileList list) {
+ add(list);
+ }
+
+ /**
+ * Add a collection of resources upon which to operate.
+ * @param rc resource collection to add.
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection rc) {
+ if (resources == null) {
+ resources = new Union();
+ }
+ resources.add(rc);
+ }
+
+ /**
+ * Set whether the filenames should be passed on the command line as
+ * absolute or relative pathnames. Paths are relative to the base
+ * directory of the corresponding fileset for source files or the
+ * dest attribute for target files.
+ * @param relative whether to pass relative pathnames.
+ */
+ public void setRelative(boolean relative) {
+ this.relative = relative;
+ }
+
+
+ /**
+ * Set whether to execute in parallel mode.
+ * If true, run the command only once, appending all files as arguments.
+ * If false, command will be executed once for every file. Defaults to false.
+ * @param parallel whether to run in parallel.
+ */
+ public void setParallel(boolean parallel) {
+ this.parallel = parallel;
+ }
+
+ /**
+ * Set whether the command works only on files, directories or both.
+ * @param type a FileDirBoth EnumeratedAttribute.
+ */
+ public void setType(FileDirBoth type) {
+ this.type = type.getValue();
+ }
+
+ /**
+ * Set whether empty filesets will be skipped. If true and
+ * no source files have been found or are newer than their
+ * corresponding target files, the command will not be run.
+ * @param skip whether to skip empty filesets.
+ */
+ public void setSkipEmptyFilesets(boolean skip) {
+ skipEmpty = skip;
+ }
+
+ /**
+ * Specify the directory where target files are to be placed.
+ * @param destDir the File object representing the destination directory.
+ */
+ public void setDest(File destDir) {
+ this.destDir = destDir;
+ }
+
+ /**
+ * Set whether the source and target file names on Windows and OS/2
+ * must use the forward slash as file separator.
+ * @param forwardSlash whether the forward slash will be forced.
+ */
+ public void setForwardslash(boolean forwardSlash) {
+ this.forwardSlash = forwardSlash;
+ }
+
+ /**
+ * Limit the command line length by passing at maximum this many
+ * sourcefiles at once to the command.
+ *
+ * <p>Set to &lt;= 0 for unlimited - this is the default.</p>
+ *
+ * @param max <code>int</code> maximum number of sourcefiles
+ * passed to the executable.
+ *
+ * @since Ant 1.6
+ */
+ public void setMaxParallel(int max) {
+ maxParallel = max;
+ }
+
+ /**
+ * Set whether to send the source file name on the command line.
+ *
+ * <p>Defaults to <code>true</code>.
+ *
+ * @param b whether to add the source file to the command line.
+ *
+ * @since Ant 1.6
+ */
+ public void setAddsourcefile(boolean b) {
+ addSourceFile = b;
+ }
+
+ /**
+ * Set whether to operate in verbose mode.
+ * If true, a verbose summary will be printed after execution.
+ * @param b whether to operate in verbose mode.
+ *
+ * @since Ant 1.6
+ */
+ public void setVerbose(boolean b) {
+ verbose = b;
+ }
+
+ /**
+ * Set whether to ignore nonexistent files from filelists.
+ * @param b whether to ignore missing files.
+ *
+ * @since Ant 1.6.2
+ */
+ public void setIgnoremissing(boolean b) {
+ ignoreMissing = b;
+ }
+
+ /**
+ * Set whether to bypass timestamp comparisons for target files.
+ * @param b whether to bypass timestamp comparisons.
+ *
+ * @since Ant 1.6.3
+ */
+ public void setForce(boolean b) {
+ force = b;
+ }
+
+ /**
+ * Create a placeholder indicating where on the command line
+ * the name of the source file should be inserted.
+ * @return <code>Commandline.Marker</code>.
+ */
+ public Commandline.Marker createSrcfile() {
+ if (srcFilePos != null) {
+ throw new BuildException(getTaskType() + " doesn\'t support multiple "
+ + "srcfile elements.", getLocation());
+ }
+ srcFilePos = cmdl.createMarker();
+ return srcFilePos;
+ }
+
+ /**
+ * Create a placeholder indicating where on the command line
+ * the name of the target file should be inserted.
+ * @return <code>Commandline.Marker</code>.
+ */
+ public Commandline.Marker createTargetfile() {
+ if (targetFilePos != null) {
+ throw new BuildException(getTaskType() + " doesn\'t support multiple "
+ + "targetfile elements.", getLocation());
+ }
+ targetFilePos = cmdl.createMarker();
+ srcIsFirst = (srcFilePos != null);
+ return targetFilePos;
+ }
+
+ /**
+ * Create a nested Mapper element to use for mapping
+ * source files to target files.
+ * @return <code>Mapper</code>.
+ * @throws BuildException if more than one mapper is defined.
+ */
+ public Mapper createMapper() throws BuildException {
+ if (mapperElement != null) {
+ throw new BuildException("Cannot define more than one mapper",
+ getLocation());
+ }
+ mapperElement = new Mapper(getProject());
+ return mapperElement;
+ }
+
+ /**
+ * Add a nested FileNameMapper.
+ * @param fileNameMapper the mapper to add.
+ * @since Ant 1.6.3
+ */
+ public void add(FileNameMapper fileNameMapper) {
+ createMapper().add(fileNameMapper);
+ }
+
+ /**
+ * Check the configuration of this ExecuteOn instance.
+ */
+ protected void checkConfiguration() {
+// * @TODO using taskName here is brittle, as a user could override it.
+// * this should probably be modified to use the classname instead.
+ if ("execon".equals(getTaskName())) {
+ log("!! execon is deprecated. Use apply instead. !!");
+ }
+ super.checkConfiguration();
+ if (filesets.size() == 0 && resources == null) {
+ throw new BuildException("no resources specified",
+ getLocation());
+ }
+ if (targetFilePos != null && mapperElement == null) {
+ throw new BuildException("targetfile specified without mapper",
+ getLocation());
+ }
+ if (destDir != null && mapperElement == null) {
+ throw new BuildException("dest specified without mapper",
+ getLocation());
+ }
+ if (mapperElement != null) {
+ mapper = mapperElement.getImplementation();
+ }
+ }
+
+ /**
+ * Create the ExecuteStreamHandler instance that will be used
+ * during execution.
+ * @return <code>ExecuteStreamHandler</code>.
+ * @throws BuildException on error.
+ */
+ protected ExecuteStreamHandler createHandler() throws BuildException {
+ //if we have a RedirectorElement, return a decoy
+ return (redirectorElement == null)
+ ? super.createHandler() : new PumpStreamHandler();
+ }
+
+ /**
+ * Set up the I/O Redirector.
+ */
+ protected void setupRedirector() {
+ super.setupRedirector();
+ redirector.setAppendProperties(true);
+ }
+
+ /**
+ * Run the specified Execute object.
+ * @param exe the Execute instance representing the external process.
+ * @throws BuildException on error
+ */
+ protected void runExec(Execute exe) throws BuildException {
+ int totalFiles = 0;
+ int totalDirs = 0;
+ boolean haveExecuted = false;
+ try {
+ Vector<String> fileNames = new Vector<String>();
+ Vector<File> baseDirs = new Vector<File>();
+ final int size = filesets.size();
+ for (int i = 0; i < size; i++) {
+ String currentType = type;
+ AbstractFileSet fs = filesets.elementAt(i);
+ if (fs instanceof DirSet) {
+ if (!FileDirBoth.DIR.equals(type)) {
+ log("Found a nested dirset but type is " + type + ". "
+ + "Temporarily switching to type=\"dir\" on the"
+ + " assumption that you really did mean"
+ + " <dirset> not <fileset>.", Project.MSG_DEBUG);
+ currentType = FileDirBoth.DIR;
+ }
+ }
+ File base = fs.getDir(getProject());
+
+ DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+
+ if (!FileDirBoth.DIR.equals(currentType)) {
+ String[] s = getFiles(base, ds);
+ for (int j = 0; j < s.length; j++) {
+ totalFiles++;
+ fileNames.addElement(s[j]);
+ baseDirs.addElement(base);
+ }
+ }
+ if (!FileDirBoth.FILE.equals(currentType)) {
+ String[] s = getDirs(base, ds);
+ for (int j = 0; j < s.length; j++) {
+ totalDirs++;
+ fileNames.addElement(s[j]);
+ baseDirs.addElement(base);
+ }
+ }
+ if (fileNames.size() == 0 && skipEmpty) {
+ logSkippingFileset(currentType, ds, base);
+ continue;
+ }
+ if (!parallel) {
+ String[] s = new String[fileNames.size()];
+ fileNames.copyInto(s);
+ for (int j = 0; j < s.length; j++) {
+ String[] command = getCommandline(s[j], base);
+ log(Commandline.describeCommand(command),
+ Project.MSG_VERBOSE);
+ exe.setCommandline(command);
+
+ if (redirectorElement != null) {
+ setupRedirector();
+ redirectorElement.configure(redirector, s[j]);
+ }
+ if (redirectorElement != null || haveExecuted) {
+ // need to reset the stream handler to restart
+ // reading of pipes;
+ // go ahead and do it always w/ nested redirectors
+ exe.setStreamHandler(redirector.createHandler());
+ }
+ runExecute(exe);
+ haveExecuted = true;
+ }
+ fileNames.removeAllElements();
+ baseDirs.removeAllElements();
+ }
+ }
+
+ if (resources != null) {
+ for (Resource res : resources) {
+
+ if (!res.isExists() && ignoreMissing) {
+ continue;
+ }
+
+ File base = null;
+ String name = res.getName();
+ FileProvider fp = res.as(FileProvider.class);
+ if (fp != null) {
+ FileResource fr = ResourceUtils.asFileResource(fp);
+ base = fr.getBaseDir();
+ if (base == null) {
+ name = fr.getFile().getAbsolutePath();
+ }
+ }
+
+ if (restrict(new String[] {name}, base).length == 0) {
+ continue;
+ }
+
+ if ((!res.isDirectory() || !res.isExists())
+ && !FileDirBoth.DIR.equals(type)) {
+ totalFiles++;
+ } else if (res.isDirectory()
+ && !FileDirBoth.FILE.equals(type)) {
+ totalDirs++;
+ } else {
+ continue;
+ }
+
+ baseDirs.add(base);
+ fileNames.add(name);
+
+ if (!parallel) {
+ String[] command = getCommandline(name, base);
+ log(Commandline.describeCommand(command),
+ Project.MSG_VERBOSE);
+ exe.setCommandline(command);
+
+ if (redirectorElement != null) {
+ setupRedirector();
+ redirectorElement.configure(redirector, name);
+ }
+ if (redirectorElement != null || haveExecuted) {
+ // need to reset the stream handler to restart
+ // reading of pipes;
+ // go ahead and do it always w/ nested redirectors
+ exe.setStreamHandler(redirector.createHandler());
+ }
+ runExecute(exe);
+ haveExecuted = true;
+ fileNames.removeAllElements();
+ baseDirs.removeAllElements();
+ }
+ }
+ }
+ if (parallel && (fileNames.size() > 0 || !skipEmpty)) {
+ runParallel(exe, fileNames, baseDirs);
+ haveExecuted = true;
+ }
+ if (haveExecuted) {
+ log("Applied " + cmdl.getExecutable() + " to "
+ + totalFiles + " file"
+ + (totalFiles != 1 ? "s" : "") + " and "
+ + totalDirs + " director"
+ + (totalDirs != 1 ? "ies" : "y") + ".",
+ verbose ? Project.MSG_INFO : Project.MSG_VERBOSE);
+ }
+ } catch (IOException e) {
+ throw new BuildException("Execute failed: " + e, e, getLocation());
+ } finally {
+ // close the output file if required
+ logFlush();
+ redirector.setAppendProperties(false);
+ redirector.setProperties();
+ }
+ }
+
+ /**
+ * log a message for skipping a fileset.
+ * @param currentType the current type.
+ * @param ds the directory scanner.
+ * @param base the dir base
+ */
+ private void logSkippingFileset(
+ String currentType, DirectoryScanner ds, File base) {
+ int includedCount
+ = ((!FileDirBoth.DIR.equals(currentType))
+ ? ds.getIncludedFilesCount() : 0)
+ + ((!FileDirBoth.FILE.equals(currentType))
+ ? ds.getIncludedDirsCount() : 0);
+
+ log("Skipping fileset for directory " + base + ". It is "
+ + ((includedCount > 0) ? "up to date." : "empty."),
+ verbose ? Project.MSG_INFO : Project.MSG_VERBOSE);
+ }
+
+ /**
+ * Construct the command line for parallel execution.
+ *
+ * @param srcFiles The filenames to add to the commandline.
+ * @param baseDirs filenames are relative to this dir.
+ * @return the command line in the form of a String[].
+ */
+ protected String[] getCommandline(String[] srcFiles, File[] baseDirs) {
+ final char fileSeparator = File.separatorChar;
+ Vector<String> targets = new Vector<String>();
+ if (targetFilePos != null) {
+ HashSet<String> addedFiles = new HashSet<String>();
+ for (int i = 0; i < srcFiles.length; i++) {
+ String[] subTargets = mapper.mapFileName(srcFiles[i]);
+ if (subTargets != null) {
+ for (int j = 0; j < subTargets.length; j++) {
+ String name = null;
+ if (!relative) {
+ name = new File(destDir, subTargets[j]).getAbsolutePath();
+ } else {
+ name = subTargets[j];
+ }
+ if (forwardSlash && fileSeparator != '/') {
+ name = name.replace(fileSeparator, '/');
+ }
+ if (!addedFiles.contains(name)) {
+ targets.addElement(name);
+ addedFiles.add(name);
+ }
+ }
+ }
+ }
+ }
+ String[] targetFiles = (String[]) targets.toArray(new String[targets.size()]);
+
+ if (!addSourceFile) {
+ srcFiles = new String[0];
+ }
+ String[] orig = cmdl.getCommandline();
+ String[] result
+ = new String[orig.length + srcFiles.length + targetFiles.length];
+
+ int srcIndex = orig.length;
+ if (srcFilePos != null) {
+ srcIndex = srcFilePos.getPosition();
+ }
+ if (targetFilePos != null) {
+ int targetIndex = targetFilePos.getPosition();
+
+ if (srcIndex < targetIndex
+ || (srcIndex == targetIndex && srcIsFirst)) {
+
+ // 0 --> srcIndex
+ System.arraycopy(orig, 0, result, 0, srcIndex);
+
+ // srcIndex --> targetIndex
+ System.arraycopy(orig, srcIndex, result,
+ srcIndex + srcFiles.length,
+ targetIndex - srcIndex);
+
+ insertTargetFiles(targetFiles, result,
+ targetIndex + srcFiles.length,
+ targetFilePos.getPrefix(),
+ targetFilePos.getSuffix());
+
+ // targetIndex --> end
+ System.arraycopy(orig, targetIndex, result,
+ targetIndex + srcFiles.length + targetFiles.length,
+ orig.length - targetIndex);
+ } else {
+ // 0 --> targetIndex
+ System.arraycopy(orig, 0, result, 0, targetIndex);
+
+ insertTargetFiles(targetFiles, result, targetIndex,
+ targetFilePos.getPrefix(),
+ targetFilePos.getSuffix());
+
+ // targetIndex --> srcIndex
+ System.arraycopy(orig, targetIndex, result,
+ targetIndex + targetFiles.length,
+ srcIndex - targetIndex);
+
+ // srcIndex --> end
+ System.arraycopy(orig, srcIndex, result,
+ srcIndex + srcFiles.length + targetFiles.length,
+ orig.length - srcIndex);
+ srcIndex += targetFiles.length;
+ }
+
+ } else { // no targetFilePos
+
+ // 0 --> srcIndex
+ System.arraycopy(orig, 0, result, 0, srcIndex);
+ // srcIndex --> end
+ System.arraycopy(orig, srcIndex, result,
+ srcIndex + srcFiles.length,
+ orig.length - srcIndex);
+ }
+ // fill in source file names
+ for (int i = 0; i < srcFiles.length; i++) {
+ String src;
+ if (relative) {
+ src = srcFiles[i];
+ } else {
+ src = new File(baseDirs[i], srcFiles[i]).getAbsolutePath();
+ }
+ if (forwardSlash && fileSeparator != '/') {
+ src = src.replace(fileSeparator, '/');
+ }
+ if (srcFilePos != null &&
+ (srcFilePos.getPrefix().length() > 0
+ || srcFilePos.getSuffix().length() > 0)) {
+ src = srcFilePos.getPrefix() + src + srcFilePos.getSuffix();
+ }
+ result[srcIndex + i] = src;
+ }
+ return result;
+ }
+
+ /**
+ * Construct the command line for serial execution.
+ *
+ * @param srcFile The filename to add to the commandline.
+ * @param baseDir filename is relative to this dir.
+ * @return the command line in the form of a String[].
+ */
+ protected String[] getCommandline(String srcFile, File baseDir) {
+ return getCommandline(new String[] {srcFile}, new File[] {baseDir});
+ }
+
+ /**
+ * Return the list of files from this DirectoryScanner that should
+ * be included on the command line.
+ * @param baseDir the File base directory.
+ * @param ds the DirectoryScanner to use for file scanning.
+ * @return a String[] containing the filenames.
+ */
+ protected String[] getFiles(File baseDir, DirectoryScanner ds) {
+ return restrict(ds.getIncludedFiles(), baseDir);
+ }
+
+ /**
+ * Return the list of Directories from this DirectoryScanner that
+ * should be included on the command line.
+ * @param baseDir the File base directory.
+ * @param ds the DirectoryScanner to use for file scanning.
+ * @return a String[] containing the directory names.
+ */
+ protected String[] getDirs(File baseDir, DirectoryScanner ds) {
+ return restrict(ds.getIncludedDirectories(), baseDir);
+ }
+
+ /**
+ * Return the list of files or directories from this FileList that
+ * should be included on the command line.
+ * @param list the FileList to check.
+ * @return a String[] containing the directory names.
+ *
+ * @since Ant 1.6.2
+ */
+ protected String[] getFilesAndDirs(FileList list) {
+ return restrict(list.getFiles(getProject()), list.getDir(getProject()));
+ }
+
+ private String[] restrict(String[] s, File baseDir) {
+ return (mapper == null || force) ? s
+ : new SourceFileScanner(this).restrict(s, baseDir, destDir, mapper);
+ }
+
+ /**
+ * Run the command in "parallel" mode, making sure that at most
+ * maxParallel sourcefiles get passed on the command line.
+ * @param exe the Executable to use.
+ * @param fileNames the Vector of filenames.
+ * @param baseDirs the Vector of base directories corresponding to fileNames.
+ * @throws IOException on I/O errors.
+ * @throws BuildException on other errors.
+ * @since Ant 1.6
+ */
+ protected void runParallel(Execute exe, Vector<String> fileNames,
+ Vector<File> baseDirs)
+ throws IOException, BuildException {
+ String[] s = new String[fileNames.size()];
+ fileNames.copyInto(s);
+ File[] b = new File[baseDirs.size()];
+ baseDirs.copyInto(b);
+
+ if (maxParallel <= 0
+ || s.length == 0 /* this is skipEmpty == false */) {
+ String[] command = getCommandline(s, b);
+ log(Commandline.describeCommand(command), Project.MSG_VERBOSE);
+ exe.setCommandline(command);
+ if (redirectorElement != null) {
+ setupRedirector();
+ redirectorElement.configure(redirector, null);
+ exe.setStreamHandler(redirector.createHandler());
+ }
+ runExecute(exe);
+ } else {
+ int stillToDo = fileNames.size();
+ int currentOffset = 0;
+ while (stillToDo > 0) {
+ int currentAmount = Math.min(stillToDo, maxParallel);
+ String[] cs = new String[currentAmount];
+ System.arraycopy(s, currentOffset, cs, 0, currentAmount);
+ File[] cb = new File[currentAmount];
+ System.arraycopy(b, currentOffset, cb, 0, currentAmount);
+ String[] command = getCommandline(cs, cb);
+ log(Commandline.describeCommand(command), Project.MSG_VERBOSE);
+ exe.setCommandline(command);
+ if (redirectorElement != null) {
+ setupRedirector();
+ redirectorElement.configure(redirector, null);
+ }
+ if (redirectorElement != null || currentOffset > 0) {
+ // need to reset the stream handler to restart
+ // reading of pipes;
+ // go ahead and do it always w/ nested redirectors
+ exe.setStreamHandler(redirector.createHandler());
+ }
+ runExecute(exe);
+
+ stillToDo -= currentAmount;
+ currentOffset += currentAmount;
+ }
+ }
+ }
+
+ /**
+ * Inserts target file names (which are already absolute paths)
+ * into the list of arguments, taking prefix and postfix into
+ * account.
+ */
+ private static void insertTargetFiles(String[] targetFiles,
+ String[] arguments,
+ int insertPosition,
+ String prefix, String suffix) {
+ if (prefix.length() == 0 && suffix.length() == 0) {
+ System.arraycopy(targetFiles, 0, arguments, insertPosition,
+ targetFiles.length);
+ } else {
+ for (int i = 0; i < targetFiles.length; i++) {
+ arguments[insertPosition + i] =
+ prefix + targetFiles[i] + suffix;
+ }
+ }
+ }
+
+ /**
+ * Enumerated attribute with the values "file", "dir" and "both"
+ * for the type attribute.
+ */
+ public static class FileDirBoth extends EnumeratedAttribute {
+ /** File value */
+ public static final String FILE = "file";
+ /** Dir value */
+ public static final String DIR = "dir";
+ /**
+ * @see EnumeratedAttribute#getValues
+ */
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return new String[] {FILE, DIR, "both"};
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteStreamHandler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteStreamHandler.java
new file mode 100644
index 00000000..1c5f9739
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteStreamHandler.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.tools.ant.taskdefs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Used by <code>Execute</code> to handle input and output stream of
+ * subprocesses.
+ *
+ * @since Ant 1.2
+ */
+public interface ExecuteStreamHandler {
+
+ /**
+ * Install a handler for the input stream of the subprocess.
+ *
+ * @param os output stream to write to the standard input stream of the
+ * subprocess
+ * @throws IOException on error
+ */
+ void setProcessInputStream(OutputStream os) throws IOException;
+
+ /**
+ * Install a handler for the error stream of the subprocess.
+ *
+ * @param is input stream to read from the error stream from the subprocess
+ * @throws IOException on error
+ */
+ void setProcessErrorStream(InputStream is) throws IOException;
+
+ /**
+ * Install a handler for the output stream of the subprocess.
+ *
+ * @param is input stream to read from the error stream from the subprocess
+ * @throws IOException on error
+ */
+ void setProcessOutputStream(InputStream is) throws IOException;
+
+ /**
+ * Start handling of the streams.
+ * @throws IOException on error
+ */
+ void start() throws IOException;
+
+ /**
+ * Stop handling of the streams - will not be restarted.
+ */
+ void stop();
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteWatchdog.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteWatchdog.java
new file mode 100644
index 00000000..cc3933e5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecuteWatchdog.java
@@ -0,0 +1,177 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.TimeoutObserver;
+import org.apache.tools.ant.util.Watchdog;
+
+/**
+ * Destroys a process running for too long.
+ * For example:
+ * <pre>
+ * ExecuteWatchdog watchdog = new ExecuteWatchdog(30000);
+ * Execute exec = new Execute(myloghandler, watchdog);
+ * exec.setCommandLine(mycmdline);
+ * int exitvalue = exec.execute();
+ * if (Execute.isFailure(exitvalue) &amp;&amp; watchdog.killedProcess()) {
+ * // it was killed on purpose by the watchdog
+ * }
+ * </pre>
+
+ * @see Execute
+ * @see org.apache.tools.ant.util.Watchdog
+ * @since Ant 1.2
+ */
+public class ExecuteWatchdog implements TimeoutObserver {
+
+ /** the process to execute and watch for duration */
+ private Process process;
+
+ /** say whether or not the watchdog is currently monitoring a process */
+ private volatile boolean watch = false;
+
+ /** exception that might be thrown during the process execution */
+ private Exception caught = null;
+
+ /** say whether or not the process was killed due to running overtime */
+ private volatile boolean killedProcess = false;
+
+ /** will tell us whether timeout has occurred */
+ private Watchdog watchdog;
+
+ /**
+ * Creates a new watchdog with a given timeout.
+ *
+ * @param timeout the timeout for the process in milliseconds.
+ * It must be greater than 0.
+ */
+ public ExecuteWatchdog(long timeout) {
+ watchdog = new Watchdog(timeout);
+ watchdog.addTimeoutObserver(this);
+ }
+
+ /**
+ * @param timeout the timeout value to use in milliseconds.
+ * @see #ExecuteWatchdog(long)
+ * @deprecated since 1.5.x.
+ * Use constructor with a long type instead.
+ * (1.4.x compatibility)
+ */
+ public ExecuteWatchdog(int timeout) {
+ this((long) timeout);
+ }
+
+ /**
+ * Watches the given process and terminates it, if it runs for too long.
+ * All information from the previous run are reset.
+ * @param process the process to monitor. It cannot be <tt>null</tt>
+ * @throws IllegalStateException if a process is still being monitored.
+ */
+ public synchronized void start(Process process) {
+ if (process == null) {
+ throw new NullPointerException("process is null.");
+ }
+ if (this.process != null) {
+ throw new IllegalStateException("Already running.");
+ }
+ this.caught = null;
+ this.killedProcess = false;
+ this.watch = true;
+ this.process = process;
+ watchdog.start();
+ }
+
+ /**
+ * Stops the watcher. It will notify all threads possibly waiting
+ * on this object.
+ */
+ public synchronized void stop() {
+ watchdog.stop();
+ cleanUp();
+ }
+
+ /**
+ * Called after watchdog has finished.
+ * This can be called in the watchdog thread
+ * @param w the watchdog
+ */
+ public synchronized void timeoutOccured(Watchdog w) {
+ try {
+ try {
+ // We must check if the process was not stopped
+ // before being here
+ process.exitValue();
+ } catch (IllegalThreadStateException itse) {
+ // the process is not terminated, if this is really
+ // a timeout and not a manual stop then kill it.
+ if (watch) {
+ killedProcess = true;
+ process.destroy();
+ }
+ }
+ } catch (Exception e) {
+ caught = e;
+ } finally {
+ cleanUp();
+ }
+ }
+
+ /**
+ * reset the monitor flag and the process.
+ */
+ protected synchronized void cleanUp() {
+ watch = false;
+ process = null;
+ }
+
+ /**
+ * This method will rethrow the exception that was possibly caught during
+ * the run of the process. It will only remains valid once the process has
+ * been terminated either by 'error', timeout or manual intervention.
+ * Information will be discarded once a new process is ran.
+ * @throws BuildException a wrapped exception over the one that was
+ * silently swallowed and stored during the process run.
+ */
+ public synchronized void checkException() throws BuildException {
+ if (caught != null) {
+ throw new BuildException("Exception in ExecuteWatchdog.run: "
+ + caught.getMessage(), caught);
+ }
+ }
+
+ /**
+ * Indicates whether or not the watchdog is still monitoring the process.
+ * @return <tt>true</tt> if the process is still running, otherwise
+ * <tt>false</tt>.
+ */
+ public boolean isWatching() {
+ return watch;
+ }
+
+ /**
+ * Indicates whether the last process run was killed on timeout or not.
+ * @return <tt>true</tt> if the process was killed otherwise
+ * <tt>false</tt>.
+ */
+ public boolean killedProcess() {
+ return killedProcess;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Exit.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Exit.java
new file mode 100644
index 00000000..f48f2484
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Exit.java
@@ -0,0 +1,235 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ExitStatusException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.taskdefs.condition.ConditionBase;
+
+/**
+ * Exits the active build, giving an additional message
+ * if available.
+ *
+ * The <code>if</code> and <code>unless</code> attributes make the
+ * failure conditional -both probe for the named property being defined.
+ * The <code>if</code> tests for the property being defined, the
+ * <code>unless</code> for a property being undefined.
+ *
+ * If both attributes are set, then the test fails only if both tests
+ * are true. i.e.
+ * <pre>fail := defined(ifProperty) &amp;&amp; !defined(unlessProperty)</pre>
+ *
+ * A single nested<code>&lt;condition&gt;</code> element can be specified
+ * instead of using <code>if</code>/<code>unless</code> (a combined
+ * effect can be achieved using <code>isset</code> conditions).
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task name="fail" category="control"
+ */
+public class Exit extends Task {
+
+ private static class NestedCondition extends ConditionBase implements Condition {
+ public boolean eval() {
+ if (countConditions() != 1) {
+ throw new BuildException(
+ "A single nested condition is required.");
+ }
+ return ((Condition) (getConditions().nextElement())).eval();
+ }
+ }
+
+ private String message;
+ private Object ifCondition, unlessCondition;
+ private NestedCondition nestedCondition;
+ private Integer status;
+
+ /**
+ * A message giving further information on why the build exited.
+ *
+ * @param value message to output
+ */
+ public void setMessage(String value) {
+ this.message = value;
+ }
+
+ /**
+ * Only fail if the given expression evaluates to true or the name
+ * of an existing property.
+ * @param c property name or evaluated expression
+ * @since Ant 1.8.0
+ */
+ public void setIf(Object c) {
+ ifCondition = c;
+ }
+
+ /**
+ * Only fail if the given expression evaluates to true or the name
+ * of an existing property.
+ * @param c property name or evaluated expression
+ */
+ public void setIf(String c) {
+ setIf((Object) c);
+ }
+
+ /**
+ * Only fail if the given expression evaluates to false or tno
+ * property of the given name exists.
+ * @param c property name or evaluated expression
+ * @since Ant 1.8.0
+ */
+ public void setUnless(Object c) {
+ unlessCondition = c;
+ }
+
+ /**
+ * Only fail if the given expression evaluates to false or tno
+ * property of the given name exists.
+ * @param c property name or evaluated expression
+ */
+ public void setUnless(String c) {
+ setUnless((Object) c);
+ }
+
+ /**
+ * Set the status code to associate with the thrown Exception.
+ * @param i the <code>int</code> status
+ */
+ public void setStatus(int i) {
+ status = new Integer(i);
+ }
+
+ /**
+ * Throw a <code>BuildException</code> to exit (fail) the build.
+ * If specified, evaluate conditions:
+ * A single nested condition is accepted, but requires that the
+ * <code>if</code>/<code>unless</code> attributes be omitted.
+ * If the nested condition evaluates to true, or the
+ * ifCondition is true or unlessCondition is false, the build will exit.
+ * The error message is constructed from the text fields, from
+ * the nested condition (if specified), or finally from
+ * the if and unless parameters (if present).
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ boolean fail = (nestedConditionPresent()) ? testNestedCondition()
+ : (testIfCondition() && testUnlessCondition());
+ if (fail) {
+ String text = null;
+ if (message != null && message.trim().length() > 0) {
+ text = message.trim();
+ } else {
+ if (ifCondition != null && !"".equals(ifCondition)
+ && testIfCondition()) {
+ text = "if=" + ifCondition;
+ }
+ if (unlessCondition != null && !"".equals(unlessCondition)
+ && testUnlessCondition()) {
+ if (text == null) {
+ text = "";
+ } else {
+ text += " and ";
+ }
+ text += "unless=" + unlessCondition;
+ }
+ if (nestedConditionPresent()) {
+ text = "condition satisfied";
+ } else {
+ if (text == null) {
+ text = "No message";
+ }
+ }
+ }
+ log("failing due to " + text, Project.MSG_DEBUG);
+ throw ((status == null) ? new BuildException(text)
+ : new ExitStatusException(text, status.intValue()));
+ }
+ }
+
+ /**
+ * Set a multiline message.
+ * @param msg the message to display
+ */
+ public void addText(String msg) {
+ if (message == null) {
+ message = "";
+ }
+ message += getProject().replaceProperties(msg);
+ }
+
+ /**
+ * Add a condition element.
+ * @return <code>ConditionBase</code>.
+ * @since Ant 1.6.2
+ */
+ public ConditionBase createCondition() {
+ if (nestedCondition != null) {
+ throw new BuildException("Only one nested condition is allowed.");
+ }
+ nestedCondition = new NestedCondition();
+ return nestedCondition;
+ }
+
+ /**
+ * test the if condition
+ * @return true if there is no if condition, or the named property exists
+ */
+ private boolean testIfCondition() {
+ return PropertyHelper.getPropertyHelper(getProject())
+ .testIfCondition(ifCondition);
+ }
+
+ /**
+ * test the unless condition
+ * @return true if there is no unless condition,
+ * or there is a named property but it doesn't exist
+ */
+ private boolean testUnlessCondition() {
+ return PropertyHelper.getPropertyHelper(getProject())
+ .testUnlessCondition(unlessCondition);
+ }
+
+ /**
+ * test the nested condition
+ * @return true if there is none, or it evaluates to true
+ */
+ private boolean testNestedCondition() {
+ boolean result = nestedConditionPresent();
+
+ if (result && ifCondition != null || unlessCondition != null) {
+ throw new BuildException("Nested conditions "
+ + "not permitted in conjunction with if/unless attributes");
+ }
+
+ return result && nestedCondition.eval();
+ }
+
+ /**
+ * test whether there is a nested condition.
+ * @return <code>boolean</code>.
+ */
+ private boolean nestedConditionPresent() {
+ return (nestedCondition != null);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Expand.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Expand.java
new file mode 100644
index 00000000..cb0c958d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Expand.java
@@ -0,0 +1,527 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.zip.ZipEntry;
+import org.apache.tools.zip.ZipFile;
+
+/**
+ * Unzip a file.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ * name="unzip"
+ * name="unjar"
+ * name="unwar"
+ */
+public class Expand extends Task {
+ private static final int BUFFER_SIZE = 1024;
+ private File dest; //req
+ private File source; // req
+ private boolean overwrite = true;
+ private Mapper mapperElement = null;
+ private Vector<PatternSet> patternsets = new Vector<PatternSet>();
+ private Union resources = new Union();
+ private boolean resourcesSpecified = false;
+ private boolean failOnEmptyArchive = false;
+ private boolean stripAbsolutePathSpec = false;
+ private boolean scanForUnicodeExtraFields = true;
+
+ public static final String NATIVE_ENCODING = "native-encoding";
+
+ private String encoding;
+ /** Error message when more that one mapper is defined */
+ public static final String ERROR_MULTIPLE_MAPPERS = "Cannot define more than one mapper";
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Creates an Expand instance and sets encoding to UTF-8.
+ */
+ public Expand() {
+ this("UTF8");
+ }
+
+ /**
+ * Creates an Expand instance and sets the given encoding.
+ *
+ * @since Ant 1.9.5
+ */
+ protected Expand(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Whether try ing to expand an empty archive would be an error.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setFailOnEmptyArchive(boolean b) {
+ failOnEmptyArchive = b;
+ }
+
+ /**
+ * Whether try ing to expand an empty archive would be an error.
+ *
+ * @since Ant 1.8.0
+ */
+ public boolean getFailOnEmptyArchive() {
+ return failOnEmptyArchive;
+ }
+
+ /**
+ * Do the work.
+ *
+ * @exception BuildException Thrown in unrecoverable error.
+ */
+ public void execute() throws BuildException {
+ if ("expand".equals(getTaskType())) {
+ log("!! expand is deprecated. Use unzip instead. !!");
+ }
+
+ if (source == null && !resourcesSpecified) {
+ throw new BuildException("src attribute and/or resources must be "
+ + "specified");
+ }
+
+ if (dest == null) {
+ throw new BuildException(
+ "Dest attribute must be specified");
+ }
+
+ if (dest.exists() && !dest.isDirectory()) {
+ throw new BuildException("Dest must be a directory.", getLocation());
+ }
+
+ if (source != null) {
+ if (source.isDirectory()) {
+ throw new BuildException("Src must not be a directory."
+ + " Use nested filesets instead.", getLocation());
+ } else if (!source.exists()) {
+ throw new BuildException("src '" + source + "' doesn't exist.");
+ } else if (!source.canRead()) {
+ throw new BuildException("src '" + source + "' cannot be read.");
+ } else {
+ expandFile(FILE_UTILS, source, dest);
+ }
+ }
+ for (Resource r : resources) {
+ if (!r.isExists()) {
+ log("Skipping '" + r.getName() + "' because it doesn't exist.");
+ continue;
+ }
+
+ FileProvider fp = r.as(FileProvider.class);
+ if (fp != null) {
+ expandFile(FILE_UTILS, fp.getFile(), dest);
+ } else {
+ expandResource(r, dest);
+ }
+ }
+ }
+
+ /**
+ * This method is to be overridden by extending unarchival tasks.
+ *
+ * @param fileUtils the fileUtils
+ * @param srcF the source file
+ * @param dir the destination directory
+ */
+ protected void expandFile(FileUtils fileUtils, File srcF, File dir) {
+ log("Expanding: " + srcF + " into " + dir, Project.MSG_INFO);
+ ZipFile zf = null;
+ FileNameMapper mapper = getMapper();
+ if (!srcF.exists()) {
+ throw new BuildException("Unable to expand "
+ + srcF
+ + " as the file does not exist",
+ getLocation());
+ }
+ try {
+ zf = new ZipFile(srcF, encoding, scanForUnicodeExtraFields);
+ boolean empty = true;
+ Enumeration<ZipEntry> e = zf.getEntries();
+ while (e.hasMoreElements()) {
+ empty = false;
+ ZipEntry ze = e.nextElement();
+ InputStream is = null;
+ log("extracting " + ze.getName(), Project.MSG_DEBUG);
+ try {
+ extractFile(fileUtils, srcF, dir,
+ is = zf.getInputStream(ze),
+ ze.getName(), new Date(ze.getTime()),
+ ze.isDirectory(), mapper);
+ } finally {
+ FileUtils.close(is);
+ }
+ }
+ if (empty && getFailOnEmptyArchive()) {
+ throw new BuildException("archive '" + srcF + "' is empty");
+ }
+ log("expand complete", Project.MSG_VERBOSE);
+ } catch (IOException ioe) {
+ throw new BuildException(
+ "Error while expanding " + srcF.getPath()
+ + "\n" + ioe.toString(),
+ ioe);
+ } finally {
+ ZipFile.closeQuietly(zf);
+ }
+ }
+
+ /**
+ * This method is to be overridden by extending unarchival tasks.
+ *
+ * @param srcR the source resource
+ * @param dir the destination directory
+ */
+ protected void expandResource(Resource srcR, File dir) {
+ throw new BuildException("only filesystem based resources are"
+ + " supported by this task.");
+ }
+
+ /**
+ * get a mapper for a file
+ * @return a filenamemapper for a file
+ */
+ protected FileNameMapper getMapper() {
+ FileNameMapper mapper = null;
+ if (mapperElement != null) {
+ mapper = mapperElement.getImplementation();
+ } else {
+ mapper = new IdentityMapper();
+ }
+ return mapper;
+ }
+
+ // CheckStyle:ParameterNumberCheck OFF - bc
+ /**
+ * extract a file to a directory
+ * @param fileUtils a fileUtils object
+ * @param srcF the source file
+ * @param dir the destination directory
+ * @param compressedInputStream the input stream
+ * @param entryName the name of the entry
+ * @param entryDate the date of the entry
+ * @param isDirectory if this is true the entry is a directory
+ * @param mapper the filename mapper to use
+ * @throws IOException on error
+ */
+ protected void extractFile(FileUtils fileUtils, File srcF, File dir,
+ InputStream compressedInputStream,
+ String entryName, Date entryDate,
+ boolean isDirectory, FileNameMapper mapper)
+ throws IOException {
+
+ if (stripAbsolutePathSpec && entryName.length() > 0
+ && (entryName.charAt(0) == File.separatorChar
+ || entryName.charAt(0) == '/'
+ || entryName.charAt(0) == '\\')) {
+ log("stripped absolute path spec from " + entryName,
+ Project.MSG_VERBOSE);
+ entryName = entryName.substring(1);
+ }
+
+ if (patternsets != null && patternsets.size() > 0) {
+ String name = entryName.replace('/', File.separatorChar)
+ .replace('\\', File.separatorChar);
+
+ boolean included = false;
+ Set<String> includePatterns = new HashSet<String>();
+ Set<String> excludePatterns = new HashSet<String>();
+ final int size = patternsets.size();
+ for (int v = 0; v < size; v++) {
+ PatternSet p = patternsets.elementAt(v);
+ String[] incls = p.getIncludePatterns(getProject());
+ if (incls == null || incls.length == 0) {
+ // no include pattern implicitly means includes="**"
+ incls = new String[] {"**"};
+ }
+
+ for (int w = 0; w < incls.length; w++) {
+ String pattern = incls[w].replace('/', File.separatorChar)
+ .replace('\\', File.separatorChar);
+ if (pattern.endsWith(File.separator)) {
+ pattern += "**";
+ }
+ includePatterns.add(pattern);
+ }
+
+ String[] excls = p.getExcludePatterns(getProject());
+ if (excls != null) {
+ for (int w = 0; w < excls.length; w++) {
+ String pattern = excls[w]
+ .replace('/', File.separatorChar)
+ .replace('\\', File.separatorChar);
+ if (pattern.endsWith(File.separator)) {
+ pattern += "**";
+ }
+ excludePatterns.add(pattern);
+ }
+ }
+ }
+
+ for (Iterator<String> iter = includePatterns.iterator();
+ !included && iter.hasNext();) {
+ String pattern = iter.next();
+ included = SelectorUtils.matchPath(pattern, name);
+ }
+
+ for (Iterator<String> iter = excludePatterns.iterator();
+ included && iter.hasNext();) {
+ String pattern = iter.next();
+ included = !SelectorUtils.matchPath(pattern, name);
+ }
+
+ if (!included) {
+ //Do not process this file
+ log("skipping " + entryName
+ + " as it is excluded or not included.",
+ Project.MSG_VERBOSE);
+ return;
+ }
+ }
+ String[] mappedNames = mapper.mapFileName(entryName);
+ if (mappedNames == null || mappedNames.length == 0) {
+ mappedNames = new String[] {entryName};
+ }
+ File f = fileUtils.resolveFile(dir, mappedNames[0]);
+ try {
+ if (!overwrite && f.exists()
+ && f.lastModified() >= entryDate.getTime()) {
+ log("Skipping " + f + " as it is up-to-date",
+ Project.MSG_DEBUG);
+ return;
+ }
+
+ log("expanding " + entryName + " to " + f,
+ Project.MSG_VERBOSE);
+ // create intermediary directories - sometimes zip don't add them
+ File dirF = f.getParentFile();
+ if (dirF != null) {
+ dirF.mkdirs();
+ }
+
+ if (isDirectory) {
+ f.mkdirs();
+ } else {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int length = 0;
+ FileOutputStream fos = null;
+ try {
+ fos = new FileOutputStream(f);
+
+ while ((length =
+ compressedInputStream.read(buffer)) >= 0) {
+ fos.write(buffer, 0, length);
+ }
+
+ fos.close();
+ fos = null;
+ } finally {
+ FileUtils.close(fos);
+ }
+ }
+
+ fileUtils.setFileLastModified(f, entryDate.getTime());
+ } catch (FileNotFoundException ex) {
+ log("Unable to expand to file " + f.getPath(),
+ ex,
+ Project.MSG_WARN);
+ }
+
+ }
+ // CheckStyle:ParameterNumberCheck ON
+
+ /**
+ * Set the destination directory. File will be unzipped into the
+ * destination directory.
+ *
+ * @param d Path to the directory.
+ */
+ public void setDest(File d) {
+ this.dest = d;
+ }
+
+ /**
+ * Set the path to zip-file.
+ *
+ * @param s Path to zip-file.
+ */
+ public void setSrc(File s) {
+ this.source = s;
+ }
+
+ /**
+ * Should we overwrite files in dest, even if they are newer than
+ * the corresponding entries in the archive?
+ * @param b a <code>boolean</code> value
+ */
+ public void setOverwrite(boolean b) {
+ overwrite = b;
+ }
+
+ /**
+ * Add a patternset.
+ * @param set a pattern set
+ */
+ public void addPatternset(PatternSet set) {
+ patternsets.addElement(set);
+ }
+
+ /**
+ * Add a fileset
+ * @param set a file set
+ */
+ public void addFileset(FileSet set) {
+ add(set);
+ }
+
+ /**
+ * Add a resource collection.
+ * @param rc a resource collection.
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection rc) {
+ resourcesSpecified = true;
+ resources.add(rc);
+ }
+
+ /**
+ * Defines the mapper to map source entries to destination files.
+ * @return a mapper to be configured
+ * @exception BuildException if more than one mapper is defined
+ * @since Ant1.7
+ */
+ public Mapper createMapper() throws BuildException {
+ if (mapperElement != null) {
+ throw new BuildException(ERROR_MULTIPLE_MAPPERS,
+ getLocation());
+ }
+ mapperElement = new Mapper(getProject());
+ return mapperElement;
+ }
+
+ /**
+ * A nested filenamemapper
+ * @param fileNameMapper the mapper to add
+ * @since Ant 1.6.3
+ */
+ public void add(FileNameMapper fileNameMapper) {
+ createMapper().add(fileNameMapper);
+ }
+
+
+ /**
+ * Sets the encoding to assume for file names and comments.
+ *
+ * <p>Set to <code>native-encoding</code> if you want your
+ * platform's native encoding, defaults to UTF8.</p>
+ * @param encoding the name of the character encoding
+ * @since Ant 1.6
+ */
+ public void setEncoding(String encoding) {
+ internalSetEncoding(encoding);
+ }
+
+ /**
+ * Supports grand-children that want to support the attribute
+ * where the child-class doesn't (i.e. Unzip in the compress
+ * Antlib).
+ *
+ * @since Ant 1.8.0
+ */
+ protected void internalSetEncoding(String encoding) {
+ if (NATIVE_ENCODING.equals(encoding)) {
+ encoding = null;
+ }
+ this.encoding = encoding;
+ }
+
+ /**
+ * @since Ant 1.8.0
+ */
+ public String getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * Whether leading path separators should be stripped.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setStripAbsolutePathSpec(boolean b) {
+ stripAbsolutePathSpec = b;
+ }
+
+ /**
+ * Whether unicode extra fields will be used if present.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setScanForUnicodeExtraFields(boolean b) {
+ internalSetScanForUnicodeExtraFields(b);
+ }
+
+ /**
+ * Supports grand-children that want to support the attribute
+ * where the child-class doesn't (i.e. Unzip in the compress
+ * Antlib).
+ *
+ * @since Ant 1.8.0
+ */
+ protected void internalSetScanForUnicodeExtraFields(boolean b) {
+ scanForUnicodeExtraFields = b;
+ }
+
+ /**
+ * @since Ant 1.8.0
+ */
+ public boolean getScanForUnicodeExtraFields() {
+ return scanForUnicodeExtraFields;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Filter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Filter.java
new file mode 100644
index 00000000..390ba5bf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Filter.java
@@ -0,0 +1,101 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Sets a token filter that is used by the file copy tasks
+ * to do token substitution. Sets multiple tokens by
+ * reading these from a file.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="filesystem"
+ */
+public class Filter extends Task {
+
+ private String token;
+ private String value;
+ private File filtersFile;
+
+ /**
+ * The token string without @ delimiters.
+ * @param token token to set
+ */
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ /**
+ * The string that should replace the token during filtered copies.
+ * @param value token replace value
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * The file from which the filters must be read.
+ * This file must be a formatted as a property file.
+ *
+ * @param filtersFile filter file
+ */
+ public void setFiltersfile(File filtersFile) {
+ this.filtersFile = filtersFile;
+ }
+
+ /**
+ * Execute the task.
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ boolean isFiltersFromFile =
+ filtersFile != null && token == null && value == null;
+ boolean isSingleFilter =
+ filtersFile == null && token != null && value != null;
+
+ if (!isFiltersFromFile && !isSingleFilter) {
+ throw new BuildException("both token and value parameters, or "
+ + "only a filtersFile parameter is "
+ + "required", getLocation());
+ }
+
+ if (isSingleFilter) {
+ getProject().getGlobalFilterSet().addFilter(token, value);
+ }
+
+ if (isFiltersFromFile) {
+ readFilters();
+ }
+ }
+
+ /**
+ * Read the filters.
+ * @throws BuildException on error
+ */
+ protected void readFilters() throws BuildException {
+ log("Reading filters from " + filtersFile, Project.MSG_VERBOSE);
+ getProject().getGlobalFilterSet().readFiltersFromFile(filtersFile);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java
new file mode 100644
index 00000000..465bf462
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java
@@ -0,0 +1,696 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.filters.ChainableReader;
+import org.apache.tools.ant.filters.FixCrLfFilter;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Converts text source files to local OS formatting conventions, as
+ * well as repair text files damaged by misconfigured or misguided editors or
+ * file transfer programs.
+ * <p>
+ * This task can take the following arguments:
+ * <ul>
+ * <li>srcdir
+ * <li>destdir
+ * <li>include
+ * <li>exclude
+ * <li>cr
+ * <li>eol
+ * <li>tab
+ * <li>eof
+ * <li>encoding
+ * <li>targetencoding
+ * </ul>
+ * Of these arguments, only <b>sourcedir</b> is required.
+ * <p>
+ * When this task executes, it will scan the srcdir based on the include
+ * and exclude properties.
+ * <p>
+ * This version generalises the handling of EOL characters, and allows
+ * for CR-only line endings (the standard on Mac systems prior to OS X).
+ * Tab handling has also been generalised to accommodate any tabwidth
+ * from 2 to 80, inclusive. Importantly, it will leave untouched any
+ * literal TAB characters embedded within string or character constants.
+ * <p>
+ * <em>Warning:</em> do not run on binary files.
+ * <em>Caution:</em> run with care on carefully formatted files.
+ * This may sound obvious, but if you don't specify asis, presume that
+ * your files are going to be modified. If "tabs" is "add" or "remove",
+ * whitespace characters may be added or removed as necessary. Similarly,
+ * for CR's - in fact "eol"="crlf" or cr="add" can result in cr
+ * characters being removed in one special case accommodated, i.e.,
+ * CRCRLF is regarded as a single EOL to handle cases where other
+ * programs have converted CRLF into CRCRLF.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="filesystem"
+ */
+
+public class FixCRLF extends MatchingTask implements ChainableReader {
+
+ private static final String FIXCRLF_ERROR = "<fixcrlf> error: ";
+ /** error string for using srcdir and file */
+ public static final String ERROR_FILE_AND_SRCDIR
+ = FIXCRLF_ERROR + "srcdir and file are mutually exclusive";
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private boolean preserveLastModified = false;
+ private File srcDir;
+ private File destDir = null;
+ private File file;
+ private FixCrLfFilter filter = new FixCrLfFilter();
+ private Vector<FilterChain> fcv = null;
+
+ /**
+ * Encoding to assume for the files
+ */
+ private String encoding = null;
+
+ /**
+ * Encoding to use for output files
+ */
+ private String outputEncoding = null;
+
+
+ /**
+ * Chain this task as a reader.
+ * @param rdr Reader to chain.
+ * @return a Reader.
+ * @since Ant 1.7?
+ */
+ public final Reader chain(final Reader rdr) {
+ return filter.chain(rdr);
+ }
+
+ /**
+ * Set the source dir to find the source text files.
+ * @param srcDir the source directory.
+ */
+ public void setSrcdir(File srcDir) {
+ this.srcDir = srcDir;
+ }
+
+ /**
+ * Set the destination where the fixed files should be placed.
+ * Default is to replace the original file.
+ * @param destDir the destination directory.
+ */
+ public void setDestdir(File destDir) {
+ this.destDir = destDir;
+ }
+
+ /**
+ * Set to true if modifying Java source files.
+ * @param javafiles whether modifying Java files.
+ */
+ public void setJavafiles(boolean javafiles) {
+ filter.setJavafiles(javafiles);
+ }
+
+ /**
+ * Set a single file to convert.
+ * @since Ant 1.6.3
+ * @param file the file to convert.
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * Specify how EndOfLine characters are to be handled.
+ *
+ * @param attr valid values:
+ * <ul>
+ * <li>asis: leave line endings alone
+ * <li>cr: convert line endings to CR
+ * <li>lf: convert line endings to LF
+ * <li>crlf: convert line endings to CRLF
+ * </ul>
+ */
+ public void setEol(CrLf attr) {
+ filter.setEol(FixCrLfFilter.CrLf.newInstance(attr.getValue()));
+ }
+
+ /**
+ * Specify how carriage return (CR) characters are to be handled.
+ *
+ * @param attr valid values:
+ * <ul>
+ * <li>add: ensure that there is a CR before every LF
+ * <li>asis: leave CR characters alone
+ * <li>remove: remove all CR characters
+ * </ul>
+ *
+ * @deprecated since 1.4.x.
+ * Use {@link #setEol setEol} instead.
+ */
+ public void setCr(AddAsisRemove attr) {
+ log("DEPRECATED: The cr attribute has been deprecated,",
+ Project.MSG_WARN);
+ log("Please use the eol attribute instead", Project.MSG_WARN);
+ String option = attr.getValue();
+ CrLf c = new CrLf();
+ if (option.equals("remove")) {
+ c.setValue("lf");
+ } else if (option.equals("asis")) {
+ c.setValue("asis");
+ } else {
+ // must be "add"
+ c.setValue("crlf");
+ }
+ setEol(c);
+ }
+
+ /**
+ * Specify how tab characters are to be handled.
+ *
+ * @param attr valid values:
+ * <ul>
+ * <li>add: convert sequences of spaces which span a tab stop to tabs
+ * <li>asis: leave tab and space characters alone
+ * <li>remove: convert tabs to spaces
+ * </ul>
+ */
+ public void setTab(AddAsisRemove attr) {
+ filter.setTab(FixCrLfFilter.AddAsisRemove.newInstance(attr.getValue()));
+ }
+
+ /**
+ * Specify tab length in characters.
+ *
+ * @param tlength specify the length of tab in spaces.
+ * @throws BuildException on error.
+ */
+ public void setTablength(int tlength) throws BuildException {
+ try {
+ filter.setTablength(tlength);
+ } catch (IOException e) {
+ // filter.setTablength throws IOException that would better be
+ // a BuildException
+ throw new BuildException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Specify how DOS EOF (control-z) characters are to be handled.
+ *
+ * @param attr valid values:
+ * <ul>
+ * <li>add: ensure that there is an eof at the end of the file
+ * <li>asis: leave eof characters alone
+ * <li>remove: remove any eof character found at the end
+ * </ul>
+ */
+ public void setEof(AddAsisRemove attr) {
+ filter.setEof(FixCrLfFilter.AddAsisRemove.newInstance(attr.getValue()));
+ }
+
+ /**
+ * Specifies the encoding Ant expects the files to be
+ * in--defaults to the platforms default encoding.
+ * @param encoding String encoding name.
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Specifies the encoding that the files are
+ * to be written in--same as input encoding by default.
+ * @param outputEncoding String outputEncoding name.
+ */
+ public void setOutputEncoding(String outputEncoding) {
+ this.outputEncoding = outputEncoding;
+ }
+
+ /**
+ * Specify whether a missing EOL will be added
+ * to the final line of a file.
+ * @param fixlast whether to fix the last line.
+ */
+ public void setFixlast(boolean fixlast) {
+ filter.setFixlast(fixlast);
+ }
+
+ /**
+ * Set whether to preserve the last modified time as the original files.
+ * @param preserve true if timestamps should be preserved.
+ * @since Ant 1.6.3
+ */
+ public void setPreserveLastModified(boolean preserve) {
+ preserveLastModified = preserve;
+ }
+
+ /**
+ * Executes the task.
+ * @throws BuildException on error.
+ */
+ public void execute() throws BuildException {
+ // first off, make sure that we've got a srcdir and destdir
+ validate();
+
+ // log options used
+ String enc = encoding == null ? "default" : encoding;
+ log("options:"
+ + " eol=" + filter.getEol().getValue()
+ + " tab=" + filter.getTab().getValue()
+ + " eof=" + filter.getEof().getValue()
+ + " tablength=" + filter.getTablength()
+ + " encoding=" + enc
+ + " outputencoding="
+ + (outputEncoding == null ? enc : outputEncoding),
+ Project.MSG_VERBOSE);
+
+ DirectoryScanner ds = super.getDirectoryScanner(srcDir);
+ String[] files = ds.getIncludedFiles();
+
+ for (int i = 0; i < files.length; i++) {
+ processFile(files[i]);
+ }
+ }
+
+ private void validate() throws BuildException {
+ if (file != null) {
+ if (srcDir != null) {
+ throw new BuildException(ERROR_FILE_AND_SRCDIR);
+ }
+ //patch file into the fileset
+ fileset.setFile(file);
+ //set our parent dir
+ srcDir = file.getParentFile();
+ }
+ if (srcDir == null) {
+ throw new BuildException(
+ FIXCRLF_ERROR + "srcdir attribute must be set!");
+ }
+ if (!srcDir.exists()) {
+ throw new BuildException(
+ FIXCRLF_ERROR + "srcdir does not exist: '" + srcDir + "'");
+ }
+ if (!srcDir.isDirectory()) {
+ throw new BuildException(
+ FIXCRLF_ERROR + "srcdir is not a directory: '" + srcDir + "'");
+ }
+ if (destDir != null) {
+ if (!destDir.exists()) {
+ throw new BuildException(
+ FIXCRLF_ERROR + "destdir does not exist: '"
+ + destDir + "'");
+ }
+ if (!destDir.isDirectory()) {
+ throw new BuildException(
+ FIXCRLF_ERROR + "destdir is not a directory: '"
+ + destDir + "'");
+ }
+ }
+ }
+
+ private void processFile(String file) throws BuildException {
+ File srcFile = new File(srcDir, file);
+ long lastModified = srcFile.lastModified();
+ File destD = destDir == null ? srcDir : destDir;
+
+ if (fcv == null) {
+ FilterChain fc = new FilterChain();
+ fc.add(filter);
+ fcv = new Vector<FilterChain>(1);
+ fcv.add(fc);
+ }
+ File tmpFile = FILE_UTILS.createTempFile("fixcrlf", "", null, true, true);
+ try {
+ FILE_UTILS.copyFile(srcFile, tmpFile, null, fcv, true, false,
+ encoding, outputEncoding == null ? encoding : outputEncoding,
+ getProject());
+
+ File destFile = new File(destD, file);
+
+ boolean destIsWrong = true;
+ if (destFile.exists()) {
+ // Compare the destination with the temp file
+ log("destFile " + destFile + " exists", Project.MSG_DEBUG);
+ destIsWrong = !FILE_UTILS.contentEquals(destFile, tmpFile);
+ log(destFile + (destIsWrong ? " is being written"
+ : " is not written, as the contents are identical"),
+ Project.MSG_DEBUG);
+ }
+ if (destIsWrong) {
+ FILE_UTILS.rename(tmpFile, destFile);
+ if (preserveLastModified) {
+ log("preserved lastModified for " + destFile,
+ Project.MSG_DEBUG);
+ FILE_UTILS.setFileLastModified(destFile, lastModified);
+ }
+ }
+ } catch (IOException e) {
+ throw new BuildException("error running fixcrlf on file " + srcFile, e);
+ } finally {
+ if (tmpFile != null && tmpFile.exists()) {
+ FILE_UTILS.tryHardToDelete(tmpFile);
+ }
+ }
+ }
+
+ /**
+ * Deprecated, the functionality has been moved to filters.FixCrLfFilter.
+ * @deprecated since 1.7.0.
+ */
+ protected class OneLiner implements Enumeration<Object> {
+ private static final int UNDEF = -1;
+ private static final int NOTJAVA = 0;
+ private static final int LOOKING = 1;
+ private static final int INBUFLEN = 8192;
+ private static final int LINEBUFLEN = 200;
+ private static final char CTRLZ = '\u001A';
+
+ private int state = filter.getJavafiles() ? LOOKING : NOTJAVA;
+
+ private StringBuffer eolStr = new StringBuffer(LINEBUFLEN);
+ private StringBuffer eofStr = new StringBuffer();
+
+ private BufferedReader reader;
+ private StringBuffer line = new StringBuffer();
+ private boolean reachedEof = false;
+ private File srcFile;
+
+ /**
+ * Constructor.
+ * @param srcFile the file to read.
+ * @throws BuildException if there is an error.
+ */
+ public OneLiner(File srcFile)
+ throws BuildException {
+ this.srcFile = srcFile;
+ try {
+ reader = new BufferedReader(
+ ((encoding == null) ? new FileReader(srcFile)
+ : new InputStreamReader(
+ new FileInputStream(srcFile), encoding)), INBUFLEN);
+
+ nextLine();
+ } catch (IOException e) {
+ throw new BuildException(srcFile + ": " + e.getMessage(),
+ e, getLocation());
+ }
+ }
+
+ /**
+ * Move to the next line.
+ * @throws BuildException if there is an error.
+ */
+ protected void nextLine()
+ throws BuildException {
+ int ch = -1;
+ int eolcount = 0;
+
+ eolStr = new StringBuffer();
+ line = new StringBuffer();
+
+ try {
+ ch = reader.read();
+ while (ch != -1 && ch != '\r' && ch != '\n') {
+ line.append((char) ch);
+ ch = reader.read();
+ }
+
+ if (ch == -1 && line.length() == 0) {
+ // Eof has been reached
+ reachedEof = true;
+ return;
+ }
+
+ switch ((char) ch) {
+ case '\r':
+ // Check for \r, \r\n and \r\r\n
+ // Regard \r\r not followed by \n as two lines
+ ++eolcount;
+ eolStr.append('\r');
+ reader.mark(2);
+ ch = reader.read();
+ switch (ch) {
+ case '\r':
+ ch = reader.read();
+ if ((char) (ch) == '\n') {
+ eolcount += 2;
+ eolStr.append("\r\n");
+ } else {
+ reader.reset();
+ }
+ break;
+ case '\n':
+ ++eolcount;
+ eolStr.append('\n');
+ break;
+ case -1:
+ // don't reposition when we've reached the end
+ // of the stream
+ break;
+ default:
+ reader.reset();
+ break;
+ } // end of switch ((char)(ch = reader.read()))
+ break;
+
+ case '\n':
+ ++eolcount;
+ eolStr.append('\n');
+ break;
+ default:
+ // Fall tru
+ } // end of switch ((char) ch)
+
+ // if at eolcount == 0 and trailing characters of string
+ // are CTRL-Zs, set eofStr
+ if (eolcount == 0) {
+ int i = line.length();
+ while (--i >= 0 && line.charAt(i) == CTRLZ) {
+ // keep searching for the first ^Z
+ }
+ if (i < line.length() - 1) {
+ // Trailing characters are ^Zs
+ // Construct new line and eofStr
+ eofStr.append(line.toString().substring(i + 1));
+ if (i < 0) {
+ line.setLength(0);
+ reachedEof = true;
+ } else {
+ line.setLength(i + 1);
+ }
+ }
+
+ } // end of if (eolcount == 0)
+
+ } catch (IOException e) {
+ throw new BuildException(srcFile + ": " + e.getMessage(),
+ e, getLocation());
+ }
+ }
+
+ /**
+ * get the eof string.
+ * @return the eof string.
+ */
+ public String getEofStr() {
+ return eofStr.substring(0);
+ }
+
+ /**
+ * get the state.
+ * @return the state.
+ */
+ public int getState() {
+ return state;
+ }
+
+ /**
+ * Set the state.
+ * @param state the value to use.
+ */
+ public void setState(int state) {
+ this.state = state;
+ }
+
+ /**
+ * @return true if there is more elements.
+ */
+ public boolean hasMoreElements() {
+ return !reachedEof;
+ }
+
+ /**
+ * get the next element.
+ * @return the next element.
+ * @throws NoSuchElementException if there is no more.
+ */
+ public Object nextElement()
+ throws NoSuchElementException {
+ if (!hasMoreElements()) {
+ throw new NoSuchElementException("OneLiner");
+ }
+ BufferLine tmpLine =
+ new BufferLine(line.toString(), eolStr.substring(0));
+ nextLine();
+ return tmpLine;
+ }
+
+ /**
+ * Close the reader.
+ * @throws IOException if there is an error.
+ */
+ public void close() throws IOException {
+ if (reader != null) {
+ reader.close();
+ }
+ }
+
+ class BufferLine {
+ private int next = 0;
+ private int column = 0;
+ private int lookahead = UNDEF;
+ private String line;
+ private String eolStr;
+
+ public BufferLine(String line, String eolStr)
+ throws BuildException {
+ next = 0;
+ column = 0;
+ this.line = line;
+ this.eolStr = eolStr;
+ }
+
+ public int getNext() {
+ return next;
+ }
+
+ public void setNext(int next) {
+ this.next = next;
+ }
+
+ public int getLookahead() {
+ return lookahead;
+ }
+
+ public void setLookahead(int lookahead) {
+ this.lookahead = lookahead;
+ }
+
+ public char getChar(int i) {
+ return line.charAt(i);
+ }
+
+ public char getNextChar() {
+ return getChar(next);
+ }
+
+ public char getNextCharInc() {
+ return getChar(next++);
+ }
+
+ public int getColumn() {
+ return column;
+ }
+
+ public void setColumn(int col) {
+ column = col;
+ }
+
+ public int incColumn() {
+ return column++;
+ }
+
+ public int length() {
+ return line.length();
+ }
+
+ public int getEolLength() {
+ return eolStr.length();
+ }
+
+ public String getLineString() {
+ return line;
+ }
+
+ public String getEol() {
+ return eolStr;
+ }
+
+ public String substring(int begin) {
+ return line.substring(begin);
+ }
+
+ public String substring(int begin, int end) {
+ return line.substring(begin, end);
+ }
+
+ public void setState(int state) {
+ OneLiner.this.setState(state);
+ }
+
+ public int getState() {
+ return OneLiner.this.getState();
+ }
+ }
+ }
+
+ /**
+ * Enumerated attribute with the values "asis", "add" and "remove".
+ */
+ public static class AddAsisRemove extends EnumeratedAttribute {
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return new String[] {"add", "asis", "remove"};
+ }
+ }
+
+ /**
+ * Enumerated attribute with the values "asis", "cr", "lf" and "crlf".
+ */
+ public static class CrLf extends EnumeratedAttribute {
+ /**
+ * @see EnumeratedAttribute#getValues
+ */
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return new String[] {"asis", "cr", "lf", "crlf",
+ "mac", "unix", "dos"};
+ }
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/GUnzip.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/GUnzip.java
new file mode 100644
index 00000000..13e88034
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/GUnzip.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.tools.ant.taskdefs;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Expands a file that has been compressed with the GZIP
+ * algorithm. Normally used to compress non-compressed archives such
+ * as TAR files.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ */
+
+public class GUnzip extends Unpack {
+ private static final int BUFFER_SIZE = 8 * 1024;
+ private static final String DEFAULT_EXTENSION = ".gz";
+
+ /**
+ * Get the default extension.
+ * @return the value ".gz"
+ */
+ protected String getDefaultExtension() {
+ return DEFAULT_EXTENSION;
+ }
+
+ /**
+ * Implement the gunzipping.
+ */
+ protected void extract() {
+ if (source.lastModified() > dest.lastModified()) {
+ log("Expanding " + source.getAbsolutePath() + " to "
+ + dest.getAbsolutePath());
+
+ FileOutputStream out = null;
+ GZIPInputStream zIn = null;
+ InputStream fis = null;
+ try {
+ out = new FileOutputStream(dest);
+ fis = srcResource.getInputStream();
+ zIn = new GZIPInputStream(fis);
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int count = 0;
+ do {
+ out.write(buffer, 0, count);
+ count = zIn.read(buffer, 0, buffer.length);
+ } while (count != -1);
+ } catch (IOException ioe) {
+ String msg = "Problem expanding gzip " + ioe.getMessage();
+ throw new BuildException(msg, ioe, getLocation());
+ } finally {
+ FileUtils.close(fis);
+ FileUtils.close(out);
+ FileUtils.close(zIn);
+ }
+ }
+ }
+
+ /**
+ * Whether this task can deal with non-file resources.
+ *
+ * <p>This implementation returns true only if this task is
+ * &lt;gunzip&gt;. Any subclass of this class that also wants to
+ * support non-file resources needs to override this method. We
+ * need to do so for backwards compatibility reasons since we
+ * can't expect subclasses to support resources.</p>
+ * @return true if this task supports non file resources.
+ * @since Ant 1.7
+ */
+ protected boolean supportsNonFileResources() {
+ return getClass().equals(GUnzip.class);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/GZip.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/GZip.java
new file mode 100644
index 00000000..029f414d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/GZip.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.tools.ant.taskdefs;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.GZIPOutputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Compresses a file with the GZIP algorithm. Normally used to compress
+ * non-compressed archives such as TAR files.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ */
+
+public class GZip extends Pack {
+ /**
+ * perform the GZip compression operation.
+ */
+ protected void pack() {
+ GZIPOutputStream zOut = null;
+ try {
+ zOut = new GZIPOutputStream(new FileOutputStream(zipFile));
+ zipResource(getSrcResource(), zOut);
+ } catch (IOException ioe) {
+ String msg = "Problem creating gzip " + ioe.getMessage();
+ throw new BuildException(msg, ioe, getLocation());
+ } finally {
+ FileUtils.close(zOut);
+ }
+ }
+
+ /**
+ * Whether this task can deal with non-file resources.
+ *
+ * <p>This implementation returns true only if this task is
+ * &lt;gzip&gt;. Any subclass of this class that also wants to
+ * support non-file resources needs to override this method. We
+ * need to do so for backwards compatibility reasons since we
+ * can't expect subclasses to support resources.</p>
+ * @return true if this case supports non file resources.
+ * @since Ant 1.7
+ */
+ protected boolean supportsNonFileResources() {
+ return getClass().equals(GZip.class);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/GenerateKey.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/GenerateKey.java
new file mode 100644
index 00000000..69e719ce
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/GenerateKey.java
@@ -0,0 +1,420 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Generates a key in a keystore.
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task name="genkey" category="java"
+ */
+public class GenerateKey extends Task {
+
+ /**
+ * A DistinguishedName parameter.
+ * This is a nested element in a dname nested element.
+ */
+ public static class DnameParam {
+ private String name;
+ private String value;
+
+ /**
+ * Set the name attribute.
+ * @param name a <code>String</code> value
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the name attribute.
+ * @return the name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the value attribute.
+ * @param value a <code>String</code> value
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Get the value attribute.
+ * @return the value.
+ */
+ public String getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * A class corresponding to the dname nested element.
+ */
+ public static class DistinguishedName {
+ private Vector<DnameParam> params = new Vector<DnameParam>();
+
+ /**
+ * Create a param nested element.
+ * @return a DnameParam object to be configured.
+ */
+ public Object createParam() {
+ DnameParam param = new DnameParam();
+ params.addElement(param);
+
+ return param;
+ }
+
+ /**
+ * Get the nested parameters.
+ * @return an enumeration of the nested parameters.
+ */
+ public Enumeration<DnameParam> getParams() {
+ return params.elements();
+ }
+
+ /**
+ * Generate a string rep of this distinguished name.
+ * The format is each of the parameters (name = value)
+ * separated by ','.
+ * This is used on the command line.
+ * @return a string rep of this name
+ */
+ public String toString() {
+ final int size = params.size();
+ final StringBuffer sb = new StringBuffer();
+ boolean firstPass = true;
+
+ for (int i = 0; i < size; i++) {
+ if (!firstPass) {
+ sb.append(" ,");
+ }
+ firstPass = false;
+
+ final DnameParam param = (DnameParam) params.elementAt(i);
+ sb.append(encode(param.getName()));
+ sb.append('=');
+ sb.append(encode(param.getValue()));
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Encode a name or value.
+ * The encoded result is the same as the input string
+ * except that each ',' is replaced by a '\,'.
+ * @param string the value to be encoded
+ * @return the encoded value.
+ */
+ public String encode(final String string) {
+ int end = string.indexOf(',');
+
+ if (-1 == end) {
+ return string;
+ }
+
+ final StringBuffer sb = new StringBuffer();
+
+ int start = 0;
+
+ while (-1 != end) {
+ sb.append(string.substring(start, end));
+ sb.append("\\,");
+ start = end + 1;
+ end = string.indexOf(',', start);
+ }
+
+ sb.append(string.substring(start));
+
+ return sb.toString();
+ }
+ }
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /**
+ * The alias of signer.
+ */
+ protected String alias;
+
+ /**
+ * The name of keystore file.
+ */
+ protected String keystore;
+ protected String storepass;
+ protected String storetype;
+ protected String keypass;
+
+ protected String sigalg;
+ protected String keyalg;
+ protected String dname;
+ protected DistinguishedName expandedDname;
+ protected int keysize;
+ protected int validity;
+ protected boolean verbose;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Distinguished name list.
+ *
+ * @return Distinguished name container.
+ * @throws BuildException If specified more than once or dname
+ * attribute is used.
+ */
+ public DistinguishedName createDname() throws BuildException {
+ if (null != expandedDname) {
+ throw new BuildException("DName sub-element can only be "
+ + "specified once.");
+ }
+ if (null != dname) {
+ throw new BuildException("It is not possible to specify dname "
+ + " both as attribute and element.");
+ }
+ expandedDname = new DistinguishedName();
+ return expandedDname;
+ }
+
+ /**
+ * The distinguished name for entity.
+ *
+ * @param dname distinguished name
+ */
+ public void setDname(final String dname) {
+ if (null != expandedDname) {
+ throw new BuildException("It is not possible to specify dname "
+ + " both as attribute and element.");
+ }
+ this.dname = dname;
+ }
+
+ /**
+ * The alias to add under.
+ *
+ * @param alias alias to add under
+ */
+ public void setAlias(final String alias) {
+ this.alias = alias;
+ }
+
+ /**
+ * Keystore location.
+ *
+ * @param keystore location
+ */
+ public void setKeystore(final String keystore) {
+ this.keystore = keystore;
+ }
+
+ /**
+ * Password for keystore integrity.
+ * Must be at least 6 characters long.
+ * @param storepass password
+ */
+ public void setStorepass(final String storepass) {
+ this.storepass = storepass;
+ }
+
+ /**
+ * Keystore type.
+ *
+ * @param storetype type
+ */
+ public void setStoretype(final String storetype) {
+ this.storetype = storetype;
+ }
+
+ /**
+ * Password for private key (if different).
+ *
+ * @param keypass password
+ */
+ public void setKeypass(final String keypass) {
+ this.keypass = keypass;
+ }
+
+ /**
+ * The algorithm to use in signing.
+ *
+ * @param sigalg algorithm
+ */
+ public void setSigalg(final String sigalg) {
+ this.sigalg = sigalg;
+ }
+
+ /**
+ * The method to use when generating name-value pair.
+ * @param keyalg algorithm
+ */
+ public void setKeyalg(final String keyalg) {
+ this.keyalg = keyalg;
+ }
+
+ /**
+ * Indicates the size of key generated.
+ *
+ * @param keysize size of key
+ * @throws BuildException If not an Integer
+ * @todo Could convert this to a plain Integer setter.
+ */
+ public void setKeysize(final String keysize) throws BuildException {
+ try {
+ this.keysize = Integer.parseInt(keysize);
+ } catch (final NumberFormatException nfe) {
+ throw new BuildException("KeySize attribute should be a integer");
+ }
+ }
+
+ /**
+ * Indicates how many days certificate is valid.
+ *
+ * @param validity days valid
+ * @throws BuildException If not an Integer
+ */
+ public void setValidity(final String validity) throws BuildException {
+ try {
+ this.validity = Integer.parseInt(validity);
+ } catch (final NumberFormatException nfe) {
+ throw new BuildException("Validity attribute should be a integer");
+ }
+ }
+
+ /**
+ * If true, verbose output when signing.
+ * @param verbose verbose or not
+ */
+ public void setVerbose(final boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ /**
+ * Execute the task.
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+
+ if (null == alias) {
+ throw new BuildException("alias attribute must be set");
+ }
+
+ if (null == storepass) {
+ throw new BuildException("storepass attribute must be set");
+ }
+
+ if (null == dname && null == expandedDname) {
+ throw new BuildException("dname must be set");
+ }
+
+ final StringBuffer sb = new StringBuffer();
+
+ sb.append("-genkey ");
+
+ if (verbose) {
+ sb.append("-v ");
+ }
+
+ sb.append("-alias \"");
+ sb.append(alias);
+ sb.append("\" ");
+
+ if (null != dname) {
+ sb.append("-dname \"");
+ sb.append(dname);
+ sb.append("\" ");
+ }
+
+ if (null != expandedDname) {
+ sb.append("-dname \"");
+ sb.append(expandedDname);
+ sb.append("\" ");
+ }
+
+ if (null != keystore) {
+ sb.append("-keystore \"");
+ sb.append(keystore);
+ sb.append("\" ");
+ }
+
+ if (null != storepass) {
+ sb.append("-storepass \"");
+ sb.append(storepass);
+ sb.append("\" ");
+ }
+
+ if (null != storetype) {
+ sb.append("-storetype \"");
+ sb.append(storetype);
+ sb.append("\" ");
+ }
+
+ sb.append("-keypass \"");
+ if (null != keypass) {
+ sb.append(keypass);
+ } else {
+ sb.append(storepass);
+ }
+ sb.append("\" ");
+
+ if (null != sigalg) {
+ sb.append("-sigalg \"");
+ sb.append(sigalg);
+ sb.append("\" ");
+ }
+
+ if (null != keyalg) {
+ sb.append("-keyalg \"");
+ sb.append(keyalg);
+ sb.append("\" ");
+ }
+
+
+ if (0 < keysize) {
+ sb.append("-keysize \"");
+ sb.append(keysize);
+ sb.append("\" ");
+ }
+
+ if (0 < validity) {
+ sb.append("-validity \"");
+ sb.append(validity);
+ sb.append("\" ");
+ }
+
+ log("Generating Key for " + alias);
+ final ExecTask cmd = new ExecTask(this);
+ cmd.setExecutable(JavaEnvUtils.getJdkExecutable("keytool"));
+ Commandline.Argument arg = cmd.createArg();
+ arg.setLine(sb.toString());
+ cmd.setFailonerror(true);
+ cmd.setTaskName(getTaskName());
+ cmd.execute();
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Get.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Get.java
new file mode 100644
index 00000000..c636ab54
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Get.java
@@ -0,0 +1,883 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Date;
+import java.util.zip.GZIPInputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Main;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.Resources;
+import org.apache.tools.ant.types.resources.URLProvider;
+import org.apache.tools.ant.types.resources.URLResource;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Gets a particular file from a URL source.
+ * Options include verbose reporting, timestamp based fetches and controlling
+ * actions on failures. NB: access through a firewall only works if the whole
+ * Java runtime is correctly configured.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="network"
+ */
+public class Get extends Task {
+ private static final int NUMBER_RETRIES = 3;
+ private static final int DOTS_PER_LINE = 50;
+ private static final int BIG_BUFFER_SIZE = 100 * 1024;
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+ private static final int REDIRECT_LIMIT = 25;
+ // HttpURLConnection doesn't have a constant for this in Java5 and
+ // what it calls HTTP_MOVED_TEMP would better be FOUND
+ private static final int HTTP_MOVED_TEMP = 307;
+
+ private static final String HTTP = "http";
+ private static final String HTTPS = "https";
+
+ private static final String DEFAULT_AGENT_PREFIX = "Apache Ant";
+ private static final String GZIP_CONTENT_ENCODING = "gzip";
+
+ private final Resources sources = new Resources();
+ private File destination; // required
+ private boolean verbose = false;
+ private boolean quiet = false;
+ private boolean useTimestamp = false; //off by default
+ private boolean ignoreErrors = false;
+ private String uname = null;
+ private String pword = null;
+ private long maxTime = 0;
+ private int numberRetries = NUMBER_RETRIES;
+ private boolean skipExisting = false;
+ private boolean httpUseCaches = true; // on by default
+ private boolean tryGzipEncoding = false;
+ private Mapper mapperElement = null;
+ private String userAgent =
+ System.getProperty(MagicNames.HTTP_AGENT_PROPERTY,
+ DEFAULT_AGENT_PREFIX + "/"
+ + Main.getShortAntVersion());
+
+ /**
+ * Does the work.
+ *
+ * @exception BuildException Thrown in unrecoverable error.
+ */
+ @Override
+ public void execute() throws BuildException {
+ checkAttributes();
+
+ for (final Resource r : sources) {
+ final URLProvider up = r.as(URLProvider.class);
+ final URL source = up.getURL();
+
+ File dest = destination;
+ if (destination.isDirectory()) {
+ if (mapperElement == null) {
+ String path = source.getPath();
+ if (path.endsWith("/")) {
+ path = path.substring(0, path.length() - 1);
+ }
+ final int slash = path.lastIndexOf("/");
+ if (slash > -1) {
+ path = path.substring(slash + 1);
+ }
+ dest = new File(destination, path);
+ } else {
+ final FileNameMapper mapper = mapperElement.getImplementation();
+ final String[] d = mapper.mapFileName(source.toString());
+ if (d == null) {
+ log("skipping " + r + " - mapper can't handle it",
+ Project.MSG_WARN);
+ continue;
+ } else if (d.length == 0) {
+ log("skipping " + r + " - mapper returns no file name",
+ Project.MSG_WARN);
+ continue;
+ } else if (d.length > 1) {
+ log("skipping " + r + " - mapper returns multiple file"
+ + " names", Project.MSG_WARN);
+ continue;
+ }
+ dest = new File(destination, d[0]);
+ }
+ }
+
+ //set up logging
+ final int logLevel = Project.MSG_INFO;
+ DownloadProgress progress = null;
+ if (verbose) {
+ progress = new VerboseProgress(System.out);
+ }
+
+ //execute the get
+ try {
+ doGet(source, dest, logLevel, progress);
+ } catch (final IOException ioe) {
+ log("Error getting " + source + " to " + dest);
+ if (!ignoreErrors) {
+ throw new BuildException(ioe, getLocation());
+ }
+ }
+ }
+ }
+
+ /**
+ * make a get request, with the supplied progress and logging info.
+ * All the other config parameters are set at the task level,
+ * source, dest, ignoreErrors, etc.
+ * @param logLevel level to log at, see {@link Project#log(String, int)}
+ * @param progress progress callback; null for no-callbacks
+ * @return true for a successful download, false otherwise.
+ * The return value is only relevant when {@link #ignoreErrors} is true, as
+ * when false all failures raise BuildExceptions.
+ * @throws IOException for network trouble
+ * @throws BuildException for argument errors, or other trouble when ignoreErrors
+ * is false.
+ * @deprecated only gets the first configured resource
+ */
+ @Deprecated
+ public boolean doGet(final int logLevel, final DownloadProgress progress)
+ throws IOException {
+ checkAttributes();
+ for (final Resource r : sources) {
+ final URLProvider up = r.as(URLProvider.class);
+ final URL source = up.getURL();
+ return doGet(source, destination, logLevel, progress);
+ }
+ /*NOTREACHED*/
+ return false;
+ }
+
+ /**
+ * make a get request, with the supplied progress and logging info.
+ *
+ * All the other config parameters like ignoreErrors are set at
+ * the task level.
+ * @param source the URL to get
+ * @param dest the target file
+ * @param logLevel level to log at, see {@link Project#log(String, int)}
+ * @param progress progress callback; null for no-callbacks
+ * @return true for a successful download, false otherwise.
+ * The return value is only relevant when {@link #ignoreErrors} is true, as
+ * when false all failures raise BuildExceptions.
+ * @throws IOException for network trouble
+ * @throws BuildException for argument errors, or other trouble when ignoreErrors
+ * is false.
+ * @since Ant 1.8.0
+ */
+ public boolean doGet(final URL source, final File dest, final int logLevel,
+ DownloadProgress progress)
+ throws IOException {
+
+ if (dest.exists() && skipExisting) {
+ log("Destination already exists (skipping): "
+ + dest.getAbsolutePath(), logLevel);
+ return true;
+ }
+
+ //dont do any progress, unless asked
+ if (progress == null) {
+ progress = new NullProgress();
+ }
+ log("Getting: " + source, logLevel);
+ log("To: " + dest.getAbsolutePath(), logLevel);
+
+ //set the timestamp to the file date.
+ long timestamp = 0;
+
+ boolean hasTimestamp = false;
+ if (useTimestamp && dest.exists()) {
+ timestamp = dest.lastModified();
+ if (verbose) {
+ final Date t = new Date(timestamp);
+ log("local file date : " + t.toString(), logLevel);
+ }
+ hasTimestamp = true;
+ }
+
+ final GetThread getThread = new GetThread(source, dest,
+ hasTimestamp, timestamp, progress,
+ logLevel, userAgent);
+ getThread.setDaemon(true);
+ getProject().registerThreadTask(getThread, this);
+ getThread.start();
+ try {
+ getThread.join(maxTime * 1000);
+ } catch (final InterruptedException ie) {
+ log("interrupted waiting for GET to finish",
+ Project.MSG_VERBOSE);
+ }
+
+ if (getThread.isAlive()) {
+ final String msg = "The GET operation took longer than " + maxTime
+ + " seconds, stopping it.";
+ if (ignoreErrors) {
+ log(msg);
+ }
+ getThread.closeStreams();
+ if (!ignoreErrors) {
+ throw new BuildException(msg);
+ }
+ return false;
+ }
+
+ return getThread.wasSuccessful();
+ }
+
+ @Override
+ public void log(final String msg, final int msgLevel) {
+ if (!quiet || msgLevel >= Project.MSG_ERR) {
+ super.log(msg, msgLevel);
+ }
+ }
+
+ /**
+ * Check the attributes.
+ */
+ private void checkAttributes() {
+
+ if (userAgent == null || userAgent.trim().length() == 0) {
+ throw new BuildException("userAgent may not be null or empty");
+ }
+
+ if (sources.size() == 0) {
+ throw new BuildException("at least one source is required",
+ getLocation());
+ }
+ for (final Resource r : sources) {
+ final URLProvider up = r.as(URLProvider.class);
+ if (up == null) {
+ throw new BuildException("Only URLProvider resources are"
+ + " supported", getLocation());
+ }
+ }
+
+ if (destination == null) {
+ throw new BuildException("dest attribute is required", getLocation());
+ }
+
+ if (destination.exists() && sources.size() > 1
+ && !destination.isDirectory()) {
+ throw new BuildException("The specified destination is not a"
+ + " directory",
+ getLocation());
+ }
+
+ if (destination.exists() && !destination.canWrite()) {
+ throw new BuildException("Can't write to "
+ + destination.getAbsolutePath(),
+ getLocation());
+ }
+
+ if (sources.size() > 1 && !destination.exists()) {
+ destination.mkdirs();
+ }
+ }
+
+ /**
+ * Set an URL to get.
+ *
+ * @param u URL for the file.
+ */
+ public void setSrc(final URL u) {
+ add(new URLResource(u));
+ }
+
+ /**
+ * Adds URLs to get.
+ * @since Ant 1.8.0
+ */
+ public void add(final ResourceCollection rc) {
+ sources.add(rc);
+ }
+
+ /**
+ * Where to copy the source file.
+ *
+ * @param dest Path to file.
+ */
+ public void setDest(final File dest) {
+ this.destination = dest;
+ }
+
+ /**
+ * If true, show verbose progress information.
+ *
+ * @param v if "true" then be verbose
+ */
+ public void setVerbose(final boolean v) {
+ verbose = v;
+ }
+
+ /**
+ * If true, set default log level to Project.MSG_ERR.
+ *
+ * @param v if "true" then be quiet
+ * @since Ant 1.9.4
+ */
+ public void setQuiet(final boolean v){
+ this.quiet = v;
+ }
+
+ /**
+ * If true, log errors but do not treat as fatal.
+ *
+ * @param v if "true" then don't report download errors up to ant
+ */
+ public void setIgnoreErrors(final boolean v) {
+ ignoreErrors = v;
+ }
+
+ /**
+ * If true, conditionally download a file based on the timestamp
+ * of the local copy.
+ *
+ * <p>In this situation, the if-modified-since header is set so
+ * that the file is only fetched if it is newer than the local
+ * file (or there is no local file) This flag is only valid on
+ * HTTP connections, it is ignored in other cases. When the flag
+ * is set, the local copy of the downloaded file will also have
+ * its timestamp set to the remote file time.</p>
+ *
+ * <p>Note that remote files of date 1/1/1970 (GMT) are treated as
+ * 'no timestamp', and web servers often serve files with a
+ * timestamp in the future by replacing their timestamp with that
+ * of the current time. Also, inter-computer clock differences can
+ * cause no end of grief.</p>
+ * @param v "true" to enable file time fetching
+ */
+ public void setUseTimestamp(final boolean v) {
+ useTimestamp = v;
+ }
+
+
+ /**
+ * Username for basic auth.
+ *
+ * @param u username for authentication
+ */
+ public void setUsername(final String u) {
+ this.uname = u;
+ }
+
+ /**
+ * password for the basic authentication.
+ *
+ * @param p password for authentication
+ */
+ public void setPassword(final String p) {
+ this.pword = p;
+ }
+
+ /**
+ * The time in seconds the download is allowed to take before
+ * being terminated.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setMaxTime(final long maxTime) {
+ this.maxTime = maxTime;
+ }
+
+ /**
+ * The number of retries to attempt upon error, defaults to 3.
+ *
+ * @param r retry count
+ *
+ * @since Ant 1.8.0
+ */
+ public void setRetries(final int r) {
+ this.numberRetries = r;
+ }
+
+ /**
+ * Skip files that already exist locally.
+ *
+ * @param s "true" to skip existing destination files
+ *
+ * @since Ant 1.8.0
+ */
+ public void setSkipExisting(final boolean s) {
+ this.skipExisting = s;
+ }
+
+ /**
+ * HTTP connections only - set the user-agent to be used
+ * when communicating with remote server. if null, then
+ * the value is considered unset and the behaviour falls
+ * back to the default of the http API.
+ *
+ * @since Ant 1.9.3
+ */
+ public void setUserAgent(final String userAgent) {
+ this.userAgent = userAgent;
+ }
+
+ /**
+ * HTTP connections only - control caching on the
+ * HttpUrlConnection: httpConnection.setUseCaches(); if false, do
+ * not allow caching on the HttpUrlConnection.
+ *
+ * <p>Defaults to true (allow caching, which is also the
+ * HttpUrlConnection default value.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public void setHttpUseCaches(final boolean httpUseCache) {
+ this.httpUseCaches = httpUseCache;
+ }
+
+ /**
+ * Whether to transparently try to reduce bandwidth by telling the
+ * server ant would support gzip encoding.
+ *
+ * <p>Setting this to true also means Ant will uncompress
+ * <code>.tar.gz</code> and similar files automatically.</p>
+ *
+ * @since Ant 1.9.5
+ */
+ public void setTryGzipEncoding(boolean b) {
+ tryGzipEncoding = b;
+ }
+
+ /**
+ * Define the mapper to map source to destination files.
+ * @return a mapper to be configured.
+ * @exception BuildException if more than one mapper is defined.
+ * @since Ant 1.8.0
+ */
+ public Mapper createMapper() throws BuildException {
+ if (mapperElement != null) {
+ throw new BuildException("Cannot define more than one mapper",
+ getLocation());
+ }
+ mapperElement = new Mapper(getProject());
+ return mapperElement;
+ }
+
+ /**
+ * Add a nested filenamemapper.
+ * @param fileNameMapper the mapper to add.
+ * @since Ant 1.8.0
+ */
+ public void add(final FileNameMapper fileNameMapper) {
+ createMapper().add(fileNameMapper);
+ }
+
+ /**
+ * Provide this for Backward Compatibility.
+ */
+ protected static class Base64Converter
+ extends org.apache.tools.ant.util.Base64Converter {
+ }
+
+ /**
+ * Interface implemented for reporting
+ * progress of downloading.
+ */
+ public interface DownloadProgress {
+ /**
+ * begin a download
+ */
+ void beginDownload();
+
+ /**
+ * tick handler
+ *
+ */
+ void onTick();
+
+ /**
+ * end a download
+ */
+ void endDownload();
+ }
+
+ /**
+ * do nothing with progress info
+ */
+ public static class NullProgress implements DownloadProgress {
+
+ /**
+ * begin a download
+ */
+ public void beginDownload() {
+ }
+
+ /**
+ * tick handler
+ *
+ */
+ public void onTick() {
+ }
+
+ /**
+ * end a download
+ */
+ public void endDownload() {
+ }
+ }
+
+ /**
+ * verbose progress system prints to some output stream
+ */
+ public static class VerboseProgress implements DownloadProgress {
+ private int dots = 0;
+ // CheckStyle:VisibilityModifier OFF - bc
+ PrintStream out;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Construct a verbose progress reporter.
+ * @param out the output stream.
+ */
+ public VerboseProgress(final PrintStream out) {
+ this.out = out;
+ }
+
+ /**
+ * begin a download
+ */
+ public void beginDownload() {
+ dots = 0;
+ }
+
+ /**
+ * tick handler
+ *
+ */
+ public void onTick() {
+ out.print(".");
+ if (dots++ > DOTS_PER_LINE) {
+ out.flush();
+ dots = 0;
+ }
+ }
+
+ /**
+ * end a download
+ */
+ public void endDownload() {
+ out.println();
+ out.flush();
+ }
+ }
+
+ private class GetThread extends Thread {
+
+ private final URL source;
+ private final File dest;
+ private final boolean hasTimestamp;
+ private final long timestamp;
+ private final DownloadProgress progress;
+ private final int logLevel;
+
+ private boolean success = false;
+ private IOException ioexception = null;
+ private BuildException exception = null;
+ private InputStream is = null;
+ private OutputStream os = null;
+ private URLConnection connection;
+ private int redirections = 0;
+ private String userAgent = null;
+
+ GetThread(final URL source, final File dest,
+ final boolean h, final long t, final DownloadProgress p, final int l, final String userAgent) {
+ this.source = source;
+ this.dest = dest;
+ hasTimestamp = h;
+ timestamp = t;
+ progress = p;
+ logLevel = l;
+ this.userAgent = userAgent;
+ }
+
+ @Override
+ public void run() {
+ try {
+ success = get();
+ } catch (final IOException ioex) {
+ ioexception = ioex;
+ } catch (final BuildException bex) {
+ exception = bex;
+ }
+ }
+
+ private boolean get() throws IOException, BuildException {
+
+ connection = openConnection(source);
+
+ if (connection == null) {
+ return false;
+ }
+
+ final boolean downloadSucceeded = downloadFile();
+
+ //if (and only if) the use file time option is set, then
+ //the saved file now has its timestamp set to that of the
+ //downloaded file
+ if (downloadSucceeded && useTimestamp) {
+ updateTimeStamp();
+ }
+
+ return downloadSucceeded;
+ }
+
+
+ private boolean redirectionAllowed(final URL aSource, final URL aDest) {
+ if (!(aSource.getProtocol().equals(aDest.getProtocol()) || (HTTP
+ .equals(aSource.getProtocol()) && HTTPS.equals(aDest
+ .getProtocol())))) {
+ final String message = "Redirection detected from "
+ + aSource.getProtocol() + " to " + aDest.getProtocol()
+ + ". Protocol switch unsafe, not allowed.";
+ if (ignoreErrors) {
+ log(message, logLevel);
+ return false;
+ } else {
+ throw new BuildException(message);
+ }
+ }
+
+ redirections++;
+ if (redirections > REDIRECT_LIMIT) {
+ final String message = "More than " + REDIRECT_LIMIT
+ + " times redirected, giving up";
+ if (ignoreErrors) {
+ log(message, logLevel);
+ return false;
+ } else {
+ throw new BuildException(message);
+ }
+ }
+
+
+ return true;
+ }
+
+ private URLConnection openConnection(final URL aSource) throws IOException {
+
+ // set up the URL connection
+ final URLConnection connection = aSource.openConnection();
+ // modify the headers
+ // NB: things like user authentication could go in here too.
+ if (hasTimestamp) {
+ connection.setIfModifiedSince(timestamp);
+ }
+ // Set the user agent
+ connection.addRequestProperty("User-Agent", this.userAgent);
+
+ // prepare Java 1.1 style credentials
+ if (uname != null || pword != null) {
+ final String up = uname + ":" + pword;
+ String encoding;
+ // we do not use the sun impl for portability,
+ // and always use our own implementation for consistent
+ // testing
+ final Base64Converter encoder = new Base64Converter();
+ encoding = encoder.encode(up.getBytes());
+ connection.setRequestProperty("Authorization", "Basic "
+ + encoding);
+ }
+
+ if (tryGzipEncoding) {
+ connection.setRequestProperty("Accept-Encoding", GZIP_CONTENT_ENCODING);
+ }
+
+ if (connection instanceof HttpURLConnection) {
+ ((HttpURLConnection) connection)
+ .setInstanceFollowRedirects(false);
+ ((HttpURLConnection) connection)
+ .setUseCaches(httpUseCaches);
+ }
+ // connect to the remote site (may take some time)
+ try {
+ connection.connect();
+ } catch (final NullPointerException e) {
+ //bad URLs can trigger NPEs in some JVMs
+ throw new BuildException("Failed to parse " + source.toString(), e);
+ }
+
+ // First check on a 301 / 302 (moved) response (HTTP only)
+ if (connection instanceof HttpURLConnection) {
+ final HttpURLConnection httpConnection = (HttpURLConnection) connection;
+ final int responseCode = httpConnection.getResponseCode();
+ if (isMoved(responseCode)) {
+ final String newLocation = httpConnection.getHeaderField("Location");
+ final String message = aSource
+ + (responseCode == HttpURLConnection.HTTP_MOVED_PERM ? " permanently"
+ : "") + " moved to " + newLocation;
+ log(message, logLevel);
+ final URL newURL = new URL(aSource, newLocation);
+ if (!redirectionAllowed(aSource, newURL)) {
+ return null;
+ }
+ return openConnection(newURL);
+ }
+ // next test for a 304 result (HTTP only)
+ final long lastModified = httpConnection.getLastModified();
+ if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED
+ || (lastModified != 0 && hasTimestamp && timestamp >= lastModified)) {
+ // not modified so no file download. just return
+ // instead and trace out something so the user
+ // doesn't think that the download happened when it
+ // didn't
+ log("Not modified - so not downloaded", logLevel);
+ return null;
+ }
+ // test for 401 result (HTTP only)
+ if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
+ final String message = "HTTP Authorization failure";
+ if (ignoreErrors) {
+ log(message, logLevel);
+ return null;
+ } else {
+ throw new BuildException(message);
+ }
+ }
+ }
+
+ //REVISIT: at this point even non HTTP connections may
+ //support the if-modified-since behaviour -we just check
+ //the date of the content and skip the write if it is not
+ //newer. Some protocols (FTP) don't include dates, of
+ //course.
+ return connection;
+ }
+
+ private boolean isMoved(final int responseCode) {
+ return responseCode == HttpURLConnection.HTTP_MOVED_PERM ||
+ responseCode == HttpURLConnection.HTTP_MOVED_TEMP ||
+ responseCode == HttpURLConnection.HTTP_SEE_OTHER ||
+ responseCode == HTTP_MOVED_TEMP;
+ }
+
+ private boolean downloadFile()
+ throws FileNotFoundException, IOException {
+ for (int i = 0; i < numberRetries; i++) {
+ // this three attempt trick is to get round quirks in different
+ // Java implementations. Some of them take a few goes to bind
+ // properly; we ignore the first couple of such failures.
+ try {
+ is = connection.getInputStream();
+ break;
+ } catch (final IOException ex) {
+ log("Error opening connection " + ex, logLevel);
+ }
+ }
+ if (is == null) {
+ log("Can't get " + source + " to " + dest, logLevel);
+ if (ignoreErrors) {
+ return false;
+ }
+ throw new BuildException("Can't get " + source + " to " + dest,
+ getLocation());
+ }
+
+ if (tryGzipEncoding
+ && GZIP_CONTENT_ENCODING.equals(connection.getContentEncoding())) {
+ is = new GZIPInputStream(is);
+ }
+
+ os = new FileOutputStream(dest);
+ progress.beginDownload();
+ boolean finished = false;
+ try {
+ final byte[] buffer = new byte[BIG_BUFFER_SIZE];
+ int length;
+ while (!isInterrupted() && (length = is.read(buffer)) >= 0) {
+ os.write(buffer, 0, length);
+ progress.onTick();
+ }
+ finished = !isInterrupted();
+ } finally {
+ FileUtils.close(os);
+ FileUtils.close(is);
+
+ // we have started to (over)write dest, but failed.
+ // Try to delete the garbage we'd otherwise leave
+ // behind.
+ if (!finished) {
+ dest.delete();
+ }
+ }
+ progress.endDownload();
+ return true;
+ }
+
+ private void updateTimeStamp() {
+ final long remoteTimestamp = connection.getLastModified();
+ if (verbose) {
+ final Date t = new Date(remoteTimestamp);
+ log("last modified = " + t.toString()
+ + ((remoteTimestamp == 0)
+ ? " - using current time instead"
+ : ""), logLevel);
+ }
+ if (remoteTimestamp != 0) {
+ FILE_UTILS.setFileLastModified(dest, remoteTimestamp);
+ }
+ }
+
+ /**
+ * Has the download completed successfully?
+ *
+ * <p>Re-throws any exception caught during executaion.</p>
+ */
+ boolean wasSuccessful() throws IOException, BuildException {
+ if (ioexception != null) {
+ throw ioexception;
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ return success;
+ }
+
+ /**
+ * Closes streams, interrupts the download, may delete the
+ * output file.
+ */
+ void closeStreams() {
+ interrupt();
+ FileUtils.close(os);
+ FileUtils.close(is);
+ if (!success && dest.exists()) {
+ dest.delete();
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/HostInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/HostInfo.java
new file mode 100644
index 00000000..5cd433ca
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/HostInfo.java
@@ -0,0 +1,256 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Sets properties to the host provided, or localhost if no information is
+ * provided. The default properties are NAME, FQDN, ADDR4, ADDR6;
+ *
+ * @since Ant 1.8
+ * @ant.task category="utility"
+ */
+
+
+public class HostInfo extends Task {
+ private static final String DEF_REM_ADDR6 = "::";
+
+ private static final String DEF_REM_ADDR4 = "0.0.0.0";
+
+ private static final String DEF_LOCAL_ADDR6 = "::1";
+
+ private static final String DEF_LOCAL_ADDR4 = "127.0.0.1";
+
+ private static final String DEF_LOCAL_NAME = "localhost";
+ private static final String DEF_DOMAIN = "localdomain";
+
+ private static final String DOMAIN = "DOMAIN";
+
+ private static final String NAME = "NAME";
+
+ private static final String ADDR4 = "ADDR4";
+
+ private static final String ADDR6 = "ADDR6";
+
+ private String prefix = "";
+
+ private String host;
+
+ private InetAddress nameAddr;
+
+ private InetAddress best6;
+
+ private InetAddress best4;
+
+ private List<InetAddress> inetAddrs;
+
+ /**
+ * Set a prefix for the properties. If the prefix does not end with a "."
+ * one is automatically added.
+ *
+ * @param aPrefix
+ * the prefix to use.
+ * @since Ant 1.8
+ */
+ public void setPrefix(String aPrefix) {
+ prefix = aPrefix;
+ if (!prefix.endsWith(".")) {
+ prefix += ".";
+ }
+ }
+
+ /**
+ * Set the host to be retrieved.
+ *
+ * @param aHost
+ * the name or the address of the host, data for the local host
+ * will be retrieved if omitted.
+ * @since Ant 1.8
+ */
+ public void setHost(String aHost) {
+ host = aHost;
+ }
+
+ /**
+ * set the properties.
+ *
+ * @throws BuildException
+ * on error.
+ */
+ public void execute() throws BuildException {
+ if (host == null || "".equals(host)) {
+ executeLocal();
+ } else {
+ executeRemote();
+ }
+ }
+
+ private void executeLocal() {
+ try {
+ inetAddrs = new LinkedList<InetAddress>();
+ Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+ while (interfaces.hasMoreElements()) {
+ NetworkInterface currentif = interfaces.nextElement();
+ Enumeration<InetAddress> addrs = currentif.getInetAddresses();
+ while (addrs.hasMoreElements()) {
+ inetAddrs.add(addrs.nextElement());
+ }
+ }
+ selectAddresses();
+
+ if (nameAddr != null && hasHostName(nameAddr)) {
+ setDomainAndName(nameAddr.getCanonicalHostName());
+ } else {
+ setProperty(DOMAIN, DEF_DOMAIN);
+ setProperty(NAME, DEF_LOCAL_NAME);
+ }
+ if (best4 != null) {
+ setProperty(ADDR4, best4.getHostAddress());
+ } else {
+ setProperty(ADDR4, DEF_LOCAL_ADDR4);
+ }
+ if (best6 != null) {
+ setProperty(ADDR6, best6.getHostAddress());
+ } else {
+ setProperty(ADDR6, DEF_LOCAL_ADDR6);
+ }
+ } catch (Exception e) {
+ log("Error retrieving local host information", e, Project.MSG_WARN);
+ setProperty(DOMAIN, DEF_DOMAIN);
+ setProperty(NAME, DEF_LOCAL_NAME);
+ setProperty(ADDR4, DEF_LOCAL_ADDR4);
+ setProperty(ADDR6, DEF_LOCAL_ADDR6);
+ }
+ }
+
+ private boolean hasHostName(InetAddress addr) {
+ return !addr.getHostAddress().equals(addr.getCanonicalHostName());
+ }
+
+ private void selectAddresses() {
+ for (InetAddress current : inetAddrs) {
+ if (!current.isMulticastAddress()) {
+ if (current instanceof Inet4Address) {
+ best4 = selectBestAddress(best4, current);
+ } else if (current instanceof Inet6Address) {
+ best6 = selectBestAddress(best6, current);
+ }
+ }
+ }
+
+ nameAddr = selectBestAddress(best4, best6);
+ }
+
+ private InetAddress selectBestAddress(InetAddress bestSoFar,
+ InetAddress current) {
+ InetAddress best = bestSoFar;
+ if (best == null) {
+ // none selected so far, so this one is better.
+ best = current;
+ } else {
+ if (current == null || current.isLoopbackAddress()) {
+ // definitely not better than the previously selected address.
+ } else if (current.isLinkLocalAddress()) {
+ // link local considered better than loopback
+ if (best.isLoopbackAddress()) {
+ best = current;
+ }
+ } else if (current.isSiteLocalAddress()) {
+ // site local considered better than link local (and loopback)
+ // address with hostname resolved considered better than
+ // address without hostname
+ if (best.isLoopbackAddress()
+ || best.isLinkLocalAddress()
+ || (best.isSiteLocalAddress() && !hasHostName(best))) {
+ best = current;
+ }
+ } else {
+ // current is a "Global address", considered better than
+ // site local (and better than link local, loopback)
+ // address with hostname resolved considered better than
+ // address without hostname
+ if (best.isLoopbackAddress()
+ || best.isLinkLocalAddress()
+ || best.isSiteLocalAddress()
+ || !hasHostName(best)) {
+ best = current;
+ }
+ }
+ }
+ return best;
+ }
+
+ private void executeRemote() {
+ try {
+ inetAddrs = Arrays.asList(InetAddress.getAllByName(host));
+
+ selectAddresses();
+
+ if (nameAddr != null && hasHostName(nameAddr)) {
+ setDomainAndName(nameAddr.getCanonicalHostName());
+ } else {
+ setDomainAndName(host);
+ }
+ if (best4 != null) {
+ setProperty(ADDR4, best4.getHostAddress());
+ } else {
+ setProperty(ADDR4, DEF_REM_ADDR4);
+ }
+ if (best6 != null) {
+ setProperty(ADDR6, best6.getHostAddress());
+ } else {
+ setProperty(ADDR6, DEF_REM_ADDR6);
+ }
+ } catch (Exception e) {
+ log("Error retrieving remote host information for host:" + host
+ + ".", e, Project.MSG_WARN);
+ setDomainAndName(host);
+ setProperty(ADDR4, DEF_REM_ADDR4);
+ setProperty(ADDR6, DEF_REM_ADDR6);
+ }
+ }
+
+ private void setDomainAndName(String fqdn) {
+ int idx = fqdn.indexOf('.');
+ if (idx > 0) {
+ setProperty(NAME, fqdn.substring(0, idx));
+ setProperty(DOMAIN, fqdn.substring(idx+1));
+ } else {
+ setProperty(NAME, fqdn);
+ setProperty(DOMAIN, DEF_DOMAIN);
+ }
+ }
+
+ private void setProperty(String name, String value) {
+ getProject().setNewProperty(prefix + name, value);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ImportTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ImportTask.java
new file mode 100644
index 00000000..6b82a364
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ImportTask.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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.ProjectHelperRepository;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.URLProvider;
+import org.apache.tools.ant.types.resources.URLResource;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Task to import another build file into the current project.
+ * <p>
+ * It must be 'top level'. On execution it will read another Ant file
+ * into the same Project.
+ * </p>
+ * <p>
+ * <b>Important</b>: Trying to understand how relative file references
+ * resolved in deep/complex build hierarchies - such as what happens
+ * when an imported file imports another file can be difficult. Use absolute references for
+ * enhanced build file stability, especially in the imported files.
+ * </p>
+ * <p>Examples:</p>
+ * <pre>
+ * &lt;import file="../common-targets.xml"/&gt;
+ * </pre>
+ * <p>Import targets from a file in a parent directory.</p>
+ * <pre>
+ * &lt;import file="${deploy-platform}.xml"/&gt;
+ * </pre>
+ * <p>Import the project defined by the property <code>deploy-platform</code>.</p>
+ *
+ * @since Ant1.6
+ * @ant.task category="control"
+ */
+public class ImportTask extends Task {
+ private String file;
+ private boolean optional;
+ private String targetPrefix = ProjectHelper.USE_PROJECT_NAME_AS_TARGET_PREFIX;
+ private String prefixSeparator = ".";
+ private final Union resources = new Union();
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ public ImportTask() {
+ resources.setCache(true);
+ }
+
+ /**
+ * sets the optional attribute
+ *
+ * @param optional if true ignore files that are not present,
+ * default is false
+ */
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+
+ /**
+ * the name of the file to import. How relative paths are resolved is still
+ * in flux: use absolute paths for safety.
+ * @param file the name of the file
+ */
+ public void setFile(String file) {
+ // I don't think we can use File - different rules
+ // for relative paths.
+ this.file = file;
+ }
+
+ /**
+ * The prefix to use when prefixing the imported target names.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setAs(String prefix) {
+ targetPrefix = prefix;
+ }
+
+ /**
+ * The separator to use between prefix and target name, default is
+ * ".".
+ *
+ * @since Ant 1.8.0
+ */
+ public void setPrefixSeparator(String s) {
+ prefixSeparator = s;
+ }
+
+ /**
+ * The resource to import.
+ *
+ * @since Ant 1.8.0
+ */
+ public void add(ResourceCollection r) {
+ resources.add(r);
+ }
+
+ public void execute() {
+ if (file == null && resources.size() == 0) {
+ throw new BuildException("import requires file attribute or"
+ + " at least one nested resource");
+ }
+ if (getOwningTarget() == null
+ || !"".equals(getOwningTarget().getName())) {
+ throw new BuildException("import only allowed as a top-level task");
+ }
+
+ ProjectHelper helper =
+ (ProjectHelper) getProject().
+ getReference(ProjectHelper.PROJECTHELPER_REFERENCE);
+
+ if (helper == null) {
+ // this happens if the projecthelper was not registered with the project.
+ throw new BuildException("import requires support in ProjectHelper");
+ }
+
+ Vector<Object> importStack = helper.getImportStack();
+
+ if (importStack.size() == 0) {
+ // this happens if ant is used with a project
+ // helper that doesn't set the import.
+ throw new BuildException("import requires support in ProjectHelper");
+ }
+
+ if (getLocation() == null || getLocation().getFileName() == null) {
+ throw new BuildException("Unable to get location of import task");
+ }
+
+ Union resourcesToImport = new Union(getProject(), resources);
+ Resource fromFileAttribute = getFileAttributeResource();
+ if (fromFileAttribute != null) {
+ resources.add(fromFileAttribute);
+ }
+ for (Resource r : resourcesToImport) {
+ importResource(helper, r);
+ }
+ }
+
+ private void importResource(ProjectHelper helper,
+ Resource importedResource) {
+ Vector<Object> importStack = helper.getImportStack();
+
+ getProject().log("Importing file " + importedResource + " from "
+ + getLocation().getFileName(), Project.MSG_VERBOSE);
+
+ if (!importedResource.isExists()) {
+ String message =
+ "Cannot find " + importedResource + " imported from "
+ + getLocation().getFileName();
+ if (optional) {
+ getProject().log(message, Project.MSG_VERBOSE);
+ return;
+ } else {
+ throw new BuildException(message);
+ }
+ }
+
+ if (!isInIncludeMode() &&
+ hasAlreadyBeenImported(importedResource, importStack)) {
+ getProject().log(
+ "Skipped already imported file:\n "
+ + importedResource + "\n", Project.MSG_VERBOSE);
+ return;
+ }
+
+ // nested invocations are possible like an imported file
+ // importing another one
+ String oldPrefix = ProjectHelper.getCurrentTargetPrefix();
+ boolean oldIncludeMode = ProjectHelper.isInIncludeMode();
+ String oldSep = ProjectHelper.getCurrentPrefixSeparator();
+ try {
+ String prefix;
+ if (isInIncludeMode() && oldPrefix != null
+ && targetPrefix != null) {
+ prefix = oldPrefix + oldSep + targetPrefix;
+ } else if (isInIncludeMode()) {
+ prefix = targetPrefix;
+ } else if (!ProjectHelper.USE_PROJECT_NAME_AS_TARGET_PREFIX.equals(targetPrefix)) {
+ prefix = targetPrefix;
+ } else {
+ prefix = oldPrefix;
+ }
+ setProjectHelperProps(prefix, prefixSeparator,
+ isInIncludeMode());
+
+ ProjectHelper subHelper = ProjectHelperRepository.getInstance().getProjectHelperForBuildFile(
+ importedResource);
+
+ // push current stacks into the sub helper
+ subHelper.getImportStack().addAll(helper.getImportStack());
+ subHelper.getExtensionStack().addAll(helper.getExtensionStack());
+ getProject().addReference(ProjectHelper.PROJECTHELPER_REFERENCE, subHelper);
+
+ subHelper.parse(getProject(), importedResource);
+
+ // push back the stack from the sub helper to the main one
+ getProject().addReference(ProjectHelper.PROJECTHELPER_REFERENCE, helper);
+ helper.getImportStack().clear();
+ helper.getImportStack().addAll(subHelper.getImportStack());
+ helper.getExtensionStack().clear();
+ helper.getExtensionStack().addAll(subHelper.getExtensionStack());
+ } catch (BuildException ex) {
+ throw ProjectHelper.addLocationToBuildException(
+ ex, getLocation());
+ } finally {
+ setProjectHelperProps(oldPrefix, oldSep, oldIncludeMode);
+ }
+ }
+
+ private Resource getFileAttributeResource() {
+ // Paths are relative to the build file they're imported from,
+ // *not* the current directory (same as entity includes).
+
+ if (file != null) {
+ if (isExistingAbsoluteFile(file)) {
+ return new FileResource(new File(file));
+ }
+
+ File buildFile =
+ new File(getLocation().getFileName()).getAbsoluteFile();
+ if (buildFile.exists()) {
+ File buildFileParent = new File(buildFile.getParent());
+ File importedFile =
+ FILE_UTILS.resolveFile(buildFileParent, file);
+ return new FileResource(importedFile);
+ }
+ // maybe this import tasks is inside an imported URL?
+ try {
+ URL buildFileURL = new URL(getLocation().getFileName());
+ URL importedFile = new URL(buildFileURL, file);
+ return new URLResource(importedFile);
+ } catch (MalformedURLException ex) {
+ log(ex.toString(), Project.MSG_VERBOSE);
+ }
+ throw new BuildException("failed to resolve " + file
+ + " relative to "
+ + getLocation().getFileName());
+ }
+ return null;
+ }
+
+ private boolean isExistingAbsoluteFile(String name) {
+ File f = new File(name);
+ return f.isAbsolute() && f.exists();
+ }
+
+ private boolean hasAlreadyBeenImported(Resource importedResource,
+ Vector<Object> importStack) {
+ File importedFile = null;
+ FileProvider fp = importedResource.as(FileProvider.class);
+ if (fp != null) {
+ importedFile = fp.getFile();
+ }
+ URL importedURL = null;
+ URLProvider up = importedResource.as(URLProvider.class);
+ if (up != null) {
+ importedURL = up.getURL();
+ }
+ for (Object o : importStack) {
+ if (isOneOf(o, importedResource, importedFile, importedURL)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isOneOf(Object o, Resource importedResource,
+ File importedFile, URL importedURL) {
+ if (o.equals(importedResource) || o.equals(importedFile)
+ || o.equals(importedURL)) {
+ return true;
+ }
+ if (o instanceof Resource) {
+ if (importedFile != null) {
+ FileProvider fp = ((Resource) o).as(FileProvider.class);
+ if (fp != null && fp.getFile().equals(importedFile)) {
+ return true;
+ }
+ }
+ if (importedURL != null) {
+ URLProvider up = ((Resource) o).as(URLProvider.class);
+ if (up != null && up.getURL().equals(importedURL)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Whether the task is in include (as opposed to import) mode.
+ *
+ * <p>In include mode included targets are only known by their
+ * prefixed names and their depends lists get rewritten so that
+ * all dependencies get the prefix as well.</p>
+ *
+ * <p>In import mode imported targets are known by an adorned as
+ * well as a prefixed name and the unadorned target may be
+ * overwritten in the importing build file. The depends list of
+ * the imported targets is not modified at all.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ protected final boolean isInIncludeMode() {
+ return "include".equals(getTaskType());
+ }
+
+ /**
+ * Sets a bunch of Thread-local ProjectHelper properties.
+ *
+ * @since Ant 1.8.0
+ */
+ private static void setProjectHelperProps(String prefix,
+ String prefixSep,
+ boolean inIncludeMode) {
+ ProjectHelper.setCurrentTargetPrefix(prefix);
+ ProjectHelper.setCurrentPrefixSeparator(prefixSep);
+ ProjectHelper.setInIncludeMode(inIncludeMode);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Input.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Input.java
new file mode 100644
index 00000000..ed051318
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Input.java
@@ -0,0 +1,259 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.input.DefaultInputHandler;
+import org.apache.tools.ant.input.GreedyInputHandler;
+import org.apache.tools.ant.input.InputHandler;
+import org.apache.tools.ant.input.InputRequest;
+import org.apache.tools.ant.input.MultipleChoiceInputRequest;
+import org.apache.tools.ant.input.PropertyFileInputHandler;
+import org.apache.tools.ant.input.SecureInputHandler;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.util.ClasspathUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Reads an input line from the console.
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="control"
+ */
+public class Input extends Task {
+
+ /**
+ * Represents an InputHandler.
+ */
+ public class Handler extends DefBase {
+
+ private String refid = null;
+ private HandlerType type = null;
+ private String classname = null;
+
+ /**
+ * Specify that the handler is a reference on the project;
+ * this allows the use of a custom inputhandler.
+ * @param refid the String refid.
+ */
+ public void setRefid(final String refid) {
+ this.refid = refid;
+ }
+ /**
+ * Get the refid of this Handler.
+ * @return String refid.
+ */
+ public String getRefid() {
+ return refid;
+ }
+ /**
+ * Set the InputHandler classname.
+ * @param classname the String classname.
+ */
+ public void setClassname(final String classname) {
+ this.classname = classname;
+ }
+ /**
+ * Get the classname of the InputHandler.
+ * @return String classname.
+ */
+ public String getClassname() {
+ return classname;
+ }
+ /**
+ * Set the handler type.
+ * @param type a HandlerType.
+ */
+ public void setType(final HandlerType type) {
+ this.type = type;
+ }
+ /**
+ * Get the handler type.
+ * @return a HandlerType object.
+ */
+ public HandlerType getType() {
+ return type;
+ }
+ private InputHandler getInputHandler() {
+ if (type != null) {
+ return type.getInputHandler();
+ }
+ if (refid != null) {
+ try {
+ return (InputHandler) (getProject().getReference(refid));
+ } catch (final ClassCastException e) {
+ throw new BuildException(
+ refid + " does not denote an InputHandler", e);
+ }
+ }
+ if (classname != null) {
+ return (InputHandler) (ClasspathUtils.newInstance(classname,
+ createLoader(), InputHandler.class));
+ }
+ throw new BuildException(
+ "Must specify refid, classname or type");
+ }
+ }
+
+ /**
+ * EnumeratedAttribute representing the built-in input handler types:
+ * "default", "propertyfile", "greedy", "secure" (since Ant 1.8).
+ */
+ public static class HandlerType extends EnumeratedAttribute {
+ private static final String[] VALUES = {"default", "propertyfile", "greedy", "secure"};
+
+ private static final InputHandler[] HANDLERS
+ = {new DefaultInputHandler(),
+ new PropertyFileInputHandler(),
+ new GreedyInputHandler(),
+ new SecureInputHandler()};
+
+ /** {@inheritDoc} */
+ @Override
+ public String[] getValues() {
+ return VALUES;
+ }
+ private InputHandler getInputHandler() {
+ return HANDLERS[getIndex()];
+ }
+ }
+
+ private String validargs = null;
+ private String message = "";
+ private String addproperty = null;
+ private String defaultvalue = null;
+ private Handler handler = null;
+ private boolean messageAttribute;
+
+ /**
+ * Defines valid input parameters as comma separated strings. If set, input
+ * task will reject any input not defined as accepted and requires the user
+ * to reenter it. Validargs are case sensitive. If you want 'a' and 'A' to
+ * be accepted you need to define both values as accepted arguments.
+ *
+ * @param validargs A comma separated String defining valid input args.
+ */
+ public void setValidargs (final String validargs) {
+ this.validargs = validargs;
+ }
+
+ /**
+ * Defines the name of a property to be created from input. Behaviour is
+ * according to property task which means that existing properties
+ * cannot be overridden.
+ *
+ * @param addproperty Name for the property to be created from input
+ */
+ public void setAddproperty (final String addproperty) {
+ this.addproperty = addproperty;
+ }
+
+ /**
+ * Sets the Message which gets displayed to the user during the build run.
+ * @param message The message to be displayed.
+ */
+ public void setMessage (final String message) {
+ this.message = message;
+ messageAttribute = true;
+ }
+
+ /**
+ * Defines the default value of the property to be created from input.
+ * Property value will be set to default if not input is received.
+ *
+ * @param defaultvalue Default value for the property if no input
+ * is received
+ */
+ public void setDefaultvalue (final String defaultvalue) {
+ this.defaultvalue = defaultvalue;
+ }
+
+ /**
+ * Set a multiline message.
+ * @param msg The message to be displayed.
+ */
+ public void addText(final String msg) {
+ if (messageAttribute && "".equals(msg.trim())) {
+ return;
+ }
+ message += getProject().replaceProperties(msg);
+ }
+
+ /**
+ * No arg constructor.
+ */
+ public Input () {
+ }
+
+ /**
+ * Actual method executed by ant.
+ * @throws BuildException on error
+ */
+ @Override
+ public void execute () throws BuildException {
+ if (addproperty != null
+ && getProject().getProperty(addproperty) != null) {
+ log("skipping " + getTaskName() + " as property " + addproperty
+ + " has already been set.");
+ return;
+ }
+
+ InputRequest request = null;
+ if (validargs != null) {
+ final Vector<String> accept = StringUtils.split(validargs, ',');
+ request = new MultipleChoiceInputRequest(message, accept);
+ } else {
+ request = new InputRequest(message);
+ }
+ request.setDefaultValue(defaultvalue);
+
+ final InputHandler h = handler == null
+ ? getProject().getInputHandler()
+ : handler.getInputHandler();
+
+ h.handleInput(request);
+
+ String value = request.getInput();
+ if ((value == null || value.trim().length() == 0)
+ && defaultvalue != null) {
+ value = defaultvalue;
+ }
+ if (addproperty != null && value != null) {
+ getProject().setNewProperty(addproperty, value);
+ }
+ }
+
+ /**
+ * Create a nested handler element.
+ * @return a Handler for this Input task.
+ */
+ public Handler createHandler() {
+ if (handler != null) {
+ throw new BuildException(
+ "Cannot define > 1 nested input handler");
+ }
+ handler = new Handler();
+ return handler;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/JDBCTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/JDBCTask.java
new file mode 100644
index 00000000..2ca6e22a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/JDBCTask.java
@@ -0,0 +1,525 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.Driver;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Handles JDBC configuration needed by SQL type tasks.
+ * <p>
+ * The following example class prints the contents of the first column of each row in TableName.
+ *</p>
+ *<pre>
+package examples;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.JDBCTask;
+
+public class SQLExampleTask extends JDBCTask {
+
+ private String tableName;
+
+ public void execute() throws BuildException {
+ Connection conn = getConnection();
+ Statement stmt=null;
+ try {
+ if (tableName == null) {
+ throw new BuildException("TableName must be specified",location);
+ }
+ String sql = "SELECT * FROM "+tableName;
+ stmt= conn.createStatement();
+ ResultSet rs = stmt.executeQuery(sql);
+ while (rs.next()) {
+ log(rs.getObject(1).toString());
+ }
+ } catch (SQLException e) {
+
+ } finally {
+ if (stmt != null) {
+ try {stmt.close();}catch (SQLException ignore) {}
+ }
+ if (conn != null) {
+ try {conn.close();}catch (SQLException ignore) {}
+ }
+ }
+ }
+ public void setTableName(String tableName) {
+ this.tableName = tableName;
+ }
+
+}
+</pre>
+ *
+ * @since Ant 1.5
+ *
+ */
+public abstract class JDBCTask extends Task {
+ private static final int HASH_TABLE_SIZE = 3;
+
+ /**
+ * Used for caching loaders / driver. This is to avoid
+ * getting an OutOfMemoryError when calling this task
+ * multiple times in a row.
+ */
+ private static Hashtable<String, AntClassLoader> LOADER_MAP = new Hashtable<String, AntClassLoader>(HASH_TABLE_SIZE);
+
+ private boolean caching = true;
+
+ private Path classpath;
+
+ private AntClassLoader loader;
+
+ /**
+ * Autocommit flag. Default value is false
+ */
+ private boolean autocommit = false;
+
+ /**
+ * DB driver.
+ */
+ private String driver = null;
+
+ /**
+ * DB url.
+ */
+ private String url = null;
+
+ /**
+ * User name.
+ */
+ private String userId = null;
+
+ /**
+ * Password
+ */
+ private String password = null;
+
+ /**
+ * RDBMS Product needed for this SQL.
+ **/
+ private String rdbms = null;
+
+ /**
+ * RDBMS Version needed for this SQL.
+ **/
+ private String version = null;
+
+ /**
+ * whether the task fails when ant fails to connect to the database.
+ * @since Ant 1.8.0
+ */
+ private boolean failOnConnectionError = true;
+
+ /**
+ * Additional properties to put into the JDBC connection string.
+ *
+ * @since Ant 1.8.0
+ */
+ private List<Property> connectionProperties = new ArrayList<Property>();
+
+ /**
+ * Sets the classpath for loading the driver.
+ * @param classpath The classpath to set
+ */
+ public void setClasspath(Path classpath) {
+ this.classpath = classpath;
+ }
+
+ /**
+ * Caching loaders / driver. This is to avoid
+ * getting an OutOfMemoryError when calling this task
+ * multiple times in a row; default: true
+ * @param enable a <code>boolean</code> value
+ */
+ public void setCaching(boolean enable) {
+ caching = enable;
+ }
+
+ /**
+ * Add a path to the classpath for loading the driver.
+ * @return a path to be configured
+ */
+ public Path createClasspath() {
+ if (this.classpath == null) {
+ this.classpath = new Path(getProject());
+ }
+ return this.classpath.createPath();
+ }
+
+ /**
+ * Set the classpath for loading the driver
+ * using the classpath reference.
+ * @param r a reference to a classpath
+ */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * Class name of the JDBC driver; required.
+ * @param driver The driver to set
+ */
+ public void setDriver(String driver) {
+ this.driver = driver.trim();
+ }
+
+ /**
+ * Sets the database connection URL; required.
+ * @param url The url to set
+ */
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ /**
+ * Sets the password; required.
+ * @param password The password to set
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ /**
+ * Auto commit flag for database connection;
+ * optional, default false.
+ * @param autocommit The autocommit to set
+ */
+ public void setAutocommit(boolean autocommit) {
+ this.autocommit = autocommit;
+ }
+
+ /**
+ * Execute task only if the lower case product name
+ * of the DB matches this
+ * @param rdbms The rdbms to set
+ */
+ public void setRdbms(String rdbms) {
+ this.rdbms = rdbms;
+ }
+
+ /**
+ * Sets the version string, execute task only if
+ * rdbms version match; optional.
+ * @param version The version to set
+ */
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ /**
+ * whether the task should cause the build to fail if it cannot
+ * connect to the database.
+ * @since Ant 1.8.0
+ */
+ public void setFailOnConnectionError(boolean b) {
+ failOnConnectionError = b;
+ }
+
+ /**
+ * Verify we are connected to the correct RDBMS
+ * @param conn the jdbc connection
+ * @return true if we are connected to the correct RDBMS
+ */
+ protected boolean isValidRdbms(Connection conn) {
+ if (rdbms == null && version == null) {
+ return true;
+ }
+
+ try {
+ DatabaseMetaData dmd = conn.getMetaData();
+
+ if (rdbms != null) {
+ String theVendor = dmd.getDatabaseProductName().toLowerCase();
+
+ log("RDBMS = " + theVendor, Project.MSG_VERBOSE);
+ if (theVendor == null || theVendor.indexOf(rdbms) < 0) {
+ log("Not the required RDBMS: " + rdbms, Project.MSG_VERBOSE);
+ return false;
+ }
+ }
+
+ if (version != null) {
+ String theVersion = dmd.getDatabaseProductVersion().toLowerCase(Locale.ENGLISH);
+
+ log("Version = " + theVersion, Project.MSG_VERBOSE);
+ if (theVersion == null
+ || !(theVersion.startsWith(version)
+ || theVersion.indexOf(" " + version) >= 0)) {
+ log("Not the required version: \"" + version + "\"", Project.MSG_VERBOSE);
+ return false;
+ }
+ }
+ } catch (SQLException e) {
+ // Could not get the required information
+ log("Failed to obtain required RDBMS information", Project.MSG_ERR);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the cache of loaders and drivers.
+ * @return a hashtable
+ */
+ protected static Hashtable<String, AntClassLoader> getLoaderMap() {
+ return LOADER_MAP;
+ }
+
+ /**
+ * Get the classloader used to create a driver.
+ * @return the classloader
+ */
+ protected AntClassLoader getLoader() {
+ return loader;
+ }
+
+ /**
+ * Additional properties to put into the JDBC connection string.
+ *
+ * @since Ant 1.8.0
+ */
+ public void addConnectionProperty(Property var) {
+ connectionProperties.add(var);
+ }
+
+ /**
+ * Creates a new Connection as using the driver, url, userid and password
+ * specified.
+ *
+ * The calling method is responsible for closing the connection.
+ *
+ * @return Connection the newly created connection or null if the
+ * connection failed and failOnConnectionError is false.
+ * @throws BuildException if the UserId/Password/Url is not set or there
+ * is no suitable driver or the driver fails to load.
+ */
+ protected Connection getConnection() throws BuildException {
+ if (userId == null) {
+ throw new BuildException("UserId attribute must be set!", getLocation());
+ }
+ if (password == null) {
+ throw new BuildException("Password attribute must be set!", getLocation());
+ }
+ if (url == null) {
+ throw new BuildException("Url attribute must be set!", getLocation());
+ }
+ try {
+
+ log("connecting to " + getUrl(), Project.MSG_VERBOSE);
+ Properties info = new Properties();
+ info.put("user", getUserId());
+ info.put("password", getPassword());
+
+ for (Iterator<Property> props = connectionProperties.iterator();
+ props.hasNext();) {
+ Property p = props.next();
+ String name = p.getName();
+ String value = p.getValue();
+ if (name == null || value == null) {
+ log("Only name/value pairs are supported as connection"
+ + " properties.", Project.MSG_WARN);
+ } else {
+ log("Setting connection property " + name + " to " + value,
+ Project.MSG_VERBOSE);
+ info.put(name, value);
+ }
+ }
+
+ Connection conn = getDriver().connect(getUrl(), info);
+
+ if (conn == null) {
+ // Driver doesn't understand the URL
+ throw new SQLException("No suitable Driver for " + url);
+ }
+
+ conn.setAutoCommit(autocommit);
+ return conn;
+ } catch (SQLException e) {
+ // failed to connect
+ if (!failOnConnectionError) {
+ log("Failed to connect: " + e.getMessage(), Project.MSG_WARN);
+ return null;
+ } else {
+ throw new BuildException(e, getLocation());
+ }
+ }
+
+ }
+
+ /**
+ * Gets an instance of the required driver.
+ * Uses the ant class loader and the optionally the provided classpath.
+ * @return Driver
+ * @throws BuildException
+ */
+ private Driver getDriver() throws BuildException {
+ if (driver == null) {
+ throw new BuildException("Driver attribute must be set!", getLocation());
+ }
+
+ Driver driverInstance = null;
+ try {
+ Class<?> dc;
+ if (classpath != null) {
+ // check first that it is not already loaded otherwise
+ // consecutive runs seems to end into an OutOfMemoryError
+ // or it fails when there is a native library to load
+ // several times.
+ // this is far from being perfect but should work
+ // in most cases.
+ synchronized (LOADER_MAP) {
+ if (caching) {
+ loader = LOADER_MAP.get(driver);
+ }
+ if (loader == null) {
+ log("Loading " + driver
+ + " using AntClassLoader with classpath "
+ + classpath, Project.MSG_VERBOSE);
+ loader = getProject().createClassLoader(classpath);
+ if (caching) {
+ LOADER_MAP.put(driver, loader);
+ }
+ } else {
+ log("Loading " + driver
+ + " using a cached AntClassLoader.",
+ Project.MSG_VERBOSE);
+ }
+ }
+ dc = loader.loadClass(driver);
+ } else {
+ log("Loading " + driver + " using system loader.",
+ Project.MSG_VERBOSE);
+ dc = Class.forName(driver);
+ }
+ driverInstance = (Driver) dc.newInstance();
+ } catch (ClassNotFoundException e) {
+ throw new BuildException(
+ "Class Not Found: JDBC driver " + driver + " could not be loaded",
+ e,
+ getLocation());
+ } catch (IllegalAccessException e) {
+ throw new BuildException(
+ "Illegal Access: JDBC driver " + driver + " could not be loaded",
+ e,
+ getLocation());
+ } catch (InstantiationException e) {
+ throw new BuildException(
+ "Instantiation Exception: JDBC driver " + driver + " could not be loaded",
+ e,
+ getLocation());
+ }
+ return driverInstance;
+ }
+
+
+ /**
+ * Set the caching attribute.
+ * @param value a <code>boolean</code> value
+ */
+ public void isCaching(boolean value) {
+ caching = value;
+ }
+
+ /**
+ * Gets the classpath.
+ * @return Returns a Path
+ */
+ public Path getClasspath() {
+ return classpath;
+ }
+
+ /**
+ * Gets the autocommit.
+ * @return Returns a boolean
+ */
+ public boolean isAutocommit() {
+ return autocommit;
+ }
+
+ /**
+ * Gets the url.
+ * @return Returns a String
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * Gets the userId.
+ * @return Returns a String
+ */
+ public String getUserId() {
+ return userId;
+ }
+
+ /**
+ * Set the user name for the connection; required.
+ * @param userId The userId to set
+ */
+ public void setUserid(String userId) {
+ this.userId = userId;
+ }
+
+ /**
+ * Gets the password.
+ * @return Returns a String
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * Gets the rdbms.
+ * @return Returns a String
+ */
+ public String getRdbms() {
+ return rdbms;
+ }
+
+ /**
+ * Gets the version.
+ * @return Returns a String
+ */
+ public String getVersion() {
+ return version;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Jar.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Jar.java
new file mode 100644
index 00000000..c2c0f0ed
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Jar.java
@@ -0,0 +1,1238 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.util.Vector;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Manifest.Section;
+import org.apache.tools.ant.types.ArchiveFileSet;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.ZipFileSet;
+import org.apache.tools.ant.types.spi.Service;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.zip.JarMarker;
+import org.apache.tools.zip.ZipExtraField;
+import org.apache.tools.zip.ZipOutputStream;
+
+/**
+ * Creates a JAR archive.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ */
+public class Jar extends Zip {
+ /** The index file name. */
+ private static final String INDEX_NAME = "META-INF/INDEX.LIST";
+
+ /** The manifest file name. */
+ private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
+
+ /**
+ * List of all known SPI Services
+ */
+ private List<Service> serviceList = new ArrayList<Service>();
+
+ /** merged manifests added through addConfiguredManifest */
+ private Manifest configuredManifest;
+
+ /** shadow of the above if upToDate check alters the value */
+ private Manifest savedConfiguredManifest;
+
+ /** merged manifests added through filesets */
+ private Manifest filesetManifest;
+
+ /**
+ * Manifest of original archive, will be set to null if not in
+ * update mode.
+ */
+ private Manifest originalManifest;
+
+ /**
+ * whether to merge fileset manifests;
+ * value is true if filesetmanifest is 'merge' or 'mergewithoutmain'
+ */
+ private FilesetManifestConfig filesetManifestConfig;
+
+ /**
+ * whether to merge the main section of fileset manifests;
+ * value is true if filesetmanifest is 'merge'
+ */
+ private boolean mergeManifestsMain = true;
+
+ /** the manifest specified by the 'manifest' attribute **/
+ private Manifest manifest;
+
+ /** The encoding to use when reading in a manifest file */
+ private String manifestEncoding;
+
+ /**
+ * The file found from the 'manifest' attribute. This can be
+ * either the location of a manifest, or the name of a jar added
+ * through a fileset. If its the name of an added jar, the
+ * manifest is looked for in META-INF/MANIFEST.MF
+ */
+ private File manifestFile;
+
+ /** jar index is JDK 1.3+ only */
+ private boolean index = false;
+
+ /** Whether to index META-INF/ and its children */
+ private boolean indexMetaInf = false;
+
+ /**
+ * whether to really create the archive in createEmptyZip, will
+ * get set in getResourcesToAdd.
+ */
+ private boolean createEmpty = false;
+
+ /**
+ * Stores all files that are in the root of the archive (i.e. that
+ * have a name that doesn't contain a slash) so they can get
+ * listed in the index.
+ *
+ * Will not be filled unless the user has asked for an index.
+ *
+ * @since Ant 1.6
+ */
+ private Vector<String> rootEntries;
+
+ /**
+ * Path containing jars that shall be indexed in addition to this archive.
+ *
+ * @since Ant 1.6.2
+ */
+ private Path indexJars;
+
+ // CheckStyle:LineLength OFF - Link is too long.
+ /**
+ * Strict mode for checking rules of the JAR-Specification.
+ * @see http://java.sun.com/j2se/1.3/docs/guide/versioning/spec/VersioningSpecification.html#PackageVersioning
+ */
+ private StrictMode strict = new StrictMode("ignore");
+
+ // CheckStyle:LineLength ON
+
+ /**
+ * whether to merge Class-Path attributes.
+ */
+ private boolean mergeClassPaths = false;
+
+ /**
+ * whether to flatten Class-Path attributes into a single one.
+ */
+ private boolean flattenClassPaths = false;
+
+ /**
+ * Extra fields needed to make Solaris recognize the archive as a jar file.
+ *
+ * @since Ant 1.6.3
+ */
+ private static final ZipExtraField[] JAR_MARKER = new ZipExtraField[] {
+ JarMarker.getInstance()
+ };
+
+ /** constructor */
+ public Jar() {
+ super();
+ archiveType = "jar";
+ emptyBehavior = "create";
+ setEncoding("UTF8");
+ setZip64Mode(Zip64ModeAttribute.NEVER);
+ rootEntries = new Vector<String>();
+ }
+
+ /**
+ * Not used for jar files.
+ * @param we not used
+ * @ant.attribute ignore="true"
+ */
+ public void setWhenempty(WhenEmpty we) {
+ log("JARs are never empty, they contain at least a manifest file",
+ Project.MSG_WARN);
+ }
+
+ /**
+ * Indicates if a jar file should be created when it would only contain a
+ * manifest file.
+ * Possible values are: <code>fail</code> (throw an exception
+ * and halt the build); <code>skip</code> (do not create
+ * any archive, but issue a warning); <code>create</code>
+ * (make an archive with only a manifest file).
+ * Default is <code>create</code>;
+ * @param we a <code>WhenEmpty</code> enumerated value
+ */
+ public void setWhenmanifestonly(WhenEmpty we) {
+ emptyBehavior = we.getValue();
+ }
+
+ /**
+ * Activate the strict mode. When set to <i>true</i> a BuildException
+ * will be thrown if the Jar-Packaging specification was broken.
+ * @param strict New value of the strict mode.
+ * @since Ant 1.7.1
+ */
+ public void setStrict(StrictMode strict) {
+ this.strict = strict;
+ }
+
+ /**
+ * Set the destination file.
+ * @param jarFile the destination file
+ * @deprecated since 1.5.x.
+ * Use setDestFile(File) instead.
+ */
+ public void setJarfile(File jarFile) {
+ setDestFile(jarFile);
+ }
+
+ /**
+ * Set whether or not to create an index list for classes.
+ * This may speed up classloading in some cases.
+ * @param flag a <code>boolean</code> value
+ */
+ public void setIndex(boolean flag) {
+ index = flag;
+ }
+
+ /**
+ * Set whether or not to add META-INF and its children to the index.
+ *
+ * <p>Doesn't have any effect if index is false.</p>
+ *
+ * <p>Sun's jar implementation used to skip the META-INF directory
+ * and Ant followed that example. The behavior has been changed
+ * with Java 5. In order to avoid problems with Ant generated
+ * jars on Java 1.4 or earlier Ant will not include META-INF
+ * unless explicitly asked to.</p>
+ *
+ * @see <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4408526">
+ * jar -i omits service providers in index.list</a>
+ * @since Ant 1.8.0
+ * @param flag a <code>boolean</code> value, defaults to false
+ */
+ public void setIndexMetaInf(boolean flag) {
+ indexMetaInf = flag;
+ }
+
+ /**
+ * The character encoding to use in the manifest file.
+ *
+ * @param manifestEncoding the character encoding
+ */
+ public void setManifestEncoding(String manifestEncoding) {
+ this.manifestEncoding = manifestEncoding;
+ }
+
+ /**
+ * Allows the manifest for the archive file to be provided inline
+ * in the build file rather than in an external file.
+ *
+ * @param newManifest an embedded manifest element
+ * @throws ManifestException on error
+ */
+ public void addConfiguredManifest(Manifest newManifest)
+ throws ManifestException {
+ if (configuredManifest == null) {
+ configuredManifest = newManifest;
+ } else {
+ configuredManifest.merge(newManifest, false, mergeClassPaths);
+ }
+ savedConfiguredManifest = configuredManifest;
+ }
+
+ /**
+ * The manifest file to use. This can be either the location of a manifest,
+ * or the name of a jar added through a fileset. If its the name of an added
+ * jar, the task expects the manifest to be in the jar at META-INF/MANIFEST.MF.
+ *
+ * @param manifestFile the manifest file to use.
+ */
+ public void setManifest(File manifestFile) {
+ if (!manifestFile.exists()) {
+ throw new BuildException("Manifest file: " + manifestFile
+ + " does not exist.", getLocation());
+ }
+
+ this.manifestFile = manifestFile;
+ }
+
+ private Manifest getManifest(File manifestFile) {
+
+ Manifest newManifest = null;
+ FileInputStream fis = null;
+ InputStreamReader isr = null;
+ try {
+ fis = new FileInputStream(manifestFile);
+ if (manifestEncoding == null) {
+ isr = new InputStreamReader(fis);
+ } else {
+ isr = new InputStreamReader(fis, manifestEncoding);
+ }
+ newManifest = getManifest(isr);
+ } catch (UnsupportedEncodingException e) {
+ throw new BuildException("Unsupported encoding while reading manifest: "
+ + e.getMessage(), e);
+ } catch (IOException e) {
+ throw new BuildException("Unable to read manifest file: "
+ + manifestFile
+ + " (" + e.getMessage() + ")", e);
+ } finally {
+ FileUtils.close(isr);
+ }
+ return newManifest;
+ }
+
+ /**
+ * @return null if jarFile doesn't contain a manifest, the
+ * manifest otherwise.
+ * @since Ant 1.5.2
+ */
+ private Manifest getManifestFromJar(File jarFile) throws IOException {
+ ZipFile zf = null;
+ try {
+ zf = new ZipFile(jarFile);
+
+ // must not use getEntry as "well behaving" applications
+ // must accept the manifest in any capitalization
+ Enumeration<? extends ZipEntry> e = zf.entries();
+ while (e.hasMoreElements()) {
+ ZipEntry ze = e.nextElement();
+ if (ze.getName().equalsIgnoreCase(MANIFEST_NAME)) {
+ InputStreamReader isr =
+ new InputStreamReader(zf.getInputStream(ze), "UTF-8");
+ return getManifest(isr);
+ }
+ }
+ return null;
+ } finally {
+ if (zf != null) {
+ try {
+ zf.close();
+ } catch (IOException e) {
+ // TODO - log an error? throw an exception?
+ }
+ }
+ }
+ }
+
+ private Manifest getManifest(Reader r) {
+
+ Manifest newManifest = null;
+ try {
+ newManifest = new Manifest(r);
+ } catch (ManifestException e) {
+ log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
+ throw new BuildException("Invalid Manifest: " + manifestFile,
+ e, getLocation());
+ } catch (IOException e) {
+ throw new BuildException("Unable to read manifest file"
+ + " (" + e.getMessage() + ")", e);
+ }
+ return newManifest;
+ }
+
+ private boolean jarHasIndex(File jarFile) throws IOException {
+ ZipFile zf = null;
+ try {
+ zf = new ZipFile(jarFile);
+ Enumeration<? extends ZipEntry> e = zf.entries();
+ while (e.hasMoreElements()) {
+ ZipEntry ze = e.nextElement();
+ if (ze.getName().equalsIgnoreCase(INDEX_NAME)) {
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ if (zf != null) {
+ try {
+ zf.close();
+ } catch (IOException e) {
+ // TODO - log an error? throw an exception?
+ }
+ }
+ }
+ }
+
+ /**
+ * Behavior when a Manifest is found in a zipfileset or zipgroupfileset file.
+ * Valid values are "skip", "merge", and "mergewithoutmain".
+ * "merge" will merge all of manifests together, and merge this into any
+ * other specified manifests.
+ * "mergewithoutmain" merges everything but the Main section of the manifests.
+ * Default value is "skip".
+ *
+ * Note: if this attribute's value is not "skip", the created jar will not
+ * be readable by using java.util.jar.JarInputStream
+ *
+ * @param config setting for found manifest behavior.
+ */
+ public void setFilesetmanifest(FilesetManifestConfig config) {
+ filesetManifestConfig = config;
+ mergeManifestsMain = "merge".equals(config.getValue());
+
+ if (filesetManifestConfig != null
+ && !filesetManifestConfig.getValue().equals("skip")) {
+
+ doubleFilePass = true;
+ }
+ }
+
+ /**
+ * Adds a zipfileset to include in the META-INF directory.
+ *
+ * @param fs zipfileset to add
+ */
+ public void addMetainf(ZipFileSet fs) {
+ // We just set the prefix for this fileset, and pass it up.
+ fs.setPrefix("META-INF/");
+ super.addFileset(fs);
+ }
+
+ /**
+ * Add a path to index jars.
+ * @param p a path
+ * @since Ant 1.6.2
+ */
+ public void addConfiguredIndexJars(Path p) {
+ if (indexJars == null) {
+ indexJars = new Path(getProject());
+ }
+ indexJars.append(p);
+ }
+
+ /**
+ * A nested SPI service element.
+ * @param service the nested element.
+ * @since Ant 1.7
+ */
+ public void addConfiguredService(Service service) {
+ // Check if the service is configured correctly
+ service.check();
+ serviceList.add(service);
+ }
+
+ /**
+ * Write SPI Information to JAR
+ */
+ private void writeServices(ZipOutputStream zOut) throws IOException {
+ for (Service service : serviceList) {
+ InputStream is = null;
+ try {
+ is = service.getAsStream();
+ //stolen from writeManifest
+ super.zipFile(is, zOut,
+ "META-INF/services/" + service.getType(),
+ System.currentTimeMillis(), null,
+ ZipFileSet.DEFAULT_FILE_MODE);
+ } finally {
+ // technically this is unnecessary since
+ // Service.getAsStream returns a ByteArrayInputStream
+ // and not closing it wouldn't do any harm.
+ FileUtils.close(is);
+ }
+ }
+ }
+
+ /**
+ * Whether to merge Class-Path attributes.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setMergeClassPathAttributes(boolean b) {
+ mergeClassPaths = b;
+ }
+
+ /**
+ * Whether to flatten multi-valued attributes (i.e. Class-Path)
+ * into a single one.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setFlattenAttributes(boolean b) {
+ flattenClassPaths = b;
+ }
+
+ /**
+ * Initialize the zip output stream.
+ * @param zOut the zip output stream
+ * @throws IOException on I/O errors
+ * @throws BuildException on other errors
+ */
+ protected void initZipOutputStream(ZipOutputStream zOut)
+ throws IOException, BuildException {
+
+ if (!skipWriting) {
+ Manifest jarManifest = createManifest();
+ writeManifest(zOut, jarManifest);
+ writeServices(zOut);
+ }
+ }
+
+ private Manifest createManifest()
+ throws BuildException {
+ try {
+ if (manifest == null) {
+ if (manifestFile != null) {
+ // if we haven't got the manifest yet, attempt to
+ // get it now and have manifest be the final merge
+ manifest = getManifest(manifestFile);
+ }
+ }
+
+ // fileset manifest must come even before the default
+ // manifest if mergewithoutmain is selected and there is
+ // no explicit manifest specified - otherwise the Main
+ // section of the fileset manifest is still merged to the
+ // final manifest.
+ boolean mergeFileSetFirst = !mergeManifestsMain
+ && filesetManifest != null
+ && configuredManifest == null && manifest == null;
+
+ Manifest finalManifest;
+ if (mergeFileSetFirst) {
+ finalManifest = new Manifest();
+ finalManifest.merge(filesetManifest, false, mergeClassPaths);
+ finalManifest.merge(Manifest.getDefaultManifest(),
+ true, mergeClassPaths);
+ } else {
+ finalManifest = Manifest.getDefaultManifest();
+ }
+
+ /*
+ * Precedence: manifestFile wins over inline manifest,
+ * over manifests read from the filesets over the original
+ * manifest.
+ *
+ * merge with null argument is a no-op
+ */
+
+ if (isInUpdateMode()) {
+ finalManifest.merge(originalManifest, false, mergeClassPaths);
+ }
+ if (!mergeFileSetFirst) {
+ finalManifest.merge(filesetManifest, false, mergeClassPaths);
+ }
+ finalManifest.merge(configuredManifest, !mergeManifestsMain,
+ mergeClassPaths);
+ finalManifest.merge(manifest, !mergeManifestsMain,
+ mergeClassPaths);
+
+ return finalManifest;
+
+ } catch (ManifestException e) {
+ log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
+ throw new BuildException("Invalid Manifest", e, getLocation());
+ }
+ }
+
+ private void writeManifest(ZipOutputStream zOut, Manifest manifest)
+ throws IOException {
+ for (Enumeration<String> e = manifest.getWarnings();
+ e.hasMoreElements();) {
+ log("Manifest warning: " + e.nextElement(),
+ Project.MSG_WARN);
+ }
+
+ zipDir((Resource) null, zOut, "META-INF/", ZipFileSet.DEFAULT_DIR_MODE,
+ JAR_MARKER);
+ // time to write the manifest
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ OutputStreamWriter osw = new OutputStreamWriter(baos, Manifest.JAR_ENCODING);
+ PrintWriter writer = new PrintWriter(osw);
+ manifest.write(writer, flattenClassPaths);
+ if (writer.checkError()) {
+ throw new IOException("Encountered an error writing the manifest");
+ }
+ writer.close();
+
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(baos.toByteArray());
+ try {
+ super.zipFile(bais, zOut, MANIFEST_NAME,
+ System.currentTimeMillis(), null,
+ ZipFileSet.DEFAULT_FILE_MODE);
+ } finally {
+ // not really required
+ FileUtils.close(bais);
+ }
+ super.initZipOutputStream(zOut);
+ }
+
+ /**
+ * Finalize the zip output stream.
+ * This creates an index list if the index attribute is true.
+ * @param zOut the zip output stream
+ * @throws IOException on I/O errors
+ * @throws BuildException on other errors
+ */
+ protected void finalizeZipOutputStream(ZipOutputStream zOut)
+ throws IOException, BuildException {
+
+ if (index) {
+ createIndexList(zOut);
+ }
+ }
+
+ /**
+ * Create the index list to speed up classloading.
+ * This is a JDK 1.3+ specific feature and is enabled by default. See
+ * <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#JAR%20Index">
+ * the JAR index specification</a> for more details.
+ *
+ * @param zOut the zip stream representing the jar being built.
+ * @throws IOException thrown if there is an error while creating the
+ * index and adding it to the zip stream.
+ */
+ private void createIndexList(ZipOutputStream zOut) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ // encoding must be UTF8 as specified in the specs.
+ PrintWriter writer = new PrintWriter(new OutputStreamWriter(baos,
+ "UTF8"));
+
+ // version-info blankline
+ writer.println("JarIndex-Version: 1.0");
+ writer.println();
+
+ // header newline
+ writer.println(zipFile.getName());
+
+ writeIndexLikeList(new ArrayList<String>(addedDirs.keySet()),
+ rootEntries, writer);
+ writer.println();
+
+ if (indexJars != null) {
+ Manifest mf = createManifest();
+ Manifest.Attribute classpath =
+ mf.getMainSection().getAttribute(Manifest.ATTRIBUTE_CLASSPATH);
+ String[] cpEntries = null;
+ if (classpath != null && classpath.getValue() != null) {
+ StringTokenizer tok = new StringTokenizer(classpath.getValue(),
+ " ");
+ cpEntries = new String[tok.countTokens()];
+ int c = 0;
+ while (tok.hasMoreTokens()) {
+ cpEntries[c++] = tok.nextToken();
+ }
+ }
+ String[] indexJarEntries = indexJars.list();
+ for (int i = 0; i < indexJarEntries.length; i++) {
+ String name = findJarName(indexJarEntries[i], cpEntries);
+ if (name != null) {
+ ArrayList<String> dirs = new ArrayList<String>();
+ ArrayList<String> files = new ArrayList<String>();
+ grabFilesAndDirs(indexJarEntries[i], dirs, files);
+ if (dirs.size() + files.size() > 0) {
+ writer.println(name);
+ writeIndexLikeList(dirs, files, writer);
+ writer.println();
+ }
+ }
+ }
+ }
+
+ if (writer.checkError()) {
+ throw new IOException("Encountered an error writing jar index");
+ }
+ writer.close();
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(baos.toByteArray());
+ try {
+ super.zipFile(bais, zOut, INDEX_NAME, System.currentTimeMillis(),
+ null, ZipFileSet.DEFAULT_FILE_MODE);
+ } finally {
+ // not really required
+ FileUtils.close(bais);
+ }
+ }
+
+ /**
+ * Overridden from Zip class to deal with manifests and index lists.
+ * @param is the stream to read data for the entry from. The
+ * caller of the method is responsible for closing the stream.
+ * @param zOut the zip output stream
+ * @param vPath the name this entry shall have in the archive
+ * @param lastModified last modification time for the entry.
+ * @param fromArchive the original archive we are copying this
+ * entry from, will be null if we are not copying from an archive.
+ * @param mode the Unix permissions to set.
+ * @throws IOException on error
+ */
+ protected void zipFile(InputStream is, ZipOutputStream zOut, String vPath,
+ long lastModified, File fromArchive, int mode)
+ throws IOException {
+ if (MANIFEST_NAME.equalsIgnoreCase(vPath)) {
+ if (isFirstPass()) {
+ filesetManifest(fromArchive, is);
+ }
+ } else if (INDEX_NAME.equalsIgnoreCase(vPath) && index) {
+ logWhenWriting("Warning: selected " + archiveType
+ + " files include a " + INDEX_NAME + " which will"
+ + " be replaced by a newly generated one.",
+ Project.MSG_WARN);
+ } else {
+ if (index && vPath.indexOf("/") == -1) {
+ rootEntries.addElement(vPath);
+ }
+ super.zipFile(is, zOut, vPath, lastModified, fromArchive, mode);
+ }
+ }
+
+ private void filesetManifest(File file, InputStream is) throws IOException {
+ if (manifestFile != null && manifestFile.equals(file)) {
+ // If this is the same name specified in 'manifest', this
+ // is the manifest to use
+ log("Found manifest " + file, Project.MSG_VERBOSE);
+ try {
+ if (is != null) {
+ InputStreamReader isr;
+ if (manifestEncoding == null) {
+ isr = new InputStreamReader(is);
+ } else {
+ isr = new InputStreamReader(is, manifestEncoding);
+ }
+ manifest = getManifest(isr);
+ } else {
+ manifest = getManifest(file);
+ }
+ } catch (UnsupportedEncodingException e) {
+ throw new BuildException("Unsupported encoding while reading "
+ + "manifest: " + e.getMessage(), e);
+ }
+ } else if (filesetManifestConfig != null
+ && !filesetManifestConfig.getValue().equals("skip")) {
+ // we add this to our group of fileset manifests
+ logWhenWriting("Found manifest to merge in file " + file,
+ Project.MSG_VERBOSE);
+
+ try {
+ Manifest newManifest = null;
+ if (is != null) {
+ InputStreamReader isr;
+ if (manifestEncoding == null) {
+ isr = new InputStreamReader(is);
+ } else {
+ isr = new InputStreamReader(is, manifestEncoding);
+ }
+ newManifest = getManifest(isr);
+ } else {
+ newManifest = getManifest(file);
+ }
+
+ if (filesetManifest == null) {
+ filesetManifest = newManifest;
+ } else {
+ filesetManifest.merge(newManifest, false, mergeClassPaths);
+ }
+ } catch (UnsupportedEncodingException e) {
+ throw new BuildException("Unsupported encoding while reading "
+ + "manifest: " + e.getMessage(), e);
+ } catch (ManifestException e) {
+ log("Manifest in file " + file + " is invalid: "
+ + e.getMessage(), Project.MSG_ERR);
+ throw new BuildException("Invalid Manifest", e, getLocation());
+ }
+ } else {
+ // assuming 'skip' otherwise
+ // don't warn if skip has been requested explicitly, warn if user
+ // didn't set the attribute
+
+ // Hide warning also as it makes no sense since
+ // the filesetmanifest attribute itself has been
+ // hidden
+
+ //int logLevel = filesetManifestConfig == null ?
+ // Project.MSG_WARN : Project.MSG_VERBOSE;
+ //log("File " + file
+ // + " includes a META-INF/MANIFEST.MF which will be ignored. "
+ // + "To include this file, set filesetManifest to a value other "
+ // + "than 'skip'.", logLevel);
+ }
+ }
+
+ /**
+ * Collect the resources that are newer than the corresponding
+ * entries (or missing) in the original archive.
+ *
+ * <p>If we are going to recreate the archive instead of updating
+ * it, all resources should be considered as new, if a single one
+ * is. Because of this, subclasses overriding this method must
+ * call <code>super.getResourcesToAdd</code> and indicate with the
+ * third arg if they already know that the archive is
+ * out-of-date.</p>
+ *
+ * @param rcs The resource collections to grab resources from
+ * @param zipFile intended archive file (may or may not exist)
+ * @param needsUpdate whether we already know that the archive is
+ * out-of-date. Subclasses overriding this method are supposed to
+ * set this value correctly in their call to
+ * super.getResourcesToAdd.
+ * @return an array of resources to add for each fileset passed in as well
+ * as a flag that indicates whether the archive is uptodate.
+ *
+ * @exception BuildException if it likes
+ */
+ protected ArchiveState getResourcesToAdd(ResourceCollection[] rcs,
+ File zipFile,
+ boolean needsUpdate)
+ throws BuildException {
+
+ if (skipWriting) {
+ // this pass is only there to construct the merged
+ // manifest this means we claim an update was needed and
+ // only include the manifests, skipping any uptodate
+ // checks here deferring them for the second run
+ Resource[][] manifests = grabManifests(rcs);
+ int count = 0;
+ for (int i = 0; i < manifests.length; i++) {
+ count += manifests[i].length;
+ }
+ log("found a total of " + count + " manifests in "
+ + manifests.length + " resource collections",
+ Project.MSG_VERBOSE);
+ return new ArchiveState(true, manifests);
+ }
+
+ // need to handle manifest as a special check
+ if (zipFile.exists()) {
+ // if it doesn't exist, it will get created anyway, don't
+ // bother with any up-to-date checks.
+
+ try {
+ originalManifest = getManifestFromJar(zipFile);
+ if (originalManifest == null) {
+ log("Updating jar since the current jar has"
+ + " no manifest", Project.MSG_VERBOSE);
+ needsUpdate = true;
+ } else {
+ Manifest mf = createManifest();
+ if (!mf.equals(originalManifest)) {
+ log("Updating jar since jar manifest has"
+ + " changed", Project.MSG_VERBOSE);
+ needsUpdate = true;
+ }
+ }
+ } catch (Throwable t) {
+ log("error while reading original manifest in file: "
+ + zipFile.toString() + " due to " + t.getMessage(),
+ Project.MSG_WARN);
+ needsUpdate = true;
+ }
+
+ } else {
+ // no existing archive
+ needsUpdate = true;
+ }
+
+ createEmpty = needsUpdate;
+ if (!needsUpdate && index) {
+ try {
+ needsUpdate = !jarHasIndex(zipFile);
+ } catch (IOException e) {
+ //if we couldn't read it, we might as well recreate it?
+ needsUpdate = true;
+ }
+ }
+ return super.getResourcesToAdd(rcs, zipFile, needsUpdate);
+ }
+
+ /**
+ * Create an empty jar file.
+ * @param zipFile the file to create
+ * @return true for historic reasons
+ * @throws BuildException on error
+ */
+ protected boolean createEmptyZip(File zipFile) throws BuildException {
+ if (!createEmpty) {
+ return true;
+ }
+
+ if (emptyBehavior.equals("skip")) {
+ if (!skipWriting) {
+ log("Warning: skipping " + archiveType + " archive "
+ + zipFile + " because no files were included.",
+ Project.MSG_WARN);
+ }
+ return true;
+ } else if (emptyBehavior.equals("fail")) {
+ throw new BuildException("Cannot create " + archiveType
+ + " archive " + zipFile
+ + ": no files were included.",
+ getLocation());
+ }
+
+ ZipOutputStream zOut = null;
+ try {
+ if (!skipWriting) {
+ log("Building MANIFEST-only jar: "
+ + getDestFile().getAbsolutePath());
+ }
+ zOut = new ZipOutputStream(getDestFile());
+
+ zOut.setEncoding(getEncoding());
+ if (isCompress()) {
+ zOut.setMethod(ZipOutputStream.DEFLATED);
+ } else {
+ zOut.setMethod(ZipOutputStream.STORED);
+ }
+ initZipOutputStream(zOut);
+ finalizeZipOutputStream(zOut);
+ } catch (IOException ioe) {
+ throw new BuildException("Could not create almost empty JAR archive"
+ + " (" + ioe.getMessage() + ")", ioe,
+ getLocation());
+ } finally {
+ // Close the output stream.
+ FileUtils.close(zOut);
+ createEmpty = false;
+ }
+ return true;
+ }
+
+ /**
+ * Make sure we don't think we already have a MANIFEST next time this task
+ * gets executed.
+ *
+ * @see Zip#cleanUp
+ */
+ protected void cleanUp() {
+ super.cleanUp();
+ checkJarSpec();
+
+ // we want to save this info if we are going to make another pass
+ if (!doubleFilePass || !skipWriting) {
+ manifest = null;
+ configuredManifest = savedConfiguredManifest;
+ filesetManifest = null;
+ originalManifest = null;
+ }
+ rootEntries.removeAllElements();
+ }
+
+ // CheckStyle:LineLength OFF - Link is too long.
+ /**
+ * Check against packaging spec
+ * @see "http://java.sun.com/j2se/1.3/docs/guide/versioning/spec/VersioningSpecification.html#PackageVersioning"
+ */
+ // CheckStyle:LineLength ON
+ private void checkJarSpec() {
+ String br = System.getProperty("line.separator");
+ StringBuffer message = new StringBuffer();
+ Section mainSection = (configuredManifest == null)
+ ? null
+ : configuredManifest.getMainSection();
+
+ if (mainSection == null) {
+ message.append("No Implementation-Title set.");
+ message.append("No Implementation-Version set.");
+ message.append("No Implementation-Vendor set.");
+ } else {
+ if (mainSection.getAttribute("Implementation-Title") == null) {
+ message.append("No Implementation-Title set.");
+ }
+ if (mainSection.getAttribute("Implementation-Version") == null) {
+ message.append("No Implementation-Version set.");
+ }
+ if (mainSection.getAttribute("Implementation-Vendor") == null) {
+ message.append("No Implementation-Vendor set.");
+ }
+ }
+
+ if (message.length() > 0) {
+ message.append(br);
+ message.append("Location: ").append(getLocation());
+ message.append(br);
+ if (strict.getValue().equalsIgnoreCase("fail")) {
+ throw new BuildException(message.toString(), getLocation());
+ } else {
+ logWhenWriting(message.toString(), strict.getLogLevel());
+ }
+ }
+ }
+
+ /**
+ * reset to default values.
+ *
+ * @see Zip#reset
+ *
+ * @since 1.44, Ant 1.5
+ */
+ public void reset() {
+ super.reset();
+ emptyBehavior = "create";
+ configuredManifest = null;
+ filesetManifestConfig = null;
+ mergeManifestsMain = false;
+ manifestFile = null;
+ index = false;
+ }
+
+ /**
+ * The manifest config enumerated type.
+ */
+ public static class FilesetManifestConfig extends EnumeratedAttribute {
+ /**
+ * Get the list of valid strings.
+ * @return the list of values - "skip", "merge" and "mergewithoutmain"
+ */
+ public String[] getValues() {
+ return new String[] {"skip", "merge", "mergewithoutmain"};
+ }
+ }
+
+ /**
+ * Writes the directory entries from the first and the filenames
+ * from the second list to the given writer, one entry per line.
+ *
+ * @param dirs a list of directories
+ * @param files a list of files
+ * @param writer the writer to write to
+ * @throws IOException on error
+ * @since Ant 1.6.2
+ */
+ protected final void writeIndexLikeList(List<String> dirs, List<String> files,
+ PrintWriter writer)
+ throws IOException {
+ // JarIndex is sorting the directories by ascending order.
+ // it has no value but cosmetic since it will be read into a
+ // hashtable by the classloader, but we'll do so anyway.
+ Collections.sort(dirs);
+ Collections.sort(files);
+ for (String dir : dirs) {
+ // try to be smart, not to be fooled by a weird directory name
+ dir = dir.replace('\\', '/');
+ if (dir.startsWith("./")) {
+ dir = dir.substring(2);
+ }
+ while (dir.startsWith("/")) {
+ dir = dir.substring(1);
+ }
+ int pos = dir.lastIndexOf('/');
+ if (pos != -1) {
+ dir = dir.substring(0, pos);
+ }
+
+ // looks like nothing from META-INF should be added
+ // and the check is not case insensitive.
+ // see sun.misc.JarIndex
+ // see also
+ // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4408526
+ if (!indexMetaInf && dir.startsWith("META-INF")) {
+ continue;
+ }
+ // name newline
+ writer.println(dir);
+ }
+
+ for (String file : files) {
+ writer.println(file);
+ }
+ }
+
+ /**
+ * try to guess the name of the given file.
+ *
+ * <p>If this jar has a classpath attribute in its manifest, we
+ * can assume that it will only require an index of jars listed
+ * there. try to find which classpath entry is most likely the
+ * one the given file name points to.</p>
+ *
+ * <p>In the absence of a classpath attribute, assume the other
+ * files will be placed inside the same directory as this jar and
+ * use their basename.</p>
+ *
+ * <p>if there is a classpath and the given file doesn't match any
+ * of its entries, return null.</p>
+ *
+ * @param fileName the name to look for
+ * @param classpath the classpath to look in (may be null)
+ * @return the matching entry, or null if the file is not found
+ * @since Ant 1.6.2
+ */
+ protected static String findJarName(String fileName,
+ String[] classpath) {
+ if (classpath == null) {
+ return (new File(fileName)).getName();
+ }
+ fileName = fileName.replace(File.separatorChar, '/');
+ TreeMap<String, String> matches = new TreeMap<String, String>(new Comparator<Object>() {
+ // longest match comes first
+ public int compare(Object o1, Object o2) {
+ if (o1 instanceof String && o2 instanceof String) {
+ return ((String) o2).length()
+ - ((String) o1).length();
+ }
+ return 0;
+ }
+ });
+
+ for (int i = 0; i < classpath.length; i++) {
+ if (fileName.endsWith(classpath[i])) {
+ matches.put(classpath[i], classpath[i]);
+ } else {
+ int slash = classpath[i].indexOf("/");
+ String candidate = classpath[i];
+ while (slash > -1) {
+ candidate = candidate.substring(slash + 1);
+ if (fileName.endsWith(candidate)) {
+ matches.put(candidate, classpath[i]);
+ break;
+ }
+ slash = candidate.indexOf("/");
+ }
+ }
+ }
+
+ return matches.size() == 0
+ ? null : (String) matches.get(matches.firstKey());
+ }
+
+ /**
+ * Grab lists of all root-level files and all directories
+ * contained in the given archive.
+ * @param file the zip file to examine
+ * @param dirs where to place the directories found
+ * @param files where to place the files found
+ * @since Ant 1.7
+ * @throws IOException on error
+ */
+ protected static void grabFilesAndDirs(String file, List<String> dirs,
+ List<String> files)
+ throws IOException {
+ org.apache.tools.zip.ZipFile zf = null;
+ try {
+ zf = new org.apache.tools.zip.ZipFile(file, "utf-8");
+ Enumeration<org.apache.tools.zip.ZipEntry> entries = zf.getEntries();
+ HashSet<String> dirSet = new HashSet<String>();
+ while (entries.hasMoreElements()) {
+ org.apache.tools.zip.ZipEntry ze =
+ entries.nextElement();
+ String name = ze.getName();
+ if (ze.isDirectory()) {
+ dirSet.add(name);
+ } else if (name.indexOf("/") == -1) {
+ files.add(name);
+ } else {
+ // a file, not in the root
+ // since the jar may be one without directory
+ // entries, add the parent dir of this file as
+ // well.
+ dirSet.add(name.substring(0, name.lastIndexOf("/") + 1));
+ }
+ }
+ dirs.addAll(dirSet);
+ } finally {
+ if (zf != null) {
+ zf.close();
+ }
+ }
+ }
+
+ private Resource[][] grabManifests(ResourceCollection[] rcs) {
+ Resource[][] manifests = new Resource[rcs.length][];
+ for (int i = 0; i < rcs.length; i++) {
+ Resource[][] resources = null;
+ if (rcs[i] instanceof FileSet) {
+ resources = grabResources(new FileSet[] {(FileSet) rcs[i]});
+ } else {
+ resources = grabNonFileSetResources(new ResourceCollection[] {
+ rcs[i]
+ });
+ }
+ for (int j = 0; j < resources[0].length; j++) {
+ String name = resources[0][j].getName().replace('\\', '/');
+ if (rcs[i] instanceof ArchiveFileSet) {
+ ArchiveFileSet afs = (ArchiveFileSet) rcs[i];
+ if (!"".equals(afs.getFullpath(getProject()))) {
+ name = afs.getFullpath(getProject());
+ } else if (!"".equals(afs.getPrefix(getProject()))) {
+ String prefix = afs.getPrefix(getProject());
+ if (!prefix.endsWith("/") && !prefix.endsWith("\\")) {
+ prefix += "/";
+ }
+ name = prefix + name;
+ }
+ }
+ if (name.equalsIgnoreCase(MANIFEST_NAME)) {
+ manifests[i] = new Resource[] {resources[0][j]};
+ break;
+ }
+ }
+ if (manifests[i] == null) {
+ manifests[i] = new Resource[0];
+ }
+ }
+ return manifests;
+ }
+
+ /** The strict enumerated type. */
+ public static class StrictMode extends EnumeratedAttribute {
+ /** Public no arg constructor. */
+ public StrictMode() {
+ }
+ /**
+ * Constructor with an arg.
+ * @param value the enumerated value as a string.
+ */
+ public StrictMode(String value) {
+ setValue(value);
+ }
+ /**
+ * Get List of valid strings.
+ * @return the list of values.
+ */
+ public String[] getValues() {
+ return new String[]{"fail", "warn", "ignore"};
+ }
+ /**
+ * @return The log level according to the strict mode.
+ */
+ public int getLogLevel() {
+ return (getValue().equals("ignore")) ? Project.MSG_VERBOSE : Project.MSG_WARN;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Java.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Java.java
new file mode 100644
index 00000000..5e065bc3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Java.java
@@ -0,0 +1,965 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ExitException;
+import org.apache.tools.ant.ExitStatusException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Assertions;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Permissions;
+import org.apache.tools.ant.types.PropertySet;
+import org.apache.tools.ant.types.RedirectorElement;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.KeepAliveInputStream;
+
+/**
+ * Launcher for Java applications. Allows use of
+ * the same JVM for the called application thus resulting in much
+ * faster operation.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="java"
+ */
+public class Java extends Task {
+
+ private CommandlineJava cmdl = new CommandlineJava();
+ private Environment env = new Environment();
+ private boolean fork = false;
+ private boolean newEnvironment = false;
+ private File dir = null;
+ private boolean failOnError = false;
+ private Long timeout = null;
+
+ //include locally for screening purposes
+ private String inputString;
+ private File input;
+ private File output;
+ private File error;
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected Redirector redirector = new Redirector(this);
+ protected RedirectorElement redirectorElement;
+ // CheckStyle:VisibilityModifier ON
+
+ private String resultProperty;
+ private Permissions perm = null;
+
+ private boolean spawn = false;
+ private boolean incompatibleWithSpawn = false;
+
+ private static final String TIMEOUT_MESSAGE =
+ "Timeout: killed the sub-process";
+
+ /**
+ * Normal constructor
+ */
+ public Java() {
+ }
+
+ /**
+ * create a bound task
+ * @param owner owner
+ */
+ public Java(Task owner) {
+ bindToOwner(owner);
+ }
+
+ /**
+ * Do the execution.
+ * @throws BuildException if failOnError is set to true and the application
+ * returns a nonzero result code.
+ */
+ public void execute() throws BuildException {
+ File savedDir = dir;
+ Permissions savedPermissions = perm;
+
+ int err = -1;
+ try {
+ checkConfiguration();
+ err = executeJava();
+ if (err != 0) {
+ if (failOnError) {
+ throw new ExitStatusException("Java returned: " + err,
+ err,
+ getLocation());
+ } else {
+ log("Java Result: " + err, Project.MSG_ERR);
+ }
+ }
+ maybeSetResultPropertyValue(err);
+ } finally {
+ dir = savedDir;
+ perm = savedPermissions;
+ }
+ }
+
+ /**
+ * Do the execution and return a return code.
+ *
+ * @return the return code from the execute java class if it was
+ * executed in a separate VM (fork = "yes") or a security manager was
+ * installed that prohibits ExitVM (default).
+ *
+ * @throws BuildException if required parameters are missing.
+ */
+ public int executeJava() throws BuildException {
+ return executeJava(getCommandLine());
+ }
+
+ /**
+ * Check configuration.
+ * @throws BuildException if required parameters are missing.
+ */
+ protected void checkConfiguration() throws BuildException {
+ String classname = getCommandLine().getClassname();
+ if (classname == null && getCommandLine().getJar() == null) {
+ throw new BuildException("Classname must not be null.");
+ }
+ if (!fork && getCommandLine().getJar() != null) {
+ throw new BuildException("Cannot execute a jar in non-forked mode."
+ + " Please set fork='true'. ");
+ }
+ if (spawn && !fork) {
+ throw new BuildException("Cannot spawn a java process in non-forked mode."
+ + " Please set fork='true'. ");
+ }
+ if (getCommandLine().getClasspath() != null
+ && getCommandLine().getJar() != null) {
+ log("When using 'jar' attribute classpath-settings are ignored. "
+ + "See the manual for more information.", Project.MSG_VERBOSE);
+ }
+ if (spawn && incompatibleWithSpawn) {
+ getProject().log("spawn does not allow attributes related to input, "
+ + "output, error, result", Project.MSG_ERR);
+ getProject().log("spawn also does not allow timeout", Project.MSG_ERR);
+ getProject().log("finally, spawn is not compatible "
+ + "with a nested I/O <redirector>", Project.MSG_ERR);
+ throw new BuildException("You have used an attribute "
+ + "or nested element which is not compatible with spawn");
+ }
+ if (getCommandLine().getAssertions() != null && !fork) {
+ log("Assertion statements are currently ignored in non-forked mode");
+ }
+ if (fork) {
+ if (perm != null) {
+ log("Permissions can not be set this way in forked mode.", Project.MSG_WARN);
+ }
+ log(getCommandLine().describeCommand(), Project.MSG_VERBOSE);
+ } else {
+ if (getCommandLine().getVmCommand().size() > 1) {
+ log("JVM args ignored when same JVM is used.",
+ Project.MSG_WARN);
+ }
+ if (dir != null) {
+ log("Working directory ignored when same JVM is used.",
+ Project.MSG_WARN);
+ }
+ if (newEnvironment || null != env.getVariables()) {
+ log("Changes to environment variables are ignored when same "
+ + "JVM is used.", Project.MSG_WARN);
+ }
+ if (getCommandLine().getBootclasspath() != null) {
+ log("bootclasspath ignored when same JVM is used.",
+ Project.MSG_WARN);
+ }
+ if (perm == null) {
+ perm = new Permissions(true);
+ log("running " + this.getCommandLine().getClassname()
+ + " with default permissions (exit forbidden)", Project.MSG_VERBOSE);
+ }
+ log("Running in same VM " + getCommandLine().describeJavaCommand(),
+ Project.MSG_VERBOSE);
+ }
+ setupRedirector();
+ }
+
+ /**
+ * Execute the specified CommandlineJava.
+ * @param commandLine CommandLineJava instance.
+ * @return the exit value of the process if forked, 0 otherwise.
+ */
+ protected int executeJava(CommandlineJava commandLine) {
+ try {
+ if (fork) {
+ if (!spawn) {
+ return fork(commandLine.getCommandline());
+ } else {
+ spawn(commandLine.getCommandline());
+ return 0;
+ }
+ } else {
+ try {
+ run(commandLine);
+ return 0;
+ } catch (ExitException ex) {
+ return ex.getStatus();
+ }
+ }
+ } catch (BuildException e) {
+ if (e.getLocation() == null && getLocation() != null) {
+ e.setLocation(getLocation());
+ }
+ if (failOnError) {
+ throw e;
+ } else {
+ if (TIMEOUT_MESSAGE.equals(e.getMessage())) {
+ log(TIMEOUT_MESSAGE);
+ } else {
+ log(e);
+ }
+ return -1;
+ }
+ } catch (ThreadDeath t) {
+ throw t; // cf. NB #47191
+ } catch (Throwable t) {
+ if (failOnError) {
+ throw new BuildException(t, getLocation());
+ } else {
+ log(t);
+ return -1;
+ }
+ }
+ }
+
+ /**
+ * Set whether or not you want the process to be spawned;
+ * default is not spawned.
+ * @param spawn if true you do not want Ant to wait for the end of the process.
+ * @since Ant 1.6
+ */
+ public void setSpawn(boolean spawn) {
+ this.spawn = spawn;
+ }
+
+ /**
+ * Set the classpath to be used when running the Java class.
+ *
+ * @param s an Ant Path object containing the classpath.
+ */
+ public void setClasspath(Path s) {
+ createClasspath().append(s);
+ }
+
+ /**
+ * Add a path to the classpath.
+ *
+ * @return created classpath.
+ */
+ public Path createClasspath() {
+ return getCommandLine().createClasspath(getProject()).createPath();
+ }
+
+ /**
+ * Add a path to the bootclasspath.
+ * @since Ant 1.6
+ *
+ * @return created bootclasspath.
+ */
+ public Path createBootclasspath() {
+ return getCommandLine().createBootclasspath(getProject()).createPath();
+ }
+
+ /**
+ * Set the permissions for the application run inside the same JVM.
+ * @since Ant 1.6
+ * @return Permissions.
+ */
+ public Permissions createPermissions() {
+ perm = (perm == null) ? new Permissions() : perm;
+ return perm;
+ }
+
+ /**
+ * Set the classpath to use by reference.
+ *
+ * @param r a reference to an existing classpath.
+ */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * Set the location of the JAR file to execute.
+ *
+ * @param jarfile the jarfile to execute.
+ *
+ * @throws BuildException if there is also a main class specified.
+ */
+ public void setJar(File jarfile) throws BuildException {
+ if (getCommandLine().getClassname() != null) {
+ throw new BuildException("Cannot use 'jar' and 'classname' "
+ + "attributes in same command.");
+ }
+ getCommandLine().setJar(jarfile.getAbsolutePath());
+ }
+
+ /**
+ * Set the Java class to execute.
+ *
+ * @param s the name of the main class.
+ *
+ * @throws BuildException if the jar attribute has been set.
+ */
+ public void setClassname(String s) throws BuildException {
+ if (getCommandLine().getJar() != null) {
+ throw new BuildException("Cannot use 'jar' and 'classname' "
+ + "attributes in same command");
+ }
+ getCommandLine().setClassname(s);
+ }
+
+ /**
+ * Deprecated: use nested arg instead.
+ * Set the command line arguments for the class.
+ *
+ * @param s arguments.
+ *
+ * @ant.attribute ignore="true"
+ */
+ public void setArgs(String s) {
+ log("The args attribute is deprecated. "
+ + "Please use nested arg elements.", Project.MSG_WARN);
+ getCommandLine().createArgument().setLine(s);
+ }
+
+ /**
+ * If set, system properties will be copied to the cloned VM--as
+ * well as the bootclasspath unless you have explicitly specified
+ * a bootclasspath.
+ *
+ * <p>Doesn't have any effect unless fork is true.</p>
+ * @param cloneVm if true copy system properties.
+ * @since Ant 1.7
+ */
+ public void setCloneVm(boolean cloneVm) {
+ getCommandLine().setCloneVm(cloneVm);
+ }
+
+ /**
+ * Add a command-line argument.
+ *
+ * @return created argument.
+ */
+ public Commandline.Argument createArg() {
+ return getCommandLine().createArgument();
+ }
+
+ /**
+ * Set the name of the property in which the return code of the
+ * command should be stored. Only of interest if failonerror=false.
+ *
+ * @param resultProperty name of property.
+ *
+ * @since Ant 1.6
+ */
+ public void setResultProperty(String resultProperty) {
+ this.resultProperty = resultProperty;
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Helper method to set result property to the
+ * passed in value if appropriate.
+ *
+ * @param result the exit code
+ */
+ protected void maybeSetResultPropertyValue(int result) {
+ String res = Integer.toString(result);
+ if (resultProperty != null) {
+ getProject().setNewProperty(resultProperty, res);
+ }
+ }
+
+ /**
+ * If true, execute in a new VM.
+ *
+ * @param s do you want to run Java in a new VM.
+ */
+ public void setFork(boolean s) {
+ this.fork = s;
+ }
+
+ /**
+ * Set the command line arguments for the JVM.
+ *
+ * @param s jvmargs.
+ */
+ public void setJvmargs(String s) {
+ log("The jvmargs attribute is deprecated. "
+ + "Please use nested jvmarg elements.", Project.MSG_WARN);
+ getCommandLine().createVmArgument().setLine(s);
+ }
+
+ /**
+ * Adds a JVM argument.
+ *
+ * @return JVM argument created.
+ */
+ public Commandline.Argument createJvmarg() {
+ return getCommandLine().createVmArgument();
+ }
+
+ /**
+ * Set the command used to start the VM (only if forking).
+ *
+ * @param s command to start the VM.
+ */
+ public void setJvm(String s) {
+ getCommandLine().setVm(s);
+ }
+
+ /**
+ * Add a system property.
+ *
+ * @param sysp system property.
+ */
+ public void addSysproperty(Environment.Variable sysp) {
+ getCommandLine().addSysproperty(sysp);
+ }
+
+ /**
+ * Add a set of properties as system properties.
+ *
+ * @param sysp set of properties to add.
+ *
+ * @since Ant 1.6
+ */
+ public void addSyspropertyset(PropertySet sysp) {
+ getCommandLine().addSyspropertyset(sysp);
+ }
+
+ /**
+ * If true, then fail if the command exits with a
+ * returncode other than zero.
+ *
+ * @param fail if true fail the build when the command exits with a
+ * nonzero returncode.
+ */
+ public void setFailonerror(boolean fail) {
+ failOnError = fail;
+ incompatibleWithSpawn |= fail;
+ }
+
+ /**
+ * Set the working directory of the process.
+ *
+ * @param d working directory.
+ *
+ */
+ public void setDir(File d) {
+ this.dir = d;
+ }
+
+ /**
+ * Set the File to which the output of the process is redirected.
+ *
+ * @param out the output File.
+ */
+ public void setOutput(File out) {
+ this.output = out;
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Set the input to use for the task.
+ *
+ * @param input name of the input file.
+ */
+ public void setInput(File input) {
+ if (inputString != null) {
+ throw new BuildException("The \"input\" and \"inputstring\" "
+ + "attributes cannot both be specified");
+ }
+ this.input = input;
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Set the string to use as input.
+ *
+ * @param inputString the string which is used as the input source.
+ */
+ public void setInputString(String inputString) {
+ if (input != null) {
+ throw new BuildException("The \"input\" and \"inputstring\" "
+ + "attributes cannot both be specified");
+ }
+ this.inputString = inputString;
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Set whether error output of exec is logged. This is only useful
+ * when output is being redirected and error output is desired in the
+ * Ant log.
+ *
+ * @param logError get in the ant log the messages coming from stderr
+ * in the case that fork = true.
+ */
+ public void setLogError(boolean logError) {
+ redirector.setLogError(logError);
+ incompatibleWithSpawn |= logError;
+ }
+
+ /**
+ * Set the File to which the error stream of the process is redirected.
+ *
+ * @param error file getting the error stream.
+ *
+ * @since Ant 1.6
+ */
+ public void setError(File error) {
+ this.error = error;
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Set the property name whose value should be set to the output of
+ * the process.
+ *
+ * @param outputProp property name.
+ *
+ */
+ public void setOutputproperty(String outputProp) {
+ redirector.setOutputProperty(outputProp);
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Set the property name whose value should be set to the error of
+ * the process.
+ *
+ * @param errorProperty property name.
+ *
+ * @since Ant 1.6
+ */
+ public void setErrorProperty(String errorProperty) {
+ redirector.setErrorProperty(errorProperty);
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Corresponds to -mx or -Xmx depending on VM version.
+ *
+ * @param max max memory parameter.
+ */
+ public void setMaxmemory(String max) {
+ getCommandLine().setMaxmemory(max);
+ }
+
+ /**
+ * Set the JVM version.
+ * @param value JVM version.
+ */
+ public void setJVMVersion(String value) {
+ getCommandLine().setVmversion(value);
+ }
+
+ /**
+ * Add an environment variable.
+ *
+ * <p>Will be ignored if we are not forking a new VM.
+ *
+ * @param var new environment variable.
+ *
+ * @since Ant 1.5
+ */
+ public void addEnv(Environment.Variable var) {
+ env.addVariable(var);
+ }
+
+ /**
+ * If true, use a completely new environment.
+ *
+ * <p>Will be ignored if we are not forking a new VM.
+ *
+ * @param newenv if true, use a completely new environment.
+ *
+ * @since Ant 1.5
+ */
+ public void setNewenvironment(boolean newenv) {
+ newEnvironment = newenv;
+ }
+
+ /**
+ * If true, append output to existing file.
+ *
+ * @param append if true, append output to existing file.
+ *
+ * @since Ant 1.5
+ */
+ public void setAppend(boolean append) {
+ redirector.setAppend(append);
+ incompatibleWithSpawn |= append;
+ }
+
+ /**
+ * Set the timeout in milliseconds after which the process will be killed.
+ *
+ * @param value timeout in milliseconds.
+ *
+ * @since Ant 1.5
+ */
+ public void setTimeout(Long value) {
+ timeout = value;
+ incompatibleWithSpawn |= timeout != null;
+ }
+
+ /**
+ * Add assertions to enable in this program (if fork=true).
+ * @param asserts assertion set.
+ * @since Ant 1.6
+ */
+ public void addAssertions(Assertions asserts) {
+ if (getCommandLine().getAssertions() != null) {
+ throw new BuildException("Only one assertion declaration is allowed");
+ }
+ getCommandLine().setAssertions(asserts);
+ }
+
+ /**
+ * Add a <code>RedirectorElement</code> to this task.
+ * @param redirectorElement <code>RedirectorElement</code>.
+ */
+ public void addConfiguredRedirector(RedirectorElement redirectorElement) {
+ if (this.redirectorElement != null) {
+ throw new BuildException("cannot have > 1 nested redirectors");
+ }
+ this.redirectorElement = redirectorElement;
+ incompatibleWithSpawn = true;
+ }
+
+ /**
+ * Pass output sent to System.out to specified output file.
+ *
+ * @param output a string of output on its way to the handlers.
+ *
+ * @since Ant 1.5
+ */
+ protected void handleOutput(String output) {
+ if (redirector.getOutputStream() != null) {
+ redirector.handleOutput(output);
+ } else {
+ super.handleOutput(output);
+ }
+ }
+
+ /**
+ * Handle an input request by this task.
+ *
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception IOException if the data cannot be read.
+ * @since Ant 1.6
+ */
+ public int handleInput(byte[] buffer, int offset, int length)
+ throws IOException {
+ // Should work whether or not redirector.inputStream == null:
+ return redirector.handleInput(buffer, offset, length);
+ }
+
+ /**
+ * Pass output sent to System.out to specified output file.
+ *
+ * @param output string of output on its way to its handlers.
+ *
+ * @since Ant 1.5.2
+ */
+ protected void handleFlush(String output) {
+ if (redirector.getOutputStream() != null) {
+ redirector.handleFlush(output);
+ } else {
+ super.handleFlush(output);
+ }
+ }
+
+ /**
+ * Handle output sent to System.err.
+ *
+ * @param output string of stderr.
+ *
+ * @since Ant 1.5
+ */
+ protected void handleErrorOutput(String output) {
+ if (redirector.getErrorStream() != null) {
+ redirector.handleErrorOutput(output);
+ } else {
+ super.handleErrorOutput(output);
+ }
+ }
+
+ /**
+ * Handle output sent to System.err and flush the stream.
+ *
+ * @param output string of stderr.
+ *
+ * @since Ant 1.5.2
+ */
+ protected void handleErrorFlush(String output) {
+ if (redirector.getErrorStream() != null) {
+ redirector.handleErrorFlush(output);
+ } else {
+ super.handleErrorFlush(output);
+ }
+ }
+
+ /**
+ * Set up properties on the redirector that we needed to store locally.
+ */
+ protected void setupRedirector() {
+ redirector.setInput(input);
+ redirector.setInputString(inputString);
+ redirector.setOutput(output);
+ redirector.setError(error);
+ if (redirectorElement != null) {
+ redirectorElement.configure(redirector);
+ }
+ if (!spawn && input == null && inputString == null) {
+ // #24918: send standard input to the process by default.
+ redirector.setInputStream(
+ new KeepAliveInputStream(getProject().getDefaultInputStream()));
+ }
+ }
+
+ /**
+ * Executes the given classname with the given arguments as it
+ * were a command line application.
+ * @param command CommandlineJava.
+ */
+ private void run(CommandlineJava command) throws BuildException {
+ try {
+ ExecuteJava exe = new ExecuteJava();
+ exe.setJavaCommand(command.getJavaCommand());
+ exe.setClasspath(command.getClasspath());
+ exe.setSystemProperties(command.getSystemProperties());
+ exe.setPermissions(perm);
+ exe.setTimeout(timeout);
+ redirector.createStreams();
+ exe.execute(getProject());
+ redirector.complete();
+ if (exe.killedProcess()) {
+ throw new BuildException(TIMEOUT_MESSAGE);
+ }
+ } catch (IOException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Executes the given classname with the given arguments in a separate VM.
+ * @param command String[] of command-line arguments.
+ */
+ private int fork(String[] command) throws BuildException {
+ Execute exe
+ = new Execute(redirector.createHandler(), createWatchdog());
+ setupExecutable(exe, command);
+
+ try {
+ int rc = exe.execute();
+ redirector.complete();
+ if (exe.killedProcess()) {
+ throw new BuildException(TIMEOUT_MESSAGE);
+ }
+ return rc;
+ } catch (IOException e) {
+ throw new BuildException(e, getLocation());
+ }
+ }
+
+ /**
+ * Executes the given classname with the given arguments in a separate VM.
+ * @param command String[] of command-line arguments.
+ */
+ private void spawn(String[] command) throws BuildException {
+ Execute exe = new Execute();
+ setupExecutable(exe, command);
+ try {
+ exe.spawn();
+ } catch (IOException e) {
+ throw new BuildException(e, getLocation());
+ }
+ }
+
+ /**
+ * Do all configuration for an executable that
+ * is common across the {@link #fork(String[])} and
+ * {@link #spawn(String[])} methods.
+ * @param exe executable.
+ * @param command command to execute.
+ */
+ private void setupExecutable(Execute exe, String[] command) {
+ exe.setAntRun(getProject());
+ setupWorkingDir(exe);
+ setupEnvironment(exe);
+ setupCommandLine(exe, command);
+ }
+
+ /**
+ * Set up our environment variables.
+ * @param exe executable.
+ */
+ private void setupEnvironment(Execute exe) {
+ String[] environment = env.getVariables();
+ if (environment != null) {
+ for (int i = 0; i < environment.length; i++) {
+ log("Setting environment variable: " + environment[i],
+ Project.MSG_VERBOSE);
+ }
+ }
+ exe.setNewenvironment(newEnvironment);
+ exe.setEnvironment(environment);
+ }
+
+ /**
+ * Set the working dir of the new process.
+ * @param exe executable.
+ * @throws BuildException if the dir doesn't exist.
+ */
+ private void setupWorkingDir(Execute exe) {
+ if (dir == null) {
+ dir = getProject().getBaseDir();
+ } else if (!dir.exists() || !dir.isDirectory()) {
+ throw new BuildException(dir.getAbsolutePath()
+ + " is not a valid directory",
+ getLocation());
+ }
+ exe.setWorkingDirectory(dir);
+ }
+
+ /**
+ * Set the command line for the exe.
+ * On VMS, hands off to {@link #setupCommandLineForVMS(Execute, String[])}.
+ * @param exe executable.
+ * @param command command to execute.
+ */
+ private void setupCommandLine(Execute exe, String[] command) {
+ //On VMS platform, we need to create a special java options file
+ //containing the arguments and classpath for the java command.
+ //The special file is supported by the "-V" switch on the VMS JVM.
+ if (Os.isFamily("openvms")) {
+ setupCommandLineForVMS(exe, command);
+ } else {
+ exe.setCommandline(command);
+ }
+ }
+
+ /**
+ * On VMS platform, we need to create a special java options file
+ * containing the arguments and classpath for the java command.
+ * The special file is supported by the "-V" switch on the VMS JVM.
+ *
+ * @param exe executable.
+ * @param command command to execute.
+ */
+ private void setupCommandLineForVMS(Execute exe, String[] command) {
+ ExecuteJava.setupCommandLineForVMS(exe, command);
+ }
+
+ /**
+ * Executes the given classname with the given arguments as if it
+ * were a command line application.
+ *
+ * @param classname the name of the class to run.
+ * @param args arguments for the class.
+ * @throws BuildException in case of IOException in the execution.
+ */
+ protected void run(String classname, Vector<String> args) throws BuildException {
+ CommandlineJava cmdj = new CommandlineJava();
+ cmdj.setClassname(classname);
+ final int size = args.size();
+ for (int i = 0; i < size; i++) {
+ cmdj.createArgument().setValue(args.elementAt(i));
+ }
+ run(cmdj);
+ }
+
+ /**
+ * Clear out the arguments to this java task.
+ */
+ public void clearArgs() {
+ getCommandLine().clearJavaArgs();
+ }
+
+ /**
+ * Create the Watchdog to kill a runaway process.
+ *
+ * @return new watchdog.
+ *
+ * @throws BuildException under unknown circumstances.
+ *
+ * @since Ant 1.5
+ */
+ protected ExecuteWatchdog createWatchdog() throws BuildException {
+ if (timeout == null) {
+ return null;
+ }
+ return new ExecuteWatchdog(timeout.longValue());
+ }
+
+ /**
+ * Log the specified Throwable.
+ * @param t the Throwable to log.
+ * @since 1.6.2
+ */
+ private void log(Throwable t) {
+ StringWriter sw = new StringWriter();
+ PrintWriter w = new PrintWriter(sw);
+ t.printStackTrace(w);
+ w.close();
+ log(sw.toString(), Project.MSG_ERR);
+ }
+
+ /**
+ * Accessor to the command line.
+ *
+ * @return the current command line.
+ * @since 1.6.3
+ */
+ public CommandlineJava getCommandLine() {
+ return cmdl;
+ }
+
+ /**
+ * Get the system properties of the command line.
+ *
+ * @return the current properties of this java invocation.
+ * @since 1.6.3
+ */
+ public CommandlineJava.SysProperties getSysProperties() {
+ return getCommandLine().getSystemProperties();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Javac.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Javac.java
new file mode 100644
index 00000000..3d77f7c4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Javac.java
@@ -0,0 +1,1270 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.compilers.CompilerAdapter;
+import org.apache.tools.ant.taskdefs.compilers.CompilerAdapterExtension;
+import org.apache.tools.ant.taskdefs.compilers.CompilerAdapterFactory;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.GlobPatternMapper;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.SourceFileScanner;
+import org.apache.tools.ant.util.facade.FacadeTaskHelper;
+
+/**
+ * Compiles Java source files. This task can take the following
+ * arguments:
+ * <ul>
+ * <li>sourcedir
+ * <li>destdir
+ * <li>deprecation
+ * <li>classpath
+ * <li>bootclasspath
+ * <li>extdirs
+ * <li>optimize
+ * <li>debug
+ * <li>encoding
+ * <li>target
+ * <li>depend
+ * <li>verbose
+ * <li>failonerror
+ * <li>includeantruntime
+ * <li>includejavaruntime
+ * <li>source
+ * <li>compiler
+ * </ul>
+ * Of these arguments, the <b>sourcedir</b> and <b>destdir</b> are required.
+ * <p>
+ * When this task executes, it will recursively scan the sourcedir and
+ * destdir looking for Java source files to compile. This task makes its
+ * compile decision based on timestamp.
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="java"
+ */
+
+public class Javac extends MatchingTask {
+
+ private static final String FAIL_MSG
+ = "Compile failed; see the compiler error output for details.";
+
+ private static final String JAVAC19 = "javac1.9";
+ private static final String JAVAC18 = "javac1.8";
+ private static final String JAVAC17 = "javac1.7";
+ private static final String JAVAC16 = "javac1.6";
+ private static final String JAVAC15 = "javac1.5";
+ private static final String JAVAC14 = "javac1.4";
+ private static final String JAVAC13 = "javac1.3";
+ private static final String JAVAC12 = "javac1.2";
+ private static final String JAVAC11 = "javac1.1";
+ private static final String MODERN = "modern";
+ private static final String CLASSIC = "classic";
+ private static final String EXTJAVAC = "extJavac";
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private Path src;
+ private File destDir;
+ private Path compileClasspath;
+ private Path compileSourcepath;
+ private String encoding;
+ private boolean debug = false;
+ private boolean optimize = false;
+ private boolean deprecation = false;
+ private boolean depend = false;
+ private boolean verbose = false;
+ private String targetAttribute;
+ private Path bootclasspath;
+ private Path extdirs;
+ private Boolean includeAntRuntime;
+ private boolean includeJavaRuntime = false;
+ private boolean fork = false;
+ private String forkedExecutable = null;
+ private boolean nowarn = false;
+ private String memoryInitialSize;
+ private String memoryMaximumSize;
+ private FacadeTaskHelper facade = null;
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected boolean failOnError = true;
+ protected boolean listFiles = false;
+ protected File[] compileList = new File[0];
+ private Map<String, Long> packageInfos = new HashMap<String, Long>();
+ // CheckStyle:VisibilityModifier ON
+
+ private String source;
+ private String debugLevel;
+ private File tmpDir;
+ private String updatedProperty;
+ private String errorProperty;
+ private boolean taskSuccess = true; // assume the best
+ private boolean includeDestClasses = true;
+ private CompilerAdapter nestedAdapter = null;
+
+ private boolean createMissingPackageInfoClass = true;
+
+ /**
+ * Javac task for compilation of Java files.
+ */
+ public Javac() {
+ facade = new FacadeTaskHelper(assumedJavaVersion());
+ }
+
+ private String assumedJavaVersion() {
+ if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_4)) {
+ return JAVAC14;
+ } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_5)) {
+ return JAVAC15;
+ } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_6)) {
+ return JAVAC16;
+ } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_7)) {
+ return JAVAC17;
+ } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_8)) {
+ return JAVAC18;
+ } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_9)) {
+ return JAVAC19;
+ } else {
+ return CLASSIC;
+ }
+ }
+
+ /**
+ * Get the value of debugLevel.
+ * @return value of debugLevel.
+ */
+ public String getDebugLevel() {
+ return debugLevel;
+ }
+
+ /**
+ * Keyword list to be appended to the -g command-line switch.
+ *
+ * This will be ignored by all implementations except modern
+ * and classic(ver &gt;= 1.2). Legal values are none or a
+ * comma-separated list of the following keywords: lines, vars,
+ * and source. If debuglevel is not specified, by default, :none
+ * will be appended to -g. If debug is not turned on, this attribute
+ * will be ignored.
+ *
+ * @param v Value to assign to debugLevel.
+ */
+ public void setDebugLevel(final String v) {
+ this.debugLevel = v;
+ }
+
+ /**
+ * Get the value of source.
+ * @return value of source.
+ */
+ public String getSource() {
+ return source != null
+ ? source : getProject().getProperty(MagicNames.BUILD_JAVAC_SOURCE);
+ }
+
+ /**
+ * Value of the -source command-line switch; will be ignored by
+ * all implementations except modern, jikes and gcj (gcj uses
+ * -fsource).
+ *
+ * <p>If you use this attribute together with jikes or gcj, you
+ * must make sure that your version of jikes supports the -source
+ * switch.</p>
+ *
+ * <p>Legal values are 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, and 5, 6, 7, 8 and 9
+ * - by default, no -source argument will be used at all.</p>
+ *
+ * @param v Value to assign to source.
+ */
+ public void setSource(final String v) {
+ this.source = v;
+ }
+
+ /**
+ * Adds a path for source compilation.
+ *
+ * @return a nested src element.
+ */
+ public Path createSrc() {
+ if (src == null) {
+ src = new Path(getProject());
+ }
+ return src.createPath();
+ }
+
+ /**
+ * Recreate src.
+ *
+ * @return a nested src element.
+ */
+ protected Path recreateSrc() {
+ src = null;
+ return createSrc();
+ }
+
+ /**
+ * Set the source directories to find the source Java files.
+ * @param srcDir the source directories as a path
+ */
+ public void setSrcdir(final Path srcDir) {
+ if (src == null) {
+ src = srcDir;
+ } else {
+ src.append(srcDir);
+ }
+ }
+
+ /**
+ * Gets the source dirs to find the source java files.
+ * @return the source directories as a path
+ */
+ public Path getSrcdir() {
+ return src;
+ }
+
+ /**
+ * Set the destination directory into which the Java source
+ * files should be compiled.
+ * @param destDir the destination director
+ */
+ public void setDestdir(final File destDir) {
+ this.destDir = destDir;
+ }
+
+ /**
+ * Gets the destination directory into which the java source files
+ * should be compiled.
+ * @return the destination directory
+ */
+ public File getDestdir() {
+ return destDir;
+ }
+
+ /**
+ * Set the sourcepath to be used for this compilation.
+ * @param sourcepath the source path
+ */
+ public void setSourcepath(final Path sourcepath) {
+ if (compileSourcepath == null) {
+ compileSourcepath = sourcepath;
+ } else {
+ compileSourcepath.append(sourcepath);
+ }
+ }
+
+ /**
+ * Gets the sourcepath to be used for this compilation.
+ * @return the source path
+ */
+ public Path getSourcepath() {
+ return compileSourcepath;
+ }
+
+ /**
+ * Adds a path to sourcepath.
+ * @return a sourcepath to be configured
+ */
+ public Path createSourcepath() {
+ if (compileSourcepath == null) {
+ compileSourcepath = new Path(getProject());
+ }
+ return compileSourcepath.createPath();
+ }
+
+ /**
+ * Adds a reference to a source path defined elsewhere.
+ * @param r a reference to a source path
+ */
+ public void setSourcepathRef(final Reference r) {
+ createSourcepath().setRefid(r);
+ }
+
+ /**
+ * Set the classpath to be used for this compilation.
+ *
+ * @param classpath an Ant Path object containing the compilation classpath.
+ */
+ public void setClasspath(final Path classpath) {
+ if (compileClasspath == null) {
+ compileClasspath = classpath;
+ } else {
+ compileClasspath.append(classpath);
+ }
+ }
+
+ /**
+ * Gets the classpath to be used for this compilation.
+ * @return the class path
+ */
+ public Path getClasspath() {
+ return compileClasspath;
+ }
+
+ /**
+ * Adds a path to the classpath.
+ * @return a class path to be configured
+ */
+ public Path createClasspath() {
+ if (compileClasspath == null) {
+ compileClasspath = new Path(getProject());
+ }
+ return compileClasspath.createPath();
+ }
+
+ /**
+ * Adds a reference to a classpath defined elsewhere.
+ * @param r a reference to a classpath
+ */
+ public void setClasspathRef(final Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * Sets the bootclasspath that will be used to compile the classes
+ * against.
+ * @param bootclasspath a path to use as a boot class path (may be more
+ * than one)
+ */
+ public void setBootclasspath(final Path bootclasspath) {
+ if (this.bootclasspath == null) {
+ this.bootclasspath = bootclasspath;
+ } else {
+ this.bootclasspath.append(bootclasspath);
+ }
+ }
+
+ /**
+ * Gets the bootclasspath that will be used to compile the classes
+ * against.
+ * @return the boot path
+ */
+ public Path getBootclasspath() {
+ return bootclasspath;
+ }
+
+ /**
+ * Adds a path to the bootclasspath.
+ * @return a path to be configured
+ */
+ public Path createBootclasspath() {
+ if (bootclasspath == null) {
+ bootclasspath = new Path(getProject());
+ }
+ return bootclasspath.createPath();
+ }
+
+ /**
+ * Adds a reference to a classpath defined elsewhere.
+ * @param r a reference to a classpath
+ */
+ public void setBootClasspathRef(final Reference r) {
+ createBootclasspath().setRefid(r);
+ }
+
+ /**
+ * Sets the extension directories that will be used during the
+ * compilation.
+ * @param extdirs a path
+ */
+ public void setExtdirs(final Path extdirs) {
+ if (this.extdirs == null) {
+ this.extdirs = extdirs;
+ } else {
+ this.extdirs.append(extdirs);
+ }
+ }
+
+ /**
+ * Gets the extension directories that will be used during the
+ * compilation.
+ * @return the extension directories as a path
+ */
+ public Path getExtdirs() {
+ return extdirs;
+ }
+
+ /**
+ * Adds a path to extdirs.
+ * @return a path to be configured
+ */
+ public Path createExtdirs() {
+ if (extdirs == null) {
+ extdirs = new Path(getProject());
+ }
+ return extdirs.createPath();
+ }
+
+ /**
+ * If true, list the source files being handed off to the compiler.
+ * @param list if true list the source files
+ */
+ public void setListfiles(final boolean list) {
+ listFiles = list;
+ }
+
+ /**
+ * Get the listfiles flag.
+ * @return the listfiles flag
+ */
+ public boolean getListfiles() {
+ return listFiles;
+ }
+
+ /**
+ * Indicates whether the build will continue
+ * even if there are compilation errors; defaults to true.
+ * @param fail if true halt the build on failure
+ */
+ public void setFailonerror(final boolean fail) {
+ failOnError = fail;
+ }
+
+ /**
+ * @ant.attribute ignore="true"
+ * @param proceed inverse of failoferror
+ */
+ public void setProceed(final boolean proceed) {
+ failOnError = !proceed;
+ }
+
+ /**
+ * Gets the failonerror flag.
+ * @return the failonerror flag
+ */
+ public boolean getFailonerror() {
+ return failOnError;
+ }
+
+ /**
+ * Indicates whether source should be
+ * compiled with deprecation information; defaults to off.
+ * @param deprecation if true turn on deprecation information
+ */
+ public void setDeprecation(final boolean deprecation) {
+ this.deprecation = deprecation;
+ }
+
+ /**
+ * Gets the deprecation flag.
+ * @return the deprecation flag
+ */
+ public boolean getDeprecation() {
+ return deprecation;
+ }
+
+ /**
+ * The initial size of the memory for the underlying VM
+ * if javac is run externally; ignored otherwise.
+ * Defaults to the standard VM memory setting.
+ * (Examples: 83886080, 81920k, or 80m)
+ * @param memoryInitialSize string to pass to VM
+ */
+ public void setMemoryInitialSize(final String memoryInitialSize) {
+ this.memoryInitialSize = memoryInitialSize;
+ }
+
+ /**
+ * Gets the memoryInitialSize flag.
+ * @return the memoryInitialSize flag
+ */
+ public String getMemoryInitialSize() {
+ return memoryInitialSize;
+ }
+
+ /**
+ * The maximum size of the memory for the underlying VM
+ * if javac is run externally; ignored otherwise.
+ * Defaults to the standard VM memory setting.
+ * (Examples: 83886080, 81920k, or 80m)
+ * @param memoryMaximumSize string to pass to VM
+ */
+ public void setMemoryMaximumSize(final String memoryMaximumSize) {
+ this.memoryMaximumSize = memoryMaximumSize;
+ }
+
+ /**
+ * Gets the memoryMaximumSize flag.
+ * @return the memoryMaximumSize flag
+ */
+ public String getMemoryMaximumSize() {
+ return memoryMaximumSize;
+ }
+
+ /**
+ * Set the Java source file encoding name.
+ * @param encoding the source file encoding
+ */
+ public void setEncoding(final String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Gets the java source file encoding name.
+ * @return the source file encoding name
+ */
+ public String getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * Indicates whether source should be compiled
+ * with debug information; defaults to off.
+ * @param debug if true compile with debug information
+ */
+ public void setDebug(final boolean debug) {
+ this.debug = debug;
+ }
+
+ /**
+ * Gets the debug flag.
+ * @return the debug flag
+ */
+ public boolean getDebug() {
+ return debug;
+ }
+
+ /**
+ * If true, compiles with optimization enabled.
+ * @param optimize if true compile with optimization enabled
+ */
+ public void setOptimize(final boolean optimize) {
+ this.optimize = optimize;
+ }
+
+ /**
+ * Gets the optimize flag.
+ * @return the optimize flag
+ */
+ public boolean getOptimize() {
+ return optimize;
+ }
+
+ /**
+ * Enables dependency-tracking for compilers
+ * that support this (jikes and classic).
+ * @param depend if true enable dependency-tracking
+ */
+ public void setDepend(final boolean depend) {
+ this.depend = depend;
+ }
+
+ /**
+ * Gets the depend flag.
+ * @return the depend flag
+ */
+ public boolean getDepend() {
+ return depend;
+ }
+
+ /**
+ * If true, asks the compiler for verbose output.
+ * @param verbose if true, asks the compiler for verbose output
+ */
+ public void setVerbose(final boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ /**
+ * Gets the verbose flag.
+ * @return the verbose flag
+ */
+ public boolean getVerbose() {
+ return verbose;
+ }
+
+ /**
+ * Sets the target VM that the classes will be compiled for. Valid
+ * values depend on the compiler, for jdk 1.4 the valid values are
+ * "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "5", "6", "7", "8", "9".
+ * @param target the target VM
+ */
+ public void setTarget(final String target) {
+ this.targetAttribute = target;
+ }
+
+ /**
+ * Gets the target VM that the classes will be compiled for.
+ * @return the target VM
+ */
+ public String getTarget() {
+ return targetAttribute != null
+ ? targetAttribute
+ : getProject().getProperty(MagicNames.BUILD_JAVAC_TARGET);
+ }
+
+ /**
+ * If true, includes Ant's own classpath in the classpath.
+ * @param include if true, includes Ant's own classpath in the classpath
+ */
+ public void setIncludeantruntime(final boolean include) {
+ includeAntRuntime = Boolean.valueOf(include);
+ }
+
+ /**
+ * Gets whether or not the ant classpath is to be included in the classpath.
+ * @return whether or not the ant classpath is to be included in the classpath
+ */
+ public boolean getIncludeantruntime() {
+ return includeAntRuntime != null ? includeAntRuntime.booleanValue() : true;
+ }
+
+ /**
+ * If true, includes the Java runtime libraries in the classpath.
+ * @param include if true, includes the Java runtime libraries in the classpath
+ */
+ public void setIncludejavaruntime(final boolean include) {
+ includeJavaRuntime = include;
+ }
+
+ /**
+ * Gets whether or not the java runtime should be included in this
+ * task's classpath.
+ * @return the includejavaruntime attribute
+ */
+ public boolean getIncludejavaruntime() {
+ return includeJavaRuntime;
+ }
+
+ /**
+ * If true, forks the javac compiler.
+ *
+ * @param f "true|false|on|off|yes|no"
+ */
+ public void setFork(final boolean f) {
+ fork = f;
+ }
+
+ /**
+ * Sets the name of the javac executable.
+ *
+ * <p>Ignored unless fork is true or extJavac has been specified
+ * as the compiler.</p>
+ * @param forkExec the name of the executable
+ */
+ public void setExecutable(final String forkExec) {
+ forkedExecutable = forkExec;
+ }
+
+ /**
+ * The value of the executable attribute, if any.
+ *
+ * @since Ant 1.6
+ * @return the name of the java executable
+ */
+ public String getExecutable() {
+ return forkedExecutable;
+ }
+
+ /**
+ * Is this a forked invocation of JDK's javac?
+ * @return true if this is a forked invocation
+ */
+ public boolean isForkedJavac() {
+ return fork || EXTJAVAC.equalsIgnoreCase(getCompiler());
+ }
+
+ /**
+ * The name of the javac executable to use in fork-mode.
+ *
+ * <p>This is either the name specified with the executable
+ * attribute or the full path of the javac compiler of the VM Ant
+ * is currently running in - guessed by Ant.</p>
+ *
+ * <p>You should <strong>not</strong> invoke this method if you
+ * want to get the value of the executable command - use {@link
+ * #getExecutable getExecutable} for this.</p>
+ * @return the name of the javac executable
+ */
+ public String getJavacExecutable() {
+ if (forkedExecutable == null && isForkedJavac()) {
+ forkedExecutable = getSystemJavac();
+ } else if (forkedExecutable != null && !isForkedJavac()) {
+ forkedExecutable = null;
+ }
+ return forkedExecutable;
+ }
+
+ /**
+ * If true, enables the -nowarn option.
+ * @param flag if true, enable the -nowarn option
+ */
+ public void setNowarn(final boolean flag) {
+ this.nowarn = flag;
+ }
+
+ /**
+ * Should the -nowarn option be used.
+ * @return true if the -nowarn option should be used
+ */
+ public boolean getNowarn() {
+ return nowarn;
+ }
+
+ /**
+ * Adds an implementation specific command-line argument.
+ * @return a ImplementationSpecificArgument to be configured
+ */
+ public ImplementationSpecificArgument createCompilerArg() {
+ final ImplementationSpecificArgument arg =
+ new ImplementationSpecificArgument();
+ facade.addImplementationArgument(arg);
+ return arg;
+ }
+
+ /**
+ * Get the additional implementation specific command line arguments.
+ * @return array of command line arguments, guaranteed to be non-null.
+ */
+ public String[] getCurrentCompilerArgs() {
+ final String chosen = facade.getExplicitChoice();
+ try {
+ // make sure facade knows about magic properties and fork setting
+ final String appliedCompiler = getCompiler();
+ facade.setImplementation(appliedCompiler);
+
+ String[] result = facade.getArgs();
+
+ final String altCompilerName = getAltCompilerName(facade.getImplementation());
+
+ if (result.length == 0 && altCompilerName != null) {
+ facade.setImplementation(altCompilerName);
+ result = facade.getArgs();
+ }
+
+ return result;
+
+ } finally {
+ facade.setImplementation(chosen);
+ }
+ }
+
+ private String getAltCompilerName(final String anImplementation) {
+ if (JAVAC19.equalsIgnoreCase(anImplementation)
+ || JAVAC18.equalsIgnoreCase(anImplementation)
+ || JAVAC17.equalsIgnoreCase(anImplementation)
+ || JAVAC16.equalsIgnoreCase(anImplementation)
+ || JAVAC15.equalsIgnoreCase(anImplementation)
+ || JAVAC14.equalsIgnoreCase(anImplementation)
+ || JAVAC13.equalsIgnoreCase(anImplementation)) {
+ return MODERN;
+ }
+ if (JAVAC12.equalsIgnoreCase(anImplementation)
+ || JAVAC11.equalsIgnoreCase(anImplementation)) {
+ return CLASSIC;
+ }
+ if (MODERN.equalsIgnoreCase(anImplementation)) {
+ final String nextSelected = assumedJavaVersion();
+ if (JAVAC19.equalsIgnoreCase(nextSelected)
+ || JAVAC18.equalsIgnoreCase(nextSelected)
+ || JAVAC17.equalsIgnoreCase(nextSelected)
+ || JAVAC16.equalsIgnoreCase(nextSelected)
+ || JAVAC15.equalsIgnoreCase(nextSelected)
+ || JAVAC14.equalsIgnoreCase(nextSelected)
+ || JAVAC13.equalsIgnoreCase(nextSelected)) {
+ return nextSelected;
+ }
+ }
+ if (CLASSIC.equalsIgnoreCase(anImplementation)) {
+ return assumedJavaVersion();
+ }
+ if (EXTJAVAC.equalsIgnoreCase(anImplementation)) {
+ return assumedJavaVersion();
+ }
+ return null;
+ }
+
+ /**
+ * Where Ant should place temporary files.
+ *
+ * @since Ant 1.6
+ * @param tmpDir the temporary directory
+ */
+ public void setTempdir(final File tmpDir) {
+ this.tmpDir = tmpDir;
+ }
+
+ /**
+ * Where Ant should place temporary files.
+ *
+ * @since Ant 1.6
+ * @return the temporary directory
+ */
+ public File getTempdir() {
+ return tmpDir;
+ }
+
+ /**
+ * The property to set on compilation success.
+ * This property will not be set if the compilation
+ * fails, or if there are no files to compile.
+ * @param updatedProperty the property name to use.
+ * @since Ant 1.7.1.
+ */
+ public void setUpdatedProperty(final String updatedProperty) {
+ this.updatedProperty = updatedProperty;
+ }
+
+ /**
+ * The property to set on compilation failure.
+ * This property will be set if the compilation
+ * fails.
+ * @param errorProperty the property name to use.
+ * @since Ant 1.7.1.
+ */
+ public void setErrorProperty(final String errorProperty) {
+ this.errorProperty = errorProperty;
+ }
+
+ /**
+ * This property controls whether to include the
+ * destination classes directory in the classpath
+ * given to the compiler.
+ * The default value is "true".
+ * @param includeDestClasses the value to use.
+ */
+ public void setIncludeDestClasses(final boolean includeDestClasses) {
+ this.includeDestClasses = includeDestClasses;
+ }
+
+ /**
+ * Get the value of the includeDestClasses property.
+ * @return the value.
+ */
+ public boolean isIncludeDestClasses() {
+ return includeDestClasses;
+ }
+
+ /**
+ * Get the result of the javac task (success or failure).
+ * @return true if compilation succeeded, or
+ * was not necessary, false if the compilation failed.
+ */
+ public boolean getTaskSuccess() {
+ return taskSuccess;
+ }
+
+ /**
+ * The classpath to use when loading the compiler implementation
+ * if it is not a built-in one.
+ *
+ * @since Ant 1.8.0
+ */
+ public Path createCompilerClasspath() {
+ return facade.getImplementationClasspath(getProject());
+ }
+
+ /**
+ * Set the compiler adapter explicitly.
+ * @since Ant 1.8.0
+ */
+ public void add(final CompilerAdapter adapter) {
+ if (nestedAdapter != null) {
+ throw new BuildException("Can't have more than one compiler"
+ + " adapter");
+ }
+ nestedAdapter = adapter;
+ }
+
+ /**
+ * Whether package-info.class files will be created by Ant
+ * matching package-info.java files that have been compiled but
+ * didn't create class files themselves.
+ *
+ * @since Ant 1.8.3
+ */
+ public void setCreateMissingPackageInfoClass(final boolean b) {
+ createMissingPackageInfoClass = b;
+ }
+
+ /**
+ * Executes the task.
+ * @exception BuildException if an error occurs
+ */
+ @Override
+ public void execute() throws BuildException {
+ checkParameters();
+ resetFileLists();
+
+ // scan source directories and dest directory to build up
+ // compile lists
+ final String[] list = src.list();
+ for (int i = 0; i < list.length; i++) {
+ final File srcDir = getProject().resolveFile(list[i]);
+ if (!srcDir.exists()) {
+ throw new BuildException("srcdir \""
+ + srcDir.getPath()
+ + "\" does not exist!", getLocation());
+ }
+
+ final DirectoryScanner ds = this.getDirectoryScanner(srcDir);
+ final String[] files = ds.getIncludedFiles();
+
+ scanDir(srcDir, destDir != null ? destDir : srcDir, files);
+ }
+
+ compile();
+ if (updatedProperty != null
+ && taskSuccess
+ && compileList.length != 0) {
+ getProject().setNewProperty(updatedProperty, "true");
+ }
+ }
+
+ /**
+ * Clear the list of files to be compiled and copied..
+ */
+ protected void resetFileLists() {
+ compileList = new File[0];
+ packageInfos = new HashMap<String, Long>();
+ }
+
+ /**
+ * Scans the directory looking for source files to be compiled.
+ * The results are returned in the class variable compileList
+ *
+ * @param srcDir The source directory
+ * @param destDir The destination directory
+ * @param files An array of filenames
+ */
+ protected void scanDir(final File srcDir, final File destDir, final String[] files) {
+ final GlobPatternMapper m = new GlobPatternMapper();
+ final String[] extensions = findSupportedFileExtensions();
+
+ for (int i = 0; i < extensions.length; i++) {
+ m.setFrom(extensions[i]);
+ m.setTo("*.class");
+ final SourceFileScanner sfs = new SourceFileScanner(this);
+ final File[] newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m);
+
+ if (newFiles.length > 0) {
+ lookForPackageInfos(srcDir, newFiles);
+ final File[] newCompileList
+ = new File[compileList.length + newFiles.length];
+ System.arraycopy(compileList, 0, newCompileList, 0,
+ compileList.length);
+ System.arraycopy(newFiles, 0, newCompileList,
+ compileList.length, newFiles.length);
+ compileList = newCompileList;
+ }
+ }
+ }
+
+ private String[] findSupportedFileExtensions() {
+ final String compilerImpl = getCompiler();
+ final CompilerAdapter adapter =
+ nestedAdapter != null ? nestedAdapter :
+ CompilerAdapterFactory.getCompiler(compilerImpl, this,
+ createCompilerClasspath());
+ String[] extensions = null;
+ if (adapter instanceof CompilerAdapterExtension) {
+ extensions =
+ ((CompilerAdapterExtension) adapter).getSupportedFileExtensions();
+ }
+
+ if (extensions == null) {
+ extensions = new String[] {"java"};
+ }
+
+ // now process the extensions to ensure that they are the
+ // right format
+ for (int i = 0; i < extensions.length; i++) {
+ if (!extensions[i].startsWith("*.")) {
+ extensions[i] = "*." + extensions[i];
+ }
+ }
+ return extensions;
+ }
+
+ /**
+ * Gets the list of files to be compiled.
+ * @return the list of files as an array
+ */
+ public File[] getFileList() {
+ return compileList;
+ }
+
+ /**
+ * Is the compiler implementation a jdk compiler
+ *
+ * @param compilerImpl the name of the compiler implementation
+ * @return true if compilerImpl is "modern", "classic",
+ * "javac1.1", "javac1.2", "javac1.3", "javac1.4", "javac1.5",
+ * "javac1.6", "javac1.7", "javac1.8" or "javac1.9".
+ */
+ protected boolean isJdkCompiler(final String compilerImpl) {
+ return MODERN.equals(compilerImpl)
+ || CLASSIC.equals(compilerImpl)
+ || JAVAC19.equals(compilerImpl)
+ || JAVAC18.equals(compilerImpl)
+ || JAVAC17.equals(compilerImpl)
+ || JAVAC16.equals(compilerImpl)
+ || JAVAC15.equals(compilerImpl)
+ || JAVAC14.equals(compilerImpl)
+ || JAVAC13.equals(compilerImpl)
+ || JAVAC12.equals(compilerImpl)
+ || JAVAC11.equals(compilerImpl);
+ }
+
+ /**
+ * @return the executable name of the java compiler
+ */
+ protected String getSystemJavac() {
+ return JavaEnvUtils.getJdkExecutable("javac");
+ }
+
+ /**
+ * Choose the implementation for this particular task.
+ * @param compiler the name of the compiler
+ * @since Ant 1.5
+ */
+ public void setCompiler(final String compiler) {
+ facade.setImplementation(compiler);
+ }
+
+ /**
+ * The implementation for this particular task.
+ *
+ * <p>Defaults to the build.compiler property but can be overridden
+ * via the compiler and fork attributes.</p>
+ *
+ * <p>If fork has been set to true, the result will be extJavac
+ * and not classic or java1.2 - no matter what the compiler
+ * attribute looks like.</p>
+ *
+ * @see #getCompilerVersion
+ * @return the compiler.
+ * @since Ant 1.5
+ */
+ public String getCompiler() {
+ String compilerImpl = getCompilerVersion();
+ if (fork) {
+ if (isJdkCompiler(compilerImpl)) {
+ compilerImpl = EXTJAVAC;
+ } else {
+ log("Since compiler setting isn't classic or modern, "
+ + "ignoring fork setting.", Project.MSG_WARN);
+ }
+ }
+ return compilerImpl;
+ }
+
+ /**
+ * The implementation for this particular task.
+ *
+ * <p>Defaults to the build.compiler property but can be overridden
+ * via the compiler attribute.</p>
+ *
+ * <p>This method does not take the fork attribute into
+ * account.</p>
+ *
+ * @see #getCompiler
+ * @return the compiler.
+ *
+ * @since Ant 1.5
+ */
+ public String getCompilerVersion() {
+ facade.setMagicValue(getProject().getProperty("build.compiler"));
+ return facade.getImplementation();
+ }
+
+ /**
+ * Check that all required attributes have been set and nothing
+ * silly has been entered.
+ *
+ * @since Ant 1.5
+ * @exception BuildException if an error occurs
+ */
+ protected void checkParameters() throws BuildException {
+ if (src == null) {
+ throw new BuildException("srcdir attribute must be set!",
+ getLocation());
+ }
+ if (src.size() == 0) {
+ throw new BuildException("srcdir attribute must be set!",
+ getLocation());
+ }
+
+ if (destDir != null && !destDir.isDirectory()) {
+ throw new BuildException("destination directory \""
+ + destDir
+ + "\" does not exist "
+ + "or is not a directory", getLocation());
+ }
+ if (includeAntRuntime == null && getProject().getProperty("build.sysclasspath") == null) {
+ log(getLocation() + "warning: 'includeantruntime' was not set, " +
+ "defaulting to build.sysclasspath=last; set to false for repeatable builds",
+ Project.MSG_WARN);
+ }
+ }
+
+ /**
+ * Perform the compilation.
+ *
+ * @since Ant 1.5
+ */
+ protected void compile() {
+ final String compilerImpl = getCompiler();
+
+ if (compileList.length > 0) {
+ log("Compiling " + compileList.length + " source file"
+ + (compileList.length == 1 ? "" : "s")
+ + (destDir != null ? " to " + destDir : ""));
+
+ if (listFiles) {
+ for (int i = 0; i < compileList.length; i++) {
+ final String filename = compileList[i].getAbsolutePath();
+ log(filename);
+ }
+ }
+
+ final CompilerAdapter adapter =
+ nestedAdapter != null ? nestedAdapter :
+ CompilerAdapterFactory.getCompiler(compilerImpl, this,
+ createCompilerClasspath());
+
+ // now we need to populate the compiler adapter
+ adapter.setJavac(this);
+
+ // finally, lets execute the compiler!!
+ if (adapter.execute()) {
+ // Success
+ if (createMissingPackageInfoClass) {
+ try {
+ generateMissingPackageInfoClasses(destDir != null
+ ? destDir
+ : getProject()
+ .resolveFile(src.list()[0]));
+ } catch (final IOException x) {
+ // Should this be made a nonfatal warning?
+ throw new BuildException(x, getLocation());
+ }
+ }
+ } else {
+ // Fail path
+ this.taskSuccess = false;
+ if (errorProperty != null) {
+ getProject().setNewProperty(
+ errorProperty, "true");
+ }
+ if (failOnError) {
+ throw new BuildException(FAIL_MSG, getLocation());
+ } else {
+ log(FAIL_MSG, Project.MSG_ERR);
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds an "compiler" attribute to Commandline$Attribute used to
+ * filter command line attributes based on the current
+ * implementation.
+ */
+ public class ImplementationSpecificArgument extends
+ org.apache.tools.ant.util.facade.ImplementationSpecificArgument {
+
+ /**
+ * @param impl the name of the compiler
+ */
+ public void setCompiler(final String impl) {
+ super.setImplementation(impl);
+ }
+ }
+
+ private void lookForPackageInfos(final File srcDir, final File[] newFiles) {
+ for (int i = 0; i < newFiles.length; i++) {
+ final File f = newFiles[i];
+ if (!f.getName().equals("package-info.java")) {
+ continue;
+ }
+ final String path = FILE_UTILS.removeLeadingPath(srcDir, f).
+ replace(File.separatorChar, '/');
+ final String suffix = "/package-info.java";
+ if (!path.endsWith(suffix)) {
+ log("anomalous package-info.java path: " + path, Project.MSG_WARN);
+ continue;
+ }
+ final String pkg = path.substring(0, path.length() - suffix.length());
+ packageInfos.put(pkg, new Long(f.lastModified()));
+ }
+ }
+
+ /**
+ * Ensure that every {@code package-info.java} produced a {@code package-info.class}.
+ * Otherwise this task's up-to-date tracking mechanisms do not work.
+ * @see <a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=43114">Bug #43114</a>
+ */
+ private void generateMissingPackageInfoClasses(final File dest) throws IOException {
+ for (final Entry<String, Long> entry : packageInfos.entrySet()) {
+ final String pkg = entry.getKey();
+ final Long sourceLastMod = entry.getValue();
+ final File pkgBinDir = new File(dest, pkg.replace('/', File.separatorChar));
+ pkgBinDir.mkdirs();
+ final File pkgInfoClass = new File(pkgBinDir, "package-info.class");
+ if (pkgInfoClass.isFile() && pkgInfoClass.lastModified() >= sourceLastMod.longValue()) {
+ continue;
+ }
+ log("Creating empty " + pkgInfoClass);
+ final OutputStream os = new FileOutputStream(pkgInfoClass);
+ try {
+ os.write(PACKAGE_INFO_CLASS_HEADER);
+ final byte[] name = pkg.getBytes("UTF-8");
+ final int length = name.length + /* "/package-info" */ 13;
+ os.write((byte) length / 256);
+ os.write((byte) length % 256);
+ os.write(name);
+ os.write(PACKAGE_INFO_CLASS_FOOTER);
+ } finally {
+ os.close();
+ }
+ }
+ }
+
+ private static final byte[] PACKAGE_INFO_CLASS_HEADER = {
+ (byte) 0xca, (byte) 0xfe, (byte) 0xba, (byte) 0xbe, 0x00, 0x00, 0x00,
+ 0x31, 0x00, 0x07, 0x07, 0x00, 0x05, 0x07, 0x00, 0x06, 0x01, 0x00, 0x0a,
+ 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x01, 0x00,
+ 0x11, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2d, 0x69, 0x6e, 0x66,
+ 0x6f, 0x2e, 0x6a, 0x61, 0x76, 0x61, 0x01
+ };
+
+ private static final byte[] PACKAGE_INFO_CLASS_FOOTER = {
+ 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2d, 0x69, 0x6e, 0x66,
+ 0x6f, 0x01, 0x00, 0x10, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e,
+ 0x67, 0x2f, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x02, 0x00, 0x00, 0x01,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x04
+ };
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Javadoc.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Javadoc.java
new file mode 100644
index 00000000..7637be74
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Javadoc.java
@@ -0,0 +1,2624 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.DirSet;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Generates Javadoc documentation for a collection
+ * of source code.
+ *
+ * <p>Current known limitations are:
+ *
+ * <p><ul>
+ * <li>patterns must be of the form "xxx.*", every other pattern doesn't
+ * work.
+ * <li>there is no control on arguments sanity since they are left
+ * to the Javadoc implementation.
+ * </ul>
+ *
+ * <p>If no <code>doclet</code> is set, then the <code>version</code> and
+ * <code>author</code> are by default <code>"yes"</code>.
+ *
+ * <p>Note: This task is run on another VM because the Javadoc code calls
+ * <code>System.exit()</code> which would break Ant functionality.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="java"
+ */
+public class Javadoc extends Task {
+ // Whether *this VM* is 1.4+ (but also check executable != null).
+
+ private static final boolean JAVADOC_5 =
+ !JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_4);
+
+ private static final String LOAD_FRAME = "function loadFrames() {";
+ private static final int LOAD_FRAME_LEN = LOAD_FRAME.length();
+
+ /**
+ * Inner class used to manage doclet parameters.
+ */
+ public class DocletParam {
+ /** The parameter name */
+ private String name;
+
+ /** The parameter value */
+ private String value;
+
+ /**
+ * Set the name of the parameter.
+ *
+ * @param name the name of the doclet parameter
+ */
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the parameter name.
+ *
+ * @return the parameter's name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the parameter value.
+ *
+ * Note that only string values are supported. No resolution of file
+ * paths is performed.
+ *
+ * @param value the parameter value.
+ */
+ public void setValue(final String value) {
+ this.value = value;
+ }
+
+ /**
+ * Get the parameter value.
+ *
+ * @return the parameter value.
+ */
+ public String getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * A project aware class used for Javadoc extensions which take a name
+ * and a path such as doclet and taglet arguments.
+ *
+ */
+ public static class ExtensionInfo extends ProjectComponent {
+ /** The name of the extension */
+ private String name;
+
+ /** The optional path to use to load the extension */
+ private Path path;
+
+ /**
+ * Set the name of the extension
+ *
+ * @param name the extension's name.
+ */
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the name of the extension.
+ *
+ * @return the extension's name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the path to use when loading the component.
+ *
+ * @param path a Path instance containing the classpath to use.
+ */
+ public void setPath(final Path path) {
+ if (this.path == null) {
+ this.path = path;
+ } else {
+ this.path.append(path);
+ }
+ }
+
+ /**
+ * Get the extension's path.
+ *
+ * @return the path to be used to load the extension.
+ * May be <code>null</code>
+ */
+ public Path getPath() {
+ return path;
+ }
+
+ /**
+ * Create an empty nested path to be configured by Ant with the
+ * classpath for the extension.
+ *
+ * @return a new Path instance to be configured.
+ */
+ public Path createPath() {
+ if (path == null) {
+ path = new Path(getProject());
+ }
+ return path.createPath();
+ }
+
+ /**
+ * Adds a reference to a CLASSPATH defined elsewhere.
+ *
+ * @param r the reference containing the path.
+ */
+ public void setPathRef(final Reference r) {
+ createPath().setRefid(r);
+ }
+ }
+
+ /**
+ * This class stores info about doclets.
+ *
+ */
+ public class DocletInfo extends ExtensionInfo {
+
+ /** Collection of doclet parameters. */
+ private final Vector<DocletParam> params = new Vector<DocletParam>();
+
+ /**
+ * Create a doclet parameter to be configured by Ant.
+ *
+ * @return a new DocletParam instance to be configured.
+ */
+ public DocletParam createParam() {
+ final DocletParam param = new DocletParam();
+ params.addElement(param);
+
+ return param;
+ }
+
+ /**
+ * Get the doclet's parameters.
+ *
+ * @return an Enumeration of DocletParam instances.
+ */
+ public Enumeration<DocletParam> getParams() {
+ return params.elements();
+ }
+ }
+
+ /**
+ * Used to track info about the packages to be javadoc'd
+ */
+ public static class PackageName {
+ /** The package name */
+ private String name;
+
+ /**
+ * Set the name of the package
+ *
+ * @param name the package name.
+ */
+ public void setName(final String name) {
+ this.name = name.trim();
+ }
+
+ /**
+ * Get the package name.
+ *
+ * @return the package's name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Return a string rep for this object.
+ * @return the package name.
+ */
+ @Override
+ public String toString() {
+ return getName();
+ }
+ }
+
+ /**
+ * This class is used to manage the source files to be processed.
+ */
+ public static class SourceFile {
+ /** The source file */
+ private File file;
+
+ /**
+ * Default constructor
+ */
+ public SourceFile() {
+ //empty
+ }
+
+ /**
+ * Constructor specifying the source file directly
+ *
+ * @param file the source file
+ */
+ public SourceFile(final File file) {
+ this.file = file;
+ }
+
+ /**
+ * Set the source file.
+ *
+ * @param file the source file.
+ */
+ public void setFile(final File file) {
+ this.file = file;
+ }
+
+ /**
+ * Get the source file.
+ *
+ * @return the source file.
+ */
+ public File getFile() {
+ return file;
+ }
+ }
+
+ /**
+ * An HTML element in the Javadoc.
+ *
+ * This class is used for those Javadoc elements which contain HTML such as
+ * footers, headers, etc.
+ */
+ public static class Html {
+ /** The text for the element */
+ private final StringBuffer text = new StringBuffer();
+
+ /**
+ * Add text to the element.
+ *
+ * @param t the text to be added.
+ */
+ public void addText(final String t) {
+ text.append(t);
+ }
+
+ /**
+ * Get the current text for the element.
+ *
+ * @return the current text.
+ */
+ public String getText() {
+ return text.substring(0);
+ }
+ }
+
+ /**
+ * EnumeratedAttribute implementation supporting the Javadoc scoping
+ * values.
+ */
+ public static class AccessType extends EnumeratedAttribute {
+ /**
+ * @return the allowed values for the access type.
+ */
+ @Override
+ public String[] getValues() {
+ // Protected first so if any GUI tool offers a default
+ // based on enum #0, it will be right.
+ return new String[] {"protected", "public", "package", "private"};
+ }
+ }
+
+ /**
+ * Holds a collection of ResourceCollections.
+ *
+ * <p>A separate kind of container is needed since this task
+ * contains special handling for FileSets that has to occur at
+ * task runtime.</p>
+ */
+ public class ResourceCollectionContainer {
+ private final ArrayList<ResourceCollection> rcs = new ArrayList<ResourceCollection>();
+ /**
+ * Add a resource collection to the container.
+ * @param rc the collection to add.
+ */
+ public void add(final ResourceCollection rc) {
+ rcs.add(rc);
+ }
+
+ /**
+ * Get an iterator on the collection.
+ * @return an iterator.
+ */
+ private Iterator<ResourceCollection> iterator() {
+ return rcs.iterator();
+ }
+ }
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** The command line built to execute Javadoc. */
+ private final Commandline cmd = new Commandline();
+
+ /**
+ * Utility method to add an argument to the command line conditionally
+ * based on the given flag.
+ *
+ * @param b the flag which controls if the argument is added.
+ * @param arg the argument value.
+ */
+ private void addArgIf(final boolean b, final String arg) {
+ if (b) {
+ cmd.createArgument().setValue(arg);
+ }
+ }
+
+ /**
+ * Utility method to add a Javadoc argument.
+ *
+ * @param key the argument name.
+ * @param value the argument value.
+ */
+ private void addArgIfNotEmpty(final String key, final String value) {
+ if (value != null && value.length() != 0) {
+ cmd.createArgument().setValue(key);
+ cmd.createArgument().setValue(value);
+ } else {
+ log("Warning: Leaving out empty argument '" + key + "'",
+ Project.MSG_WARN);
+ }
+ }
+
+ /**
+ * Flag which indicates if the task should fail if there is a
+ * Javadoc error.
+ */
+ private boolean failOnError = false;
+ /**
+ * Flag which indicates if the task should fail if there is a
+ * Javadoc warning.
+ */
+ private boolean failOnWarning = false;
+ private Path sourcePath = null;
+ private File destDir = null;
+ private final Vector<SourceFile> sourceFiles = new Vector<SourceFile>();
+ private final Vector<PackageName> packageNames = new Vector<PackageName>();
+ private final Vector<PackageName> excludePackageNames = new Vector<PackageName>(1);
+ private boolean author = true;
+ private boolean version = true;
+ private DocletInfo doclet = null;
+ private Path classpath = null;
+ private Path bootclasspath = null;
+ private String group = null;
+ private String packageList = null;
+ private final Vector<LinkArgument> links = new Vector<LinkArgument>();
+ private final Vector<GroupArgument> groups = new Vector<GroupArgument>();
+ private final Vector<Object> tags = new Vector<Object>();
+ private boolean useDefaultExcludes = true;
+ private Html doctitle = null;
+ private Html header = null;
+ private Html footer = null;
+ private Html bottom = null;
+ private boolean useExternalFile = false;
+ private String source = null;
+ private boolean linksource = false;
+ private boolean breakiterator = false;
+ private String noqualifier;
+ private boolean includeNoSourcePackages = false;
+ private String executable = null;
+ private boolean docFilesSubDirs = false;
+ private String excludeDocFilesSubDir = null;
+ private String docEncoding = null;
+ private boolean postProcessGeneratedJavadocs = true;
+
+ private final ResourceCollectionContainer nestedSourceFiles
+ = new ResourceCollectionContainer();
+ private final Vector<DirSet> packageSets = new Vector<DirSet>();
+
+ /**
+ * Work around command line length limit by using an external file
+ * for the sourcefiles.
+ *
+ * @param b true if an external file is to be used.
+ */
+ public void setUseExternalFile(final boolean b) {
+ useExternalFile = b;
+ }
+
+ /**
+ * Sets whether default exclusions should be used or not.
+ *
+ * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
+ * should be used, "false"|"off"|"no" when they
+ * shouldn't be used.
+ */
+ public void setDefaultexcludes(final boolean useDefaultExcludes) {
+ this.useDefaultExcludes = useDefaultExcludes;
+ }
+
+ /**
+ * Set the maximum memory to be used by the javadoc process
+ *
+ * @param max a string indicating the maximum memory according to the
+ * JVM conventions (e.g. 128m is 128 Megabytes)
+ */
+ public void setMaxmemory(final String max) {
+ cmd.createArgument().setValue("-J-Xmx" + max);
+ }
+
+ /**
+ * Set an additional parameter on the command line
+ *
+ * @param add the additional command line parameter for the javadoc task.
+ */
+ public void setAdditionalparam(final String add) {
+ cmd.createArgument().setLine(add);
+ }
+
+ /**
+ * Adds a command-line argument.
+ * @return a command-line argument to configure
+ * @since Ant 1.6
+ */
+ public Commandline.Argument createArg() {
+ return cmd.createArgument();
+ }
+
+ /**
+ * Specify where to find source file
+ *
+ * @param src a Path instance containing the various source directories.
+ */
+ public void setSourcepath(final Path src) {
+ if (sourcePath == null) {
+ sourcePath = src;
+ } else {
+ sourcePath.append(src);
+ }
+ }
+
+ /**
+ * Create a path to be configured with the locations of the source
+ * files.
+ *
+ * @return a new Path instance to be configured by the Ant core.
+ */
+ public Path createSourcepath() {
+ if (sourcePath == null) {
+ sourcePath = new Path(getProject());
+ }
+ return sourcePath.createPath();
+ }
+
+ /**
+ * Adds a reference to a CLASSPATH defined elsewhere.
+ *
+ * @param r the reference containing the source path definition.
+ */
+ public void setSourcepathRef(final Reference r) {
+ createSourcepath().setRefid(r);
+ }
+
+ /**
+ * Set the directory where the Javadoc output will be generated.
+ *
+ * @param dir the destination directory.
+ */
+ public void setDestdir(final File dir) {
+ destDir = dir;
+ cmd.createArgument().setValue("-d");
+ cmd.createArgument().setFile(destDir);
+ }
+
+ /**
+ * Set the list of source files to process.
+ *
+ * @param src a comma separated list of source files.
+ */
+ public void setSourcefiles(final String src) {
+ final StringTokenizer tok = new StringTokenizer(src, ",");
+ while (tok.hasMoreTokens()) {
+ final String f = tok.nextToken();
+ final SourceFile sf = new SourceFile();
+ sf.setFile(getProject().resolveFile(f.trim()));
+ addSource(sf);
+ }
+ }
+
+ /**
+ * Add a single source file.
+ *
+ * @param sf the source file to be processed.
+ */
+ public void addSource(final SourceFile sf) {
+ sourceFiles.addElement(sf);
+ }
+
+ /**
+ * Set the package names to be processed.
+ *
+ * @param packages a comma separated list of packages specs
+ * (may be wildcarded).
+ *
+ * @see #addPackage for wildcard information.
+ */
+ public void setPackagenames(final String packages) {
+ final StringTokenizer tok = new StringTokenizer(packages, ",");
+ while (tok.hasMoreTokens()) {
+ final String p = tok.nextToken();
+ final PackageName pn = new PackageName();
+ pn.setName(p);
+ addPackage(pn);
+ }
+ }
+
+ /**
+ * Add a single package to be processed.
+ *
+ * If the package name ends with &quot;.*&quot; the Javadoc task
+ * will find and process all subpackages.
+ *
+ * @param pn the package name, possibly wildcarded.
+ */
+ public void addPackage(final PackageName pn) {
+ packageNames.addElement(pn);
+ }
+
+ /**
+ * Set the list of packages to be excluded.
+ *
+ * @param packages a comma separated list of packages to be excluded.
+ * This may not include wildcards.
+ */
+ public void setExcludePackageNames(final String packages) {
+ final StringTokenizer tok = new StringTokenizer(packages, ",");
+ while (tok.hasMoreTokens()) {
+ final String p = tok.nextToken();
+ final PackageName pn = new PackageName();
+ pn.setName(p);
+ addExcludePackage(pn);
+ }
+ }
+
+ /**
+ * Add a package to be excluded from the Javadoc run.
+ *
+ * @param pn the name of the package (wildcards are not permitted).
+ */
+ public void addExcludePackage(final PackageName pn) {
+ excludePackageNames.addElement(pn);
+ }
+
+ /**
+ * Specify the file containing the overview to be included in the generated
+ * documentation.
+ *
+ * @param f the file containing the overview.
+ */
+ public void setOverview(final File f) {
+ cmd.createArgument().setValue("-overview");
+ cmd.createArgument().setFile(f);
+ }
+
+ /**
+ * Indicate whether only public classes and members are to be included in
+ * the scope processed
+ *
+ * @param b true if scope is to be public.
+ */
+ public void setPublic(final boolean b) {
+ addArgIf(b, "-public");
+ }
+
+ /**
+ * Indicate whether only protected and public classes and members are to
+ * be included in the scope processed
+ *
+ * @param b true if scope is to be protected.
+ */
+ public void setProtected(final boolean b) {
+ addArgIf(b, "-protected");
+ }
+
+ /**
+ * Indicate whether only package, protected and public classes and
+ * members are to be included in the scope processed
+ *
+ * @param b true if scope is to be package level.
+ */
+ public void setPackage(final boolean b) {
+ addArgIf(b, "-package");
+ }
+
+ /**
+ * Indicate whether all classes and
+ * members are to be included in the scope processed
+ *
+ * @param b true if scope is to be private level.
+ */
+ public void setPrivate(final boolean b) {
+ addArgIf(b, "-private");
+ }
+
+ /**
+ * Set the scope to be processed. This is an alternative to the
+ * use of the setPublic, setPrivate, etc methods. It gives better build
+ * file control over what scope is processed.
+ *
+ * @param at the scope to be processed.
+ */
+ public void setAccess(final AccessType at) {
+ cmd.createArgument().setValue("-" + at.getValue());
+ }
+
+ /**
+ * Set the class that starts the doclet used in generating the
+ * documentation.
+ *
+ * @param docletName the name of the doclet class.
+ */
+ public void setDoclet(final String docletName) {
+ if (doclet == null) {
+ doclet = new DocletInfo();
+ doclet.setProject(getProject());
+ }
+ doclet.setName(docletName);
+ }
+
+ /**
+ * Set the classpath used to find the doclet class.
+ *
+ * @param docletPath the doclet classpath.
+ */
+ public void setDocletPath(final Path docletPath) {
+ if (doclet == null) {
+ doclet = new DocletInfo();
+ doclet.setProject(getProject());
+ }
+ doclet.setPath(docletPath);
+ }
+
+ /**
+ * Set the classpath used to find the doclet class by reference.
+ *
+ * @param r the reference to the Path instance to use as the doclet
+ * classpath.
+ */
+ public void setDocletPathRef(final Reference r) {
+ if (doclet == null) {
+ doclet = new DocletInfo();
+ doclet.setProject(getProject());
+ }
+ doclet.createPath().setRefid(r);
+ }
+
+ /**
+ * Create a doclet to be used in the documentation generation.
+ *
+ * @return a new DocletInfo instance to be configured.
+ */
+ public DocletInfo createDoclet() {
+ if (doclet == null) {
+ doclet = new DocletInfo();
+ }
+ return doclet;
+ }
+
+ /**
+ * Add a taglet
+ *
+ * @param tagletInfo information about the taglet.
+ */
+ public void addTaglet(final ExtensionInfo tagletInfo) {
+ tags.addElement(tagletInfo);
+ }
+
+ /**
+ * Indicate whether Javadoc should produce old style (JDK 1.1)
+ * documentation.
+ *
+ * This is not supported by JDK 1.1 and has been phased out in JDK 1.4
+ *
+ * @param b if true attempt to generate old style documentation.
+ */
+ public void setOld(final boolean b) {
+ log("Javadoc 1.4 doesn't support the -1.1 switch anymore",
+ Project.MSG_WARN);
+ }
+
+ /**
+ * Set the classpath to be used for this Javadoc run.
+ *
+ * @param path an Ant Path object containing the compilation
+ * classpath.
+ */
+ public void setClasspath(final Path path) {
+ if (classpath == null) {
+ classpath = path;
+ } else {
+ classpath.append(path);
+ }
+ }
+
+ /**
+ * Create a Path to be configured with the classpath to use
+ *
+ * @return a new Path instance to be configured with the classpath.
+ */
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(getProject());
+ }
+ return classpath.createPath();
+ }
+
+ /**
+ * Adds a reference to a CLASSPATH defined elsewhere.
+ *
+ * @param r the reference to an instance defining the classpath.
+ */
+ public void setClasspathRef(final Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * Set the boot classpath to use.
+ *
+ * @param path the boot classpath.
+ */
+ public void setBootclasspath(final Path path) {
+ if (bootclasspath == null) {
+ bootclasspath = path;
+ } else {
+ bootclasspath.append(path);
+ }
+ }
+
+ /**
+ * Create a Path to be configured with the boot classpath
+ *
+ * @return a new Path instance to be configured with the boot classpath.
+ */
+ public Path createBootclasspath() {
+ if (bootclasspath == null) {
+ bootclasspath = new Path(getProject());
+ }
+ return bootclasspath.createPath();
+ }
+
+ /**
+ * Adds a reference to a CLASSPATH defined elsewhere.
+ *
+ * @param r the reference to an instance defining the bootclasspath.
+ */
+ public void setBootClasspathRef(final Reference r) {
+ createBootclasspath().setRefid(r);
+ }
+
+ /**
+ * Set the location of the extensions directories.
+ *
+ * @param path the string version of the path.
+ * @deprecated since 1.5.x.
+ * Use the {@link #setExtdirs(Path)} version.
+ */
+ @Deprecated
+ public void setExtdirs(final String path) {
+ cmd.createArgument().setValue("-extdirs");
+ cmd.createArgument().setValue(path);
+ }
+
+ /**
+ * Set the location of the extensions directories.
+ *
+ * @param path a path containing the extension directories.
+ */
+ public void setExtdirs(final Path path) {
+ cmd.createArgument().setValue("-extdirs");
+ cmd.createArgument().setPath(path);
+ }
+
+ /**
+ * Run javadoc in verbose mode
+ *
+ * @param b true if operation is to be verbose.
+ */
+ public void setVerbose(final boolean b) {
+ addArgIf(b, "-verbose");
+ }
+
+ /**
+ * Set the local to use in documentation generation.
+ *
+ * @param locale the locale to use.
+ */
+ public void setLocale(final String locale) {
+ // createArgument(true) is necessary to make sure -locale
+ // is the first argument (required in 1.3+).
+ cmd.createArgument(true).setValue(locale);
+ cmd.createArgument(true).setValue("-locale");
+ }
+
+ /**
+ * Set the encoding name of the source files,
+ *
+ * @param enc the name of the encoding for the source files.
+ */
+ public void setEncoding(final String enc) {
+ cmd.createArgument().setValue("-encoding");
+ cmd.createArgument().setValue(enc);
+ }
+
+ /**
+ * Include the version tag in the generated documentation.
+ *
+ * @param b true if the version tag should be included.
+ */
+ public void setVersion(final boolean b) {
+ this.version = b;
+ }
+
+ /**
+ * Generate the &quot;use&quot; page for each package.
+ *
+ * @param b true if the use page should be generated.
+ */
+ public void setUse(final boolean b) {
+ addArgIf(b, "-use");
+ }
+
+
+ /**
+ * Include the author tag in the generated documentation.
+ *
+ * @param b true if the author tag should be included.
+ */
+ public void setAuthor(final boolean b) {
+ author = b;
+ }
+
+ /**
+ * Generate a split index
+ *
+ * @param b true if the index should be split into a file per letter.
+ */
+ public void setSplitindex(final boolean b) {
+ addArgIf(b, "-splitindex");
+ }
+
+ /**
+ * Set the title to be placed in the HTML &lt;title&gt; tag of the
+ * generated documentation.
+ *
+ * @param title the window title to use.
+ */
+ public void setWindowtitle(final String title) {
+ addArgIfNotEmpty("-windowtitle", title);
+ }
+
+ /**
+ * Set the title of the generated overview page.
+ *
+ * @param doctitle the Document title.
+ */
+ public void setDoctitle(final String doctitle) {
+ final Html h = new Html();
+ h.addText(doctitle);
+ addDoctitle(h);
+ }
+
+ /**
+ * Add a document title to use for the overview page.
+ *
+ * @param text the HTML element containing the document title.
+ */
+ public void addDoctitle(final Html text) {
+ doctitle = text;
+ }
+
+ /**
+ * Set the header text to be placed at the top of each output file.
+ *
+ * @param header the header text
+ */
+ public void setHeader(final String header) {
+ final Html h = new Html();
+ h.addText(header);
+ addHeader(h);
+ }
+
+ /**
+ * Set the header text to be placed at the top of each output file.
+ *
+ * @param text the header text
+ */
+ public void addHeader(final Html text) {
+ header = text;
+ }
+
+ /**
+ * Set the footer text to be placed at the bottom of each output file.
+ *
+ * @param footer the footer text.
+ */
+ public void setFooter(final String footer) {
+ final Html h = new Html();
+ h.addText(footer);
+ addFooter(h);
+ }
+
+ /**
+ * Set the footer text to be placed at the bottom of each output file.
+ *
+ * @param text the footer text.
+ */
+ public void addFooter(final Html text) {
+ footer = text;
+ }
+
+ /**
+ * Set the text to be placed at the bottom of each output file.
+ *
+ * @param bottom the bottom text.
+ */
+ public void setBottom(final String bottom) {
+ final Html h = new Html();
+ h.addText(bottom);
+ addBottom(h);
+ }
+
+ /**
+ * Set the text to be placed at the bottom of each output file.
+ *
+ * @param text the bottom text.
+ */
+ public void addBottom(final Html text) {
+ bottom = text;
+ }
+
+ /**
+ * Link to docs at "url" using package list at "url2"
+ * - separate the URLs by using a space character.
+ *
+ * @param src the offline link specification (url and package list)
+ */
+ public void setLinkoffline(final String src) {
+ final LinkArgument le = createLink();
+ le.setOffline(true);
+ final String linkOfflineError = "The linkoffline attribute must include"
+ + " a URL and a package-list file location separated by a"
+ + " space";
+ if (src.trim().length() == 0) {
+ throw new BuildException(linkOfflineError);
+ }
+ final StringTokenizer tok = new StringTokenizer(src, " ", false);
+ le.setHref(tok.nextToken());
+
+ if (!tok.hasMoreTokens()) {
+ throw new BuildException(linkOfflineError);
+ }
+ le.setPackagelistLoc(getProject().resolveFile(tok.nextToken()));
+ }
+
+ /**
+ * Group specified packages together in overview page.
+ *
+ * @param src the group packages - a command separated list of group specs,
+ * each one being a group name and package specification separated
+ * by a space.
+ */
+ public void setGroup(final String src) {
+ group = src;
+ }
+
+ /**
+ * Create links to Javadoc output at the given URL.
+ * @param src the URL to link to
+ */
+ public void setLink(final String src) {
+ createLink().setHref(src);
+ }
+
+ /**
+ * Control deprecation information
+ *
+ * @param b If true, do not include deprecated information.
+ */
+ public void setNodeprecated(final boolean b) {
+ addArgIf(b, "-nodeprecated");
+ }
+
+ /**
+ * Control deprecated list generation
+ *
+ * @param b if true, do not generate deprecated list.
+ */
+ public void setNodeprecatedlist(final boolean b) {
+ addArgIf(b, "-nodeprecatedlist");
+ }
+
+ /**
+ * Control class tree generation.
+ *
+ * @param b if true, do not generate class hierarchy.
+ */
+ public void setNotree(final boolean b) {
+ addArgIf(b, "-notree");
+ }
+
+ /**
+ * Control generation of index.
+ *
+ * @param b if true, do not generate index.
+ */
+ public void setNoindex(final boolean b) {
+ addArgIf(b, "-noindex");
+ }
+
+ /**
+ * Control generation of help link.
+ *
+ * @param b if true, do not generate help link
+ */
+ public void setNohelp(final boolean b) {
+ addArgIf(b, "-nohelp");
+ }
+
+ /**
+ * Control generation of the navigation bar.
+ *
+ * @param b if true, do not generate navigation bar.
+ */
+ public void setNonavbar(final boolean b) {
+ addArgIf(b, "-nonavbar");
+ }
+
+ /**
+ * Control warnings about serial tag.
+ *
+ * @param b if true, generate warning about the serial tag.
+ */
+ public void setSerialwarn(final boolean b) {
+ addArgIf(b, "-serialwarn");
+ }
+
+ /**
+ * Specifies the CSS stylesheet file to use.
+ *
+ * @param f the file with the CSS to use.
+ */
+ public void setStylesheetfile(final File f) {
+ cmd.createArgument().setValue("-stylesheetfile");
+ cmd.createArgument().setFile(f);
+ }
+
+ /**
+ * Specifies the HTML help file to use.
+ *
+ * @param f the file containing help content.
+ */
+ public void setHelpfile(final File f) {
+ cmd.createArgument().setValue("-helpfile");
+ cmd.createArgument().setFile(f);
+ }
+
+ /**
+ * Output file encoding name.
+ *
+ * @param enc name of the encoding to use.
+ */
+ public void setDocencoding(final String enc) {
+ cmd.createArgument().setValue("-docencoding");
+ cmd.createArgument().setValue(enc);
+ docEncoding = enc;
+ }
+
+ /**
+ * The name of a file containing the packages to process.
+ *
+ * @param src the file containing the package list.
+ */
+ public void setPackageList(final String src) {
+ packageList = src;
+ }
+
+ /**
+ * Create link to Javadoc output at the given URL.
+ *
+ * @return link argument to configure
+ */
+ public LinkArgument createLink() {
+ final LinkArgument la = new LinkArgument();
+ links.addElement(la);
+ return la;
+ }
+
+ /**
+ * Represents a link triplet (href, whether link is offline,
+ * location of the package list if off line)
+ */
+ public class LinkArgument {
+ private String href;
+ private boolean offline = false;
+ private File packagelistLoc;
+ private URL packagelistURL;
+ private boolean resolveLink = false;
+
+ /** Constructor for LinkArgument */
+ public LinkArgument() {
+ //empty
+ }
+
+ /**
+ * Set the href attribute.
+ * @param hr a <code>String</code> value
+ */
+ public void setHref(final String hr) {
+ href = hr;
+ }
+
+ /**
+ * Get the href attribute.
+ * @return the href attribute.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Set the packetlist location attribute.
+ * @param src a <code>File</code> value
+ */
+ public void setPackagelistLoc(final File src) {
+ packagelistLoc = src;
+ }
+
+ /**
+ * Get the packetList location attribute.
+ * @return the packetList location attribute.
+ */
+ public File getPackagelistLoc() {
+ return packagelistLoc;
+ }
+
+ /**
+ * Set the packetlist location attribute.
+ * @param src an <code>URL</code> value
+ */
+ public void setPackagelistURL(final URL src) {
+ packagelistURL = src;
+ }
+
+ /**
+ * Get the packetList location attribute.
+ * @return the packetList location attribute.
+ */
+ public URL getPackagelistURL() {
+ return packagelistURL;
+ }
+
+ /**
+ * Set the offline attribute.
+ * @param offline a <code>boolean</code> value
+ */
+ public void setOffline(final boolean offline) {
+ this.offline = offline;
+ }
+
+ /**
+ * Get the linkOffline attribute.
+ * @return the linkOffline attribute.
+ */
+ public boolean isLinkOffline() {
+ return offline;
+ }
+
+ /**
+ * Sets whether Ant should resolve the link attribute relative
+ * to the current basedir.
+ * @param resolve a <code>boolean</code> value
+ */
+ public void setResolveLink(final boolean resolve) {
+ this.resolveLink = resolve;
+ }
+
+ /**
+ * should Ant resolve the link attribute relative to the
+ * current basedir?
+ * @return the resolveLink attribute.
+ */
+ public boolean shouldResolveLink() {
+ return resolveLink;
+ }
+
+ }
+
+ /**
+ * Creates and adds a -tag argument. This is used to specify
+ * custom tags. This argument is only available for Javadoc 1.4,
+ * and will generate a verbose message (and then be ignored)
+ * when run on Java versions below 1.4.
+ * @return tag argument to be configured
+ */
+ public TagArgument createTag() {
+ final TagArgument ta = new TagArgument();
+ tags.addElement (ta);
+ return ta;
+ }
+
+ /**
+ * Scope element verbose names. (Defined here as fields
+ * cannot be static in inner classes.) The first letter
+ * from each element is used to build up the scope string.
+ */
+ static final String[] SCOPE_ELEMENTS = {
+ "overview", "packages", "types", "constructors",
+ "methods", "fields"
+ };
+
+ /**
+ * Class representing a -tag argument.
+ */
+ public class TagArgument extends FileSet {
+ /** Name of the tag. */
+ private String name = null;
+ /** Whether or not the tag is enabled. */
+ private boolean enabled = true;
+ /**
+ * Scope string of the tag. This will form the middle
+ * argument of the -tag parameter when the tag is enabled
+ * (with an X prepended for and is parsed from human-readable form.
+ */
+ private String scope = "a";
+
+ /** Sole constructor. */
+ public TagArgument () {
+ //empty
+ }
+
+ /**
+ * Sets the name of the tag.
+ *
+ * @param name The name of the tag.
+ * Must not be <code>null</code> or empty.
+ */
+ public void setName (final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Sets the scope of the tag. This is in comma-separated
+ * form, with each element being one of "all" (the default),
+ * "overview", "packages", "types", "constructors", "methods",
+ * "fields". The elements are treated in a case-insensitive
+ * manner.
+ *
+ * @param verboseScope The scope of the tag.
+ * Must not be <code>null</code>,
+ * should not be empty.
+ *
+ * @exception BuildException if all is specified along with
+ * other elements, if any elements are repeated, if no
+ * elements are specified, or if any unrecognised elements are
+ * specified.
+ */
+ public void setScope (String verboseScope) throws BuildException {
+ verboseScope = verboseScope.toLowerCase(Locale.ENGLISH);
+
+ final boolean[] elements = new boolean[SCOPE_ELEMENTS.length];
+
+ boolean gotAll = false;
+ boolean gotNotAll = false;
+
+ // Go through the tokens one at a time, updating the
+ // elements array and issuing warnings where appropriate.
+ final StringTokenizer tok = new StringTokenizer (verboseScope, ",");
+ while (tok.hasMoreTokens()) {
+ final String next = tok.nextToken().trim();
+ if (next.equals("all")) {
+ if (gotAll) {
+ getProject().log ("Repeated tag scope element: all",
+ Project.MSG_VERBOSE);
+ }
+ gotAll = true;
+ } else {
+ int i;
+ for (i = 0; i < SCOPE_ELEMENTS.length; i++) {
+ if (next.equals (SCOPE_ELEMENTS[i])) {
+ break;
+ }
+ }
+ if (i == SCOPE_ELEMENTS.length) {
+ throw new BuildException ("Unrecognised scope element: "
+ + next);
+ } else {
+ if (elements[i]) {
+ getProject().log ("Repeated tag scope element: "
+ + next, Project.MSG_VERBOSE);
+ }
+ elements[i] = true;
+ gotNotAll = true;
+ }
+ }
+ }
+
+ if (gotNotAll && gotAll) {
+ throw new BuildException ("Mixture of \"all\" and other scope "
+ + "elements in tag parameter.");
+ }
+ if (!gotNotAll && !gotAll) {
+ throw new BuildException ("No scope elements specified in tag "
+ + "parameter.");
+ }
+ if (gotAll) {
+ this.scope = "a";
+ } else {
+ final StringBuffer buff = new StringBuffer (elements.length);
+ for (int i = 0; i < elements.length; i++) {
+ if (elements[i]) {
+ buff.append (SCOPE_ELEMENTS[i].charAt(0));
+ }
+ }
+ this.scope = buff.toString();
+ }
+ }
+
+ /**
+ * Sets whether or not the tag is enabled.
+ *
+ * @param enabled Whether or not this tag is enabled.
+ */
+ public void setEnabled (final boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ /**
+ * Returns the -tag parameter this argument represented.
+ * @return the -tag parameter as a string
+ * @exception BuildException if either the name or description
+ * is <code>null</code> or empty.
+ */
+ public String getParameter() throws BuildException {
+ if (name == null || name.equals("")) {
+ throw new BuildException ("No name specified for custom tag.");
+ }
+ if (getDescription() != null) {
+ return name + ":" + (enabled ? "" : "X")
+ + scope + ":" + getDescription();
+ } else if (!enabled || !"a".equals(scope)) {
+ return name + ":" + (enabled ? "" : "X") + scope;
+ } else {
+ return name;
+ }
+ }
+ }
+
+ /**
+ * Separates packages on the overview page into whatever
+ * groups you specify, one group per table.
+ * @return a group argument to be configured
+ */
+ public GroupArgument createGroup() {
+ final GroupArgument ga = new GroupArgument();
+ groups.addElement(ga);
+ return ga;
+ }
+
+
+ /**
+ * A class corresponding to the group nested element.
+ */
+ public class GroupArgument {
+ private Html title;
+ private final Vector<PackageName> packages = new Vector<PackageName>();
+
+ /** Constructor for GroupArgument */
+ public GroupArgument() {
+ //empty
+ }
+
+ /**
+ * Set the title attribute using a string.
+ * @param src a <code>String</code> value
+ */
+ public void setTitle(final String src) {
+ final Html h = new Html();
+ h.addText(src);
+ addTitle(h);
+ }
+ /**
+ * Set the title attribute using a nested Html value.
+ * @param text a <code>Html</code> value
+ */
+ public void addTitle(final Html text) {
+ title = text;
+ }
+
+ /**
+ * Get the title.
+ * @return the title
+ */
+ public String getTitle() {
+ return title != null ? title.getText() : null;
+ }
+
+ /**
+ * Set the packages to Javadoc on.
+ * @param src a comma separated list of packages
+ */
+ public void setPackages(final String src) {
+ final StringTokenizer tok = new StringTokenizer(src, ",");
+ while (tok.hasMoreTokens()) {
+ final String p = tok.nextToken();
+ final PackageName pn = new PackageName();
+ pn.setName(p);
+ addPackage(pn);
+ }
+ }
+ /**
+ * Add a package nested element.
+ * @param pn a nested element specifying the package.
+ */
+ public void addPackage(final PackageName pn) {
+ packages.addElement(pn);
+ }
+
+ /**
+ * Get the packages as a colon separated list.
+ * @return the packages as a string
+ */
+ public String getPackages() {
+ final StringBuffer p = new StringBuffer();
+ final int size = packages.size();
+ for (int i = 0; i < size; i++) {
+ if (i > 0) {
+ p.append(":");
+ }
+ p.append(packages.elementAt(i).toString());
+ }
+ return p.toString();
+ }
+ }
+
+ /**
+ * Charset for cross-platform viewing of generated documentation.
+ * @param src the name of the charset
+ */
+ public void setCharset(final String src) {
+ this.addArgIfNotEmpty("-charset", src);
+ }
+
+ /**
+ * Should the build process fail if Javadoc fails (as indicated by
+ * a non zero return code)?
+ *
+ * <p>Default is false.</p>
+ * @param b a <code>boolean</code> value
+ */
+ public void setFailonerror(final boolean b) {
+ failOnError = b;
+ }
+
+ /**
+ * Should the build process fail if Javadoc warns (as indicated by
+ * the word "warning" on stdout)?
+ *
+ * <p>Default is false.</p>
+ * @param b a <code>boolean</code> value
+ * @since Ant 1.9.4
+ */
+ public void setFailonwarning(final boolean b) {
+ failOnWarning = b;
+ }
+
+ /**
+ * Enables the -source switch, will be ignored if Javadoc is not
+ * the 1.4 version.
+ * @param source a <code>String</code> value
+ * @since Ant 1.5
+ */
+ public void setSource(final String source) {
+ this.source = source;
+ }
+
+ /**
+ * Sets the actual executable command to invoke, instead of the binary
+ * <code>javadoc</code> found in Ant's JDK.
+ * @param executable the command to invoke.
+ * @since Ant 1.6.3
+ */
+ public void setExecutable(final String executable) {
+ this.executable = executable;
+ }
+
+ /**
+ * Adds a packageset.
+ *
+ * <p>All included directories will be translated into package
+ * names be converting the directory separator into dots.</p>
+ * @param packageSet a directory set
+ * @since 1.5
+ */
+ public void addPackageset(final DirSet packageSet) {
+ packageSets.addElement(packageSet);
+ }
+
+ /**
+ * Adds a fileset.
+ *
+ * <p>All included files will be added as sourcefiles. The task
+ * will automatically add
+ * <code>includes=&quot;**&#47;*.java&quot;</code> to the
+ * fileset.</p>
+ * @param fs a file set
+ * @since 1.5
+ */
+ public void addFileset(final FileSet fs) {
+ createSourceFiles().add(fs);
+ }
+
+ /**
+ * Adds a container for resource collections.
+ *
+ * <p>All included files will be added as sourcefiles.</p>
+ * @return the source files to configure.
+ * @since 1.7
+ */
+ public ResourceCollectionContainer createSourceFiles() {
+ return nestedSourceFiles;
+ }
+
+ /**
+ * Enables the -linksource switch, will be ignored if Javadoc is not
+ * the 1.4 version. Default is false
+ * @param b a <code>String</code> value
+ * @since Ant 1.6
+ */
+ public void setLinksource(final boolean b) {
+ this.linksource = b;
+ }
+
+ /**
+ * Enables the -linksource switch, will be ignored if Javadoc is not
+ * the 1.4 version. Default is false
+ * @param b a <code>String</code> value
+ * @since Ant 1.6
+ */
+ public void setBreakiterator(final boolean b) {
+ this.breakiterator = b;
+ }
+
+ /**
+ * Enables the -noqualifier switch, will be ignored if Javadoc is not
+ * the 1.4 version.
+ * @param noqualifier the parameter to the -noqualifier switch
+ * @since Ant 1.6
+ */
+ public void setNoqualifier(final String noqualifier) {
+ this.noqualifier = noqualifier;
+ }
+
+ /**
+ * If set to true, Ant will also accept packages that only hold
+ * package.html files but no Java sources.
+ * @param b a <code>boolean</code> value.
+ * @since Ant 1.6.3
+ */
+ public void setIncludeNoSourcePackages(final boolean b) {
+ this.includeNoSourcePackages = b;
+ }
+
+ /**
+ * Enables deep-copying of <code>doc-files</code> directories.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setDocFilesSubDirs(final boolean b) {
+ docFilesSubDirs = b;
+ }
+
+ /**
+ * Colon-separated list of <code>doc-files</code> subdirectories
+ * to skip if {@link #setDocFilesSubDirs docFilesSubDirs is true}.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setExcludeDocFilesSubDir(final String s) {
+ excludeDocFilesSubDir = s;
+ }
+
+ /**
+ * Whether to post-process the generated javadocs in order to mitigate CVE-2013-1571.
+ * @since Ant 1.9.2
+ */
+ public void setPostProcessGeneratedJavadocs(final boolean b) {
+ postProcessGeneratedJavadocs = b;
+ }
+
+ /**
+ * Execute the task.
+ * @throws BuildException on error
+ */
+ @Override
+ public void execute() throws BuildException {
+ checkTaskName();
+
+ final Vector<String> packagesToDoc = new Vector<String>();
+ final Path sourceDirs = new Path(getProject());
+
+ checkPackageAndSourcePath();
+
+ if (sourcePath != null) {
+ sourceDirs.addExisting(sourcePath);
+ }
+
+ parsePackages(packagesToDoc, sourceDirs);
+ checkPackages(packagesToDoc, sourceDirs);
+
+ @SuppressWarnings("unchecked")
+ final Vector<SourceFile> sourceFilesToDoc = (Vector<SourceFile>) sourceFiles.clone();
+ addSourceFiles(sourceFilesToDoc);
+
+ checkPackagesToDoc(packagesToDoc, sourceFilesToDoc);
+
+ log("Generating Javadoc", Project.MSG_INFO);
+
+ final Commandline toExecute = (Commandline) cmd.clone();
+ if (executable != null) {
+ toExecute.setExecutable(executable);
+ } else {
+ toExecute.setExecutable(JavaEnvUtils.getJdkExecutable("javadoc"));
+ }
+
+ // Javadoc arguments
+ generalJavadocArguments(toExecute); // general Javadoc arguments
+ doSourcePath(toExecute, sourceDirs); // sourcepath
+ doDoclet(toExecute); // arguments for default doclet
+ doBootPath(toExecute); // bootpath
+ doLinks(toExecute); // links arguments
+ doGroup(toExecute); // group attribute
+ doGroups(toExecute); // groups attribute
+ doDocFilesSubDirs(toExecute); // docfilessubdir attribute
+
+ doJava14(toExecute);
+ if (breakiterator && (doclet == null || JAVADOC_5)) {
+ toExecute.createArgument().setValue("-breakiterator");
+ }
+ // If using an external file, write the command line options to it
+ if (useExternalFile) {
+ writeExternalArgs(toExecute);
+ }
+
+ File tmpList = null;
+ FileWriter wr = null;
+ try {
+ /**
+ * Write sourcefiles and package names to a temporary file
+ * if requested.
+ */
+ BufferedWriter srcListWriter = null;
+ if (useExternalFile) {
+ tmpList = FILE_UTILS.createTempFile("javadoc", "", null, true, true);
+ toExecute.createArgument()
+ .setValue("@" + tmpList.getAbsolutePath());
+ wr = new FileWriter(tmpList.getAbsolutePath(), true);
+ srcListWriter = new BufferedWriter(wr);
+ }
+
+ doSourceAndPackageNames(
+ toExecute, packagesToDoc, sourceFilesToDoc,
+ useExternalFile, tmpList, srcListWriter);
+
+ if (useExternalFile) {
+ srcListWriter.flush();
+ }
+ } catch (final IOException e) {
+ tmpList.delete();
+ throw new BuildException("Error creating temporary file",
+ e, getLocation());
+ } finally {
+ FileUtils.close(wr);
+ }
+
+ if (packageList != null) {
+ toExecute.createArgument().setValue("@" + packageList);
+ }
+ log(toExecute.describeCommand(), Project.MSG_VERBOSE);
+
+ log("Javadoc execution", Project.MSG_INFO);
+
+ final JavadocOutputStream out = new JavadocOutputStream(Project.MSG_INFO);
+ final JavadocOutputStream err = new JavadocOutputStream(Project.MSG_WARN);
+ final Execute exe = new Execute(new PumpStreamHandler(out, err));
+ exe.setAntRun(getProject());
+
+ /*
+ * No reason to change the working directory as all filenames and
+ * path components have been resolved already.
+ *
+ * Avoid problems with command line length in some environments.
+ */
+ exe.setWorkingDirectory(null);
+ try {
+ exe.setCommandline(toExecute.getCommandline());
+ final int ret = exe.execute();
+ if (ret != 0 && failOnError) {
+ throw new BuildException("Javadoc returned " + ret,
+ getLocation());
+ }
+ if (out.sawWarnings() && failOnWarning) {
+ throw new BuildException("Javadoc issued warnings.",
+ getLocation());
+ }
+ postProcessGeneratedJavadocs();
+ } catch (final IOException e) {
+ throw new BuildException("Javadoc failed: " + e, e, getLocation());
+ } finally {
+ if (tmpList != null) {
+ tmpList.delete();
+ tmpList = null;
+ }
+
+ out.logFlush();
+ err.logFlush();
+ try {
+ out.close();
+ err.close();
+ } catch (final IOException e) {
+ // ignore
+ }
+ }
+ }
+
+ private void checkTaskName() {
+ if ("javadoc2".equals(getTaskType())) {
+ log("Warning: the task name <javadoc2> is deprecated."
+ + " Use <javadoc> instead.",
+ Project.MSG_WARN);
+ }
+ }
+
+ private void checkPackageAndSourcePath() {
+ if (packageList != null && sourcePath == null) {
+ final String msg = "sourcePath attribute must be set when "
+ + "specifying packagelist.";
+ throw new BuildException(msg);
+ }
+ }
+
+ private void checkPackages(final Vector<String> packagesToDoc, final Path sourceDirs) {
+ if (packagesToDoc.size() != 0 && sourceDirs.size() == 0) {
+ final String msg = "sourcePath attribute must be set when "
+ + "specifying package names.";
+ throw new BuildException(msg);
+ }
+ }
+
+ private void checkPackagesToDoc(
+ final Vector<String> packagesToDoc, final Vector<SourceFile> sourceFilesToDoc) {
+ if (packageList == null && packagesToDoc.size() == 0
+ && sourceFilesToDoc.size() == 0) {
+ throw new BuildException("No source files and no packages have "
+ + "been specified.");
+ }
+ }
+
+ private void doSourcePath(final Commandline toExecute, final Path sourceDirs) {
+ if (sourceDirs.size() > 0) {
+ toExecute.createArgument().setValue("-sourcepath");
+ toExecute.createArgument().setPath(sourceDirs);
+ }
+ }
+
+ private void generalJavadocArguments(final Commandline toExecute) {
+ if (doctitle != null) {
+ toExecute.createArgument().setValue("-doctitle");
+ toExecute.createArgument().setValue(expand(doctitle.getText()));
+ }
+ if (header != null) {
+ toExecute.createArgument().setValue("-header");
+ toExecute.createArgument().setValue(expand(header.getText()));
+ }
+ if (footer != null) {
+ toExecute.createArgument().setValue("-footer");
+ toExecute.createArgument().setValue(expand(footer.getText()));
+ }
+ if (bottom != null) {
+ toExecute.createArgument().setValue("-bottom");
+ toExecute.createArgument().setValue(expand(bottom.getText()));
+ }
+
+ if (classpath == null) {
+ classpath = (new Path(getProject())).concatSystemClasspath("last");
+ } else {
+ classpath = classpath.concatSystemClasspath("ignore");
+ }
+
+ if (classpath.size() > 0) {
+ toExecute.createArgument().setValue("-classpath");
+ toExecute.createArgument().setPath(classpath);
+ }
+
+ if (version && doclet == null) {
+ toExecute.createArgument().setValue("-version");
+ }
+ if (author && doclet == null) {
+ toExecute.createArgument().setValue("-author");
+ }
+
+ if (doclet == null && destDir == null) {
+ throw new BuildException("destdir attribute must be set!");
+ }
+ }
+
+ private void doDoclet(final Commandline toExecute) {
+ if (doclet != null) {
+ if (doclet.getName() == null) {
+ throw new BuildException("The doclet name must be "
+ + "specified.", getLocation());
+ } else {
+ toExecute.createArgument().setValue("-doclet");
+ toExecute.createArgument().setValue(doclet.getName());
+ if (doclet.getPath() != null) {
+ final Path docletPath
+ = doclet.getPath().concatSystemClasspath("ignore");
+ if (docletPath.size() != 0) {
+ toExecute.createArgument().setValue("-docletpath");
+ toExecute.createArgument().setPath(docletPath);
+ }
+ }
+ for (final Enumeration<DocletParam> e = doclet.getParams();
+ e.hasMoreElements();) {
+ final DocletParam param = e.nextElement();
+ if (param.getName() == null) {
+ throw new BuildException("Doclet parameters must "
+ + "have a name");
+ }
+
+ toExecute.createArgument().setValue(param.getName());
+ if (param.getValue() != null) {
+ toExecute.createArgument()
+ .setValue(param.getValue());
+ }
+ }
+ }
+ }
+ }
+
+ private void writeExternalArgs(final Commandline toExecute) {
+ // If using an external file, write the command line options to it
+ File optionsTmpFile = null;
+ BufferedWriter optionsListWriter = null;
+ try {
+ optionsTmpFile = FILE_UTILS.createTempFile(
+ "javadocOptions", "", null, true, true);
+ final String[] listOpt = toExecute.getArguments();
+ toExecute.clearArgs();
+ toExecute.createArgument().setValue(
+ "@" + optionsTmpFile.getAbsolutePath());
+ optionsListWriter = new BufferedWriter(
+ new FileWriter(optionsTmpFile.getAbsolutePath(), true));
+ for (int i = 0; i < listOpt.length; i++) {
+ final String string = listOpt[i];
+ if (string.startsWith("-J-")) {
+ toExecute.createArgument().setValue(string);
+ } else {
+ if (string.startsWith("-")) {
+ optionsListWriter.write(string);
+ optionsListWriter.write(" ");
+ } else {
+ optionsListWriter.write(quoteString(string));
+ optionsListWriter.newLine();
+ }
+ }
+ }
+ optionsListWriter.close();
+ } catch (final IOException ex) {
+ if (optionsTmpFile != null) {
+ optionsTmpFile.delete();
+ }
+ throw new BuildException(
+ "Error creating or writing temporary file for javadoc options",
+ ex, getLocation());
+ } finally {
+ FileUtils.close(optionsListWriter);
+ }
+ }
+
+ private void doBootPath(final Commandline toExecute) {
+ Path bcp = new Path(getProject());
+ if (bootclasspath != null) {
+ bcp.append(bootclasspath);
+ }
+ bcp = bcp.concatSystemBootClasspath("ignore");
+ if (bcp.size() > 0) {
+ toExecute.createArgument().setValue("-bootclasspath");
+ toExecute.createArgument().setPath(bcp);
+ }
+ }
+
+ private void doLinks(final Commandline toExecute) {
+ if (links.size() != 0) {
+ for (final Enumeration<LinkArgument> e = links.elements(); e.hasMoreElements();) {
+ final LinkArgument la = e.nextElement();
+
+ if (la.getHref() == null || la.getHref().length() == 0) {
+ log("No href was given for the link - skipping",
+ Project.MSG_VERBOSE);
+ continue;
+ }
+ String link = null;
+ if (la.shouldResolveLink()) {
+ final File hrefAsFile =
+ getProject().resolveFile(la.getHref());
+ if (hrefAsFile.exists()) {
+ try {
+ link = FILE_UTILS.getFileURL(hrefAsFile)
+ .toExternalForm();
+ } catch (final MalformedURLException ex) {
+ // should be impossible
+ log("Warning: link location was invalid "
+ + hrefAsFile, Project.MSG_WARN);
+ }
+ }
+ }
+ if (link == null) {
+ // is the href a valid URL
+ try {
+ final URL base = new URL("file://.");
+ new URL(base, la.getHref());
+ link = la.getHref();
+ } catch (final MalformedURLException mue) {
+ // ok - just skip
+ log("Link href \"" + la.getHref()
+ + "\" is not a valid url - skipping link",
+ Project.MSG_WARN);
+ continue;
+ }
+ }
+
+ if (la.isLinkOffline()) {
+ final File packageListLocation = la.getPackagelistLoc();
+ URL packageListURL = la.getPackagelistURL();
+ if (packageListLocation == null
+ && packageListURL == null) {
+ throw new BuildException("The package list"
+ + " location for link "
+ + la.getHref()
+ + " must be provided "
+ + "because the link is "
+ + "offline");
+ }
+ if (packageListLocation != null) {
+ final File packageListFile =
+ new File(packageListLocation, "package-list");
+ if (packageListFile.exists()) {
+ try {
+ packageListURL =
+ FILE_UTILS.getFileURL(packageListLocation);
+ } catch (final MalformedURLException ex) {
+ log("Warning: Package list location was "
+ + "invalid " + packageListLocation,
+ Project.MSG_WARN);
+ }
+ } else {
+ log("Warning: No package list was found at "
+ + packageListLocation, Project.MSG_VERBOSE);
+ }
+ }
+ if (packageListURL != null) {
+ toExecute.createArgument().setValue("-linkoffline");
+ toExecute.createArgument().setValue(link);
+ toExecute.createArgument()
+ .setValue(packageListURL.toExternalForm());
+ }
+ } else {
+ toExecute.createArgument().setValue("-link");
+ toExecute.createArgument().setValue(link);
+ }
+ }
+ }
+ }
+
+ private void doGroup(final Commandline toExecute) {
+ // add the single group arguments
+ // Javadoc 1.2 rules:
+ // Multiple -group args allowed.
+ // Each arg includes 3 strings: -group [name] [packagelist].
+ // Elements in [packagelist] are colon-delimited.
+ // An element in [packagelist] may end with the * wildcard.
+
+ // Ant javadoc task rules for group attribute:
+ // Args are comma-delimited.
+ // Each arg is 2 space-delimited strings.
+ // E.g., group="XSLT_Packages org.apache.xalan.xslt*,
+ // XPath_Packages org.apache.xalan.xpath*"
+ if (group != null) {
+ final StringTokenizer tok = new StringTokenizer(group, ",", false);
+ while (tok.hasMoreTokens()) {
+ final String grp = tok.nextToken().trim();
+ final int space = grp.indexOf(" ");
+ if (space > 0) {
+ final String name = grp.substring(0, space);
+ final String pkgList = grp.substring(space + 1);
+ toExecute.createArgument().setValue("-group");
+ toExecute.createArgument().setValue(name);
+ toExecute.createArgument().setValue(pkgList);
+ }
+ }
+ }
+ }
+
+ // add the group arguments
+ private void doGroups(final Commandline toExecute) {
+ if (groups.size() != 0) {
+ for (final Enumeration<GroupArgument> e = groups.elements(); e.hasMoreElements();) {
+ final GroupArgument ga = e.nextElement();
+ final String title = ga.getTitle();
+ final String packages = ga.getPackages();
+ if (title == null || packages == null) {
+ throw new BuildException("The title and packages must "
+ + "be specified for group "
+ + "elements.");
+ }
+ toExecute.createArgument().setValue("-group");
+ toExecute.createArgument().setValue(expand(title));
+ toExecute.createArgument().setValue(packages);
+ }
+ }
+ }
+
+ // Do java1.4 arguments
+ private void doJava14(final Commandline toExecute) {
+ for (final Enumeration<Object> e = tags.elements(); e.hasMoreElements();) {
+ final Object element = e.nextElement();
+ if (element instanceof TagArgument) {
+ final TagArgument ta = (TagArgument) element;
+ final File tagDir = ta.getDir(getProject());
+ if (tagDir == null) {
+ // The tag element is not used as a fileset,
+ // but specifies the tag directly.
+ toExecute.createArgument().setValue ("-tag");
+ toExecute.createArgument()
+ .setValue (ta.getParameter());
+ } else {
+ // The tag element is used as a
+ // fileset. Parse all the files and create
+ // -tag arguments.
+ final DirectoryScanner tagDefScanner =
+ ta.getDirectoryScanner(getProject());
+ final String[] files = tagDefScanner.getIncludedFiles();
+ for (int i = 0; i < files.length; i++) {
+ final File tagDefFile = new File(tagDir, files[i]);
+ try {
+ final BufferedReader in
+ = new BufferedReader(
+ new FileReader(tagDefFile)
+ );
+ String line = null;
+ while ((line = in.readLine()) != null) {
+ toExecute.createArgument()
+ .setValue("-tag");
+ toExecute.createArgument()
+ .setValue(line);
+ }
+ in.close();
+ } catch (final IOException ioe) {
+ throw new BuildException(
+ "Couldn't read "
+ + " tag file from "
+ + tagDefFile.getAbsolutePath(), ioe);
+ }
+ }
+ }
+ } else {
+ final ExtensionInfo tagletInfo = (ExtensionInfo) element;
+ toExecute.createArgument().setValue("-taglet");
+ toExecute.createArgument().setValue(tagletInfo
+ .getName());
+ if (tagletInfo.getPath() != null) {
+ final Path tagletPath = tagletInfo.getPath()
+ .concatSystemClasspath("ignore");
+ if (tagletPath.size() != 0) {
+ toExecute.createArgument()
+ .setValue("-tagletpath");
+ toExecute.createArgument().setPath(tagletPath);
+ }
+ }
+ }
+ }
+
+ final String sourceArg = source != null ? source
+ : getProject().getProperty(MagicNames.BUILD_JAVAC_SOURCE);
+ if (sourceArg != null) {
+ toExecute.createArgument().setValue("-source");
+ toExecute.createArgument().setValue(sourceArg);
+ }
+
+ if (linksource && doclet == null) {
+ toExecute.createArgument().setValue("-linksource");
+ }
+ if (noqualifier != null && doclet == null) {
+ toExecute.createArgument().setValue("-noqualifier");
+ toExecute.createArgument().setValue(noqualifier);
+ }
+ }
+
+ private void doDocFilesSubDirs(final Commandline toExecute) {
+ if (docFilesSubDirs) {
+ toExecute.createArgument().setValue("-docfilessubdirs");
+ if (excludeDocFilesSubDir != null
+ && excludeDocFilesSubDir.trim().length() > 0) {
+ toExecute.createArgument().setValue("-excludedocfilessubdir");
+ toExecute.createArgument().setValue(excludeDocFilesSubDir);
+ }
+ }
+ }
+
+ private void doSourceAndPackageNames(
+ final Commandline toExecute,
+ final Vector<String> packagesToDoc,
+ final Vector<SourceFile> sourceFilesToDoc,
+ final boolean useExternalFile,
+ final File tmpList,
+ final BufferedWriter srcListWriter)
+ throws IOException {
+ for (final String packageName : packagesToDoc) {
+ if (useExternalFile) {
+ srcListWriter.write(packageName);
+ srcListWriter.newLine();
+ } else {
+ toExecute.createArgument().setValue(packageName);
+ }
+ }
+
+ for (final SourceFile sf : sourceFilesToDoc) {
+ final String sourceFileName = sf.getFile().getAbsolutePath();
+ if (useExternalFile) {
+ // TODO what is the following doing?
+ // should it run if !javadoc4 && executable != null?
+ if (sourceFileName.indexOf(" ") > -1) {
+ String name = sourceFileName;
+ if (File.separatorChar == '\\') {
+ name = sourceFileName.replace(File.separatorChar, '/');
+ }
+ srcListWriter.write("\"" + name + "\"");
+ } else {
+ srcListWriter.write(sourceFileName);
+ }
+ srcListWriter.newLine();
+ } else {
+ toExecute.createArgument().setValue(sourceFileName);
+ }
+ }
+ }
+
+ /**
+ * Quote a string to place in a @ file.
+ * @param str the string to quote
+ * @return the quoted string, if there is no need to quote the string,
+ * return the original string.
+ */
+ private String quoteString(final String str) {
+ if (!containsWhitespace(str)
+ && str.indexOf('\'') == -1
+ && str.indexOf('"') == -1) {
+ return str;
+ }
+ if (str.indexOf('\'') == -1) {
+ return quoteString(str, '\'');
+ } else {
+ return quoteString(str, '"');
+ }
+ }
+
+ private boolean containsWhitespace(final String s) {
+ final int len = s.length();
+ for (int i = 0; i < len; i++) {
+ if (Character.isWhitespace(s.charAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private String quoteString(final String str, final char delim) {
+ final StringBuffer buf = new StringBuffer(str.length() * 2);
+ buf.append(delim);
+ final int len = str.length();
+ boolean lastCharWasCR = false;
+ for (int i = 0; i < len; i++) {
+ final char c = str.charAt(i);
+ if (c == delim) { // can't put the non-constant delim into a case
+ buf.append('\\').append(c);
+ lastCharWasCR = false;
+ } else {
+ switch (c) {
+ case '\\':
+ buf.append("\\\\");
+ lastCharWasCR = false;
+ break;
+ case '\r':
+ // insert a line continuation marker
+ buf.append("\\\r");
+ lastCharWasCR = true;
+ break;
+ case '\n':
+ // insert a line continuation marker unless this
+ // is a \r\n sequence in which case \r already has
+ // created the marker
+ if (!lastCharWasCR) {
+ buf.append("\\\n");
+ } else {
+ buf.append("\n");
+ }
+ lastCharWasCR = false;
+ break;
+ default:
+ buf.append(c);
+ lastCharWasCR = false;
+ break;
+ }
+ }
+ }
+ buf.append(delim);
+ return buf.toString();
+ }
+
+ /**
+ * Add the files matched by the nested source files to the Vector
+ * as SourceFile instances.
+ *
+ * @since 1.7
+ */
+ private void addSourceFiles(final Vector<SourceFile> sf) {
+ final Iterator<ResourceCollection> e = nestedSourceFiles.iterator();
+ while (e.hasNext()) {
+ ResourceCollection rc = e.next();
+ if (!rc.isFilesystemOnly()) {
+ throw new BuildException("only file system based resources are"
+ + " supported by javadoc");
+ }
+ if (rc instanceof FileSet) {
+ final FileSet fs = (FileSet) rc;
+ if (!fs.hasPatterns() && !fs.hasSelectors()) {
+ final FileSet fs2 = (FileSet) fs.clone();
+ fs2.createInclude().setName("**/*.java");
+ if (includeNoSourcePackages) {
+ fs2.createInclude().setName("**/package.html");
+ }
+ rc = fs2;
+ }
+ }
+ for (final Resource r : rc) {
+ sf.addElement(new SourceFile(r.as(FileProvider.class).getFile()));
+ }
+ }
+ }
+
+ /**
+ * Add the directories matched by the nested dirsets to the Vector
+ * and the base directories of the dirsets to the Path. It also
+ * handles the packages and excludepackages attributes and
+ * elements.
+ *
+ * @since 1.5
+ */
+ private void parsePackages(final Vector<String> pn, final Path sp) {
+ final HashSet<String> addedPackages = new HashSet<String>();
+ @SuppressWarnings("unchecked")
+ final Vector<DirSet> dirSets = (Vector<DirSet>) packageSets.clone();
+
+ // for each sourcePath entry, add a directoryset with includes
+ // taken from packagenames attribute and nested package
+ // elements and excludes taken from excludepackages attribute
+ // and nested excludepackage elements
+ if (sourcePath != null) {
+ final PatternSet ps = new PatternSet();
+ ps.setProject(getProject());
+ if (packageNames.size() > 0) {
+ final Enumeration<PackageName> e = packageNames.elements();
+ while (e.hasMoreElements()) {
+ final PackageName p = e.nextElement();
+ String pkg = p.getName().replace('.', '/');
+ if (pkg.endsWith("*")) {
+ pkg += "*";
+ }
+ ps.createInclude().setName(pkg);
+ }
+ } else {
+ ps.createInclude().setName("**");
+ }
+
+ final Enumeration<PackageName> e = excludePackageNames.elements();
+ while (e.hasMoreElements()) {
+ final PackageName p = e.nextElement();
+ String pkg = p.getName().replace('.', '/');
+ if (pkg.endsWith("*")) {
+ pkg += "*";
+ }
+ ps.createExclude().setName(pkg);
+ }
+
+
+ final String[] pathElements = sourcePath.list();
+ for (int i = 0; i < pathElements.length; i++) {
+ final File dir = new File(pathElements[i]);
+ if (dir.isDirectory()) {
+ final DirSet ds = new DirSet();
+ ds.setProject(getProject());
+ ds.setDefaultexcludes(useDefaultExcludes);
+ ds.setDir(dir);
+ ds.createPatternSet().addConfiguredPatternset(ps);
+ dirSets.addElement(ds);
+ } else {
+ log("Skipping " + pathElements[i]
+ + " since it is no directory.", Project.MSG_WARN);
+ }
+ }
+ }
+
+ final Enumeration<DirSet> e = dirSets.elements();
+ while (e.hasMoreElements()) {
+ final DirSet ds = e.nextElement();
+ final File baseDir = ds.getDir(getProject());
+ log("scanning " + baseDir + " for packages.", Project.MSG_DEBUG);
+ final DirectoryScanner dsc = ds.getDirectoryScanner(getProject());
+ final String[] dirs = dsc.getIncludedDirectories();
+ boolean containsPackages = false;
+ for (int i = 0; i < dirs.length; i++) {
+ // are there any java files in this directory?
+ final File pd = new File(baseDir, dirs[i]);
+ final String[] files = pd.list(new FilenameFilter () {
+ public boolean accept(final File dir1, final String name) {
+ return name.endsWith(".java")
+ || (includeNoSourcePackages
+ && name.equals("package.html"));
+ }
+ });
+
+ if (files.length > 0) {
+ if ("".equals(dirs[i])) {
+ log(baseDir
+ + " contains source files in the default package,"
+ + " you must specify them as source files"
+ + " not packages.",
+ Project.MSG_WARN);
+ } else {
+ containsPackages = true;
+ final String packageName =
+ dirs[i].replace(File.separatorChar, '.');
+ if (!addedPackages.contains(packageName)) {
+ addedPackages.add(packageName);
+ pn.addElement(packageName);
+ }
+ }
+ }
+ }
+ if (containsPackages) {
+ // We don't need to care for duplicates here,
+ // Path.list does it for us.
+ sp.createPathElement().setLocation(baseDir);
+ } else {
+ log(baseDir + " doesn\'t contain any packages, dropping it.",
+ Project.MSG_VERBOSE);
+ }
+ }
+ }
+
+ private void postProcessGeneratedJavadocs() throws IOException {
+ if (!postProcessGeneratedJavadocs) {
+ return;
+ }
+ if (destDir != null && !destDir.isDirectory()) {
+ log("No javadoc created, no need to post-process anything",
+ Project.MSG_VERBOSE);
+ return;
+ }
+ final String fixData;
+ final InputStream in = Javadoc.class
+ .getResourceAsStream("javadoc-frame-injections-fix.txt");
+ if (in == null) {
+ throw new FileNotFoundException("Missing resource "
+ + "'javadoc-frame-injections-fix.txt' in "
+ + "classpath.");
+ }
+ try {
+ fixData =
+ fixLineFeeds(FileUtils
+ .readFully(new InputStreamReader(in, "US-ASCII")))
+ .trim();
+ } finally {
+ FileUtils.close(in);
+ }
+
+ final DirectoryScanner ds = new DirectoryScanner();
+ ds.setBasedir(destDir);
+ ds.setCaseSensitive(false);
+ ds.setIncludes(new String[] {
+ "**/index.html", "**/index.htm", "**/toc.html", "**/toc.htm"
+ });
+ ds.addDefaultExcludes();
+ ds.scan();
+ int patched = 0;
+ for (final String f : ds.getIncludedFiles()) {
+ patched += postProcess(new File(destDir, f), fixData);
+ }
+ if (patched > 0) {
+ log("Patched " + patched + " link injection vulnerable javadocs",
+ Project.MSG_INFO);
+ }
+ }
+
+ private int postProcess(final File file, final String fixData) throws IOException {
+ final String enc = docEncoding != null ? docEncoding
+ : FILE_UTILS.getDefaultEncoding();
+ // we load the whole file as one String (toc/index files are
+ // generally small, because they only contain frameset declaration):
+ final InputStream fin = new FileInputStream(file);
+ String fileContents;
+ try {
+ fileContents =
+ fixLineFeeds(FileUtils
+ .safeReadFully(new InputStreamReader(fin, enc)));
+ } finally {
+ FileUtils.close(fin);
+ }
+
+ // check if file may be vulnerable because it was not
+ // patched with "validURL(url)":
+ if (fileContents.indexOf("function validURL(url) {") < 0) {
+ // we need to patch the file!
+ final String patchedFileContents = patchContent(fileContents, fixData);
+ if (!patchedFileContents.equals(fileContents)) {
+ final FileOutputStream fos = new FileOutputStream(file);
+ try {
+ final OutputStreamWriter w = new OutputStreamWriter(fos, enc);
+ w.write(patchedFileContents);
+ w.close();
+ return 1;
+ } finally {
+ FileUtils.close(fos);
+ }
+ }
+ }
+ return 0;
+ }
+
+ private String fixLineFeeds(final String orig) {
+ return orig.replace("\r\n", "\n")
+ .replace("\n", StringUtils.LINE_SEP);
+ }
+
+ private String patchContent(final String fileContents, final String fixData) {
+ // using regexes here looks like overkill
+ final int start = fileContents.indexOf(LOAD_FRAME);
+ if (start >= 0) {
+ return fileContents.substring(0, start) + fixData
+ + fileContents.substring(start + LOAD_FRAME_LEN);
+ }
+ return fileContents;
+ }
+
+ private class JavadocOutputStream extends LogOutputStream {
+ JavadocOutputStream(final int level) {
+ super(Javadoc.this, level);
+ }
+
+ //
+ // Override the logging of output in order to filter out Generating
+ // messages. Generating messages are set to a priority of VERBOSE
+ // unless they appear after what could be an informational message.
+ //
+ private String queuedLine = null;
+ private boolean sawWarnings = false;
+
+ @Override
+ protected void processLine(final String line, final int messageLevel) {
+ if (line.contains("warning")) {
+ sawWarnings = true;
+ }
+ if (messageLevel == Project.MSG_INFO
+ && line.startsWith("Generating ")) {
+ if (queuedLine != null) {
+ super.processLine(queuedLine, Project.MSG_VERBOSE);
+ }
+ queuedLine = line;
+ } else {
+ if (queuedLine != null) {
+ if (line.startsWith("Building ")) {
+ super.processLine(queuedLine, Project.MSG_VERBOSE);
+ } else {
+ super.processLine(queuedLine, Project.MSG_INFO);
+ }
+ queuedLine = null;
+ }
+ super.processLine(line, messageLevel);
+ }
+ }
+
+
+ protected void logFlush() {
+ if (queuedLine != null) {
+ super.processLine(queuedLine, Project.MSG_VERBOSE);
+ queuedLine = null;
+ }
+ }
+
+ public boolean sawWarnings() {
+ return sawWarnings;
+ }
+ }
+
+ /**
+ * Convenience method to expand properties.
+ * @param content the string to expand
+ * @return the converted string
+ */
+ protected String expand(final String content) {
+ return getProject().replaceProperties(content);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Jikes.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Jikes.java
new file mode 100644
index 00000000..4bc6835b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Jikes.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.tools.ant.taskdefs;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Locale;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Encapsulates a Jikes compiler, by directly executing an external
+ * process.
+ *
+ * <p><strong>As of Ant 1.2, this class is considered to be dead code
+ * by the Ant developers and is unmaintained. Don't use
+ * it.</strong></p>
+ *
+ * @deprecated since 1.2.
+ * Merged into the class Javac.
+ */
+public class Jikes {
+ // There have been reports that 300 files could be compiled
+ // on a command line so 250 is a conservative approach
+ private static final int MAX_FILES_ON_COMMAND_LINE = 250;
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected JikesOutputParser jop;
+ protected String command;
+ protected Project project;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Constructs a new Jikes object.
+ * @param jop Parser to send jike's output to
+ * @param command name of jikes executable
+ * @param project the current project
+ */
+ protected Jikes(JikesOutputParser jop, String command, Project project) {
+ super();
+
+ System.err.println("As of Ant 1.2 released in October 2000, "
+ + "the Jikes class");
+ System.err.println("is considered to be dead code by the Ant "
+ + "developers and is unmaintained.");
+ System.err.println("Don\'t use it!");
+
+ this.jop = jop;
+ this.command = command;
+ this.project = project;
+ }
+
+ /**
+ * Do the compile with the specified arguments.
+ * @param args - arguments to pass to process on command line
+ */
+ protected void compile(String[] args) {
+ String[] commandArray = null;
+ File tmpFile = null;
+
+ try {
+ String myos = System.getProperty("os.name");
+
+ // Windows has a 32k limit on total arg size, so
+ // create a temporary file to store all the arguments
+
+ if (myos.toLowerCase(Locale.ENGLISH).indexOf("windows") >= 0
+ && args.length > MAX_FILES_ON_COMMAND_LINE) {
+ BufferedWriter out = null;
+ try {
+ tmpFile = FileUtils.getFileUtils().createTempFile("jikes",
+ "tmp", null, false, true);
+ out = new BufferedWriter(new FileWriter(tmpFile));
+ for (int i = 0; i < args.length; i++) {
+ out.write(args[i]);
+ out.newLine();
+ }
+ out.flush();
+ commandArray = new String[] {command,
+ "@" + tmpFile.getAbsolutePath()};
+ } catch (IOException e) {
+ throw new BuildException("Error creating temporary file",
+ e);
+ } finally {
+ FileUtils.close(out);
+ }
+ } else {
+ commandArray = new String[args.length + 1];
+ commandArray[0] = command;
+ System.arraycopy(args, 0, commandArray, 1, args.length);
+ }
+
+ // We assume, that everything jikes writes goes to
+ // standard output, not to standard error. The option
+ // -Xstdout that is given to Jikes in Javac.doJikesCompile()
+ // should guarantee this. At least I hope so. :)
+ try {
+ Execute exe = new Execute(jop);
+ exe.setAntRun(project);
+ exe.setWorkingDirectory(project.getBaseDir());
+ exe.setCommandline(commandArray);
+ exe.execute();
+ } catch (IOException e) {
+ throw new BuildException("Error running Jikes compiler", e);
+ }
+ } finally {
+ if (tmpFile != null) {
+ if (!tmpFile.delete()) {
+ tmpFile.deleteOnExit();
+ }
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/JikesOutputParser.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/JikesOutputParser.java
new file mode 100644
index 00000000..18bce30c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/JikesOutputParser.java
@@ -0,0 +1,183 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Parses output from jikes and
+ * passes errors and warnings
+ * into the right logging channels of Project.
+ *
+ * <p><strong>As of Ant 1.2, this class is considered to be dead code
+ * by the Ant developers and is unmaintained. Don't use
+ * it.</strong></p>
+ *
+ * @deprecated since 1.2.
+ * Use Jikes' exit value to detect compilation failure.
+ */
+public class JikesOutputParser implements ExecuteStreamHandler {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected Task task;
+ protected boolean errorFlag = false; // no errors so far
+ protected int errors;
+ protected int warnings;
+ protected boolean error = false;
+ protected boolean emacsMode;
+
+ protected BufferedReader br;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Ignore.
+ * @param os ignored
+ */
+ public void setProcessInputStream(OutputStream os) {
+ }
+
+ /**
+ * Ignore.
+ * @param is ignored
+ */
+ public void setProcessErrorStream(InputStream is) {
+ }
+
+ /**
+ * Set the inputstream
+ * @param is the input stream
+ * @throws IOException on error
+ */
+ public void setProcessOutputStream(InputStream is) throws IOException {
+ br = new BufferedReader(new InputStreamReader(is));
+ }
+
+ /**
+ * Invokes parseOutput.
+ * @throws IOException on error
+ */
+ public void start() throws IOException {
+ parseOutput(br);
+ }
+
+ /**
+ * Ignore.
+ */
+ public void stop() {
+ }
+
+ /**
+ * Construct a new Parser object
+ * @param task task in which context we are called
+ * @param emacsMode if true output in emacs mode
+ */
+ protected JikesOutputParser(Task task, boolean emacsMode) {
+ super();
+
+ System.err.println("As of Ant 1.2 released in October 2000, the "
+ + "JikesOutputParser class");
+ System.err.println("is considered to be dead code by the Ant "
+ + "developers and is unmaintained.");
+ System.err.println("Don\'t use it!");
+
+ this.task = task;
+ this.emacsMode = emacsMode;
+ }
+
+ /**
+ * Parse the output of a jikes compiler
+ * @param reader - Reader used to read jikes's output
+ * @throws IOException on error
+ */
+ protected void parseOutput(BufferedReader reader) throws IOException {
+ if (emacsMode) {
+ parseEmacsOutput(reader);
+ } else {
+ parseStandardOutput(reader);
+ }
+ }
+
+ private void parseStandardOutput(BufferedReader reader) throws IOException {
+ String line;
+ String lower;
+ // We assume, that every output, jikes does, stands for an error/warning
+ // TODO
+ // Is this correct?
+
+ // TODO:
+ // A warning line, that shows code, which contains a variable
+ // error will cause some trouble. The parser should definitely
+ // be much better.
+
+ while ((line = reader.readLine()) != null) {
+ lower = line.toLowerCase();
+ if (line.trim().equals("")) {
+ continue;
+ }
+ if (lower.indexOf("error") != -1) {
+ setError(true);
+ } else if (lower.indexOf("warning") != -1) {
+ setError(false);
+ } else {
+ // If we don't know the type of the line
+ // and we are in emacs mode, it will be
+ // an error, because in this mode, jikes won't
+ // always print "error", but sometimes other
+ // keywords like "Syntax". We should look for
+ // all those keywords.
+ if (emacsMode) {
+ setError(true);
+ }
+ }
+ log(line);
+ }
+ }
+
+ private void parseEmacsOutput(BufferedReader reader) throws IOException {
+ // This may change, if we add advanced parsing capabilities.
+ parseStandardOutput(reader);
+ }
+
+ private void setError(boolean err) {
+ error = err;
+ if (error) {
+ errorFlag = true;
+ }
+ }
+
+ private void log(String line) {
+ if (!emacsMode) {
+ task.log("", (error ? Project.MSG_ERR : Project.MSG_WARN));
+ }
+ task.log(line, (error ? Project.MSG_ERR : Project.MSG_WARN));
+ }
+
+ /**
+ * Indicate if there were errors during the compile
+ * @return if errors occurred
+ */
+ protected boolean getErrorFlag() {
+ return errorFlag;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/KeySubst.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/KeySubst.java
new file mode 100644
index 00000000..6ff67c04
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/KeySubst.java
@@ -0,0 +1,191 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Keyword substitution. Input file is written to output file.
+ * Do not make input file same as output file.
+ * Keywords in input files look like this: @foo@. See the docs for the
+ * setKeys method to understand how to do the substitutions.
+ *
+ * @since Ant 1.1
+ * @deprecated KeySubst is deprecated since Ant 1.1. Use Filter + Copy
+ * instead.
+ */
+public class KeySubst extends Task {
+ private File source = null;
+ private File dest = null;
+ private String sep = "*";
+ private Hashtable<String, String> replacements = new Hashtable<String, String>();
+
+ /**
+ * Do the execution.
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ log("!! KeySubst is deprecated. Use Filter + Copy instead. !!");
+ log("Performing Substitutions");
+ if (source == null || dest == null) {
+ log("Source and destinations must not be null");
+ return;
+ }
+ BufferedReader br = null;
+ BufferedWriter bw = null;
+ try {
+ br = new BufferedReader(new FileReader(source));
+ dest.delete();
+ bw = new BufferedWriter(new FileWriter(dest));
+
+ String line = null;
+ String newline = null;
+ line = br.readLine();
+ while (line != null) {
+ if (line.length() == 0) {
+ bw.newLine();
+ } else {
+ newline = KeySubst.replace(line, replacements);
+ bw.write(newline);
+ bw.newLine();
+ }
+ line = br.readLine();
+ }
+ bw.flush();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ } finally {
+ FileUtils.close(bw);
+ FileUtils.close(br);
+ }
+ }
+
+ /**
+ * Set the source file.
+ * @param s the source file
+ */
+ public void setSrc(File s) {
+ this.source = s;
+ }
+
+ /**
+ * Set the destination file.
+ * @param dest the destination file
+ */
+ public void setDest(File dest) {
+ this.dest = dest;
+ }
+
+ /**
+ * Sets the separator between name=value arguments
+ * in setKeys(). By default it is "*".
+ * @param sep the separator string
+ */
+ public void setSep(String sep) {
+ this.sep = sep;
+ }
+ /**
+ * Sets the keys.
+ *
+ * Format string is like this:
+ * <p>
+ * name=value*name2=value
+ * <p>
+ * Names are case sensitive.
+ * <p>
+ * Use the setSep() method to change the * to something else
+ * if you need to use * as a name or value.
+ * @param keys a <code>String</code> value
+ */
+ public void setKeys(String keys) {
+ if (keys != null && keys.length() > 0) {
+ StringTokenizer tok =
+ new StringTokenizer(keys, this.sep, false);
+ while (tok.hasMoreTokens()) {
+ String token = tok.nextToken().trim();
+ StringTokenizer itok =
+ new StringTokenizer(token, "=", false);
+
+ String name = itok.nextToken();
+ String value = itok.nextToken();
+ replacements.put(name, value);
+ }
+ }
+ }
+
+
+ /**
+ * A test method.
+ * @param args not used
+ */
+ public static void main(String[] args) {
+ try {
+ Hashtable<String, String> hash = new Hashtable<String, String>();
+ hash.put("VERSION", "1.0.3");
+ hash.put("b", "ffff");
+ System.out.println(KeySubst.replace("$f ${VERSION} f ${b} jj $",
+ hash));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Does replacement on text using the hashtable of keys.
+ * @param origString an input string
+ * @param keys mapping of keys to values
+ * @return the string with the replacements in it.
+ * @throws BuildException on error
+ */
+ public static String replace(String origString, Hashtable<String, String> keys)
+ throws BuildException {
+ StringBuffer finalString = new StringBuffer();
+ int index = 0;
+ int i = 0;
+ String key = null;
+ // CheckStyle:MagicNumber OFF
+ while ((index = origString.indexOf("${", i)) > -1) {
+ key = origString.substring(index + 2, origString.indexOf("}",
+ index + 3));
+ finalString.append (origString.substring(i, index));
+ if (keys.containsKey(key)) {
+ finalString.append (keys.get(key));
+ } else {
+ finalString.append ("${");
+ finalString.append (key);
+ finalString.append ("}");
+ }
+ i = index + 3 + key.length();
+ }
+ // CheckStyle:MagicNumber ON
+ finalString.append (origString.substring(i));
+ return finalString.toString();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Length.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Length.java
new file mode 100644
index 00000000..bd4da442
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Length.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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.types.Comparison;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.Resources;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.PropertyOutputStream;
+
+/**
+ * Gets lengths: of files/resources, byte size; of strings, length (optionally trimmed).
+ * The task is overloaded in this way for semantic reasons, much like Available.
+ * @since Ant 1.6.3
+ */
+public class Length extends Task implements Condition {
+
+ private static final String ALL = "all";
+ private static final String EACH = "each";
+ private static final String STRING = "string";
+
+ private static final String LENGTH_REQUIRED
+ = "Use of the Length condition requires that the length attribute be set.";
+
+ private String property;
+ private String string;
+ private Boolean trim;
+ private String mode = ALL;
+ private Comparison when = Comparison.EQUAL;
+ private Long length;
+ private Resources resources;
+
+ /**
+ * The property in which the length will be stored.
+ * @param property the <code>String</code> property key.
+ */
+ public synchronized void setProperty(String property) {
+ this.property = property;
+ }
+
+ /**
+ * Set the single resource for this task.
+ * @param resource the Resource whose length to retrieve.
+ */
+ public synchronized void setResource(Resource resource) {
+ add(resource);
+ }
+
+ /**
+ * Set the single file for this task.
+ * @param file the <code>File</code> whose length to retrieve.
+ */
+ public synchronized void setFile(File file) {
+ add(new FileResource(file));
+ }
+
+ /**
+ * Add a FileSet.
+ * @param fs the <code>FileSet</code> to add.
+ */
+ public synchronized void add(FileSet fs) {
+ add((ResourceCollection) fs);
+ }
+
+ /**
+ * Add a ResourceCollection.
+ * @param c the <code>ResourceCollection</code> to add.
+ * @since Ant 1.7
+ */
+ public synchronized void add(ResourceCollection c) {
+ if (c == null) {
+ return;
+ }
+ resources = (resources == null) ? new Resources() : resources;
+ resources.add(c);
+ }
+
+ /**
+ * Set the target count number for use as a Condition.
+ * @param ell the long length to compare with.
+ */
+ public synchronized void setLength(long ell) {
+ length = new Long(ell);
+ }
+
+ /**
+ * Set the comparison for use as a Condition.
+ * @param w EnumeratedAttribute When.
+ * @see org.apache.tools.ant.types.Comparison
+ */
+ public synchronized void setWhen(When w) {
+ setWhen((Comparison) w);
+ }
+
+ /**
+ * Set the comparison for use as a Condition.
+ * @param c Comparison.
+ * @see org.apache.tools.ant.types.Comparison
+ * @since Ant 1.7
+ */
+ public synchronized void setWhen(Comparison c) {
+ when = c;
+ }
+
+ /**
+ * Set the execution mode for working with files.
+ * @param m the <code>FileMode</code> to use.
+ */
+ public synchronized void setMode(FileMode m) {
+ this.mode = m.getValue();
+ }
+
+ /**
+ * Set the string whose length to get.
+ * @param string <code>String</code>.
+ */
+ public synchronized void setString(String string) {
+ this.string = string;
+ this.mode = STRING;
+ }
+
+ /**
+ * Set whether to trim in string mode. Default false.
+ * @param trim <code>boolean</code>.
+ */
+ public synchronized void setTrim(boolean trim) {
+ this.trim = trim ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ /**
+ * Learn whether strings will be trimmed. Default false.
+ * @return boolean trim setting.
+ */
+ public boolean getTrim() {
+ return trim != null && trim.booleanValue();
+ }
+
+ /**
+ * Execute the length task.
+ */
+ public void execute() {
+ validate();
+ PrintStream ps = new PrintStream((property != null)
+ ? (OutputStream) new PropertyOutputStream(getProject(), property)
+ : (OutputStream) new LogOutputStream(this, Project.MSG_INFO));
+
+ if (STRING.equals(mode)) {
+ ps.print(getLength(string, getTrim()));
+ ps.close();
+ } else if (EACH.equals(mode)) {
+ handleResources(new EachHandler(ps));
+ } else if (ALL.equals(mode)) {
+ handleResources(new AllHandler(ps));
+ }
+ }
+
+ /**
+ * Fulfill the condition contract.
+ * @return true if the condition is true.
+ * @throws BuildException if an error occurs.
+ */
+ public boolean eval() {
+ validate();
+ if (length == null) {
+ throw new BuildException(LENGTH_REQUIRED);
+ }
+ Long ell;
+ if (STRING.equals(mode)) {
+ ell = new Long(getLength(string, getTrim()));
+ } else {
+ AccumHandler h = new AccumHandler();
+ handleResources(h);
+ ell = new Long(h.getAccum());
+ }
+ return when.evaluate(ell.compareTo(length));
+ }
+
+ private void validate() {
+ if (string != null) {
+ if (resources != null) {
+ throw new BuildException("the string length function"
+ + " is incompatible with the file/resource length function");
+ }
+ if (!(STRING.equals(mode))) {
+ throw new BuildException("the mode attribute is for use"
+ + " with the file/resource length function");
+ }
+ } else if (resources != null) {
+ if (!(EACH.equals(mode) || ALL.equals(mode))) {
+ throw new BuildException("invalid mode setting for"
+ + " file/resource length function: \"" + mode + "\"");
+ } else if (trim != null) {
+ throw new BuildException("the trim attribute is"
+ + " for use with the string length function only");
+ }
+ } else {
+ throw new BuildException("you must set either the string attribute"
+ + " or specify one or more files using the file attribute or"
+ + " nested resource collections");
+ }
+ }
+
+ private void handleResources(Handler h) {
+ for (Resource r : resources) {
+ if (!r.isExists()) {
+ log(r + " does not exist", Project.MSG_WARN);
+ }
+ if (r.isDirectory()) {
+ log(r + " is a directory; length may not be meaningful", Project.MSG_WARN);
+ }
+ h.handle(r);
+ }
+ h.complete();
+ }
+
+ private static long getLength(String s, boolean t) {
+ return (t ? s.trim() : s).length();
+ }
+
+ /** EnumeratedAttribute operation mode */
+ public static class FileMode extends EnumeratedAttribute {
+ static final String[] MODES = new String[] {EACH, ALL};
+
+ /**
+ * Return the possible values for FileMode.
+ * @return <code>String[]</code>.
+ */
+ public String[] getValues() {
+ return MODES;
+ }
+
+ }
+
+ /**
+ * EnumeratedAttribute for the when attribute.
+ */
+ public static class When extends Comparison {
+ //extend Comparison; retain for BC only
+ }
+
+ private abstract class Handler {
+ private PrintStream ps;
+ Handler(PrintStream ps) {
+ this.ps = ps;
+ }
+
+ protected PrintStream getPs() {
+ return ps;
+ }
+
+ protected abstract void handle(Resource r);
+
+ void complete() {
+ FileUtils.close(ps);
+ }
+ }
+
+ private class EachHandler extends Handler {
+ EachHandler(PrintStream ps) {
+ super(ps);
+ }
+ protected void handle(Resource r) {
+ getPs().print(r.toString());
+ getPs().print(" : ");
+ //when writing to the log, we'll see what's happening:
+ long size = r.getSize();
+ if (size == Resource.UNKNOWN_SIZE) {
+ getPs().println("unknown");
+ } else {
+ getPs().println(size);
+ }
+ }
+ }
+
+ private class AccumHandler extends Handler {
+ private long accum = 0L;
+
+ AccumHandler() {
+ super(null);
+ }
+ protected AccumHandler(PrintStream ps) {
+ super(ps);
+ }
+ protected long getAccum() {
+ return accum;
+ }
+ protected synchronized void handle(Resource r) {
+ long size = r.getSize();
+ if (size == Resource.UNKNOWN_SIZE) {
+ log("Size unknown for " + r.toString(), Project.MSG_WARN);
+ } else {
+ accum += size;
+ }
+ }
+ }
+
+ private class AllHandler extends AccumHandler {
+ AllHandler(PrintStream ps) {
+ super(ps);
+ }
+ void complete() {
+ getPs().print(getAccum());
+ super.complete();
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LoadFile.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LoadFile.java
new file mode 100644
index 00000000..c450a0e8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LoadFile.java
@@ -0,0 +1,41 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+
+import org.apache.tools.ant.types.resources.FileResource;
+
+
+/**
+ * Load a file into a property
+ *
+ * @since Ant 1.5
+ * @ant.task category="utility"
+ */
+public class LoadFile extends LoadResource {
+
+ /**
+ * Sets the file to load.
+ *
+ * @param srcFile The new SrcFile value
+ */
+ public final void setSrcFile(final File srcFile) {
+ addConfigured(new FileResource(srcFile));
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LoadProperties.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LoadProperties.java
new file mode 100644
index 00000000..4c20c7c9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LoadProperties.java
@@ -0,0 +1,248 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.JavaResource;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.ResourceUtils;
+
+/**
+ * Load a file's contents as Ant properties.
+ *
+ * @since Ant 1.5
+ * @ant.task category="utility"
+ */
+public class LoadProperties extends Task {
+
+ /**
+ * Source resource.
+ */
+ private Resource src = null;
+
+ /**
+ * Holds filterchains
+ */
+ private final Vector<FilterChain> filterChains = new Vector<FilterChain>();
+
+ /**
+ * Encoding to use for input; defaults to the platform's default encoding.
+ */
+ private String encoding = null;
+
+ /**
+ * Prefix for loaded properties.
+ */
+ private String prefix = null;
+ private boolean prefixValues = true;
+
+ /**
+ * Set the file to load.
+ *
+ * @param srcFile The new SrcFile value
+ */
+ public final void setSrcFile(final File srcFile) {
+ addConfigured(new FileResource(srcFile));
+ }
+
+ /**
+ * Set the resource name of a property file to load.
+ *
+ * @param resource resource on classpath
+ */
+ public void setResource(String resource) {
+ getRequiredJavaResource().setName(resource);
+ }
+
+ /**
+ * Encoding to use for input, defaults to the platform's default
+ * encoding. <p>
+ *
+ * For a list of possible values see
+ * <a href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">
+ * http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html
+ * </a>.</p>
+ *
+ * @param encoding The new Encoding value
+ */
+ public final void setEncoding(final String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Set the classpath to use when looking up a resource.
+ * @param classpath to add to any existing classpath
+ */
+ public void setClasspath(Path classpath) {
+ getRequiredJavaResource().setClasspath(classpath);
+ }
+
+ /**
+ * Add a classpath to use when looking up a resource.
+ * @return The classpath to be configured
+ */
+ public Path createClasspath() {
+ return getRequiredJavaResource().createClasspath();
+ }
+
+ /**
+ * Set the classpath to use when looking up a resource,
+ * given as reference to a &lt;path&gt; defined elsewhere
+ * @param r The reference value
+ */
+ public void setClasspathRef(Reference r) {
+ getRequiredJavaResource().setClasspathRef(r);
+ }
+
+ /**
+ * get the classpath used by this <code>LoadProperties</code>.
+ * @return The classpath
+ */
+ public Path getClasspath() {
+ return getRequiredJavaResource().getClasspath();
+ }
+
+ /**
+ * Set the prefix to load these properties under.
+ * @param prefix to set
+ */
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ /**
+ * Whether to apply the prefix when expanding properties on the
+ * right hand side of a properties file as well.
+ *
+ * @since Ant 1.8.2
+ */
+ public void setPrefixValues(boolean b) {
+ prefixValues = b;
+ }
+
+ /**
+ * load Ant properties from the source file or resource
+ *
+ * @exception BuildException if something goes wrong with the build
+ */
+ public final void execute() throws BuildException {
+ //validation
+ if (src == null) {
+ throw new BuildException("A source resource is required.");
+ }
+ if (!src.isExists()) {
+ if (src instanceof JavaResource) {
+ // dreaded backwards compatibility
+ log("Unable to find resource " + src, Project.MSG_WARN);
+ return;
+ }
+ throw new BuildException("Source resource does not exist: " + src);
+ }
+ BufferedInputStream bis = null;
+ Reader instream = null;
+ ByteArrayInputStream tis = null;
+
+ try {
+ bis = new BufferedInputStream(src.getInputStream());
+ if (encoding == null) {
+ instream = new InputStreamReader(bis);
+ } else {
+ instream = new InputStreamReader(bis, encoding);
+ }
+ ChainReaderHelper crh = new ChainReaderHelper();
+ crh.setPrimaryReader(instream);
+ crh.setFilterChains(filterChains);
+ crh.setProject(getProject());
+ instream = crh.getAssembledReader();
+
+ String text = crh.readFully(instream);
+
+ if (text != null && text.length() != 0) {
+ if (!text.endsWith("\n")) {
+ text = text + "\n";
+ }
+ tis = new ByteArrayInputStream(text.getBytes(ResourceUtils.ISO_8859_1));
+ final Properties props = new Properties();
+ props.load(tis);
+
+ Property propertyTask = new Property();
+ propertyTask.bindToOwner(this);
+ propertyTask.setPrefix(prefix);
+ propertyTask.setPrefixValues(prefixValues);
+ propertyTask.addProperties(props);
+ }
+ } catch (final IOException ioe) {
+ throw new BuildException("Unable to load file: " + ioe, ioe, getLocation());
+ } finally {
+ FileUtils.close(bis);
+ FileUtils.close(tis);
+ }
+ }
+
+ /**
+ * Adds a FilterChain.
+ * @param filter the filter to add
+ */
+ public final void addFilterChain(FilterChain filter) {
+ filterChains.addElement(filter);
+ }
+
+ /**
+ * Set the source resource.
+ * @param a the resource to load as a single element Resource collection.
+ * @since Ant 1.7
+ */
+ public synchronized void addConfigured(ResourceCollection a) {
+ if (src != null) {
+ throw new BuildException("only a single source is supported");
+ }
+ if (a.size() != 1) {
+ throw new BuildException(
+ "only single-element resource collections are supported");
+ }
+ src = a.iterator().next();
+ }
+
+ private synchronized JavaResource getRequiredJavaResource() {
+ if (src == null) {
+ src = new JavaResource();
+ src.setProject(getProject());
+ } else if (!(src instanceof JavaResource)) {
+ throw new BuildException("expected a java resource as source");
+ }
+ return (JavaResource) src;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LoadResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LoadResource.java
new file mode 100644
index 00000000..f68b5ba4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LoadResource.java
@@ -0,0 +1,236 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Load a resource into a property
+ *
+ * @since Ant 1.7
+ * @ant.task category="utility"
+ */
+public class LoadResource extends Task {
+
+ /**
+ * The resource to load.
+ */
+ private Resource src;
+
+ /**
+ * what to do when it goes pear-shaped
+ */
+ private boolean failOnError = true;
+
+ /**
+ * suppress error message if it goes pear-shaped, sets failOnError=false
+ */
+ private boolean quiet = false;
+
+ /**
+ * Encoding to use for filenames, defaults to the platform's default
+ * encoding.
+ */
+ private String encoding = null;
+
+ /**
+ * name of property
+ */
+ private String property = null;
+
+ /**
+ * Holds FilterChains
+ */
+ private final Vector<FilterChain> filterChains = new Vector<FilterChain>();
+
+ /**
+ * Encoding to use for input, defaults to the platform's default
+ * encoding. <p>
+ *
+ * For a list of possible values see
+ * <a href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">
+ * http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html
+ * </a>.</p>
+ *
+ * @param encoding The new Encoding value
+ */
+
+ public final void setEncoding(final String encoding) {
+ this.encoding = encoding;
+ }
+
+
+ /**
+ * Property name to save to.
+ *
+ * @param property The new Property value
+ */
+ public final void setProperty(final String property) {
+ this.property = property;
+ }
+
+ /**
+ * If true, fail on load error.
+ *
+ * @param fail The new Failonerror value
+ */
+ public final void setFailonerror(final boolean fail) {
+ failOnError = fail;
+ }
+
+ /**
+ * If true, suppress the load error report and set the
+ * the failonerror value to false.
+ * @param quiet The new Quiet value
+ */
+ public void setQuiet(final boolean quiet) {
+ this.quiet = quiet;
+ if (quiet) {
+ this.failOnError = false;
+ }
+ }
+
+ /**
+ * read in a source file to a property
+ *
+ * @exception BuildException if something goes wrong with the build
+ */
+ public final void execute()
+ throws BuildException {
+ //validation
+ if (src == null) {
+ throw new BuildException("source resource not defined");
+ }
+ if (property == null) {
+ throw new BuildException("output property not defined");
+ }
+ if (quiet && failOnError) {
+ throw new BuildException("quiet and failonerror cannot both be "
+ + "set to true");
+ }
+ if (!src.isExists()) {
+ String message = src + " doesn't exist";
+ if (failOnError) {
+ throw new BuildException(message);
+ } else {
+ log(message, quiet ? Project.MSG_WARN : Project.MSG_ERR);
+ return;
+ }
+ }
+ InputStream is = null;
+ BufferedInputStream bis = null;
+ Reader instream = null;
+ log("loading " + src + " into property " + property,
+ Project.MSG_VERBOSE);
+ try {
+ final long len = src.getSize();
+ log("resource size = "
+ + (len != Resource.UNKNOWN_SIZE ? String.valueOf(len)
+ : "unknown"), Project.MSG_DEBUG);
+ //discard most of really big resources
+ final int size = (int) len;
+ //open up the resource
+ is = src.getInputStream();
+ bis = new BufferedInputStream(is);
+ if (encoding == null) {
+ instream = new InputStreamReader(bis);
+ } else {
+ instream = new InputStreamReader(bis, encoding);
+ }
+
+ String text = "";
+ if (size != 0) {
+ ChainReaderHelper crh = new ChainReaderHelper();
+ if (len != Resource.UNKNOWN_SIZE) {
+ crh.setBufferSize(size);
+ }
+ crh.setPrimaryReader(instream);
+ crh.setFilterChains(filterChains);
+ crh.setProject(getProject());
+ instream = crh.getAssembledReader();
+
+ text = crh.readFully(instream);
+ } else {
+ log("Do not set property " + property + " as its length is 0.",
+ quiet ? Project.MSG_VERBOSE : Project.MSG_INFO);
+ }
+
+ if (text != null) {
+ if (text.length() > 0) {
+ getProject().setNewProperty(property, text);
+ log("loaded " + text.length() + " characters",
+ Project.MSG_VERBOSE);
+ log(property + " := " + text, Project.MSG_DEBUG);
+ }
+ }
+
+ } catch (final IOException ioe) {
+ final String message = "Unable to load resource: "
+ + ioe.toString();
+ if (failOnError) {
+ throw new BuildException(message, ioe, getLocation());
+ } else {
+ log(message, quiet ? Project.MSG_VERBOSE : Project.MSG_ERR);
+ }
+ } catch (final BuildException be) {
+ if (failOnError) {
+ throw be;
+ } else {
+ log(be.getMessage(),
+ quiet ? Project.MSG_VERBOSE : Project.MSG_ERR);
+ }
+ } finally {
+ FileUtils.close(is);
+ }
+ }
+
+ /**
+ * Add the FilterChain element.
+ * @param filter the filter to add
+ */
+ public final void addFilterChain(FilterChain filter) {
+ filterChains.addElement(filter);
+ }
+
+ /**
+ * Set the source resource.
+ * @param a the resource to load as a single element Resource collection.
+ */
+ public void addConfigured(ResourceCollection a) {
+ if (a.size() != 1) {
+ throw new BuildException("only single argument resource collections"
+ + " are supported");
+ }
+ src = a.iterator().next();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Local.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Local.java
new file mode 100644
index 00000000..3fdf9b59
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Local.java
@@ -0,0 +1,47 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.property.LocalProperties;
+
+/**
+ * Task to create a local property in the current scope.
+ */
+public class Local extends Task {
+ private String name;
+
+ /**
+ * Set the name attribute.
+ * @param name the name of the local property.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Run the task.
+ */
+ public void execute() {
+ if (name == null) {
+ throw new BuildException("Missing attribute name");
+ }
+ LocalProperties.get(getProject()).addLocal(name);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LogOutputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LogOutputStream.java
new file mode 100644
index 00000000..b2c34684
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LogOutputStream.java
@@ -0,0 +1,111 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.IOException;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.LineOrientedOutputStream;
+
+/**
+ * Logs each line written to this stream to the log system of ant.
+ *
+ * Tries to be smart about line separators.<br>
+ *
+ * @since Ant 1.2
+ */
+public class LogOutputStream extends LineOrientedOutputStream {
+
+ private ProjectComponent pc;
+ private int level = Project.MSG_INFO;
+
+ /**
+ * Create a new LogOutputStream for the specified ProjectComponent.
+ *
+ * @param pc the project component for whom to log
+ * @since Ant 1.7.1
+ */
+ public LogOutputStream(ProjectComponent pc) {
+ this.pc = pc;
+ }
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param task the task for whom to log
+ * @param level loglevel used to log data written to this stream.
+ */
+ public LogOutputStream(Task task, int level) {
+ this((ProjectComponent) task, level);
+ }
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param pc the project component for whom to log
+ * @param level loglevel used to log data written to this stream.
+ * @since Ant 1.6.3
+ */
+ public LogOutputStream(ProjectComponent pc, int level) {
+ this(pc);
+ this.level = level;
+ }
+
+ /**
+ * Converts the buffer to a string and sends it to <code>processLine</code>
+ */
+ protected void processBuffer() {
+ try {
+ super.processBuffer();
+ } catch (IOException e) {
+ // impossible since *our* processLine doesn't throw an IOException
+ throw new RuntimeException("Impossible IOException caught: " + e);
+ }
+ }
+
+ /**
+ * Logs a line to the log system of ant.
+ *
+ * @param line the line to log.
+ */
+ protected void processLine(String line) {
+ processLine(line, level);
+ }
+
+ /**
+ * Logs a line to the log system of ant.
+ *
+ * @param line the line to log.
+ * @param level the logging level to use.
+ */
+ protected void processLine(String line, int level) {
+ pc.log(line, level);
+ }
+
+ /**
+ * Get the level.
+ * @return the log level.
+ */
+ public int getMessageLevel() {
+ return level;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LogStreamHandler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LogStreamHandler.java
new file mode 100644
index 00000000..ed16cf36
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/LogStreamHandler.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.tools.ant.taskdefs;
+
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+
+/**
+ * Logs standard output and error of a subprocess to the log system of ant.
+ *
+ * @since Ant 1.2
+ */
+public class LogStreamHandler extends PumpStreamHandler {
+
+ /**
+ * Creates log stream handler
+ *
+ * @param task the task for whom to log
+ * @param outlevel the loglevel used to log standard output
+ * @param errlevel the loglevel used to log standard error
+ */
+ public LogStreamHandler(Task task, int outlevel, int errlevel) {
+ this((ProjectComponent) task, outlevel, errlevel);
+ }
+
+ /**
+ * Creates log stream handler
+ *
+ * @param pc the project component for whom to log
+ * @param outlevel the loglevel used to log standard output
+ * @param errlevel the loglevel used to log standard error
+ */
+ public LogStreamHandler(ProjectComponent pc, int outlevel, int errlevel) {
+ super(new LogOutputStream(pc, outlevel),
+ new LogOutputStream(pc, errlevel));
+ }
+
+ /**
+ * Stop the log stream handler.
+ */
+ public void stop() {
+ super.stop();
+ try {
+ getErr().close();
+ getOut().close();
+ } catch (IOException e) {
+ // plain impossible
+ throw new BuildException(e);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MacroDef.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MacroDef.java
new file mode 100644
index 00000000..63f68c5a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MacroDef.java
@@ -0,0 +1,851 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.tools.ant.AntTypeDefinition;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.RuntimeConfigurable;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.UnknownElement;
+
+/**
+ * Describe class <code>MacroDef</code> here.
+ *
+ * @since Ant 1.6
+ */
+public class MacroDef extends AntlibDefinition {
+
+ private NestedSequential nestedSequential;
+ private String name;
+ private boolean backTrace = true;
+ private List<Attribute> attributes = new ArrayList<Attribute>();
+ private Map<String, TemplateElement> elements = new HashMap<String, TemplateElement>();
+ private String textName = null;
+ private Text text = null;
+ private boolean hasImplicitElement = false;
+
+ /**
+ * Name of the definition
+ * @param name the name of the definition
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Add the text element.
+ * @param text the nested text element to add
+ * @since ant 1.6.1
+ */
+ public void addConfiguredText(Text text) {
+ if (this.text != null) {
+ throw new BuildException(
+ "Only one nested text element allowed");
+ }
+ if (text.getName() == null) {
+ throw new BuildException(
+ "the text nested element needed a \"name\" attribute");
+ }
+ // Check if used by attributes
+ for (Attribute attribute : attributes) {
+ if (text.getName().equals(attribute.getName())) {
+ throw new BuildException(
+ "the name \"" + text.getName()
+ + "\" is already used as an attribute");
+ }
+ }
+ this.text = text;
+ this.textName = text.getName();
+ }
+
+ /**
+ * @return the nested text element
+ * @since ant 1.6.1
+ */
+ public Text getText() {
+ return text;
+ }
+
+ /**
+ * Set the backTrace attribute.
+ *
+ * @param backTrace if true and the macro instance generates
+ * an error, a backtrace of the location within
+ * the macro and call to the macro will be output.
+ * if false, only the location of the call to the
+ * macro will be shown. Default is true.
+ * @since ant 1.7
+ */
+ public void setBackTrace(boolean backTrace) {
+ this.backTrace = backTrace;
+ }
+
+ /**
+ * @return the backTrace attribute.
+ * @since ant 1.7
+ */
+ public boolean getBackTrace() {
+ return backTrace;
+ }
+
+ /**
+ * This is the sequential nested element of the macrodef.
+ *
+ * @return a sequential element to be configured.
+ */
+ public NestedSequential createSequential() {
+ if (this.nestedSequential != null) {
+ throw new BuildException("Only one sequential allowed");
+ }
+ this.nestedSequential = new NestedSequential();
+ return this.nestedSequential;
+ }
+
+ /**
+ * The class corresponding to the sequential nested element.
+ * This is a simple task container.
+ */
+ public static class NestedSequential implements TaskContainer {
+ private List<Task> nested = new ArrayList<Task>();
+
+ /**
+ * Add a task or type to the container.
+ *
+ * @param task an unknown element.
+ */
+ public void addTask(Task task) {
+ nested.add(task);
+ }
+
+ /**
+ * @return the list of unknown elements
+ */
+ public List<Task> getNested() {
+ return nested;
+ }
+
+ /**
+ * A compare function to compare this with another
+ * NestedSequential.
+ * It calls similar on the nested unknown elements.
+ *
+ * @param other the nested sequential to compare with.
+ * @return true if they are similar, false otherwise
+ */
+ public boolean similar(NestedSequential other) {
+ final int size = nested.size();
+ if (size != other.nested.size()) {
+ return false;
+ }
+ for (int i = 0; i < size; ++i) {
+ UnknownElement me = (UnknownElement) nested.get(i);
+ UnknownElement o = (UnknownElement) other.nested.get(i);
+ if (!me.similar(o)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Convert the nested sequential to an unknown element
+ * @return the nested sequential as an unknown element.
+ */
+ public UnknownElement getNestedTask() {
+ UnknownElement ret = new UnknownElement("sequential");
+ ret.setTaskName("sequential");
+ ret.setNamespace("");
+ ret.setQName("sequential");
+ new RuntimeConfigurable(ret, "sequential");
+ final int size = nestedSequential.getNested().size();
+ for (int i = 0; i < size; ++i) {
+ UnknownElement e =
+ (UnknownElement) nestedSequential.getNested().get(i);
+ ret.addChild(e);
+ ret.getWrapper().addChild(e.getWrapper());
+ }
+ return ret;
+ }
+
+ /**
+ * Gets this macro's attribute (and define?) list.
+ *
+ * @return the nested Attributes
+ */
+ public List<Attribute> getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * Gets this macro's elements.
+ *
+ * @return the map nested elements, keyed by element name, with
+ * {@link TemplateElement} values.
+ */
+ public Map<String, TemplateElement> getElements() {
+ return elements;
+ }
+
+ /**
+ * Check if a character is a valid character for an element or
+ * attribute name.
+ *
+ * @param c the character to check
+ * @return true if the character is a letter or digit or '.' or '-'
+ * attribute name
+ */
+ public static boolean isValidNameCharacter(char c) {
+ // ? is there an xml api for this ?
+ return Character.isLetterOrDigit(c) || c == '.' || c == '-';
+ }
+
+ /**
+ * Check if a string is a valid name for an element or attribute.
+ *
+ * @param name the string to check
+ * @return true if the name consists of valid name characters
+ */
+ private static boolean isValidName(String name) {
+ if (name.length() == 0) {
+ return false;
+ }
+ for (int i = 0; i < name.length(); ++i) {
+ if (!isValidNameCharacter(name.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Add an attribute element.
+ *
+ * @param attribute an attribute nested element.
+ */
+ public void addConfiguredAttribute(Attribute attribute) {
+ if (attribute.getName() == null) {
+ throw new BuildException(
+ "the attribute nested element needed a \"name\" attribute");
+ }
+ if (attribute.getName().equals(textName)) {
+ throw new BuildException(
+ "the name \"" + attribute.getName()
+ + "\" has already been used by the text element");
+ }
+ final int size = attributes.size();
+ for (int i = 0; i < size; ++i) {
+ Attribute att = (Attribute) attributes.get(i);
+ if (att.getName().equals(attribute.getName())) {
+ throw new BuildException(
+ "the name \"" + attribute.getName()
+ + "\" has already been used in "
+ + "another attribute element");
+ }
+ }
+ attributes.add(attribute);
+ }
+
+ /**
+ * Add an element element.
+ *
+ * @param element an element nested element.
+ */
+ public void addConfiguredElement(TemplateElement element) {
+ if (element.getName() == null) {
+ throw new BuildException(
+ "the element nested element needed a \"name\" attribute");
+ }
+ if (elements.get(element.getName()) != null) {
+ throw new BuildException(
+ "the element " + element.getName()
+ + " has already been specified");
+ }
+ if (hasImplicitElement
+ || (element.isImplicit() && elements.size() != 0)) {
+ throw new BuildException(
+ "Only one element allowed when using implicit elements");
+ }
+ hasImplicitElement = element.isImplicit();
+ elements.put(element.getName(), element);
+ }
+
+ /**
+ * Create a new ant type based on the embedded tasks and types.
+ */
+ public void execute() {
+ if (nestedSequential == null) {
+ throw new BuildException("Missing sequential element");
+ }
+ if (name == null) {
+ throw new BuildException("Name not specified");
+ }
+
+ name = ProjectHelper.genComponentName(getURI(), name);
+
+ MyAntTypeDefinition def = new MyAntTypeDefinition(this);
+ def.setName(name);
+ def.setClass(MacroInstance.class);
+
+ ComponentHelper helper = ComponentHelper.getComponentHelper(
+ getProject());
+
+ helper.addDataTypeDefinition(def);
+ log("creating macro " + name, Project.MSG_VERBOSE);
+ }
+
+
+ /**
+ * An attribute for the MacroDef task.
+ *
+ */
+ public static class Attribute {
+ private String name;
+ private String defaultValue;
+ private String description;
+ private boolean doubleExpanding = true;
+
+ /**
+ * The name of the attribute.
+ *
+ * @param name the name of the attribute
+ */
+ public void setName(String name) {
+ if (!isValidName(name)) {
+ throw new BuildException(
+ "Illegal name [" + name + "] for attribute");
+ }
+ this.name = name.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * @return the name of the attribute
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * The default value to use if the parameter is not
+ * used in the templated instance.
+ *
+ * @param defaultValue the default value
+ */
+ public void setDefault(String defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ /**
+ * @return the default value, null if not set
+ */
+ public String getDefault() {
+ return defaultValue;
+ }
+
+ /**
+ * @param desc Description of the element.
+ * @since ant 1.6.1
+ */
+ public void setDescription(String desc) {
+ description = desc;
+ }
+
+ /**
+ * @return the description of the element, or <code>null</code> if
+ * no description is available.
+ * @since ant 1.6.1
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * See {@link #isDoubleExpanding} for explanation.
+ * @param doubleExpanding true to expand twice, false for just once
+ * @since Ant 1.8.3
+ */
+ public void setDoubleExpanding(boolean doubleExpanding) {
+ this.doubleExpanding = doubleExpanding;
+ }
+
+ /**
+ * Determines whether {@link RuntimeConfigurable#maybeConfigure(Project, boolean)} will reevaluate this property.
+ * For compatibility reasons (#52621) it will, though for most applications (#42046) it should not.
+ * @return true if expanding twice (the default), false for just once
+ * @since Ant 1.8.3
+ */
+ public boolean isDoubleExpanding() {
+ return doubleExpanding;
+ }
+
+ /**
+ * equality method
+ *
+ * @param obj an <code>Object</code> value
+ * @return a <code>boolean</code> value
+ */
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (obj.getClass() != getClass()) {
+ return false;
+ }
+ Attribute other = (Attribute) obj;
+ if (name == null) {
+ if (other.name != null) {
+ return false;
+ }
+ } else if (!name.equals(other.name)) {
+ return false;
+ }
+ if (defaultValue == null) {
+ if (other.defaultValue != null) {
+ return false;
+ }
+ } else if (!defaultValue.equals(other.defaultValue)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @return a hash code value for this object.
+ */
+ public int hashCode() {
+ return objectHashCode(defaultValue) + objectHashCode(name);
+ }
+ }
+
+ /**
+ * A nested text element for the MacroDef task.
+ * @since ant 1.6.1
+ */
+ public static class Text {
+ private String name;
+ private boolean optional;
+ private boolean trim;
+ private String description;
+ private String defaultString;
+
+ /**
+ * The name of the attribute.
+ *
+ * @param name the name of the attribute
+ */
+ public void setName(String name) {
+ if (!isValidName(name)) {
+ throw new BuildException(
+ "Illegal name [" + name + "] for attribute");
+ }
+ this.name = name.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * @return the name of the attribute
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * The optional attribute of the text element.
+ *
+ * @param optional if true this is optional
+ */
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+
+ /**
+ * @return true if the text is optional
+ */
+ public boolean getOptional() {
+ return optional;
+ }
+
+ /**
+ * The trim attribute of the text element.
+ *
+ * @param trim if true this String.trim() is called on
+ * the contents of the text element.
+ */
+ public void setTrim(boolean trim) {
+ this.trim = trim;
+ }
+
+ /**
+ * @return true if the text is trim
+ */
+ public boolean getTrim() {
+ return trim;
+ }
+
+ /**
+ * @param desc Description of the text.
+ */
+ public void setDescription(String desc) {
+ description = desc;
+ }
+
+ /**
+ * @return the description of the text, or <code>null</code> if
+ * no description is available.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * @param defaultString default text for the string.
+ */
+ public void setDefault(String defaultString) {
+ this.defaultString = defaultString;
+ }
+
+ /**
+ * @return the default text if set, null otherwise.
+ */
+ public String getDefault() {
+ return defaultString;
+ }
+
+ /**
+ * equality method
+ *
+ * @param obj an <code>Object</code> value
+ * @return a <code>boolean</code> value
+ */
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (obj.getClass() != getClass()) {
+ return false;
+ }
+ Text other = (Text) obj;
+ return safeCompare(name, other.name)
+ && optional == other.optional
+ && trim == other.trim
+ && safeCompare(defaultString, other.defaultString);
+ }
+
+ /**
+ * @return a hash code value for this object.
+ */
+ public int hashCode() {
+ return objectHashCode(name);
+ }
+ }
+
+ private static boolean safeCompare(Object a, Object b) {
+ return a == null ? b == null : a.equals(b);
+ }
+
+ /**
+ * A nested element for the MacroDef task.
+ */
+ public static class TemplateElement {
+
+ private String name;
+ private String description;
+ private boolean optional = false;
+ private boolean implicit = false;
+
+ /**
+ * Sets the name of this element.
+ *
+ * @param name the name of the element
+ */
+ public void setName(String name) {
+ if (!isValidName(name)) {
+ throw new BuildException(
+ "Illegal name [" + name + "] for macro element");
+ }
+ this.name = name.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Gets the name of this element.
+ *
+ * @return the name of the element.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets a textual description of this element,
+ * for build documentation purposes only.
+ *
+ * @param desc Description of the element.
+ * @since ant 1.6.1
+ */
+ public void setDescription(String desc) {
+ description = desc;
+ }
+
+ /**
+ * Gets the description of this element.
+ *
+ * @return the description of the element, or <code>null</code> if
+ * no description is available.
+ * @since ant 1.6.1
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets whether this element is optional.
+ *
+ * @param optional if true this element may be left out, default
+ * is false.
+ */
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+
+ /**
+ * Gets whether this element is optional.
+ *
+ * @return the optional attribute
+ */
+ public boolean isOptional() {
+ return optional;
+ }
+
+ /**
+ * Sets whether this element is implicit.
+ *
+ * @param implicit if true this element may be left out, default
+ * is false.
+ */
+ public void setImplicit(boolean implicit) {
+ this.implicit = implicit;
+ }
+
+ /**
+ * Gets whether this element is implicit.
+ *
+ * @return the implicit attribute
+ */
+ public boolean isImplicit() {
+ return implicit;
+ }
+
+ /**
+ * equality method.
+ *
+ * @param obj an <code>Object</code> value
+ * @return a <code>boolean</code> value
+ */
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj == null || !obj.getClass().equals(getClass())) {
+ return false;
+ }
+ TemplateElement t = (TemplateElement) obj;
+ return
+ (name == null ? t.name == null : name.equals(t.name))
+ && optional == t.optional
+ && implicit == t.implicit;
+ }
+
+ /**
+ * @return a hash code value for this object.
+ */
+ public int hashCode() {
+ return objectHashCode(name)
+ + (optional ? 1 : 0) + (implicit ? 1 : 0);
+ }
+
+ } // END static class TemplateElement
+
+ /**
+ * same or similar equality method for macrodef, ignores project and
+ * runtime info.
+ *
+ * @param obj an <code>Object</code> value
+ * @param same if true test for sameness, otherwise just similar
+ * @return a <code>boolean</code> value
+ */
+ private boolean sameOrSimilar(Object obj, boolean same) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (obj == null) {
+ return false;
+ }
+ if (!obj.getClass().equals(getClass())) {
+ return false;
+ }
+ MacroDef other = (MacroDef) obj;
+ if (name == null) {
+ return other.name == null;
+ }
+ if (!name.equals(other.name)) {
+ return false;
+ }
+ // Allow two macro definitions with the same location
+ // to be treated as similar - bugzilla 31215
+ if (other.getLocation() != null
+ && other.getLocation().equals(getLocation())
+ && !same) {
+ return true;
+ }
+ if (text == null) {
+ if (other.text != null) {
+ return false;
+ }
+ } else {
+ if (!text.equals(other.text)) {
+ return false;
+ }
+ }
+ if (getURI() == null || getURI().equals("")
+ || getURI().equals(ProjectHelper.ANT_CORE_URI)) {
+ if (!(other.getURI() == null || other.getURI().equals("")
+ || other.getURI().equals(ProjectHelper.ANT_CORE_URI))) {
+ return false;
+ }
+ } else {
+ if (!getURI().equals(other.getURI())) {
+ return false;
+ }
+ }
+
+ if (!nestedSequential.similar(other.nestedSequential)) {
+ return false;
+ }
+ if (!attributes.equals(other.attributes)) {
+ return false;
+ }
+ if (!elements.equals(other.elements)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Similar method for this definition
+ *
+ * @param obj another definition
+ * @return true if the definitions are similar
+ */
+ public boolean similar(Object obj) {
+ return sameOrSimilar(obj, false);
+ }
+
+ /**
+ * Equality method for this definition
+ *
+ * @param obj another definition
+ * @return true if the definitions are the same
+ */
+ public boolean sameDefinition(Object obj) {
+ return sameOrSimilar(obj, true);
+ }
+
+ /**
+ * extends AntTypeDefinition, on create
+ * of the object, the template macro definition
+ * is given.
+ */
+ private static class MyAntTypeDefinition extends AntTypeDefinition {
+ private MacroDef macroDef;
+
+ /**
+ * Creates a new <code>MyAntTypeDefinition</code> instance.
+ *
+ * @param macroDef a <code>MacroDef</code> value
+ */
+ public MyAntTypeDefinition(MacroDef macroDef) {
+ this.macroDef = macroDef;
+ }
+
+ /**
+ * Create an instance of the definition.
+ * The instance may be wrapped in a proxy class.
+ * @param project the current project
+ * @return the created object
+ */
+ public Object create(Project project) {
+ Object o = super.create(project);
+ if (o == null) {
+ return null;
+ }
+ ((MacroInstance) o).setMacroDef(macroDef);
+ return o;
+ }
+
+ /**
+ * Equality method for this definition
+ *
+ * @param other another definition
+ * @param project the current project
+ * @return true if the definitions are the same
+ */
+ public boolean sameDefinition(AntTypeDefinition other, Project project) {
+ if (!super.sameDefinition(other, project)) {
+ return false;
+ }
+ MyAntTypeDefinition otherDef = (MyAntTypeDefinition) other;
+ return macroDef.sameDefinition(otherDef.macroDef);
+ }
+
+ /**
+ * Similar method for this definition
+ *
+ * @param other another definition
+ * @param project the current project
+ * @return true if the definitions are the same
+ */
+ public boolean similarDefinition(
+ AntTypeDefinition other, Project project) {
+ if (!super.similarDefinition(other, project)) {
+ return false;
+ }
+ MyAntTypeDefinition otherDef = (MyAntTypeDefinition) other;
+ return macroDef.similar(otherDef.macroDef);
+ }
+ }
+
+ private static int objectHashCode(Object o) {
+ if (o == null) {
+ return 0;
+ } else {
+ return o.hashCode();
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MacroInstance.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MacroInstance.java
new file mode 100644
index 00000000..32b1a1a2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MacroInstance.java
@@ -0,0 +1,411 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DynamicAttribute;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.RuntimeConfigurable;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.UnknownElement;
+import org.apache.tools.ant.property.LocalProperties;
+import org.apache.tools.ant.taskdefs.MacroDef.Attribute;
+
+/**
+ * The class to be placed in the ant type definition.
+ * It is given a pointer to the template definition,
+ * and makes a copy of the unknown element, substituting
+ * the parameter values in attributes and text.
+ * @since Ant 1.6
+ */
+public class MacroInstance extends Task implements DynamicAttribute, TaskContainer {
+ private MacroDef macroDef;
+ private Map<String, String> map = new HashMap<String, String>();
+ private Map<String, MacroDef.TemplateElement> nsElements = null;
+ private Map<String, UnknownElement> presentElements;
+ private Hashtable<String, String> localAttributes;
+ private String text = null;
+ private String implicitTag = null;
+ private List<Task> unknownElements = new ArrayList<Task>();
+
+ /**
+ * Called from MacroDef.MyAntTypeDefinition#create()
+ *
+ * @param macroDef a <code>MacroDef</code> value
+ */
+ public void setMacroDef(MacroDef macroDef) {
+ this.macroDef = macroDef;
+ }
+
+ /**
+ * @return the macro definition object for this macro instance.
+ */
+ public MacroDef getMacroDef() {
+ return macroDef;
+ }
+
+ /**
+ * A parameter name value pair as a xml attribute.
+ *
+ * @param name the name of the attribute
+ * @param value the value of the attribute
+ */
+ public void setDynamicAttribute(String name, String value) {
+ map.put(name, value);
+ }
+
+ /**
+ * Method present for BC purposes.
+ * @param name not used
+ * @return nothing
+ * @deprecated since 1.6.x.
+ * @throws BuildException always
+ */
+ public Object createDynamicElement(String name) throws BuildException {
+ throw new BuildException("Not implemented any more");
+ }
+
+ private Map<String, MacroDef.TemplateElement> getNsElements() {
+ if (nsElements == null) {
+ nsElements = new HashMap<String, MacroDef.TemplateElement>();
+ for (Entry<String, MacroDef.TemplateElement> entry : macroDef.getElements().entrySet()) {
+ nsElements.put((String) entry.getKey(),
+ entry.getValue());
+ MacroDef.TemplateElement te = (MacroDef.TemplateElement)
+ entry.getValue();
+ if (te.isImplicit()) {
+ implicitTag = te.getName();
+ }
+ }
+ }
+ return nsElements;
+ }
+
+ /**
+ * Add a unknownElement for the macro instances nested elements.
+ *
+ * @param nestedTask a nested element.
+ */
+ public void addTask(Task nestedTask) {
+ unknownElements.add(nestedTask);
+ }
+
+ private void processTasks() {
+ if (implicitTag != null) {
+ return;
+ }
+ for (Iterator<Task> i = unknownElements.iterator(); i.hasNext();) {
+ UnknownElement ue = (UnknownElement) i.next();
+ String name = ProjectHelper.extractNameFromComponentName(
+ ue.getTag()).toLowerCase(Locale.ENGLISH);
+ if (getNsElements().get(name) == null) {
+ throw new BuildException("unsupported element " + name);
+ }
+ if (presentElements.get(name) != null) {
+ throw new BuildException("Element " + name + " already present");
+ }
+ presentElements.put(name, ue);
+ }
+ }
+
+ /**
+ * Embedded element in macro instance
+ */
+ public static class Element implements TaskContainer {
+ private List<Task> unknownElements = new ArrayList<Task>();
+
+ /**
+ * Add an unknown element (to be snipped into the macroDef instance)
+ *
+ * @param nestedTask an unknown element
+ */
+ public void addTask(Task nestedTask) {
+ unknownElements.add(nestedTask);
+ }
+
+ /**
+ * @return the list of unknown elements
+ */
+ public List<Task> getUnknownElements() {
+ return unknownElements;
+ }
+ }
+
+ private static final int STATE_NORMAL = 0;
+ private static final int STATE_EXPECT_BRACKET = 1;
+ private static final int STATE_EXPECT_NAME = 2;
+
+ private String macroSubs(String s, Map<String, String> macroMapping) {
+ if (s == null) {
+ return null;
+ }
+ StringBuffer ret = new StringBuffer();
+ StringBuffer macroName = null;
+
+ int state = STATE_NORMAL;
+ for (int i = 0; i < s.length(); ++i) {
+ char ch = s.charAt(i);
+ switch (state) {
+ case STATE_NORMAL:
+ if (ch == '@') {
+ state = STATE_EXPECT_BRACKET;
+ } else {
+ ret.append(ch);
+ }
+ break;
+ case STATE_EXPECT_BRACKET:
+ if (ch == '{') {
+ state = STATE_EXPECT_NAME;
+ macroName = new StringBuffer();
+ } else if (ch == '@') {
+ state = STATE_NORMAL;
+ ret.append('@');
+ } else {
+ state = STATE_NORMAL;
+ ret.append('@');
+ ret.append(ch);
+ }
+ break;
+ case STATE_EXPECT_NAME:
+ if (ch == '}') {
+ state = STATE_NORMAL;
+ String name = macroName.toString().toLowerCase(Locale.ENGLISH);
+ String value = (String) macroMapping.get(name);
+ if (value == null) {
+ ret.append("@{");
+ ret.append(name);
+ ret.append("}");
+ } else {
+ ret.append(value);
+ }
+ macroName = null;
+ } else {
+ macroName.append(ch);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ switch (state) {
+ case STATE_NORMAL:
+ break;
+ case STATE_EXPECT_BRACKET:
+ ret.append('@');
+ break;
+ case STATE_EXPECT_NAME:
+ ret.append("@{");
+ ret.append(macroName.toString());
+ break;
+ default:
+ break;
+ }
+
+ return ret.toString();
+ }
+
+ /**
+ * Set the text contents for the macro.
+ * @param text the text to be added to the macro.
+ */
+
+ public void addText(String text) {
+ this.text = text;
+ }
+
+ private UnknownElement copy(UnknownElement ue, boolean nested) {
+ UnknownElement ret = new UnknownElement(ue.getTag());
+ ret.setNamespace(ue.getNamespace());
+ ret.setProject(getProject());
+ ret.setQName(ue.getQName());
+ ret.setTaskType(ue.getTaskType());
+ ret.setTaskName(ue.getTaskName());
+ ret.setLocation(
+ macroDef.getBackTrace() ? ue.getLocation() : getLocation());
+ if (getOwningTarget() == null) {
+ Target t = new Target();
+ t.setProject(getProject());
+ ret.setOwningTarget(t);
+ } else {
+ ret.setOwningTarget(getOwningTarget());
+ }
+ RuntimeConfigurable rc = new RuntimeConfigurable(
+ ret, ue.getTaskName());
+ rc.setPolyType(ue.getWrapper().getPolyType());
+ Map<String, Object> m = ue.getWrapper().getAttributeMap();
+ for (Map.Entry<String, Object> entry : m.entrySet()) {
+ rc.setAttribute(
+ entry.getKey(),
+ macroSubs((String) entry.getValue(), localAttributes));
+ }
+ rc.addText(macroSubs(ue.getWrapper().getText().toString(),
+ localAttributes));
+
+ Enumeration<RuntimeConfigurable> e = ue.getWrapper().getChildren();
+ while (e.hasMoreElements()) {
+ RuntimeConfigurable r = e.nextElement();
+ UnknownElement unknownElement = (UnknownElement) r.getProxy();
+ String tag = unknownElement.getTaskType();
+ if (tag != null) {
+ tag = tag.toLowerCase(Locale.ENGLISH);
+ }
+ MacroDef.TemplateElement templateElement =
+ getNsElements().get(tag);
+ if (templateElement == null || nested) {
+ UnknownElement child = copy(unknownElement, nested);
+ rc.addChild(child.getWrapper());
+ ret.addChild(child);
+ } else if (templateElement.isImplicit()) {
+ if (unknownElements.size() == 0 && !templateElement.isOptional()) {
+ throw new BuildException(
+ "Missing nested elements for implicit element "
+ + templateElement.getName());
+ }
+ for (Iterator<Task> i = unknownElements.iterator();
+ i.hasNext();) {
+ UnknownElement child
+ = copy((UnknownElement) i.next(), true);
+ rc.addChild(child.getWrapper());
+ ret.addChild(child);
+ }
+ } else {
+ UnknownElement presentElement =
+ (UnknownElement) presentElements.get(tag);
+ if (presentElement == null) {
+ if (!templateElement.isOptional()) {
+ throw new BuildException(
+ "Required nested element "
+ + templateElement.getName() + " missing");
+ }
+ continue;
+ }
+ String presentText =
+ presentElement.getWrapper().getText().toString();
+ if (!"".equals(presentText)) {
+ rc.addText(macroSubs(presentText, localAttributes));
+ }
+ List<UnknownElement> list = presentElement.getChildren();
+ if (list != null) {
+ for (Iterator<UnknownElement> i = list.iterator();
+ i.hasNext();) {
+ UnknownElement child
+ = copy(i.next(), true);
+ rc.addChild(child.getWrapper());
+ ret.addChild(child);
+ }
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Execute the templates instance.
+ * Copies the unknown element, substitutes the attributes,
+ * and calls perform on the unknown element.
+ *
+ */
+ public void execute() {
+ presentElements = new HashMap<String, UnknownElement>();
+ getNsElements();
+ processTasks();
+ localAttributes = new Hashtable<String, String>();
+ Set<String> copyKeys = new HashSet<String>(map.keySet());
+ for (Attribute attribute : macroDef.getAttributes()) {
+ String value = (String) map.get(attribute.getName());
+ if (value == null && "description".equals(attribute.getName())) {
+ value = getDescription();
+ }
+ if (value == null) {
+ value = attribute.getDefault();
+ value = macroSubs(value, localAttributes);
+ }
+ if (value == null) {
+ throw new BuildException(
+ "required attribute " + attribute.getName() + " not set");
+ }
+ localAttributes.put(attribute.getName(), value);
+ copyKeys.remove(attribute.getName());
+ }
+ if (copyKeys.contains("id")) {
+ copyKeys.remove("id");
+ }
+ if (macroDef.getText() != null) {
+ if (text == null) {
+ String defaultText = macroDef.getText().getDefault();
+ if (!macroDef.getText().getOptional() && defaultText == null) {
+ throw new BuildException(
+ "required text missing");
+ }
+ text = defaultText == null ? "" : defaultText;
+ }
+ if (macroDef.getText().getTrim()) {
+ text = text.trim();
+ }
+ localAttributes.put(macroDef.getText().getName(), text);
+ } else {
+ if (text != null && !text.trim().equals("")) {
+ throw new BuildException(
+ "The \"" + getTaskName() + "\" macro does not support"
+ + " nested text data.");
+ }
+ }
+ if (copyKeys.size() != 0) {
+ throw new BuildException(
+ "Unknown attribute" + (copyKeys.size() > 1 ? "s " : " ")
+ + copyKeys);
+ }
+
+ // need to set the project on unknown element
+ UnknownElement c = copy(macroDef.getNestedTask(), false);
+ c.init();
+ LocalProperties localProperties
+ = LocalProperties.get(getProject());
+ localProperties.enterScope();
+ try {
+ c.perform();
+ } catch (BuildException ex) {
+ if (macroDef.getBackTrace()) {
+ throw ProjectHelper.addLocationToBuildException(
+ ex, getLocation());
+ } else {
+ ex.setLocation(getLocation());
+ throw ex;
+ }
+ } finally {
+ presentElements = null;
+ localAttributes = null;
+ localProperties.exitScope();
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MakeUrl.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MakeUrl.java
new file mode 100644
index 00000000..e23c0253
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MakeUrl.java
@@ -0,0 +1,298 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * <p>This task takes file and turns them into a URL, which it then assigns
+ * to a property. Use when for setting up RMI codebases.</p>
+ *
+ * <p>nested filesets are supported; if present, these are turned into the
+ * url with the given separator between them (default = " ").</p>
+ *
+ * @ant.task category="core" name="makeurl"
+ */
+
+public class MakeUrl extends Task {
+
+ /**
+ * name of the property to set
+ */
+ private String property;
+
+ /**
+ * name of a file to turn into a URL
+ */
+ private File file;
+
+ /**
+ * separator char
+ */
+ private String separator = " ";
+
+ /**
+ * filesets of nested files to add to this url
+ */
+ private List<FileSet> filesets = new LinkedList<FileSet>();
+
+ /**
+ * paths to add
+ */
+ private List<Path> paths = new LinkedList<Path>();
+
+ /**
+ * validation flag
+ */
+ private boolean validate = true;
+
+ // error message strings
+ /** Missing file */
+ public static final String ERROR_MISSING_FILE = "A source file is missing: ";
+ /** No property defined */
+ public static final String ERROR_NO_PROPERTY = "No property defined";
+ /** No files defined */
+ public static final String ERROR_NO_FILES = "No files defined";
+
+ /**
+ * set the name of a property to fill with the URL
+ *
+ * @param property the name of the property.
+ */
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ /**
+ * the name of a file to be converted into a URL
+ *
+ * @param file the file to be converted.
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * a fileset of jar files to include in the URL, each
+ * separated by the separator
+ *
+ * @param fileset the fileset to be added.
+ */
+ public void addFileSet(FileSet fileset) {
+ filesets.add(fileset);
+ }
+
+ /**
+ * set the separator for the multi-url option.
+ *
+ * @param separator the separator to use.
+ */
+ public void setSeparator(String separator) {
+ this.separator = separator;
+ }
+
+ /**
+ * set this flag to trigger validation that every named file exists.
+ * Optional: default=true
+ *
+ * @param validate a <code>boolean</code> value.
+ */
+ public void setValidate(boolean validate) {
+ this.validate = validate;
+ }
+
+ /**
+ * add a path to the URL. All elements in the path
+ * will be converted to individual URL entries
+ *
+ * @param path a path value.
+ */
+ public void addPath(Path path) {
+ paths.add(path);
+ }
+
+ /**
+ * convert the filesets to urls.
+ *
+ * @return null for no files
+ */
+ private String filesetsToURL() {
+ if (filesets.isEmpty()) {
+ return "";
+ }
+ int count = 0;
+ StringBuilder urls = new StringBuilder();
+ ListIterator<FileSet> list = filesets.listIterator();
+ while (list.hasNext()) {
+ FileSet set = list.next();
+ DirectoryScanner scanner = set.getDirectoryScanner(getProject());
+ String[] files = scanner.getIncludedFiles();
+ for (int i = 0; i < files.length; i++) {
+ File f = new File(scanner.getBasedir(), files[i]);
+ validateFile(f);
+ String asUrl = toURL(f);
+ urls.append(asUrl);
+ log(asUrl, Project.MSG_DEBUG);
+ urls.append(separator);
+ count++;
+ }
+ }
+ //at this point there is one trailing space to remove, if the list is not empty.
+ return stripTrailingSeparator(urls, count);
+ }
+
+ /**
+ * convert the string buffer to a string, potentially stripping
+ * out any trailing separator
+ *
+ * @param urls URL buffer
+ * @param count number of URL entries
+ * @return trimmed string, or empty string
+ */
+ private String stripTrailingSeparator(StringBuilder urls,
+ int count) {
+ if (count > 0) {
+ urls.delete(urls.length() - separator.length(), urls.length());
+ return new String(urls);
+ } else {
+ return "";
+ }
+ }
+
+
+ /**
+ * convert all paths to URLs
+ *
+ * @return the paths as a separated list of URLs
+ */
+ private String pathsToURL() {
+ if (paths.isEmpty()) {
+ return "";
+ }
+ int count = 0;
+ StringBuilder urls = new StringBuilder();
+ ListIterator<Path> list = paths.listIterator();
+ while (list.hasNext()) {
+ Path path = list.next();
+ String[] elements = path.list();
+ for (int i = 0; i < elements.length; i++) {
+ File f = new File(elements[i]);
+ validateFile(f);
+ String asUrl = toURL(f);
+ urls.append(asUrl);
+ log(asUrl, Project.MSG_DEBUG);
+ urls.append(separator);
+ count++;
+ }
+ }
+ //at this point there is one trailing space to remove, if the list is not empty.
+ return stripTrailingSeparator(urls, count);
+ }
+
+ /**
+ * verify that the file exists, if {@link #validate} is set
+ *
+ * @param fileToCheck file that may need to exist
+ * @throws BuildException with text beginning {@link #ERROR_MISSING_FILE}
+ */
+ private void validateFile(File fileToCheck) {
+ if (validate && !fileToCheck.exists()) {
+ throw new BuildException(ERROR_MISSING_FILE + fileToCheck.toString());
+ }
+ }
+
+ /**
+ * Create the url
+ *
+ * @throws org.apache.tools.ant.BuildException
+ * if something goes wrong with the build
+ */
+ @Override
+ public void execute() throws BuildException {
+ validate();
+ //now exit here if the property is already set
+ if (getProject().getProperty(property) != null) {
+ return;
+ }
+ String url;
+ String filesetURL = filesetsToURL();
+ if (file != null) {
+ validateFile(file);
+ url = toURL(file);
+ //and add any files if also defined
+ if (filesetURL.length() > 0) {
+ url = url + separator + filesetURL;
+ }
+ } else {
+ url = filesetURL;
+ }
+ //add path URLs
+ String pathURL = pathsToURL();
+ if (pathURL.length() > 0) {
+ if (url.length() > 0) {
+ url = url + separator + pathURL;
+ } else {
+ url = pathURL;
+ }
+ }
+ log("Setting " + property + " to URL " + url, Project.MSG_VERBOSE);
+ getProject().setNewProperty(property, url);
+ }
+
+ /**
+ * check for errors
+ * @throws BuildException if we are not configured right
+ */
+ private void validate() {
+ //validation
+ if (property == null) {
+ throw new BuildException(ERROR_NO_PROPERTY);
+ }
+ if (file == null && filesets.isEmpty() && paths.isEmpty()) {
+ throw new BuildException(ERROR_NO_FILES);
+ }
+ }
+
+ /**
+ * convert a file to a URL;
+ *
+ * @param fileToConvert
+ * @return the file converted to a URL
+ */
+ private String toURL(File fileToConvert) {
+ String url;
+ //create the URL
+ //ant equivalent of fileToConvert.toURI().toURL().toExternalForm();
+ url = FileUtils.getFileUtils().toURI(fileToConvert.getAbsolutePath());
+
+ return url;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Manifest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Manifest.java
new file mode 100644
index 00000000..06c74ddc
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Manifest.java
@@ -0,0 +1,1183 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.CollectionUtils;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Holds the data of a jar manifest.
+ *
+ * Manifests are processed according to the
+ * {@link <a href="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html">Jar
+ * file specification.</a>}.
+ * Specifically, a manifest element consists of
+ * a set of attributes and sections. These sections in turn may contain
+ * attributes. Note in particular that this may result in manifest lines
+ * greater than 72 bytes being wrapped and continued on the next
+ * line. If an application can not handle the continuation mechanism, it
+ * is a defect in the application, not this task.
+ *
+ *
+ * @since Ant 1.4
+ */
+public class Manifest {
+ /** The standard manifest version header */
+ public static final String ATTRIBUTE_MANIFEST_VERSION
+ = "Manifest-Version";
+
+ /** The standard Signature Version header */
+ public static final String ATTRIBUTE_SIGNATURE_VERSION
+ = "Signature-Version";
+
+ /** The Name Attribute is the first in a named section */
+ public static final String ATTRIBUTE_NAME = "Name";
+
+ /** The From Header is disallowed in a Manifest */
+ public static final String ATTRIBUTE_FROM = "From";
+
+ /** The Class-Path Header is special - it can be duplicated */
+ public static final String ATTRIBUTE_CLASSPATH = "Class-Path";
+
+ /** Default Manifest version if one is not specified */
+ public static final String DEFAULT_MANIFEST_VERSION = "1.0";
+
+ /** The max length of a line in a Manifest */
+ public static final int MAX_LINE_LENGTH = 72;
+
+ /**
+ * Max length of a line section which is continued. Need to allow
+ * for the CRLF.
+ */
+ public static final int MAX_SECTION_LENGTH = MAX_LINE_LENGTH - 2;
+
+ /** The End-Of-Line marker in manifests */
+ public static final String EOL = "\r\n";
+ /** Error for attributes */
+ public static final String ERROR_FROM_FORBIDDEN = "Manifest attributes should not start "
+ + "with \"" + ATTRIBUTE_FROM + "\" in \"";
+
+ /** Encoding to be used for JAR files. */
+ public static final String JAR_ENCODING = "UTF-8";
+
+ private static final String ATTRIBUTE_MANIFEST_VERSION_LC =
+ ATTRIBUTE_MANIFEST_VERSION.toLowerCase(Locale.ENGLISH);
+ private static final String ATTRIBUTE_NAME_LC =
+ ATTRIBUTE_NAME.toLowerCase(Locale.ENGLISH);
+ private static final String ATTRIBUTE_FROM_LC =
+ ATTRIBUTE_FROM.toLowerCase(Locale.ENGLISH);
+ private static final String ATTRIBUTE_CLASSPATH_LC =
+ ATTRIBUTE_CLASSPATH.toLowerCase(Locale.ENGLISH);
+
+ /**
+ * An attribute for the manifest.
+ * Those attributes that are not nested into a section will be added to the "Main" section.
+ */
+ public static class Attribute {
+
+ /**
+ * Maximum length of the name to have the value starting on the same
+ * line as the name. This to stay under 72 bytes total line length
+ * (including CRLF).
+ */
+ private static final int MAX_NAME_VALUE_LENGTH = 68;
+
+ /**
+ * Maximum length of the name according to the jar specification.
+ * In this case the first line will have 74 bytes total line length
+ * (including CRLF). This conflicts with the 72 bytes total line length
+ * max, but is the only possible conclusion from the manifest specification, if
+ * names with 70 bytes length are allowed, have to be on the first line, and
+ * have to be followed by ": ".
+ */
+ private static final int MAX_NAME_LENGTH = 70;
+
+ /** The attribute's name */
+ private String name = null;
+
+ /** The attribute's value */
+ private Vector<String> values = new Vector<String>();
+
+ /**
+ * For multivalued attributes, this is the index of the attribute
+ * currently being defined.
+ */
+ private int currentIndex = 0;
+
+ /**
+ * Construct an empty attribute */
+ public Attribute() {
+ }
+
+ /**
+ * Construct an attribute by parsing a line from the Manifest
+ *
+ * @param line the line containing the attribute name and value
+ *
+ * @throws ManifestException if the line is not valid
+ */
+ public Attribute(String line) throws ManifestException {
+ parse(line);
+ }
+
+ /**
+ * Construct a manifest by specifying its name and value
+ *
+ * @param name the attribute's name
+ * @param value the Attribute's value
+ */
+ public Attribute(String name, String value) {
+ this.name = name;
+ setValue(value);
+ }
+
+ /**
+ * @see java.lang.Object#hashCode
+ * @return a hashcode based on the key and values.
+ */
+ @Override
+ public int hashCode() {
+ int hashCode = 0;
+
+ if (name != null) {
+ hashCode += getKey().hashCode();
+ }
+
+ hashCode += values.hashCode();
+ return hashCode;
+ }
+
+ /**
+ * @param rhs the object to check for equality.
+ * @see java.lang.Object#equals
+ * @return true if the key and values are the same.
+ */
+ @Override
+ public boolean equals(Object rhs) {
+ if (rhs == null || rhs.getClass() != getClass()) {
+ return false;
+ }
+
+ if (rhs == this) {
+ return true;
+ }
+
+ Attribute rhsAttribute = (Attribute) rhs;
+ String lhsKey = getKey();
+ String rhsKey = rhsAttribute.getKey();
+ if ((lhsKey == null && rhsKey != null)
+ || (lhsKey != null && !lhsKey.equals(rhsKey))) {
+ return false;
+ }
+
+ return values.equals(rhsAttribute.values);
+ }
+
+ /**
+ * Parse a line into name and value pairs
+ *
+ * @param line the line to be parsed
+ *
+ * @throws ManifestException if the line does not contain a colon
+ * separating the name and value
+ */
+ public void parse(String line) throws ManifestException {
+ int index = line.indexOf(": ");
+ if (index == -1) {
+ throw new ManifestException("Manifest line \"" + line
+ + "\" is not valid as it does not "
+ + "contain a name and a value separated by ': ' ");
+ }
+ name = line.substring(0, index);
+ setValue(line.substring(index + 2));
+ }
+
+ /**
+ * Set the Attribute's name; required
+ *
+ * @param name the attribute's name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the Attribute's name
+ *
+ * @return the attribute's name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the attribute's Key - its name in lower case.
+ *
+ * @return the attribute's key.
+ */
+ public String getKey() {
+ if (name == null) {
+ return null;
+ }
+ return name.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Set the Attribute's value; required
+ *
+ * @param value the attribute's value
+ */
+ public void setValue(String value) {
+ if (currentIndex >= values.size()) {
+ values.addElement(value);
+ currentIndex = values.size() - 1;
+ } else {
+ values.setElementAt(value, currentIndex);
+ }
+ }
+
+ /**
+ * Get the Attribute's value.
+ *
+ * @return the attribute's value.
+ */
+ public String getValue() {
+ if (values.size() == 0) {
+ return null;
+ }
+
+ String fullValue = "";
+ for (Enumeration<String> e = getValues(); e.hasMoreElements();) {
+ String value = e.nextElement();
+ fullValue += value + " ";
+ }
+ return fullValue.trim();
+ }
+
+ /**
+ * Add a new value to this attribute - making it multivalued.
+ *
+ * @param value the attribute's additional value
+ */
+ public void addValue(String value) {
+ currentIndex++;
+ setValue(value);
+ }
+
+ /**
+ * Get all the attribute's values.
+ *
+ * @return an enumeration of the attributes values
+ */
+ public Enumeration<String> getValues() {
+ return values.elements();
+ }
+
+ /**
+ * Add a continuation line from the Manifest file.
+ *
+ * When lines are too long in a manifest, they are continued on the
+ * next line by starting with a space. This method adds the continuation
+ * data to the attribute value by skipping the first character.
+ *
+ * @param line the continuation line.
+ */
+ public void addContinuation(String line) {
+ String currentValue = values.elementAt(currentIndex);
+ setValue(currentValue + line.substring(1));
+ }
+
+ /**
+ * Write the attribute out to a print writer without
+ * flattening multi-values attributes (i.e. Class-Path).
+ *
+ * @param writer the Writer to which the attribute is written
+ *
+ * @throws IOException if the attribute value cannot be written
+ */
+ public void write(PrintWriter writer) throws IOException {
+ write(writer, false);
+ }
+
+ /**
+ * Write the attribute out to a print writer.
+ *
+ * @param writer the Writer to which the attribute is written
+ * @param flatten whether to collapse multi-valued attributes
+ * (i.e. potentially Class-Path) Class-Path into a
+ * single attribute.
+ *
+ * @throws IOException if the attribute value cannot be written
+ * @since Ant 1.8.0
+ */
+ public void write(PrintWriter writer, boolean flatten)
+ throws IOException {
+ if (!flatten) {
+ for (Enumeration<String> e = getValues(); e.hasMoreElements();) {
+ writeValue(writer, e.nextElement());
+ }
+ } else {
+ writeValue(writer, getValue());
+ }
+ }
+
+ /**
+ * Write a single attribute value out
+ *
+ * @param writer the Writer to which the attribute is written
+ * @param value the attribute value
+ *
+ * @throws IOException if the attribute value cannot be written
+ */
+ private void writeValue(PrintWriter writer, String value)
+ throws IOException {
+ String line = null;
+ int nameLength = name.getBytes(JAR_ENCODING).length;
+ if (nameLength > MAX_NAME_VALUE_LENGTH) {
+ if (nameLength > MAX_NAME_LENGTH) {
+ throw new IOException("Unable to write manifest line "
+ + name + ": " + value);
+ }
+ writer.print(name + ": " + EOL);
+ line = " " + value;
+ } else {
+ line = name + ": " + value;
+ }
+ while (line.getBytes(JAR_ENCODING).length > MAX_SECTION_LENGTH) {
+ // try to find a MAX_LINE_LENGTH byte section
+ int breakIndex = MAX_SECTION_LENGTH;
+ if (breakIndex >= line.length()) {
+ breakIndex = line.length() - 1;
+ }
+ String section = line.substring(0, breakIndex);
+ while (section.getBytes(JAR_ENCODING).length > MAX_SECTION_LENGTH
+ && breakIndex > 0) {
+ breakIndex--;
+ section = line.substring(0, breakIndex);
+ }
+ if (breakIndex == 0) {
+ throw new IOException("Unable to write manifest line "
+ + name + ": " + value);
+ }
+ writer.print(section + EOL);
+ line = " " + line.substring(breakIndex);
+ }
+ writer.print(line + EOL);
+ }
+ }
+
+ /**
+ * A manifest section - you can nest attribute elements into sections.
+ * A section consists of a set of attribute values,
+ * separated from other sections by a blank line.
+ */
+ public static class Section {
+ /** Warnings for this section */
+ private Vector<String> warnings = new Vector<String>();
+
+ /**
+ * The section's name if any. The main section in a
+ * manifest is unnamed.
+ */
+ private String name = null;
+
+ /** The section's attributes.*/
+ private Map<String, Attribute> attributes = new LinkedHashMap<String, Attribute>();
+
+ /**
+ * The name of the section; optional -default is the main section.
+ * @param name the section's name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the Section's name.
+ *
+ * @return the section's name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Read a section through a reader.
+ *
+ * @param reader the reader from which the section is read
+ *
+ * @return the name of the next section if it has been read as
+ * part of this section - This only happens if the
+ * Manifest is malformed.
+ *
+ * @throws ManifestException if the section is not valid according
+ * to the JAR spec
+ * @throws IOException if the section cannot be read from the reader.
+ */
+ public String read(BufferedReader reader)
+ throws ManifestException, IOException {
+ Attribute attribute = null;
+ while (true) {
+ String line = reader.readLine();
+ if (line == null || line.length() == 0) {
+ return null;
+ }
+ if (line.charAt(0) == ' ') {
+ // continuation line
+ if (attribute == null) {
+ if (name != null) {
+ // a continuation on the first line is a
+ // continuation of the name - concatenate this
+ // line and the name
+ name += line.substring(1);
+ } else {
+ throw new ManifestException("Can't start an "
+ + "attribute with a continuation line " + line);
+ }
+ } else {
+ attribute.addContinuation(line);
+ }
+ } else {
+ attribute = new Attribute(line);
+ String nameReadAhead = addAttributeAndCheck(attribute);
+ // refresh attribute in case of multivalued attributes.
+ attribute = getAttribute(attribute.getKey());
+ if (nameReadAhead != null) {
+ return nameReadAhead;
+ }
+ }
+ }
+ }
+
+ /**
+ * Merge in another section without merging Class-Path attributes.
+ *
+ * @param section the section to be merged with this one.
+ *
+ * @throws ManifestException if the sections cannot be merged.
+ */
+ public void merge(Section section) throws ManifestException {
+ merge(section, false);
+ }
+
+ /**
+ * Merge in another section
+ *
+ * @param section the section to be merged with this one.
+ * @param mergeClassPaths whether Class-Path attributes should
+ * be merged.
+ *
+ * @throws ManifestException if the sections cannot be merged.
+ */
+ public void merge(Section section, boolean mergeClassPaths)
+ throws ManifestException {
+ if (name == null && section.getName() != null
+ || (name != null && section.getName() != null
+ && !(name.toLowerCase(Locale.ENGLISH)
+ .equals(section.getName().toLowerCase(Locale.ENGLISH))))
+ ) {
+ throw new ManifestException("Unable to merge sections "
+ + "with different names");
+ }
+
+ Enumeration<String> e = section.getAttributeKeys();
+ Attribute classpathAttribute = null;
+ while (e.hasMoreElements()) {
+ String attributeName = e.nextElement();
+ Attribute attribute = section.getAttribute(attributeName);
+ if (attributeName.equalsIgnoreCase(ATTRIBUTE_CLASSPATH)) {
+ if (classpathAttribute == null) {
+ classpathAttribute = new Attribute();
+ classpathAttribute.setName(ATTRIBUTE_CLASSPATH);
+ }
+ Enumeration<String> cpe = attribute.getValues();
+ while (cpe.hasMoreElements()) {
+ String value = cpe.nextElement();
+ classpathAttribute.addValue(value);
+ }
+ } else {
+ // the merge file always wins
+ storeAttribute(attribute);
+ }
+ }
+
+ if (classpathAttribute != null) {
+ if (mergeClassPaths) {
+ Attribute currentCp = getAttribute(ATTRIBUTE_CLASSPATH);
+ if (currentCp != null) {
+ for (Enumeration<String> attribEnum = currentCp.getValues();
+ attribEnum.hasMoreElements();) {
+ String value = attribEnum.nextElement();
+ classpathAttribute.addValue(value);
+ }
+ }
+ }
+ storeAttribute(classpathAttribute);
+ }
+
+ // add in the warnings
+ Enumeration<String> warnEnum = section.warnings.elements();
+ while (warnEnum.hasMoreElements()) {
+ warnings.addElement(warnEnum.nextElement());
+ }
+ }
+
+ /**
+ * Write the section out to a print writer without flattening
+ * multi-values attributes (i.e. Class-Path).
+ *
+ * @param writer the Writer to which the section is written
+ *
+ * @throws IOException if the section cannot be written
+ */
+ public void write(PrintWriter writer) throws IOException {
+ write(writer, false);
+ }
+
+ /**
+ * Write the section out to a print writer.
+ *
+ * @param writer the Writer to which the section is written
+ * @param flatten whether to collapse multi-valued attributes
+ * (i.e. potentially Class-Path) Class-Path into a
+ * single attribute.
+ *
+ * @throws IOException if the section cannot be written
+ * @since Ant 1.8.0
+ */
+ public void write(PrintWriter writer, boolean flatten)
+ throws IOException {
+ if (name != null) {
+ Attribute nameAttr = new Attribute(ATTRIBUTE_NAME, name);
+ nameAttr.write(writer);
+ }
+ Enumeration<String> e = getAttributeKeys();
+ while (e.hasMoreElements()) {
+ String key = e.nextElement();
+ Attribute attribute = getAttribute(key);
+ attribute.write(writer, flatten);
+ }
+ writer.print(EOL);
+ }
+
+ /**
+ * Get a attribute of the section
+ *
+ * @param attributeName the name of the attribute
+ * @return a Manifest.Attribute instance if the attribute is
+ * single-valued, otherwise a Vector of Manifest.Attribute
+ * instances.
+ */
+ public Attribute getAttribute(String attributeName) {
+ return attributes.get(attributeName.toLowerCase(Locale.ENGLISH));
+ }
+
+ /**
+ * Get the attribute keys.
+ *
+ * @return an Enumeration of Strings, each string being the lower case
+ * key of an attribute of the section.
+ */
+ public Enumeration<String> getAttributeKeys() {
+ return CollectionUtils.asEnumeration(attributes.keySet().iterator());
+ }
+
+ /**
+ * Get the value of the attribute with the name given.
+ *
+ * @param attributeName the name of the attribute to be returned.
+ *
+ * @return the attribute's value or null if the attribute does not exist
+ * in the section
+ */
+ public String getAttributeValue(String attributeName) {
+ Attribute attribute = getAttribute(attributeName.toLowerCase(Locale.ENGLISH));
+ if (attribute == null) {
+ return null;
+ }
+ return attribute.getValue();
+ }
+
+ /**
+ * Remove the given attribute from the section
+ *
+ * @param attributeName the name of the attribute to be removed.
+ */
+ public void removeAttribute(String attributeName) {
+ String key = attributeName.toLowerCase(Locale.ENGLISH);
+ attributes.remove(key);
+ }
+
+ /**
+ * Add an attribute to the section.
+ *
+ * @param attribute the attribute to be added to the section
+ *
+ * @exception ManifestException if the attribute is not valid.
+ */
+ public void addConfiguredAttribute(Attribute attribute)
+ throws ManifestException {
+ String check = addAttributeAndCheck(attribute);
+ if (check != null) {
+ throw new BuildException("Specify the section name using "
+ + "the \"name\" attribute of the <section> element rather "
+ + "than using a \"Name\" manifest attribute");
+ }
+ }
+
+ /**
+ * Add an attribute to the section
+ *
+ * @param attribute the attribute to be added.
+ *
+ * @return the value of the attribute if it is a name
+ * attribute - null other wise
+ *
+ * @exception ManifestException if the attribute already
+ * exists in this section.
+ */
+ public String addAttributeAndCheck(Attribute attribute)
+ throws ManifestException {
+ if (attribute.getName() == null || attribute.getValue() == null) {
+ throw new BuildException("Attributes must have name and value");
+ }
+ String attributeKey = attribute.getKey();
+ if (attributeKey.equals(ATTRIBUTE_NAME_LC)) {
+ warnings.addElement("\"" + ATTRIBUTE_NAME + "\" attributes "
+ + "should not occur in the main section and must be the "
+ + "first element in all other sections: \""
+ + attribute.getName() + ": " + attribute.getValue() + "\"");
+ return attribute.getValue();
+ }
+
+ if (attributeKey.startsWith(ATTRIBUTE_FROM_LC)) {
+ warnings.addElement(ERROR_FROM_FORBIDDEN
+ + attribute.getName() + ": " + attribute.getValue() + "\"");
+ } else {
+ // classpath attributes go into a vector
+ if (attributeKey.equals(ATTRIBUTE_CLASSPATH_LC)) {
+ Attribute classpathAttribute =
+ attributes.get(attributeKey);
+
+ if (classpathAttribute == null) {
+ storeAttribute(attribute);
+ } else {
+ warnings.addElement("Multiple Class-Path attributes "
+ + "are supported but violate the Jar "
+ + "specification and may not be correctly "
+ + "processed in all environments");
+ Enumeration<String> e = attribute.getValues();
+ while (e.hasMoreElements()) {
+ String value = e.nextElement();
+ classpathAttribute.addValue(value);
+ }
+ }
+ } else if (attributes.containsKey(attributeKey)) {
+ throw new ManifestException("The attribute \""
+ + attribute.getName() + "\" may not occur more "
+ + "than once in the same section");
+ } else {
+ storeAttribute(attribute);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Clone this section
+ *
+ * @return the cloned Section
+ * @since Ant 1.5.2
+ */
+ @Override
+ public Object clone() {
+ Section cloned = new Section();
+ cloned.setName(name);
+ Enumeration<String> e = getAttributeKeys();
+ while (e.hasMoreElements()) {
+ String key = e.nextElement();
+ Attribute attribute = getAttribute(key);
+ cloned.storeAttribute(new Attribute(attribute.getName(),
+ attribute.getValue()));
+ }
+ return cloned;
+ }
+
+ /**
+ * Store an attribute and update the index.
+ *
+ * @param attribute the attribute to be stored
+ */
+ private void storeAttribute(Attribute attribute) {
+ if (attribute == null) {
+ return;
+ }
+ String attributeKey = attribute.getKey();
+ attributes.put(attributeKey, attribute);
+ }
+
+ /**
+ * Get the warnings for this section.
+ *
+ * @return an Enumeration of warning strings.
+ */
+ public Enumeration<String> getWarnings() {
+ return warnings.elements();
+ }
+
+ /**
+ * @see java.lang.Object#hashCode
+ * @return a hash value based on the attributes.
+ */
+ @Override
+ public int hashCode() {
+ return attributes.hashCode();
+ }
+
+ /**
+ * @see java.lang.Object#equals
+ * @param rhs the object to check for equality.
+ * @return true if the attributes are the same.
+ */
+ @Override
+ public boolean equals(Object rhs) {
+ if (rhs == null || rhs.getClass() != getClass()) {
+ return false;
+ }
+
+ if (rhs == this) {
+ return true;
+ }
+
+ Section rhsSection = (Section) rhs;
+
+ return attributes.equals(rhsSection.attributes);
+ }
+ }
+
+
+ /** The version of this manifest */
+ private String manifestVersion = DEFAULT_MANIFEST_VERSION;
+
+ /** The main section of this manifest */
+ private Section mainSection = new Section();
+
+ /** The named sections of this manifest */
+ private Map<String, Section> sections = new LinkedHashMap<String, Section>();
+
+ /**
+ * Construct a manifest from Ant's default manifest file.
+ *
+ * @return the default manifest.
+ * @exception BuildException if there is a problem loading the
+ * default manifest
+ */
+ public static Manifest getDefaultManifest() throws BuildException {
+ InputStream in = null;
+ InputStreamReader insr = null;
+ try {
+ String defManifest = "/org/apache/tools/ant/defaultManifest.mf";
+ in = Manifest.class.getResourceAsStream(defManifest);
+ if (in == null) {
+ throw new BuildException("Could not find default manifest: "
+ + defManifest);
+ }
+ try {
+ insr = new InputStreamReader(in, "UTF-8");
+ Manifest defaultManifest = new Manifest(insr);
+ String version = System.getProperty("java.runtime.version");
+ if (version == null) {
+ version = System.getProperty("java.vm.version");
+ }
+ Attribute createdBy = new Attribute("Created-By",
+ version + " ("
+ + System.getProperty("java.vm.vendor") + ")");
+ defaultManifest.getMainSection().storeAttribute(createdBy);
+ return defaultManifest;
+ } catch (UnsupportedEncodingException e) {
+ insr = new InputStreamReader(in);
+ return new Manifest(insr);
+ }
+ } catch (ManifestException e) {
+ throw new BuildException("Default manifest is invalid !!", e);
+ } catch (IOException e) {
+ throw new BuildException("Unable to read default manifest", e);
+ } finally {
+ FileUtils.close(insr);
+ FileUtils.close(in);
+ }
+ }
+
+ /** Construct an empty manifest */
+ public Manifest() {
+ manifestVersion = null;
+ }
+
+ /**
+ * Read a manifest file from the given reader
+ *
+ * @param r is the reader from which the Manifest is read
+ *
+ * @throws ManifestException if the manifest is not valid according
+ * to the JAR spec
+ * @throws IOException if the manifest cannot be read from the reader.
+ */
+ public Manifest(Reader r) throws ManifestException, IOException {
+ BufferedReader reader = new BufferedReader(r);
+ // This should be the manifest version
+ String nextSectionName = mainSection.read(reader);
+ String readManifestVersion
+ = mainSection.getAttributeValue(ATTRIBUTE_MANIFEST_VERSION);
+ if (readManifestVersion != null) {
+ manifestVersion = readManifestVersion;
+ mainSection.removeAttribute(ATTRIBUTE_MANIFEST_VERSION);
+ }
+
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ if (line.length() == 0) {
+ continue;
+ }
+
+ Section section = new Section();
+ if (nextSectionName == null) {
+ Attribute sectionName = new Attribute(line);
+ if (!sectionName.getName().equalsIgnoreCase(ATTRIBUTE_NAME)) {
+ throw new ManifestException("Manifest sections should "
+ + "start with a \"" + ATTRIBUTE_NAME
+ + "\" attribute and not \""
+ + sectionName.getName() + "\"");
+ }
+ nextSectionName = sectionName.getValue();
+ } else {
+ // we have already started reading this section
+ // this line is the first attribute. set it and then
+ // let the normal read handle the rest
+ Attribute firstAttribute = new Attribute(line);
+ section.addAttributeAndCheck(firstAttribute);
+ }
+
+ section.setName(nextSectionName);
+ nextSectionName = section.read(reader);
+ addConfiguredSection(section);
+ }
+ }
+
+ /**
+ * Add a section to the manifest
+ *
+ * @param section the manifest section to be added
+ *
+ * @exception ManifestException if the secti0on is not valid.
+ */
+ public void addConfiguredSection(Section section)
+ throws ManifestException {
+ String sectionName = section.getName();
+ if (sectionName == null) {
+ throw new BuildException("Sections must have a name");
+ }
+ sections.put(sectionName, section);
+ }
+
+ /**
+ * Add an attribute to the manifest - it is added to the main section.
+ *
+ * @param attribute the attribute to be added.
+ *
+ * @exception ManifestException if the attribute is not valid.
+ */
+ public void addConfiguredAttribute(Attribute attribute)
+ throws ManifestException {
+ if (attribute.getKey() == null || attribute.getValue() == null) {
+ throw new BuildException("Attributes must have name and value");
+ }
+ if (attribute.getKey().equals(ATTRIBUTE_MANIFEST_VERSION_LC)) {
+ manifestVersion = attribute.getValue();
+ } else {
+ mainSection.addConfiguredAttribute(attribute);
+ }
+ }
+
+ /**
+ * Merge the contents of the given manifest into this manifest
+ * without merging Class-Path attributes.
+ *
+ * @param other the Manifest to be merged with this one.
+ *
+ * @throws ManifestException if there is a problem merging the
+ * manifest according to the Manifest spec.
+ */
+ public void merge(Manifest other) throws ManifestException {
+ merge(other, false);
+ }
+
+ /**
+ * Merge the contents of the given manifest into this manifest
+ * without merging Class-Path attributes.
+ *
+ * @param other the Manifest to be merged with this one.
+ * @param overwriteMain whether to overwrite the main section
+ * of the current manifest
+ *
+ * @throws ManifestException if there is a problem merging the
+ * manifest according to the Manifest spec.
+ */
+ public void merge(Manifest other, boolean overwriteMain)
+ throws ManifestException {
+ merge(other, overwriteMain, false);
+ }
+
+ /**
+ * Merge the contents of the given manifest into this manifest
+ *
+ * @param other the Manifest to be merged with this one.
+ * @param overwriteMain whether to overwrite the main section
+ * of the current manifest
+ * @param mergeClassPaths whether Class-Path attributes should be
+ * merged.
+ *
+ * @throws ManifestException if there is a problem merging the
+ * manifest according to the Manifest spec.
+ *
+ * @since Ant 1.8.0
+ */
+ public void merge(Manifest other, boolean overwriteMain,
+ boolean mergeClassPaths)
+ throws ManifestException {
+ if (other != null) {
+ if (overwriteMain) {
+ mainSection = (Section) other.mainSection.clone();
+ } else {
+ mainSection.merge(other.mainSection, mergeClassPaths);
+ }
+
+ if (other.manifestVersion != null) {
+ manifestVersion = other.manifestVersion;
+ }
+
+ Enumeration<String> e = other.getSectionNames();
+ while (e.hasMoreElements()) {
+ String sectionName = e.nextElement();
+ Section ourSection = sections.get(sectionName);
+ Section otherSection
+ = other.sections.get(sectionName);
+ if (ourSection == null) {
+ if (otherSection != null) {
+ addConfiguredSection((Section) otherSection.clone());
+ }
+ } else {
+ ourSection.merge(otherSection, mergeClassPaths);
+ }
+ }
+ }
+ }
+
+ /**
+ * Write the manifest out to a print writer without flattening
+ * multi-values attributes (i.e. Class-Path).
+ *
+ * @param writer the Writer to which the manifest is written
+ *
+ * @throws IOException if the manifest cannot be written
+ */
+ public void write(PrintWriter writer) throws IOException {
+ write(writer, false);
+ }
+
+ /**
+ * Write the manifest out to a print writer.
+ *
+ * @param writer the Writer to which the manifest is written
+ * @param flatten whether to collapse multi-valued attributes
+ * (i.e. potentially Class-Path) Class-Path into a single
+ * attribute.
+ *
+ * @throws IOException if the manifest cannot be written
+ * @since Ant 1.8.0
+ */
+ public void write(PrintWriter writer, boolean flatten) throws IOException {
+ writer.print(ATTRIBUTE_MANIFEST_VERSION + ": " + manifestVersion + EOL);
+ String signatureVersion
+ = mainSection.getAttributeValue(ATTRIBUTE_SIGNATURE_VERSION);
+ if (signatureVersion != null) {
+ writer.print(ATTRIBUTE_SIGNATURE_VERSION + ": "
+ + signatureVersion + EOL);
+ mainSection.removeAttribute(ATTRIBUTE_SIGNATURE_VERSION);
+ }
+ mainSection.write(writer, flatten);
+
+ // add it back
+ if (signatureVersion != null) {
+ try {
+ Attribute svAttr = new Attribute(ATTRIBUTE_SIGNATURE_VERSION,
+ signatureVersion);
+ mainSection.addConfiguredAttribute(svAttr);
+ } catch (ManifestException e) {
+ // shouldn't happen - ignore
+ }
+ }
+
+ for (String sectionName : sections.keySet()) {
+ Section section = getSection(sectionName);
+ section.write(writer, flatten);
+ }
+ }
+
+ /**
+ * Convert the manifest to its string representation
+ *
+ * @return a multiline string with the Manifest as it
+ * appears in a Manifest file.
+ */
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ try {
+ write(new PrintWriter(sw));
+ } catch (IOException e) {
+ return null;
+ }
+ return sw.toString();
+ }
+
+ /**
+ * Get the warnings for this manifest.
+ *
+ * @return an enumeration of warning strings
+ */
+ public Enumeration<String> getWarnings() {
+ Vector<String> warnings = new Vector<String>();
+
+ Enumeration<String> warnEnum = mainSection.getWarnings();
+ while (warnEnum.hasMoreElements()) {
+ warnings.addElement(warnEnum.nextElement());
+ }
+
+ // create a vector and add in the warnings for all the sections
+ for (Section section : sections.values()) {
+ Enumeration<String> e2 = section.getWarnings();
+ while (e2.hasMoreElements()) {
+ warnings.addElement(e2.nextElement());
+ }
+ }
+
+ return warnings.elements();
+ }
+
+ /**
+ * @see java.lang.Object#hashCode
+ * @return a hashcode based on the version, main and sections.
+ */
+ @Override
+ public int hashCode() {
+ int hashCode = 0;
+
+ if (manifestVersion != null) {
+ hashCode += manifestVersion.hashCode();
+ }
+ hashCode += mainSection.hashCode();
+ hashCode += sections.hashCode();
+
+ return hashCode;
+ }
+
+ /**
+ * @see java.lang.Object#equals
+ * @param rhs the object to check for equality.
+ * @return true if the version, main and sections are the same.
+ */
+ @Override
+ public boolean equals(Object rhs) {
+ if (rhs == null || rhs.getClass() != getClass()) {
+ return false;
+ }
+
+ if (rhs == this) {
+ return true;
+ }
+
+ Manifest rhsManifest = (Manifest) rhs;
+ if (manifestVersion == null) {
+ if (rhsManifest.manifestVersion != null) {
+ return false;
+ }
+ } else if (!manifestVersion.equals(rhsManifest.manifestVersion)) {
+ return false;
+ }
+
+ if (!mainSection.equals(rhsManifest.mainSection)) {
+ return false;
+ }
+
+ return sections.equals(rhsManifest.sections);
+ }
+
+ /**
+ * Get the version of the manifest
+ *
+ * @return the manifest's version string
+ */
+ public String getManifestVersion() {
+ return manifestVersion;
+ }
+
+ /**
+ * Get the main section of the manifest
+ *
+ * @return the main section of the manifest
+ */
+ public Section getMainSection() {
+ return mainSection;
+ }
+
+ /**
+ * Get a particular section from the manifest
+ *
+ * @param name the name of the section desired.
+ * @return the specified section or null if that section
+ * does not exist in the manifest
+ */
+ public Section getSection(String name) {
+ return sections.get(name);
+ }
+
+ /**
+ * Get the section names in this manifest.
+ *
+ * @return an Enumeration of section names
+ */
+ public Enumeration<String> getSectionNames() {
+ return CollectionUtils.asEnumeration(sections.keySet().iterator());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ManifestClassPath.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ManifestClassPath.java
new file mode 100644
index 00000000..4a4f2312
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ManifestClassPath.java
@@ -0,0 +1,180 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.launch.Locator;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Converts a Path into a property suitable as a Manifest classpath.
+ *
+ * @since Ant 1.7
+ *
+ * @ant.task category="property"
+ */
+public class ManifestClassPath extends Task {
+
+ /** The property name to hold the classpath value. */
+ private String name;
+
+ /** The directory the classpath will be relative from. */
+ private File dir;
+
+ /** The maximum parent directory level to traverse. */
+ private int maxParentLevels = 2;
+
+ /** The classpath to convert. */
+ private Path path;
+
+ /**
+ * Sets a property, which must not already exist, with a space
+ * separated list of files and directories relative to the jar
+ * file's parent directory.
+ */
+ public void execute() {
+ if (name == null) {
+ throw new BuildException("Missing 'property' attribute!");
+ }
+ if (dir == null) {
+ throw new BuildException("Missing 'jarfile' attribute!");
+ }
+ if (getProject().getProperty(name) != null) {
+ throw new BuildException("Property '" + name + "' already set!");
+ }
+ if (path == null) {
+ throw new BuildException("Missing nested <classpath>!");
+ }
+
+ StringBuffer tooLongSb = new StringBuffer();
+ for (int i = 0; i < maxParentLevels + 1; i++) {
+ tooLongSb.append("../");
+ }
+ final String tooLongPrefix = tooLongSb.toString();
+
+ // Normalize the reference directory (containing the jar)
+ final FileUtils fileUtils = FileUtils.getFileUtils();
+ dir = fileUtils.normalize(dir.getAbsolutePath());
+
+ String[] elements = path.list();
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < elements.length; ++i) {
+ // Normalize the current file
+ File pathEntry = new File(elements[i]);
+ String fullPath = pathEntry.getAbsolutePath();
+ pathEntry = fileUtils.normalize(fullPath);
+
+ String relPath = null;
+ String canonicalPath = null;
+ try {
+ if (dir.equals(pathEntry)) {
+ relPath = ".";
+ } else {
+ relPath = FileUtils.getRelativePath(dir, pathEntry);
+ }
+
+ canonicalPath = pathEntry.getCanonicalPath();
+ // getRelativePath always uses '/' as separator, adapt
+ if (File.separatorChar != '/') {
+ canonicalPath =
+ canonicalPath.replace(File.separatorChar, '/');
+ }
+ } catch (Exception e) {
+ throw new BuildException("error trying to get the relative path"
+ + " from " + dir + " to " + fullPath,
+ e);
+ }
+
+ // No match, so bail out!
+ if (relPath.equals(canonicalPath)
+ || relPath.startsWith(tooLongPrefix)) {
+ throw new BuildException("No suitable relative path from "
+ + dir + " to " + fullPath);
+ }
+
+ if (pathEntry.isDirectory() && !relPath.endsWith("/")) {
+ relPath = relPath + '/';
+ }
+ try {
+ relPath = Locator.encodeURI(relPath);
+ } catch (UnsupportedEncodingException exc) {
+ throw new BuildException(exc);
+ }
+ // Manifest's ClassPath: attribute always uses forward
+ // slashes '/', and is space-separated. Ant will properly
+ // format it on 72 columns with proper line continuation
+ buffer.append(relPath);
+ buffer.append(' ');
+ }
+
+ // Finally assign the property with the manifest classpath
+ getProject().setNewProperty(name, buffer.toString().trim());
+ }
+
+ /**
+ * Sets the property name to hold the classpath value.
+ *
+ * @param name the property name
+ */
+ public void setProperty(String name) {
+ this.name = name;
+ }
+
+ /**
+ * The JAR file to contain the classpath attribute in its manifest.
+ *
+ * @param jarfile the JAR file. Need not exist yet, but its parent
+ * directory must exist on the other hand.
+ */
+ public void setJarFile(File jarfile) {
+ File parent = jarfile.getParentFile();
+ if (!parent.isDirectory()) {
+ throw new BuildException("Jar's directory not found: " + parent);
+ }
+ this.dir = parent;
+ }
+
+ /**
+ * Sets the maximum parent directory levels allowed when computing
+ * a relative path.
+ *
+ * @param levels the max level. Defaults to 2.
+ */
+ public void setMaxParentLevels(int levels) {
+ if (levels < 0) {
+ throw new BuildException("maxParentLevels must not be a negative"
+ + " number");
+ }
+ this.maxParentLevels = levels;
+ }
+
+ /**
+ * Adds the classpath to convert.
+ *
+ * @param path the classpath to convert.
+ */
+ public void addClassPath(Path path) {
+ this.path = path;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ManifestException.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ManifestException.java
new file mode 100644
index 00000000..c4e72132
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ManifestException.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.tools.ant.taskdefs;
+
+/**
+ * Exception thrown indicating problems in a JAR Manifest
+ *
+ * @since Ant 1.4
+ */
+public class ManifestException extends Exception {
+
+ private static final long serialVersionUID = 7685634200457515207L;
+
+ /**
+ * Constructs an exception with the given descriptive message.
+ * @param msg Description of or information about the exception.
+ */
+ public ManifestException(String msg) {
+ super(msg);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java
new file mode 100644
index 00000000..9b600db5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java
@@ -0,0 +1,293 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Manifest.Attribute;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Creates a manifest file for inclusion in a JAR, Ant task wrapper
+ * around {@link Manifest Manifest}. This task can be used to write a
+ * Manifest file, optionally replacing or updating an existing file.
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="java"
+ */
+public class ManifestTask extends Task {
+
+ /**
+ * Specifies the valid characters which can be used in attribute names.
+ * {@value}
+ */
+ public static final String VALID_ATTRIBUTE_CHARS =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
+
+ /**
+ * Holds the real data.
+ */
+ private Manifest nestedManifest = new Manifest();
+
+ /**
+ * The file to which the manifest should be written when used as a task
+ */
+ private File manifestFile;
+
+ /**
+ * The mode with which the manifest file is written
+ */
+ private Mode mode;
+
+ /**
+ * The encoding of the manifest file
+ */
+ private String encoding;
+
+ /**
+ * whether to merge Class-Path attributes.
+ */
+ private boolean mergeClassPaths = false;
+
+ /**
+ * whether to flatten Class-Path attributes into a single one.
+ */
+ private boolean flattenClassPaths = false;
+
+ /**
+ * Helper class for Manifest's mode attribute.
+ */
+ public static class Mode extends EnumeratedAttribute {
+ /**
+ * Get Allowed values for the mode attribute.
+ *
+ * @return a String array of the allowed values.
+ */
+ public String[] getValues() {
+ return new String[] {"update", "replace"};
+ }
+ }
+
+ /**
+ * Default constructor
+ */
+ public ManifestTask() {
+ mode = new Mode();
+ mode.setValue("replace");
+ }
+
+ /**
+ * Add a section to the manifest
+ *
+ * @param section the manifest section to be added
+ *
+ * @exception ManifestException if the section is not valid.
+ */
+ public void addConfiguredSection(Manifest.Section section)
+ throws ManifestException {
+ Enumeration<String> attributeKeys = section.getAttributeKeys();
+ while (attributeKeys.hasMoreElements()) {
+ Attribute attribute = section.getAttribute(
+ attributeKeys.nextElement());
+ checkAttribute(attribute);
+ }
+ nestedManifest.addConfiguredSection(section);
+ }
+
+ /**
+ * Add an attribute to the manifest - it is added to the main section.
+ *
+ * @param attribute the attribute to be added.
+ *
+ * @exception ManifestException if the attribute is not valid.
+ */
+ public void addConfiguredAttribute(Manifest.Attribute attribute)
+ throws ManifestException {
+ checkAttribute(attribute);
+ nestedManifest.addConfiguredAttribute(attribute);
+ }
+
+ /**
+ * Checks the attribute against the Jar-specification.
+ *
+ * Jar-Specification <i>"Name-Value pairs and Sections"</i>: <pre>
+ * name: alphanum *headerchar
+ * alphanum: {A-Z} | {a-z} | {0-9}
+ * headerchar: alphanum | - | _
+ * </pre>
+ * So the resulting regexp would be <tt>[A-Za-z0-9][A-Za-z0-9-_]*</tt>.
+ *
+ * Because of JDK 1.2 compliance and the possible absence of a
+ * regexp matcher we can not use regexps here. Instead we have to
+ * check each character.
+ *
+ * @param attribute The attribute to check
+ * @throws BuildException if the check fails
+ */
+ private void checkAttribute(Manifest.Attribute attribute) throws BuildException {
+ String name = attribute.getName();
+ char ch = name.charAt(0);
+
+ if (ch == '-' || ch == '_') {
+ throw new BuildException("Manifest attribute names must not start with '" + ch + "'.");
+ }
+
+ for (int i = 0; i < name.length(); i++) {
+ ch = name.charAt(i);
+ if (VALID_ATTRIBUTE_CHARS.indexOf(ch) < 0) {
+ throw new BuildException("Manifest attribute names must not contain '" + ch + "'");
+ }
+ }
+ }
+
+ /**
+ * The name of the manifest file to create/update.
+ * Required if used as a task.
+ * @param f the Manifest file to be written
+ */
+ public void setFile(File f) {
+ manifestFile = f;
+ }
+
+ /**
+ * The encoding to use for reading in an existing manifest file
+ * @param encoding the manifest file encoding.
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Update policy: either "update" or "replace"; default is "replace".
+ * @param m the mode value - update or replace.
+ */
+ public void setMode(Mode m) {
+ mode = m;
+ }
+
+ /**
+ * Whether to merge Class-Path attributes.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setMergeClassPathAttributes(boolean b) {
+ mergeClassPaths = b;
+ }
+
+ /**
+ * Whether to flatten multi-valued attributes (i.e. Class-Path)
+ * into a single one.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setFlattenAttributes(boolean b) {
+ flattenClassPaths = b;
+ }
+
+ /**
+ * Create or update the Manifest when used as a task.
+ *
+ * @throws BuildException if the manifest cannot be written.
+ */
+ public void execute() throws BuildException {
+ if (manifestFile == null) {
+ throw new BuildException("the file attribute is required");
+ }
+
+ Manifest toWrite = Manifest.getDefaultManifest();
+ Manifest current = null;
+ BuildException error = null;
+
+ if (manifestFile.exists()) {
+ FileInputStream fis = null;
+ InputStreamReader isr = null;
+ try {
+ fis = new FileInputStream(manifestFile);
+ if (encoding == null) {
+ isr = new InputStreamReader(fis, "UTF-8");
+ } else {
+ isr = new InputStreamReader(fis, encoding);
+ }
+ current = new Manifest(isr);
+ } catch (ManifestException m) {
+ error = new BuildException("Existing manifest " + manifestFile
+ + " is invalid", m, getLocation());
+ } catch (IOException e) {
+ error = new BuildException("Failed to read " + manifestFile,
+ e, getLocation());
+ } finally {
+ FileUtils.close(isr);
+ }
+ }
+
+ //look for and print warnings
+ for (Enumeration<String> e = nestedManifest.getWarnings();
+ e.hasMoreElements();) {
+ log("Manifest warning: " + e.nextElement(),
+ Project.MSG_WARN);
+ }
+ try {
+ if (mode.getValue().equals("update") && manifestFile.exists()) {
+ if (current != null) {
+ toWrite.merge(current, false, mergeClassPaths);
+ } else if (error != null) {
+ throw error;
+ }
+ }
+
+ toWrite.merge(nestedManifest, false, mergeClassPaths);
+ } catch (ManifestException m) {
+ throw new BuildException("Manifest is invalid", m, getLocation());
+ }
+
+ if (toWrite.equals(current)) {
+ log("Manifest has not changed, do not recreate",
+ Project.MSG_VERBOSE);
+ return;
+ }
+
+ PrintWriter w = null;
+ try {
+ FileOutputStream fos = new FileOutputStream(manifestFile);
+ OutputStreamWriter osw = new OutputStreamWriter(fos, Manifest.JAR_ENCODING);
+ w = new PrintWriter(osw);
+ toWrite.write(w, flattenClassPaths);
+ if (w.checkError()) {
+ throw new IOException("Encountered an error writing manifest");
+ }
+ } catch (IOException e) {
+ throw new BuildException("Failed to write " + manifestFile,
+ e, getLocation());
+ } finally {
+ FileUtils.close(w);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MatchingTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MatchingTask.java
new file mode 100644
index 00000000..113ff5eb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/MatchingTask.java
@@ -0,0 +1,446 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.selectors.AndSelector;
+import org.apache.tools.ant.types.selectors.ContainsRegexpSelector;
+import org.apache.tools.ant.types.selectors.ContainsSelector;
+import org.apache.tools.ant.types.selectors.DateSelector;
+import org.apache.tools.ant.types.selectors.DependSelector;
+import org.apache.tools.ant.types.selectors.DepthSelector;
+import org.apache.tools.ant.types.selectors.DifferentSelector;
+import org.apache.tools.ant.types.selectors.ExtendSelector;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.FilenameSelector;
+import org.apache.tools.ant.types.selectors.MajoritySelector;
+import org.apache.tools.ant.types.selectors.NoneSelector;
+import org.apache.tools.ant.types.selectors.NotSelector;
+import org.apache.tools.ant.types.selectors.OrSelector;
+import org.apache.tools.ant.types.selectors.PresentSelector;
+import org.apache.tools.ant.types.selectors.SelectSelector;
+import org.apache.tools.ant.types.selectors.SelectorContainer;
+import org.apache.tools.ant.types.selectors.SizeSelector;
+import org.apache.tools.ant.types.selectors.TypeSelector;
+import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector;
+
+/**
+ * This is an abstract task that should be used by all those tasks that
+ * require to include or exclude files based on pattern matching.
+ *
+ * @since Ant 1.1
+ */
+
+public abstract class MatchingTask extends Task implements SelectorContainer {
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected FileSet fileset = new FileSet();
+ // CheckStyle:VisibilityModifier ON
+
+ /** {@inheritDoc}. */
+ public void setProject(Project project) {
+ super.setProject(project);
+ fileset.setProject(project);
+ }
+
+ /**
+ * add a name entry on the include list
+ * @return a NameEntry object to be configured
+ */
+ public PatternSet.NameEntry createInclude() {
+ return fileset.createInclude();
+ }
+
+ /**
+ * add a name entry on the include files list
+ * @return an NameEntry object to be configured
+ */
+ public PatternSet.NameEntry createIncludesFile() {
+ return fileset.createIncludesFile();
+ }
+
+ /**
+ * add a name entry on the exclude list
+ * @return an NameEntry object to be configured
+ */
+ public PatternSet.NameEntry createExclude() {
+ return fileset.createExclude();
+ }
+
+ /**
+ * add a name entry on the include files list
+ * @return an NameEntry object to be configured
+ */
+ public PatternSet.NameEntry createExcludesFile() {
+ return fileset.createExcludesFile();
+ }
+
+ /**
+ * add a set of patterns
+ * @return PatternSet object to be configured
+ */
+ public PatternSet createPatternSet() {
+ return fileset.createPatternSet();
+ }
+
+ /**
+ * Sets the set of include patterns. Patterns may be separated by a comma
+ * or a space.
+ *
+ * @param includes the string containing the include patterns
+ */
+ public void setIncludes(String includes) {
+ fileset.setIncludes(includes);
+ }
+
+ // CheckStyle:MethodNameCheck OFF - bc
+ /**
+ * Set this to be the items in the base directory that you want to be
+ * included. You can also specify "*" for the items (ie: items="*")
+ * and it will include all the items in the base directory.
+ *
+ * @param itemString the string containing the files to include.
+ */
+ public void XsetItems(String itemString) {
+ log("The items attribute is deprecated. "
+ + "Please use the includes attribute.", Project.MSG_WARN);
+ if (itemString == null || itemString.equals("*")
+ || itemString.equals(".")) {
+ createInclude().setName("**");
+ } else {
+ StringTokenizer tok = new StringTokenizer(itemString, ", ");
+ while (tok.hasMoreTokens()) {
+ String pattern = tok.nextToken().trim();
+ if (pattern.length() > 0) {
+ createInclude().setName(pattern + "/**");
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the set of exclude patterns. Patterns may be separated by a comma
+ * or a space.
+ *
+ * @param excludes the string containing the exclude patterns
+ */
+ public void setExcludes(String excludes) {
+ fileset.setExcludes(excludes);
+ }
+
+ /**
+ * List of filenames and directory names to not include. They should be
+ * either , or " " (space) separated. The ignored files will be logged.
+ *
+ * @param ignoreString the string containing the files to ignore.
+ */
+ public void XsetIgnore(String ignoreString) {
+ log("The ignore attribute is deprecated."
+ + "Please use the excludes attribute.", Project.MSG_WARN);
+ if (ignoreString != null && ignoreString.length() > 0) {
+ StringTokenizer tok = new StringTokenizer(ignoreString, ", ",
+ false);
+ while (tok.hasMoreTokens()) {
+ createExclude().setName("**/" + tok.nextToken().trim() + "/**");
+ }
+ }
+ }
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Sets whether default exclusions should be used or not.
+ *
+ * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
+ * should be used, "false"|"off"|"no" when they
+ * shouldn't be used.
+ */
+ public void setDefaultexcludes(boolean useDefaultExcludes) {
+ fileset.setDefaultexcludes(useDefaultExcludes);
+ }
+
+ /**
+ * Returns the directory scanner needed to access the files to process.
+ * @param baseDir the base directory to use with the fileset
+ * @return a directory scanner
+ */
+ protected DirectoryScanner getDirectoryScanner(File baseDir) {
+ fileset.setDir(baseDir);
+ return fileset.getDirectoryScanner(getProject());
+ }
+
+ /**
+ * Sets the name of the file containing the includes patterns.
+ *
+ * @param includesfile A string containing the filename to fetch
+ * the include patterns from.
+ */
+ public void setIncludesfile(File includesfile) {
+ fileset.setIncludesfile(includesfile);
+ }
+
+ /**
+ * Sets the name of the file containing the includes patterns.
+ *
+ * @param excludesfile A string containing the filename to fetch
+ * the include patterns from.
+ */
+ public void setExcludesfile(File excludesfile) {
+ fileset.setExcludesfile(excludesfile);
+ }
+
+ /**
+ * Sets case sensitivity of the file system
+ *
+ * @param isCaseSensitive "true"|"on"|"yes" if file system is case
+ * sensitive, "false"|"off"|"no" when not.
+ */
+ public void setCaseSensitive(boolean isCaseSensitive) {
+ fileset.setCaseSensitive(isCaseSensitive);
+ }
+
+ /**
+ * Sets whether or not symbolic links should be followed.
+ *
+ * @param followSymlinks whether or not symbolic links should be followed
+ */
+ public void setFollowSymlinks(boolean followSymlinks) {
+ fileset.setFollowSymlinks(followSymlinks);
+ }
+
+ /**
+ * Indicates whether there are any selectors here.
+ *
+ * @return whether any selectors are in this container
+ */
+ public boolean hasSelectors() {
+ return fileset.hasSelectors();
+ }
+
+ /**
+ * Gives the count of the number of selectors in this container
+ *
+ * @return the number of selectors in this container
+ */
+ public int selectorCount() {
+ return fileset.selectorCount();
+ }
+
+ /**
+ * Returns the set of selectors as an array.
+ * @param p the current project
+ * @return an array of selectors in this container
+ */
+ public FileSelector[] getSelectors(Project p) {
+ return fileset.getSelectors(p);
+ }
+
+ /**
+ * Returns an enumerator for accessing the set of selectors.
+ *
+ * @return an enumerator that goes through each of the selectors
+ */
+ public Enumeration<FileSelector> selectorElements() {
+ return fileset.selectorElements();
+ }
+
+ /**
+ * Add a new selector into this container.
+ *
+ * @param selector the new selector to add
+ */
+ public void appendSelector(FileSelector selector) {
+ fileset.appendSelector(selector);
+ }
+
+ /* Methods below all add specific selectors */
+
+ /**
+ * add a "Select" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addSelector(SelectSelector selector) {
+ fileset.addSelector(selector);
+ }
+
+ /**
+ * add an "And" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addAnd(AndSelector selector) {
+ fileset.addAnd(selector);
+ }
+
+ /**
+ * add an "Or" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addOr(OrSelector selector) {
+ fileset.addOr(selector);
+ }
+
+ /**
+ * add a "Not" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addNot(NotSelector selector) {
+ fileset.addNot(selector);
+ }
+
+ /**
+ * add a "None" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addNone(NoneSelector selector) {
+ fileset.addNone(selector);
+ }
+
+ /**
+ * add a majority selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addMajority(MajoritySelector selector) {
+ fileset.addMajority(selector);
+ }
+
+ /**
+ * add a selector date entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addDate(DateSelector selector) {
+ fileset.addDate(selector);
+ }
+
+ /**
+ * add a selector size entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addSize(SizeSelector selector) {
+ fileset.addSize(selector);
+ }
+
+ /**
+ * add a selector filename entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addFilename(FilenameSelector selector) {
+ fileset.addFilename(selector);
+ }
+
+ /**
+ * add an extended selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addCustom(ExtendSelector selector) {
+ fileset.addCustom(selector);
+ }
+
+ /**
+ * add a contains selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addContains(ContainsSelector selector) {
+ fileset.addContains(selector);
+ }
+
+ /**
+ * add a present selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addPresent(PresentSelector selector) {
+ fileset.addPresent(selector);
+ }
+
+ /**
+ * add a depth selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addDepth(DepthSelector selector) {
+ fileset.addDepth(selector);
+ }
+
+ /**
+ * add a depends selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addDepend(DependSelector selector) {
+ fileset.addDepend(selector);
+ }
+
+ /**
+ * add a regular expression selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addContainsRegexp(ContainsRegexpSelector selector) {
+ fileset.addContainsRegexp(selector);
+ }
+
+ /**
+ * add a type selector entry on the type list
+ * @param selector the selector to add
+ * @since ant 1.6
+ */
+ public void addDifferent(DifferentSelector selector) {
+ fileset.addDifferent(selector);
+ }
+
+ /**
+ * add a type selector entry on the type list
+ * @param selector the selector to add
+ * @since ant 1.6
+ */
+ public void addType(TypeSelector selector) {
+ fileset.addType(selector);
+ }
+
+ /**
+ * add the modified selector
+ * @param selector the selector to add
+ * @since ant 1.6
+ */
+ public void addModified(ModifiedSelector selector) {
+ fileset.addModified(selector);
+ }
+
+ /**
+ * add an arbitrary selector
+ * @param selector the selector to add
+ * @since Ant 1.6
+ */
+ public void add(FileSelector selector) {
+ fileset.add(selector);
+ }
+
+ /**
+ * Accessor for the implicit fileset.
+ * @return the implicit fileset
+ * @since Ant 1.5.2
+ */
+ protected final FileSet getImplicitFileSet() {
+ return fileset;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Mkdir.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Mkdir.java
new file mode 100644
index 00000000..71b6c94a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Mkdir.java
@@ -0,0 +1,114 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Creates a given directory.
+ * Creates a directory and any non-existent parent directories, when
+ * necessary
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="filesystem"
+ */
+
+public class Mkdir extends Task {
+
+ private static final int MKDIR_RETRY_SLEEP_MILLIS = 10;
+ /**
+ * our little directory
+ */
+ private File dir;
+
+ /**
+ * create the directory and all parents
+ * @throws BuildException if dir is somehow invalid, or creation failed.
+ */
+ public void execute() throws BuildException {
+ if (dir == null) {
+ throw new BuildException("dir attribute is required", getLocation());
+ }
+
+ if (dir.isFile()) {
+ throw new BuildException("Unable to create directory as a file "
+ + "already exists with that name: "
+ + dir.getAbsolutePath());
+ }
+
+ if (!dir.exists()) {
+ boolean result = mkdirs(dir);
+ if (!result) {
+ if (dir.exists()) {
+ log("A different process or task has already created "
+ + "dir " + dir.getAbsolutePath(),
+ Project.MSG_VERBOSE);
+ return;
+ }
+ String msg = "Directory " + dir.getAbsolutePath()
+ + " creation was not successful for an unknown reason";
+ throw new BuildException(msg, getLocation());
+ }
+ log("Created dir: " + dir.getAbsolutePath());
+ } else {
+ log("Skipping " + dir.getAbsolutePath()
+ + " because it already exists.", Project.MSG_VERBOSE);
+ }
+ }
+
+ /**
+ * the directory to create; required.
+ *
+ * @param dir the directory to be made.
+ */
+ public void setDir(File dir) {
+ this.dir = dir;
+ }
+
+ /**
+ * Get the directory to create.
+ * @return File
+ */
+ public File getDir() {
+ return dir;
+ }
+
+ /**
+ * Attempt to fix possible race condition when creating
+ * directories on WinXP. If the mkdirs does not work,
+ * wait a little and try again.
+ */
+ private boolean mkdirs(File f) {
+ if (!f.mkdirs()) {
+ try {
+ Thread.sleep(MKDIR_RETRY_SLEEP_MILLIS);
+ return f.mkdirs();
+ } catch (InterruptedException ex) {
+ return f.mkdirs();
+ }
+ }
+ return true;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Move.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Move.java
new file mode 100644
index 00000000..7f5d9680
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Move.java
@@ -0,0 +1,382 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.FilterSet;
+import org.apache.tools.ant.types.FilterSetCollection;
+
+/**
+ * Moves a file or directory to a new file or directory.
+ * By default, the
+ * destination file is overwritten if it already exists.
+ * When <i>overwrite</i> is
+ * turned off, then files are only moved if the source file is
+ * newer than the destination file, or when the destination file does
+ * not exist.
+ *
+ * <p>Source files and directories are only deleted when the file or
+ * directory has been copied to the destination successfully. Filtering
+ * also works.</p>
+ *
+ * <p>This implementation is based on Arnout Kuiper's initial design
+ * document, the following mailing list discussions, and the
+ * copyfile/copydir tasks.</p>
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="filesystem"
+ */
+public class Move extends Copy {
+
+ private boolean performGc = Os.isFamily("windows");
+
+ /**
+ * Constructor of object.
+ * This sets the forceOverwrite attribute of the Copy parent class
+ * to true.
+ *
+ */
+ public Move() {
+ super();
+ setOverwrite(true);
+ }
+
+ /**
+ * Whether to perform a garbage collection before retrying a failed delete.
+ *
+ * <p>This may be required on Windows (where it is set to true by
+ * default) but also on other operating systems, for example when
+ * deleting directories from an NFS share.</p>
+ *
+ * @since Ant 1.8.3
+ */
+ public void setPerformGcOnFailedDelete(boolean b) {
+ performGc = b;
+ }
+
+ /** {@inheritDoc}. */
+ protected void validateAttributes() throws BuildException {
+ if (file != null && file.isDirectory()) {
+ if ((destFile != null && destDir != null)
+ || (destFile == null && destDir == null)) {
+ throw new BuildException("One and only one of tofile and todir must be set.");
+ }
+ destFile = destFile == null ? new File(destDir, file.getName()) : destFile;
+ destDir = destDir == null ? destFile.getParentFile() : destDir;
+
+ completeDirMap.put(file, destFile);
+ file = null;
+ } else {
+ super.validateAttributes();
+ }
+ }
+
+//************************************************************************
+// protected and private methods
+//************************************************************************
+
+ /**
+ * Override copy's doFileOperations to move the files instead of copying them.
+ */
+ protected void doFileOperations() {
+ //Attempt complete directory renames, if any, first.
+ if (completeDirMap.size() > 0) {
+ for (Iterator fromDirs = completeDirMap.keySet().iterator(); fromDirs.hasNext();) {
+ File fromDir = (File) fromDirs.next();
+ File toDir = (File) completeDirMap.get(fromDir);
+ boolean renamed = false;
+ try {
+ log("Attempting to rename dir: " + fromDir + " to " + toDir, verbosity);
+ renamed = renameFile(fromDir, toDir, filtering, forceOverwrite);
+ } catch (IOException ioe) {
+ String msg = "Failed to rename dir " + fromDir
+ + " to " + toDir + " due to " + ioe.getMessage();
+ throw new BuildException(msg, ioe, getLocation());
+ }
+ if (!renamed) {
+ FileSet fs = new FileSet();
+ fs.setProject(getProject());
+ fs.setDir(fromDir);
+ addFileset(fs);
+ DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+ String[] files = ds.getIncludedFiles();
+ String[] dirs = ds.getIncludedDirectories();
+ scan(fromDir, toDir, files, dirs);
+ }
+ }
+ }
+ int moveCount = fileCopyMap.size();
+ if (moveCount > 0) { // files to move
+ log("Moving " + moveCount + " file" + ((moveCount == 1) ? "" : "s")
+ + " to " + destDir.getAbsolutePath());
+
+ for (Iterator fromFiles = fileCopyMap.keySet().iterator(); fromFiles.hasNext();) {
+ String fromFile = (String) fromFiles.next();
+ File f = new File(fromFile);
+ boolean selfMove = false;
+ if (f.exists()) { //Is this file still available to be moved?
+ String[] toFiles = (String[]) fileCopyMap.get(fromFile);
+ for (int i = 0; i < toFiles.length; i++) {
+ String toFile = (String) toFiles[i];
+
+ if (fromFile.equals(toFile)) {
+ log("Skipping self-move of " + fromFile, verbosity);
+ selfMove = true;
+
+ // if this is the last time through the loop then
+ // move will not occur, but that's what we want
+ continue;
+ }
+ File d = new File(toFile);
+ if ((i + 1) == toFiles.length && !selfMove) {
+ // Only try to move if this is the last mapped file
+ // and one of the mappings isn't to itself
+ moveFile(f, d, filtering, forceOverwrite);
+ } else {
+ copyFile(f, d, filtering, forceOverwrite);
+ }
+ }
+ }
+ }
+ }
+
+ if (includeEmpty) {
+ int createCount = 0;
+ for (Iterator fromDirNames = dirCopyMap.keySet().iterator(); fromDirNames.hasNext();) {
+ String fromDirName = (String) fromDirNames.next();
+ String[] toDirNames = (String[]) dirCopyMap.get(fromDirName);
+ boolean selfMove = false;
+ for (int i = 0; i < toDirNames.length; i++) {
+ if (fromDirName.equals(toDirNames[i])) {
+ log("Skipping self-move of " + fromDirName, verbosity);
+ selfMove = true;
+ continue;
+ }
+ File d = new File(toDirNames[i]);
+ if (!d.exists()) {
+ if (!(d.mkdirs() || d.exists())) {
+ log("Unable to create directory "
+ + d.getAbsolutePath(), Project.MSG_ERR);
+ } else {
+ createCount++;
+ }
+ }
+ }
+ File fromDir = new File(fromDirName);
+ if (!selfMove && okToDelete(fromDir)) {
+ deleteDir(fromDir);
+ }
+ }
+ if (createCount > 0) {
+ log("Moved " + dirCopyMap.size()
+ + " empty director"
+ + (dirCopyMap.size() == 1 ? "y" : "ies")
+ + " to " + createCount
+ + " empty director"
+ + (createCount == 1 ? "y" : "ies") + " under "
+ + destDir.getAbsolutePath());
+ }
+ }
+ }
+
+ /**
+ * Try to move the file via a rename, but if this fails or filtering
+ * is enabled, copy the file then delete the sourceFile.
+ */
+ private void moveFile(File fromFile, File toFile, boolean filtering, boolean overwrite) {
+ boolean moved = false;
+ try {
+ log("Attempting to rename: " + fromFile + " to " + toFile, verbosity);
+ moved = renameFile(fromFile, toFile, filtering, forceOverwrite);
+ } catch (IOException ioe) {
+ String msg = "Failed to rename " + fromFile
+ + " to " + toFile + " due to " + ioe.getMessage();
+ throw new BuildException(msg, ioe, getLocation());
+ }
+
+ if (!moved) {
+ copyFile(fromFile, toFile, filtering, overwrite);
+ if (!getFileUtils().tryHardToDelete(fromFile, performGc)) {
+ throw new BuildException("Unable to delete " + "file "
+ + fromFile.getAbsolutePath());
+ }
+ }
+ }
+
+ /**
+ * Copy fromFile to toFile.
+ * @param fromFile
+ * @param toFile
+ * @param filtering
+ * @param overwrite
+ */
+ private void copyFile(File fromFile, File toFile, boolean filtering, boolean overwrite) {
+ try {
+ log("Copying " + fromFile + " to " + toFile, verbosity);
+
+ FilterSetCollection executionFilters = new FilterSetCollection();
+ if (filtering) {
+ executionFilters.addFilterSet(getProject().getGlobalFilterSet());
+ }
+ for (Iterator filterIter = getFilterSets().iterator(); filterIter.hasNext();) {
+ executionFilters.addFilterSet((FilterSet) filterIter.next());
+ }
+ getFileUtils().copyFile(fromFile, toFile, executionFilters,
+ getFilterChains(),
+ forceOverwrite,
+ getPreserveLastModified(),
+ /* append: */ false,
+ getEncoding(),
+ getOutputEncoding(),
+ getProject(), getForce());
+ } catch (IOException ioe) {
+ String msg = "Failed to copy " + fromFile
+ + " to " + toFile + " due to " + ioe.getMessage();
+ throw new BuildException(msg, ioe, getLocation());
+ }
+ }
+
+ /**
+ * Its only ok to delete a directory tree if there are no files in it.
+ * @param d the directory to check
+ * @return true if a deletion can go ahead
+ */
+ protected boolean okToDelete(File d) {
+ String[] list = d.list();
+ if (list == null) {
+ return false;
+ } // maybe io error?
+
+ for (int i = 0; i < list.length; i++) {
+ String s = list[i];
+ File f = new File(d, s);
+ if (f.isDirectory()) {
+ if (!okToDelete(f)) {
+ return false;
+ }
+ } else {
+ return false; // found a file
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Go and delete the directory tree.
+ * @param d the directory to delete
+ */
+ protected void deleteDir(File d) {
+ deleteDir(d, false);
+ }
+
+ /**
+ * Go and delete the directory tree.
+ * @param d the directory to delete
+ * @param deleteFiles whether to delete files
+ */
+ protected void deleteDir(File d, boolean deleteFiles) {
+ String[] list = d.list();
+ if (list == null) {
+ return;
+ } // on an io error list() can return null
+
+ for (int i = 0; i < list.length; i++) {
+ String s = list[i];
+ File f = new File(d, s);
+ if (f.isDirectory()) {
+ deleteDir(f);
+ } else if (deleteFiles && !getFileUtils().tryHardToDelete(f,
+ performGc)) {
+ throw new BuildException("Unable to delete file " + f.getAbsolutePath());
+ } else {
+ throw new BuildException("UNEXPECTED ERROR - The file "
+ + f.getAbsolutePath() + " should not exist!");
+ }
+ }
+ log("Deleting directory " + d.getAbsolutePath(), verbosity);
+ if (!getFileUtils().tryHardToDelete(d, performGc)) {
+ throw new BuildException("Unable to delete directory " + d.getAbsolutePath());
+ }
+ }
+
+ /**
+ * Attempts to rename a file from a source to a destination.
+ * If overwrite is set to true, this method overwrites existing file
+ * even if the destination file is newer. Otherwise, the source file is
+ * renamed only if the destination file is older than it.
+ * Method then checks if token filtering is used. If it is, this method
+ * returns false assuming it is the responsibility to the copyFile method.
+ *
+ * @param sourceFile the file to rename
+ * @param destFile the destination file
+ * @param filtering if true, filtering is in operation, file will
+ * be copied/deleted instead of renamed
+ * @param overwrite if true force overwrite even if destination file
+ * is newer than source file
+ * @return true if the file was renamed
+ * @exception IOException if an error occurs
+ * @exception BuildException if an error occurs
+ */
+ protected boolean renameFile(File sourceFile, File destFile, boolean filtering,
+ boolean overwrite) throws IOException, BuildException {
+ if (destFile.isDirectory() || filtering || getFilterSets().size() > 0
+ || getFilterChains().size() > 0) {
+ return false;
+ }
+
+ // identical logic lives in ResourceUtils.copyResource():
+ if (destFile.isFile() && !destFile.canWrite()) {
+ if (!getForce()) {
+ throw new IOException("can't replace read-only destination "
+ + "file " + destFile);
+ } else if (!getFileUtils().tryHardToDelete(destFile)) {
+ throw new IOException("failed to delete read-only "
+ + "destination file " + destFile);
+ }
+ }
+
+ // identical logic lives in FileUtils.rename():
+ File parent = destFile.getParentFile();
+ if (parent != null && !parent.exists()) {
+ parent.mkdirs();
+ } else if (destFile.isFile()) {
+ sourceFile = getFileUtils().normalize(sourceFile.getAbsolutePath()).getCanonicalFile();
+ destFile = getFileUtils().normalize(destFile.getAbsolutePath());
+ if (destFile.getAbsolutePath().equals(sourceFile.getAbsolutePath())) {
+ //no point in renaming a file to its own canonical version...
+ log("Rename of " + sourceFile + " to " + destFile
+ + " is a no-op.", Project.MSG_VERBOSE);
+ return true;
+ }
+ if (!(getFileUtils().areSame(sourceFile, destFile)
+ || getFileUtils().tryHardToDelete(destFile, performGc))) {
+ throw new BuildException("Unable to remove existing file " + destFile);
+ }
+ }
+ return sourceFile.renameTo(destFile);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Nice.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Nice.java
new file mode 100644
index 00000000..5898caee
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Nice.java
@@ -0,0 +1,99 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * A task to provide "nice-ness" to the current thread, and/or to
+ * query the current value.
+ * Examples:
+ * <pre> &lt;nice currentPriority="current.value" &gt;</pre><p>
+ * Set <code>currentPriority</code> to the current priority
+ * <pre> &lt;nice newPriority="10" &gt;</pre><p>
+ * Raise the priority of the build process (But not forked programs)
+ * <pre> &lt;nice currentPriority="old" newPriority="3" &gt;</pre><p>
+ * Lower the priority of the build process (But not forked programs), and save
+ * the old value to the property <code>old</code>.
+ *
+ * @ant.task name="nice" category="control"
+ */
+public class Nice extends Task {
+
+ /**
+ * the new priority
+ */
+ private Integer newPriority;
+
+ /**
+ * the current priority
+ */
+ private String currentPriority;
+
+
+
+ /**
+ * Execute the task
+ * @exception BuildException if something goes wrong with the build
+ */
+ public void execute() throws BuildException {
+
+ Thread self = Thread.currentThread();
+ int priority = self.getPriority();
+ if (currentPriority != null) {
+ String current = Integer.toString(priority);
+ getProject().setNewProperty(currentPriority, current);
+ }
+ //if there is a new priority, and it is different, change it
+ if (newPriority != null && priority != newPriority.intValue()) {
+ try {
+ self.setPriority(newPriority.intValue());
+ } catch (SecurityException e) {
+ //catch permissions denial and keep going
+ log("Unable to set new priority -a security manager is in the way",
+ Project.MSG_WARN);
+ } catch (IllegalArgumentException iae) {
+ throw new BuildException("Priority out of range", iae);
+ }
+ }
+ }
+
+ /**
+ * The name of a property to set to the value of the current
+ * thread priority. Optional
+ * @param currentPriority the property name.
+ */
+ public void setCurrentPriority(String currentPriority) {
+ this.currentPriority = currentPriority;
+ }
+
+ /**
+ * the new priority, in the range 1-10.
+ * @param newPriority the new priority value.
+ */
+ public void setNewPriority(int newPriority) {
+ if (newPriority < Thread.MIN_PRIORITY || newPriority > Thread.MAX_PRIORITY) {
+ throw new BuildException("The thread priority is out of the range 1-10");
+ }
+ this.newPriority = new Integer(newPriority);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Pack.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Pack.java
new file mode 100644
index 00000000..daabd6ba
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Pack.java
@@ -0,0 +1,212 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+
+/**
+ * Abstract Base class for pack tasks.
+ *
+ * @since Ant 1.5
+ */
+
+public abstract class Pack extends Task {
+ private static final int BUFFER_SIZE = 8 * 1024;
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected File zipFile;
+ protected File source;
+ // CheckStyle:VisibilityModifier ON
+ private Resource src;
+
+ /**
+ * the required destination file.
+ * @param zipFile the destination file
+ */
+ public void setZipfile(File zipFile) {
+ this.zipFile = zipFile;
+ }
+
+ /**
+ * the required destination file.
+ * @param zipFile the destination file
+ */
+ public void setDestfile(File zipFile) {
+ setZipfile(zipFile);
+ }
+
+ /**
+ * the file to compress; required.
+ * @param src the source file
+ */
+ public void setSrc(File src) {
+ setSrcResource(new FileResource(src));
+ }
+
+ /**
+ * The resource to pack; required.
+ * @param src resource to expand
+ */
+ public void setSrcResource(Resource src) {
+ if (src.isDirectory()) {
+ throw new BuildException("the source can't be a directory");
+ }
+ FileProvider fp = src.as(FileProvider.class);
+ if (fp != null) {
+ source = fp.getFile();
+ } else if (!supportsNonFileResources()) {
+ throw new BuildException("Only FileSystem resources are supported.");
+ }
+ this.src = src;
+ }
+
+ /**
+ * Set the source resource.
+ * @param a the resource to pack as a single element Resource collection.
+ */
+ public void addConfigured(ResourceCollection a) {
+ if (a.size() == 0) {
+ throw new BuildException("No resource selected, " + getTaskName()
+ + " needs exactly one resource.");
+ }
+ if (a.size() != 1) {
+ throw new BuildException(getTaskName()
+ + " cannot handle multiple resources at once. (" + a.size()
+ + " resources were selected.)");
+ }
+ setSrcResource(a.iterator().next());
+ }
+
+ /**
+ * validation routine
+ * @throws BuildException if anything is invalid
+ */
+ private void validate() throws BuildException {
+ if (zipFile == null) {
+ throw new BuildException("zipfile attribute is required", getLocation());
+ }
+
+ if (zipFile.isDirectory()) {
+ throw new BuildException("zipfile attribute must not "
+ + "represent a directory!", getLocation());
+ }
+
+ if (getSrcResource() == null) {
+ throw new BuildException("src attribute or nested resource is"
+ + " required", getLocation());
+ }
+ }
+
+ /**
+ * validate, then hand off to the subclass
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ validate();
+
+ Resource s = getSrcResource();
+ if (!s.isExists()) {
+ log("Nothing to do: " + s.toString()
+ + " doesn't exist.");
+ } else if (zipFile.lastModified() < s.getLastModified()) {
+ log("Building: " + zipFile.getAbsolutePath());
+ pack();
+ } else {
+ log("Nothing to do: " + zipFile.getAbsolutePath()
+ + " is up to date.");
+ }
+ }
+
+ /**
+ * zip a stream to an output stream
+ * @param in the stream to zip
+ * @param zOut the output stream
+ * @throws IOException
+ */
+ private void zipFile(InputStream in, OutputStream zOut)
+ throws IOException {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int count = 0;
+ do {
+ zOut.write(buffer, 0, count);
+ count = in.read(buffer, 0, buffer.length);
+ } while (count != -1);
+ }
+
+ /**
+ * zip a file to an output stream
+ * @param file the file to zip
+ * @param zOut the output stream
+ * @throws IOException on error
+ */
+ protected void zipFile(File file, OutputStream zOut)
+ throws IOException {
+ zipResource(new FileResource(file), zOut);
+ }
+
+ /**
+ * zip a resource to an output stream
+ * @param resource the resource to zip
+ * @param zOut the output stream
+ * @throws IOException on error
+ */
+ protected void zipResource(Resource resource, OutputStream zOut)
+ throws IOException {
+ InputStream rIn = resource.getInputStream();
+ try {
+ zipFile(rIn, zOut);
+ } finally {
+ rIn.close();
+ }
+ }
+
+ /**
+ * subclasses must implement this method to do their compression
+ */
+ protected abstract void pack();
+
+ /**
+ * The source resource.
+ * @return the source.
+ * @since Ant 1.7
+ */
+ public Resource getSrcResource() {
+ return src;
+ }
+
+ /**
+ * Whether this task can deal with non-file resources.
+ *
+ * <p>This implementation returns false.</p>
+ * @return false.
+ * @since Ant 1.7
+ */
+ protected boolean supportsNonFileResources() {
+ return false;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Parallel.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Parallel.java
new file mode 100644
index 00000000..c4f5c9e9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Parallel.java
@@ -0,0 +1,489 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ExitStatusException;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.property.LocalProperties;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Executes the contained tasks in separate threads, continuing
+ * once all are completed.
+ * <p>
+ * New behavior allows for the ant script to specify a maximum number of
+ * threads that will be executed in parallel. One should be very careful about
+ * using the <code>waitFor</code> task when specifying <code>threadCount</code>
+ * as it can cause deadlocks if the number of threads is too small or if one of
+ * the nested tasks fails to execute completely. The task selection algorithm
+ * will insure that the tasks listed before a task have started before that
+ * task is started, but it will not insure a successful completion of those
+ * tasks or that those tasks will finish first (i.e. it's a classic race
+ * condition).
+ * </p>
+ * @since Ant 1.4
+ *
+ * @ant.task category="control"
+ */
+public class Parallel extends Task
+ implements TaskContainer {
+
+ private static final int NUMBER_TRIES = 100;
+
+ /** Class which holds a list of tasks to execute */
+ public static class TaskList implements TaskContainer {
+ /** Collection holding the nested tasks */
+ private List tasks = new ArrayList();
+
+ /**
+ * Add a nested task to execute parallel (asynchron).
+ * <p>
+ * @param nestedTask Nested task to be executed in parallel.
+ * must not be null.
+ */
+ public void addTask(Task nestedTask) {
+ tasks.add(nestedTask);
+ }
+ }
+
+ /** Collection holding the nested tasks */
+ private Vector nestedTasks = new Vector();
+
+ /** Semaphore to notify of completed threads */
+ private final Object semaphore = new Object();
+
+ /** Total number of threads to run */
+ private int numThreads = 0;
+
+ /** Total number of threads per processor to run. */
+ private int numThreadsPerProcessor = 0;
+
+ /** The timeout period in milliseconds */
+ private long timeout;
+
+ /** Indicates threads are still running and new threads can be issued */
+ private volatile boolean stillRunning;
+
+ /** Indicates that the execution timedout */
+ private boolean timedOut;
+
+ /**
+ * Indicates whether failure of any of the nested tasks should end
+ * execution
+ */
+ private boolean failOnAny;
+
+ /** The dameon task list if any */
+ private TaskList daemonTasks;
+
+ /** Accumulation of exceptions messages from all nested tasks */
+ private StringBuffer exceptionMessage;
+
+ /** Number of exceptions from nested tasks */
+ private int numExceptions = 0;
+
+ /** The first exception encountered */
+ private Throwable firstException;
+
+ /** The location of the first exception */
+ private Location firstLocation;
+
+ /** The status of the first ExitStatusException. */
+ private Integer firstExitStatus;
+
+ /**
+ * Add a group of daemon threads
+ * @param daemonTasks The tasks to be executed as daemon.
+ */
+ public void addDaemons(TaskList daemonTasks) {
+ if (this.daemonTasks != null) {
+ throw new BuildException("Only one daemon group is supported");
+ }
+ this.daemonTasks = daemonTasks;
+ }
+
+ /**
+ * Interval to poll for completed threads when threadCount or
+ * threadsPerProcessor is specified. Integer in milliseconds.; optional
+ *
+ * @param pollInterval New value of property pollInterval.
+ */
+ public void setPollInterval(int pollInterval) {
+ }
+
+ /**
+ * Control whether a failure in a nested task halts execution. Note that
+ * the task will complete but existing threads will continue to run - they
+ * are not stopped
+ *
+ * @param failOnAny if true any nested task failure causes parallel to
+ * complete.
+ */
+ public void setFailOnAny(boolean failOnAny) {
+ this.failOnAny = failOnAny;
+ }
+
+ /**
+ * Add a nested task to execute in parallel.
+ * @param nestedTask Nested task to be executed in parallel
+ */
+ public void addTask(Task nestedTask) {
+ nestedTasks.addElement(nestedTask);
+ }
+
+ /**
+ * Dynamically generates the number of threads to execute based on the
+ * number of available processors (via
+ * <code>java.lang.Runtime.availableProcessors()</code>).
+ * Will overwrite the value set in threadCount; optional
+ * @param numThreadsPerProcessor Number of threads to create per available
+ * processor.
+ *
+ */
+ public void setThreadsPerProcessor(int numThreadsPerProcessor) {
+ this.numThreadsPerProcessor = numThreadsPerProcessor;
+ }
+
+ /**
+ * Statically determine the maximum number of tasks to execute
+ * simultaneously. If there are less tasks than threads then all will be
+ * executed at once, if there are more then only <code>threadCount</code>
+ * tasks will be executed at one time. If <code>threadsPerProcessor</code>
+ * is set then this value is
+ * ignored.; optional
+ *
+ * @param numThreads total number of threads.
+ *
+ */
+ public void setThreadCount(int numThreads) {
+ this.numThreads = numThreads;
+ }
+
+ /**
+ * Sets the timeout on this set of tasks. If the timeout is reached
+ * before the other threads complete, the execution of this
+ * task completes with an exception.
+ *
+ * Note that existing threads continue to run.
+ *
+ * @param timeout timeout in milliseconds.
+ */
+ public void setTimeout(long timeout) {
+ this.timeout = timeout;
+ }
+
+
+
+ /**
+ * Execute the parallel tasks
+ *
+ * @exception BuildException if any of the threads failed.
+ */
+ public void execute() throws BuildException {
+ updateThreadCounts();
+ if (numThreads == 0) {
+ numThreads = nestedTasks.size();
+ }
+ spinThreads();
+ }
+
+ /**
+ * Determine the number of threads based on the number of processors
+ */
+ private void updateThreadCounts() {
+ if (numThreadsPerProcessor != 0) {
+ numThreads = Runtime.getRuntime().availableProcessors() *
+ numThreadsPerProcessor;
+ }
+ }
+
+ private void processExceptions(TaskRunnable[] runnables) {
+ if (runnables == null) {
+ return;
+ }
+ for (int i = 0; i < runnables.length; ++i) {
+ Throwable t = runnables[i].getException();
+ if (t != null) {
+ numExceptions++;
+ if (firstException == null) {
+ firstException = t;
+ }
+ if (t instanceof BuildException
+ && firstLocation == Location.UNKNOWN_LOCATION) {
+ firstLocation = ((BuildException) t).getLocation();
+ }
+ if (t instanceof ExitStatusException
+ && firstExitStatus == null) {
+ ExitStatusException ex = (ExitStatusException) t;
+ firstExitStatus = ex.getStatus();
+ // potentially overwriting existing value but the
+ // location should match the exit status
+ firstLocation = ex.getLocation();
+ }
+ exceptionMessage.append(StringUtils.LINE_SEP);
+ exceptionMessage.append(t.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Spin up required threads with a maximum number active at any given time.
+ *
+ * @exception BuildException if any of the threads failed.
+ */
+ private void spinThreads() throws BuildException {
+ final int numTasks = nestedTasks.size();
+ TaskRunnable[] runnables = new TaskRunnable[numTasks];
+ stillRunning = true;
+ timedOut = false;
+ boolean interrupted = false;
+
+ int threadNumber = 0;
+ for (Enumeration e = nestedTasks.elements(); e.hasMoreElements();
+ threadNumber++) {
+ Task nestedTask = (Task) e.nextElement();
+ runnables[threadNumber]
+ = new TaskRunnable(nestedTask);
+ }
+
+ final int maxRunning = numTasks < numThreads ? numTasks : numThreads;
+ TaskRunnable[] running = new TaskRunnable[maxRunning];
+
+ threadNumber = 0;
+ ThreadGroup group = new ThreadGroup("parallel");
+
+ TaskRunnable[] daemons = null;
+ if (daemonTasks != null && daemonTasks.tasks.size() != 0) {
+ daemons = new TaskRunnable[daemonTasks.tasks.size()];
+ }
+
+ synchronized (semaphore) {
+ // When we leave this block we can be sure all data is really
+ // stored in main memory before the new threads start, the new
+ // threads will for sure load the data from main memory.
+ //
+ // This probably is slightly paranoid.
+ }
+
+ synchronized (semaphore) {
+ // start any daemon threads
+ if (daemons != null) {
+ for (int i = 0; i < daemons.length; ++i) {
+ daemons[i] = new TaskRunnable((Task) daemonTasks.tasks.get(i));
+ Thread daemonThread = new Thread(group, daemons[i]);
+ daemonThread.setDaemon(true);
+ daemonThread.start();
+ }
+ }
+
+ // now run main threads in limited numbers...
+ // start initial batch of threads
+ for (int i = 0; i < maxRunning; ++i) {
+ running[i] = runnables[threadNumber++];
+ Thread thread = new Thread(group, running[i]);
+ thread.start();
+ }
+
+ if (timeout != 0) {
+ // start the timeout thread
+ Thread timeoutThread = new Thread() {
+ public synchronized void run() {
+ try {
+ wait(timeout);
+ synchronized (semaphore) {
+ stillRunning = false;
+ timedOut = true;
+ semaphore.notifyAll();
+ }
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ };
+ timeoutThread.start();
+ }
+
+ try {
+ // now find available running slots for the remaining threads
+ outer: while (threadNumber < numTasks && stillRunning) {
+ for (int i = 0; i < maxRunning; i++) {
+ if (running[i] == null || running[i].isFinished()) {
+ running[i] = runnables[threadNumber++];
+ Thread thread = new Thread(group, running[i]);
+ thread.start();
+ // continue on outer while loop to get another
+ // available slot
+ continue outer;
+ }
+ }
+
+ // if we got here all slots in use, so sleep until
+ // something happens
+ semaphore.wait();
+ }
+
+ // are all threads finished
+ outer2: while (stillRunning) {
+ for (int i = 0; i < maxRunning; ++i) {
+ if (running[i] != null && !running[i].isFinished()) {
+ // System.out.println("Thread " + i + " is still
+ // alive ");
+ // still running - wait for it
+ semaphore.wait();
+ continue outer2;
+ }
+ }
+ stillRunning = false;
+ }
+ } catch (InterruptedException ie) {
+ interrupted = true;
+ }
+
+ if (!timedOut && !failOnAny) {
+ // https://issues.apache.org/bugzilla/show_bug.cgi?id=49527
+ killAll(running);
+ }
+ }
+
+ if (interrupted) {
+ throw new BuildException("Parallel execution interrupted.");
+ }
+ if (timedOut) {
+ throw new BuildException("Parallel execution timed out");
+ }
+
+ // now did any of the threads throw an exception
+ exceptionMessage = new StringBuffer();
+ numExceptions = 0;
+ firstException = null;
+ firstExitStatus = null;
+ firstLocation = Location.UNKNOWN_LOCATION;
+ processExceptions(daemons);
+ processExceptions(runnables);
+
+ if (numExceptions == 1) {
+ if (firstException instanceof BuildException) {
+ throw (BuildException) firstException;
+ } else {
+ throw new BuildException(firstException);
+ }
+ } else if (numExceptions > 1) {
+ if (firstExitStatus == null) {
+ throw new BuildException(exceptionMessage.toString(),
+ firstLocation);
+ } else {
+ throw new ExitStatusException(exceptionMessage.toString(),
+ firstExitStatus, firstLocation);
+ }
+ }
+ }
+
+ /**
+ * Doesn't do anything if all threads where already gone,
+ * else it tries to interrupt the threads 100 times.
+ * @param running The list of tasks that may currently be running.
+ */
+ private void killAll(TaskRunnable[] running) {
+ boolean oneAlive;
+ int tries = 0;
+ do {
+ oneAlive = false;
+ for (int i = 0; i < running.length; i++) {
+ if (running[i] != null && !running[i].isFinished()) {
+ running[i].interrupt();
+ Thread.yield();
+ oneAlive = true;
+ }
+ }
+ if (oneAlive) {
+ tries++;
+ Thread.yield();
+ }
+ } while (oneAlive && tries < NUMBER_TRIES);
+ }
+
+ /**
+ * thread that execs a task
+ */
+ private class TaskRunnable implements Runnable {
+ private Throwable exception;
+ private Task task;
+ private boolean finished;
+ private volatile Thread thread;
+
+ /**
+ * Construct a new TaskRunnable.<p>
+ *
+ * @param task the Task to be executed in a separate thread
+ */
+ TaskRunnable(Task task) {
+ this.task = task;
+ }
+
+ /**
+ * Executes the task within a thread and takes care about
+ * Exceptions raised within the task.
+ */
+ public void run() {
+ try {
+ LocalProperties.get(getProject()).copy();
+ thread = Thread.currentThread();
+ task.perform();
+ } catch (Throwable t) {
+ exception = t;
+ if (failOnAny) {
+ stillRunning = false;
+ }
+ } finally {
+ synchronized (semaphore) {
+ finished = true;
+ semaphore.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * get any exception that got thrown during execution;
+ * @return an exception or null for no exception/not yet finished
+ */
+ public Throwable getException() {
+ return exception;
+ }
+
+ /**
+ * Provides the indicator that the task has been finished.
+ * @return Returns true when the task is finished.
+ */
+ boolean isFinished() {
+ return finished;
+ }
+
+ void interrupt() {
+ thread.interrupt();
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Patch.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Patch.java
new file mode 100644
index 00000000..96ab0823
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Patch.java
@@ -0,0 +1,215 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Patches a file by applying a 'diff' file to it; requires "patch" to be
+ * on the execution path.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="utility"
+ */
+public class Patch extends Task {
+
+ private File originalFile;
+ private File directory;
+ private boolean havePatchfile = false;
+ private Commandline cmd = new Commandline();
+
+ /**
+ * Halt on error return value from patch invocation.
+ */
+ private boolean failOnError = false;
+
+ /**
+ * The file to patch; optional if it can be inferred from
+ * the diff file
+ * @param file the file to patch
+ */
+ public void setOriginalfile(File file) {
+ originalFile = file;
+ }
+
+ /**
+ * The name of a file to send the output to, instead of patching
+ * the file(s) in place; optional.
+ * @param file the file to send the output to
+ * @since Ant 1.6
+ */
+ public void setDestfile(File file) {
+ if (file != null) {
+ cmd.createArgument().setValue("-o");
+ cmd.createArgument().setFile(file);
+ }
+ }
+
+ /**
+ * The file containing the diff output; required.
+ * @param file the file containing the diff output
+ */
+ public void setPatchfile(File file) {
+ if (!file.exists()) {
+ throw new BuildException("patchfile " + file + " doesn\'t exist",
+ getLocation());
+ }
+ cmd.createArgument().setValue("-i");
+ cmd.createArgument().setFile(file);
+ havePatchfile = true;
+ }
+
+ /**
+ * flag to create backups; optional, default=false
+ * @param backups if true create backups
+ */
+ public void setBackups(boolean backups) {
+ if (backups) {
+ cmd.createArgument().setValue("-b");
+ }
+ }
+
+ /**
+ * flag to ignore whitespace differences; default=false
+ * @param ignore if true ignore whitespace differences
+ */
+ public void setIgnorewhitespace(boolean ignore) {
+ if (ignore) {
+ cmd.createArgument().setValue("-l");
+ }
+ }
+
+ /**
+ * Strip the smallest prefix containing <i>num</i> leading slashes
+ * from filenames.
+ *
+ * <p>patch's <i>-p</i> option.
+ * @param num number of lines to strip
+ * @exception BuildException if num is &lt; 0, or other errors
+ */
+ public void setStrip(int num) throws BuildException {
+ if (num < 0) {
+ throw new BuildException("strip has to be >= 0", getLocation());
+ }
+ cmd.createArgument().setValue("-p" + num);
+ }
+
+ /**
+ * Work silently unless an error occurs; optional, default=false
+ * @param q if true suppress set the -s option on the patch command
+ */
+ public void setQuiet(boolean q) {
+ if (q) {
+ cmd.createArgument().setValue("-s");
+ }
+ }
+
+ /**
+ * Assume patch was created with old and new files swapped; optional,
+ * default=false
+ * @param r if true set the -R option on the patch command
+ */
+ public void setReverse(boolean r) {
+ if (r) {
+ cmd.createArgument().setValue("-R");
+ }
+ }
+
+ /**
+ * The directory to run the patch command in, defaults to the
+ * project's base directory.
+ * @param directory the directory to run the patch command in
+ * @since Ant 1.5
+ */
+ public void setDir(File directory) {
+ this.directory = directory;
+ }
+
+ /**
+ * If <code>true</code>, stop the build process if the patch command
+ * exits with an error status.
+ * @param value <code>true</code> if it should halt, otherwise
+ * <code>false</code>. The default is <code>false</code>.
+ * @since Ant 1.8.0
+ */
+ public void setFailOnError(boolean value) {
+ failOnError = value;
+ }
+
+ private static final String PATCH = "patch";
+
+ /**
+ * execute patch
+ * @throws BuildException when it all goes a bit pear shaped
+ */
+ public void execute() throws BuildException {
+ if (!havePatchfile) {
+ throw new BuildException("patchfile argument is required",
+ getLocation());
+ }
+ Commandline toExecute = (Commandline) cmd.clone();
+ toExecute.setExecutable(PATCH);
+
+ if (originalFile != null) {
+ toExecute.createArgument().setFile(originalFile);
+ }
+
+ Execute exe = new Execute(new LogStreamHandler(this, Project.MSG_INFO,
+ Project.MSG_WARN),
+ null);
+ exe.setCommandline(toExecute.getCommandline());
+
+ if (directory != null) {
+ if (directory.exists() && directory.isDirectory()) {
+ exe.setWorkingDirectory(directory);
+ } else if (!directory.isDirectory()) {
+ throw new BuildException(directory + " is not a directory.",
+ getLocation());
+ } else {
+ throw new BuildException("directory " + directory
+ + " doesn\'t exist", getLocation());
+ }
+ } else {
+ exe.setWorkingDirectory(getProject().getBaseDir());
+ }
+
+ log(toExecute.describeCommand(), Project.MSG_VERBOSE);
+ try {
+ int returncode = exe.execute();
+ if (Execute.isFailure(returncode)) {
+ String msg = "'" + PATCH + "' failed with exit code "
+ + returncode;
+ if (failOnError) {
+ throw new BuildException(msg);
+ } else {
+ log(msg, Project.MSG_ERR);
+ }
+ }
+ } catch (IOException e) {
+ throw new BuildException(e, getLocation());
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PathConvert.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PathConvert.java
new file mode 100644
index 00000000..abbc5fd6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PathConvert.java
@@ -0,0 +1,511 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.Resources;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+
+/**
+ * Converts path and classpath information to a specific target OS
+ * format. The resulting formatted path is placed into the specified property.
+ *
+ * @since Ant 1.4
+ * @ant.task category="utility"
+ */
+public class PathConvert extends Task {
+
+ /**
+ * Set if we're running on windows
+ */
+ private static boolean onWindows = Os.isFamily("dos");
+
+ // Members
+ /**
+ * Path to be converted
+ */
+ private Resources path = null;
+ /**
+ * Reference to path/fileset to convert
+ */
+ private Reference refid = null;
+ /**
+ * The target OS type
+ */
+ private String targetOS = null;
+ /**
+ * Set when targetOS is set to windows
+ */
+ private boolean targetWindows = false;
+ /**
+ * Set if we should create a new property even if the result is empty
+ */
+ private boolean setonempty = true;
+ /**
+ * The property to receive the conversion
+ */
+ private String property = null;
+ /**
+ * Path prefix map
+ */
+ private Vector prefixMap = new Vector();
+ /**
+ * User override on path sep char
+ */
+ private String pathSep = null;
+ /**
+ * User override on directory sep char
+ */
+ private String dirSep = null;
+
+ /** Filename mapper */
+ private Mapper mapper = null;
+
+ private boolean preserveDuplicates;
+
+ /**
+ * Construct a new instance of the PathConvert task.
+ */
+ public PathConvert() {
+ }
+
+ /**
+ * Helper class, holds the nested &lt;map&gt; values. Elements will look like
+ * this: &lt;map from=&quot;d:&quot; to=&quot;/foo&quot;/&gt;
+ *
+ * When running on windows, the prefix comparison will be case
+ * insensitive.
+ */
+ public class MapEntry {
+
+ // Members
+ private String from = null;
+ private String to = null;
+
+ /**
+ * Set the &quot;from&quot; attribute of the map entry.
+ * @param from the prefix string to search for; required.
+ * Note that this value is case-insensitive when the build is
+ * running on a Windows platform and case-sensitive when running on
+ * a Unix platform.
+ */
+ public void setFrom(String from) {
+ this.from = from;
+ }
+
+ /**
+ * Set the replacement text to use when from is matched; required.
+ * @param to new prefix.
+ */
+ public void setTo(String to) {
+ this.to = to;
+ }
+
+ /**
+ * Apply this map entry to a given path element.
+ *
+ * @param elem Path element to process.
+ * @return String Updated path element after mapping.
+ */
+ public String apply(String elem) {
+ if (from == null || to == null) {
+ throw new BuildException("Both 'from' and 'to' must be set "
+ + "in a map entry");
+ }
+ // If we're on windows, then do the comparison ignoring case
+ // and treat the two directory characters the same
+ String cmpElem =
+ onWindows ? elem.toLowerCase().replace('\\', '/') : elem;
+ String cmpFrom =
+ onWindows ? from.toLowerCase().replace('\\', '/') : from;
+
+ // If the element starts with the configured prefix, then
+ // convert the prefix to the configured 'to' value.
+
+ return cmpElem.startsWith(cmpFrom)
+ ? to + elem.substring(from.length()) : elem;
+ }
+ }
+
+ /**
+ * An enumeration of supported targets:
+ * "windows", "unix", "netware", and "os/2".
+ */
+ public static class TargetOs extends EnumeratedAttribute {
+ /**
+ * @return the list of values for this enumerated attribute.
+ */
+ @Override
+ public String[] getValues() {
+ return new String[]{"windows", "unix", "netware", "os/2", "tandem"};
+ }
+ }
+
+ /**
+ * Create a nested path element.
+ * @return a Path to be used by Ant reflection.
+ */
+ public Path createPath() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ Path result = new Path(getProject());
+ add(result);
+ return result;
+ }
+
+ /**
+ * Add an arbitrary ResourceCollection.
+ * @param rc the ResourceCollection to add.
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection rc) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ getPath().add(rc);
+ }
+
+ private synchronized Resources getPath() {
+ if (path == null) {
+ path = new Resources(getProject());
+ path.setCache(true);
+ }
+ return path;
+ }
+
+ /**
+ * Create a nested MAP element.
+ * @return a Map to configure.
+ */
+ public MapEntry createMap() {
+ MapEntry entry = new MapEntry();
+ prefixMap.addElement(entry);
+ return entry;
+ }
+
+ /**
+ * Set targetos to a platform to one of
+ * "windows", "unix", "netware", or "os/2";
+ * current platform settings are used by default.
+ * @param target the target os.
+ * @deprecated since 1.5.x.
+ * Use the method taking a TargetOs argument instead.
+ * @see #setTargetos(PathConvert.TargetOs)
+ */
+ @Deprecated
+ public void setTargetos(String target) {
+ TargetOs to = new TargetOs();
+ to.setValue(target);
+ setTargetos(to);
+ }
+
+ /**
+ * Set targetos to a platform to one of
+ * "windows", "unix", "netware", or "os/2";
+ * current platform settings are used by default.
+ * @param target the target os
+ *
+ * @since Ant 1.5
+ */
+ public void setTargetos(TargetOs target) {
+ targetOS = target.getValue();
+
+ // Currently, we deal with only two path formats: Unix and Windows
+ // And Unix is everything that is not Windows
+
+ // for NetWare and OS/2, piggy-back on Windows, since in the
+ // validateSetup code, the same assumptions can be made as
+ // with windows - that ; is the path separator
+
+ targetWindows = !targetOS.equals("unix") && !targetOS.equals("tandem");
+ }
+
+ /**
+ * Set whether the specified property will be set if the result
+ * is the empty string.
+ * @param setonempty true or false.
+ *
+ * @since Ant 1.5
+ */
+ public void setSetonempty(boolean setonempty) {
+ this.setonempty = setonempty;
+ }
+
+ /**
+ * Set the name of the property into which the converted path will be placed.
+ * @param p the property name.
+ */
+ public void setProperty(String p) {
+ property = p;
+ }
+
+ /**
+ * Add a reference to a Path, FileSet, DirSet, or FileList defined elsewhere.
+ * @param r the reference to a path, fileset, dirset or filelist.
+ */
+ public void setRefid(Reference r) {
+ if (path != null) {
+ throw noChildrenAllowed();
+ }
+ refid = r;
+ }
+
+ /**
+ * Set the default path separator string; defaults to current JVM
+ * {@link java.io.File#pathSeparator File.pathSeparator}.
+ * @param sep path separator string.
+ */
+ public void setPathSep(String sep) {
+ pathSep = sep;
+ }
+
+
+ /**
+ * Set the default directory separator string;
+ * defaults to current JVM {@link java.io.File#separator File.separator}.
+ * @param sep directory separator string.
+ */
+ public void setDirSep(String sep) {
+ dirSep = sep;
+ }
+
+ /**
+ * Set the preserveDuplicates.
+ * @param preserveDuplicates the boolean to set
+ * @since Ant 1.8
+ */
+ public void setPreserveDuplicates(boolean preserveDuplicates) {
+ this.preserveDuplicates = preserveDuplicates;
+ }
+
+ /**
+ * Get the preserveDuplicates.
+ * @return boolean
+ * @since Ant 1.8
+ */
+ public boolean isPreserveDuplicates() {
+ return preserveDuplicates;
+ }
+
+ /**
+ * Learn whether the refid attribute of this element been set.
+ * @return true if refid is valid.
+ */
+ public boolean isReference() {
+ return refid != null;
+ }
+
+ /**
+ * Do the execution.
+ * @throws BuildException if something is invalid.
+ */
+ @Override
+ public void execute() throws BuildException {
+ Resources savedPath = path;
+ String savedPathSep = pathSep; // may be altered in validateSetup
+ String savedDirSep = dirSep; // may be altered in validateSetup
+
+ try {
+ // If we are a reference, create a Path from the reference
+ if (isReference()) {
+ Object o = refid.getReferencedObject(getProject());
+ if (!(o instanceof ResourceCollection)) {
+ throw new BuildException("refid '" + refid.getRefId()
+ + "' does not refer to a resource collection.");
+ }
+ getPath().add((ResourceCollection) o);
+ }
+ validateSetup(); // validate our setup
+
+ // Currently, we deal with only two path formats: Unix and Windows
+ // And Unix is everything that is not Windows
+ // (with the exception for NetWare and OS/2 below)
+
+ // for NetWare and OS/2, piggy-back on Windows, since here and
+ // in the apply code, the same assumptions can be made as with
+ // windows - that \\ is an OK separator, and do comparisons
+ // case-insensitive.
+ String fromDirSep = onWindows ? "\\" : "/";
+
+ StringBuffer rslt = new StringBuffer();
+
+ ResourceCollection resources = isPreserveDuplicates() ? (ResourceCollection) path : new Union(path);
+ List ret = new ArrayList();
+ FileNameMapper mapperImpl = mapper == null ? new IdentityMapper() : mapper.getImplementation();
+ for (Resource r : resources) {
+ String[] mapped = mapperImpl.mapFileName(String.valueOf(r));
+ for (int m = 0; mapped != null && m < mapped.length; ++m) {
+ ret.add(mapped[m]);
+ }
+ }
+ boolean first = true;
+ for (Iterator mappedIter = ret.iterator(); mappedIter.hasNext();) {
+ String elem = mapElement((String) mappedIter.next()); // Apply the path prefix map
+
+ // Now convert the path and file separator characters from the
+ // current os to the target os.
+
+ if (!first) {
+ rslt.append(pathSep);
+ }
+ first = false;
+
+ StringTokenizer stDirectory = new StringTokenizer(elem, fromDirSep, true);
+
+ while (stDirectory.hasMoreTokens()) {
+ String token = stDirectory.nextToken();
+ rslt.append(fromDirSep.equals(token) ? dirSep : token);
+ }
+ }
+ // Place the result into the specified property,
+ // unless setonempty == false
+ if (setonempty || rslt.length() > 0) {
+ String value = rslt.toString();
+ if (property == null) {
+ log(value);
+ } else {
+ log("Set property " + property + " = " + value, Project.MSG_VERBOSE);
+ getProject().setNewProperty(property, value);
+ }
+ }
+ } finally {
+ path = savedPath;
+ dirSep = savedDirSep;
+ pathSep = savedPathSep;
+ }
+ }
+
+ /**
+ * Apply the configured map to a path element. The map is used to convert
+ * between Windows drive letters and Unix paths. If no map is configured,
+ * then the input string is returned unchanged.
+ *
+ * @param elem The path element to apply the map to.
+ * @return String Updated element.
+ */
+ private String mapElement(String elem) {
+
+ int size = prefixMap.size();
+
+ if (size != 0) {
+
+ // Iterate over the map entries and apply each one.
+ // Stop when one of the entries actually changes the element.
+
+ for (int i = 0; i < size; i++) {
+ MapEntry entry = (MapEntry) prefixMap.elementAt(i);
+ String newElem = entry.apply(elem);
+
+ // Note I'm using "!=" to see if we got a new object back from
+ // the apply method.
+
+ if (newElem != elem) {
+ elem = newElem;
+ break; // We applied one, so we're done
+ }
+ }
+ }
+ return elem;
+ }
+
+ /**
+ * Add a mapper to convert the file names.
+ *
+ * @param mapper a <code>Mapper</code> value.
+ */
+ public void addMapper(Mapper mapper) {
+ if (this.mapper != null) {
+ throw new BuildException(
+ "Cannot define more than one mapper");
+ }
+ this.mapper = mapper;
+ }
+
+ /**
+ * Add a nested filenamemapper.
+ * @param fileNameMapper the mapper to add.
+ * @since Ant 1.6.3
+ */
+ public void add(FileNameMapper fileNameMapper) {
+ Mapper m = new Mapper(getProject());
+ m.add(fileNameMapper);
+ addMapper(m);
+ }
+
+ /**
+ * Validate that all our parameters have been properly initialized.
+ *
+ * @throws BuildException if something is not set up properly.
+ */
+ private void validateSetup() throws BuildException {
+
+ if (path == null) {
+ throw new BuildException("You must specify a path to convert");
+ }
+ // Determine the separator strings. The dirsep and pathsep attributes
+ // override the targetOS settings.
+ String dsep = File.separator;
+ String psep = File.pathSeparator;
+
+ if (targetOS != null) {
+ psep = targetWindows ? ";" : ":";
+ dsep = targetWindows ? "\\" : "/";
+ }
+ if (pathSep != null) {
+ // override with pathsep=
+ psep = pathSep;
+ }
+ if (dirSep != null) {
+ // override with dirsep=
+ dsep = dirSep;
+ }
+ pathSep = psep;
+ dirSep = dsep;
+ }
+
+ /**
+ * Creates an exception that indicates that this XML element must not have
+ * child elements if the refid attribute is set.
+ * @return BuildException.
+ */
+ private BuildException noChildrenAllowed() {
+ return new BuildException("You must not specify nested "
+ + "elements when using the refid attribute.");
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PreSetDef.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PreSetDef.java
new file mode 100644
index 00000000..fe577044
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PreSetDef.java
@@ -0,0 +1,275 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.AntTypeDefinition;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.UnknownElement;
+
+/**
+ * The preset definition task generates a new definition
+ * based on a current definition with some attributes or
+ * elements preset.
+ * <pre>
+ * &lt;presetdef name="my.javac"&gt;
+ * &lt;javac deprecation="${deprecation}" debug="${debug}"/&gt;
+ * &lt;/presetdef&gt;
+ * &lt;my.javac srcdir="src" destdir="classes"/&gt;
+ * </pre>
+ *
+ * @since Ant 1.6
+ */
+public class PreSetDef extends AntlibDefinition implements TaskContainer {
+ private UnknownElement nestedTask;
+ private String name;
+
+ /**
+ * Set the name of this definition.
+ * @param name the name of the definition.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Add a nested task to predefine attributes and elements on.
+ * @param nestedTask Nested task/type to extend.
+ */
+ public void addTask(Task nestedTask) {
+ if (this.nestedTask != null) {
+ throw new BuildException("Only one nested element allowed");
+ }
+ if (!(nestedTask instanceof UnknownElement)) {
+ throw new BuildException(
+ "addTask called with a task that is not an unknown element");
+ }
+ this.nestedTask = (UnknownElement) nestedTask;
+ }
+
+
+ /**
+ * Make a new definition.
+ */
+ public void execute() {
+ if (nestedTask == null) {
+ throw new BuildException("Missing nested element");
+ }
+ if (name == null) {
+ throw new BuildException("Name not specified");
+ }
+ name = ProjectHelper.genComponentName(getURI(), name);
+
+ ComponentHelper helper = ComponentHelper.getComponentHelper(
+ getProject());
+
+ String componentName = ProjectHelper.genComponentName(
+ nestedTask.getNamespace(), nestedTask.getTag());
+
+ AntTypeDefinition def = helper.getDefinition(componentName);
+ if (def == null) {
+ throw new BuildException(
+ "Unable to find typedef " + componentName);
+ }
+ PreSetDefinition newDef = new PreSetDefinition(def, nestedTask);
+
+ newDef.setName(name);
+
+ helper.addDataTypeDefinition(newDef);
+ log("defining preset " + name, Project.MSG_VERBOSE);
+ }
+
+ /**
+ * This class contains the unknown element and the object
+ * that is predefined.
+ * @see AntTypeDefinition
+ */
+ public static class PreSetDefinition extends AntTypeDefinition {
+ private AntTypeDefinition parent;
+ private UnknownElement element;
+
+ /**
+ * Creates a new <code>PresetDefinition</code> instance.
+ *
+ * @param parent The parent of this predefinition.
+ * @param el The predefined attributes, nested elements and text.
+ */
+ public PreSetDefinition(AntTypeDefinition parent, UnknownElement el) {
+ if (parent instanceof PreSetDefinition) {
+ PreSetDefinition p = (PreSetDefinition) parent;
+ el.applyPreSet(p.element);
+ parent = p.parent;
+ }
+ this.parent = parent;
+ this.element = el;
+ }
+
+ /**
+ * Override so that it is not allowed.
+ *
+ * @param clazz a <code>Class</code> value.
+ */
+ public void setClass(Class clazz) {
+ throw new BuildException("Not supported");
+ }
+
+ /**
+ * Override so that it is not allowed.
+ *
+ * @param className a <code>String</code> value.
+ */
+ public void setClassName(String className) {
+ throw new BuildException("Not supported");
+ }
+
+ /**
+ * Get the classname of the definition.
+ * @return the name of the class of this definition.
+ */
+ public String getClassName() {
+ return parent.getClassName();
+ }
+
+ /**
+ * Set the adapter class for this definition.
+ * NOT Supported
+ * @param adapterClass the adapterClass.
+ */
+ public void setAdapterClass(Class adapterClass) {
+ throw new BuildException("Not supported");
+ }
+
+ /**
+ * Set the assignable class for this definition.
+ * NOT SUPPORTED
+ * @param adaptToClass the assignable class.
+ */
+ public void setAdaptToClass(Class adaptToClass) {
+ throw new BuildException("Not supported");
+ }
+
+ /**
+ * Set the classloader to use to create an instance
+ * of the definition.
+ * NOT SUPPORTED
+ * @param classLoader the classLoader.
+ */
+ public void setClassLoader(ClassLoader classLoader) {
+ throw new BuildException("Not supported");
+ }
+
+ /**
+ * Get the classloader for this definition.
+ * @return the classloader for this definition.
+ */
+ public ClassLoader getClassLoader() {
+ return parent.getClassLoader();
+ }
+
+ /**
+ * Get the exposed class for this definition.
+ * @param project the current project.
+ * @return the exposed class.
+ */
+ public Class getExposedClass(Project project) {
+ return parent.getExposedClass(project);
+ }
+
+ /**
+ * Get the definition class.
+ * @param project the current project.
+ * @return the type of the definition.
+ */
+ public Class getTypeClass(Project project) {
+ return parent.getTypeClass(project);
+ }
+
+
+ /**
+ * Check if the attributes are correct.
+ * @param project the current project.
+ */
+ public void checkClass(Project project) {
+ parent.checkClass(project);
+ }
+
+ /**
+ * Create an instance of the definition. The instance may be wrapped
+ * in a proxy class. This is a special version of create for
+ * IntrospectionHelper and UnknownElement.
+ * @param project the current project.
+ * @return the created object.
+ */
+ public Object createObject(Project project) {
+ return parent.create(project);
+ }
+
+ /**
+ * Get the preset values.
+ * @return the predefined attributes, elements and text as
+ * an UnknownElement.
+ */
+ public UnknownElement getPreSets() {
+ return element;
+ }
+
+ /**
+ * Fake create an object, used by IntrospectionHelper and UnknownElement
+ * to see that this is a predefined object.
+ *
+ * @param project the current project.
+ * @return this object.
+ */
+ public Object create(Project project) {
+ return this;
+ }
+
+ /**
+ * Equality method for this definition.
+ *
+ * @param other another definition.
+ * @param project the current project.
+ * @return true if the definitions are the same.
+ */
+ public boolean sameDefinition(AntTypeDefinition other, Project project) {
+ return (other != null && other.getClass() == getClass() && parent != null
+ && parent.sameDefinition(((PreSetDefinition) other).parent, project)
+ && element.similar(((PreSetDefinition) other).element));
+ }
+
+ /**
+ * Similar method for this definition.
+ *
+ * @param other another definition.
+ * @param project the current project.
+ * @return true if the definitions are similar.
+ */
+ public boolean similarDefinition(
+ AntTypeDefinition other, Project project) {
+ return (other != null && other.getClass().getName().equals(
+ getClass().getName()) && parent != null
+ && parent.similarDefinition(((PreSetDefinition) other).parent, project)
+ && element.similar(((PreSetDefinition) other).element));
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ProcessDestroyer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ProcessDestroyer.java
new file mode 100644
index 00000000..ba9c9d48
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ProcessDestroyer.java
@@ -0,0 +1,220 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * Destroys all registered <code>Process</code>es when the VM exits.
+ *
+ * @since Ant 1.5
+ */
+class ProcessDestroyer implements Runnable {
+ private static final int THREAD_DIE_TIMEOUT = 20000;
+ private HashSet processes = new HashSet();
+ // methods to register and unregister shutdown hooks
+ private Method addShutdownHookMethod;
+ private Method removeShutdownHookMethod;
+ private ProcessDestroyerImpl destroyProcessThread = null;
+
+ // whether or not this ProcessDestroyer has been registered as a
+ // shutdown hook
+ private boolean added = false;
+ // whether or not this ProcessDestroyer is currently running as
+ // shutdown hook
+ private boolean running = false;
+
+ private class ProcessDestroyerImpl extends Thread {
+ private boolean shouldDestroy = true;
+
+ public ProcessDestroyerImpl() {
+ super("ProcessDestroyer Shutdown Hook");
+ }
+ public void run() {
+ if (shouldDestroy) {
+ ProcessDestroyer.this.run();
+ }
+ }
+
+ public void setShouldDestroy(boolean shouldDestroy) {
+ this.shouldDestroy = shouldDestroy;
+ }
+ }
+
+ /**
+ * Constructs a <code>ProcessDestroyer</code> and obtains
+ * <code>Runtime.addShutdownHook()</code> and
+ * <code>Runtime.removeShutdownHook()</code> through reflection. The
+ * ProcessDestroyer manages a list of processes to be destroyed when the
+ * VM exits. If a process is added when the list is empty,
+ * this <code>ProcessDestroyer</code> is registered as a shutdown hook. If
+ * removing a process results in an empty list, the
+ * <code>ProcessDestroyer</code> is removed as a shutdown hook.
+ */
+ ProcessDestroyer() {
+ try {
+ // check to see if the shutdown hook methods exists
+ // (support pre-JDK 1.3 and Non-Sun VMs)
+ Class[] paramTypes = {Thread.class};
+ addShutdownHookMethod =
+ Runtime.class.getMethod("addShutdownHook", paramTypes);
+
+ removeShutdownHookMethod =
+ Runtime.class.getMethod("removeShutdownHook", paramTypes);
+ // wait to add shutdown hook as needed
+ } catch (NoSuchMethodException e) {
+ // it just won't be added as a shutdown hook... :(
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Registers this <code>ProcessDestroyer</code> as a shutdown hook,
+ * uses reflection to ensure pre-JDK 1.3 compatibility.
+ */
+ private void addShutdownHook() {
+ if (addShutdownHookMethod != null && !running) {
+ destroyProcessThread = new ProcessDestroyerImpl();
+ Object[] args = {destroyProcessThread};
+ try {
+ addShutdownHookMethod.invoke(Runtime.getRuntime(), args);
+ added = true;
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getTargetException();
+ if (t != null && t.getClass() == IllegalStateException.class) {
+ // shutdown already is in progress
+ running = true;
+ } else {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes this <code>ProcessDestroyer</code> as a shutdown hook,
+ * uses reflection to ensure pre-JDK 1.3 compatibility
+ */
+ private void removeShutdownHook() {
+ if (removeShutdownHookMethod != null && added && !running) {
+ Object[] args = {destroyProcessThread};
+ try {
+ Boolean removed =
+ (Boolean) removeShutdownHookMethod.invoke(
+ Runtime.getRuntime(),
+ args);
+ if (!removed.booleanValue()) {
+ System.err.println("Could not remove shutdown hook");
+ }
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getTargetException();
+ if (t != null && t.getClass() == IllegalStateException.class) {
+ // shutdown already is in progress
+ running = true;
+ } else {
+ e.printStackTrace();
+ }
+ }
+ // start the hook thread, a unstarted thread may not be
+ // eligible for garbage collection
+ // Cf.: http://developer.java.sun.com/developer/bugParade/bugs/4533087.html
+ destroyProcessThread.setShouldDestroy(false);
+ if (!destroyProcessThread.getThreadGroup().isDestroyed()) {
+ // start() would throw IllegalThreadStateException from
+ // ThreadGroup.add if it were destroyed
+ destroyProcessThread.start();
+ }
+ // this should return quickly, since it basically is a NO-OP.
+ try {
+ destroyProcessThread.join(THREAD_DIE_TIMEOUT);
+ } catch (InterruptedException ie) {
+ // the thread didn't die in time
+ // it should not kill any processes unexpectedly
+ }
+ destroyProcessThread = null;
+ added = false;
+ }
+ }
+
+ /**
+ * Returns whether or not the ProcessDestroyer is registered as
+ * as shutdown hook
+ * @return true if this is currently added as shutdown hook
+ */
+ public boolean isAddedAsShutdownHook() {
+ return added;
+ }
+
+ /**
+ * Returns <code>true</code> if the specified <code>Process</code> was
+ * successfully added to the list of processes to destroy upon VM exit.
+ *
+ * @param process the process to add
+ * @return <code>true</code> if the specified <code>Process</code> was
+ * successfully added
+ */
+ public boolean add(Process process) {
+ synchronized (processes) {
+ // if this list is empty, register the shutdown hook
+ if (processes.size() == 0) {
+ addShutdownHook();
+ }
+ return processes.add(process);
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if the specified <code>Process</code> was
+ * successfully removed from the list of processes to destroy upon VM exit.
+ *
+ * @param process the process to remove
+ * @return <code>true</code> if the specified <code>Process</code> was
+ * successfully removed
+ */
+ public boolean remove(Process process) {
+ synchronized (processes) {
+ boolean processRemoved = processes.remove(process);
+ if (processRemoved && processes.size() == 0) {
+ removeShutdownHook();
+ }
+ return processRemoved;
+ }
+ }
+
+ /**
+ * Invoked by the VM when it is exiting.
+ */
+ public void run() {
+ synchronized (processes) {
+ running = true;
+ Iterator e = processes.iterator();
+ while (e.hasNext()) {
+ ((Process) e.next()).destroy();
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ProjectHelperTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ProjectHelperTask.java
new file mode 100644
index 00000000..8f348a74
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ProjectHelperTask.java
@@ -0,0 +1,50 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.ProjectHelperRepository;
+import org.apache.tools.ant.Task;
+
+/**
+ * Task to install project helper into Ant's runtime
+ *
+ * @since Ant 1.8.2
+ */
+public class ProjectHelperTask extends Task {
+
+ private List projectHelpers = new ArrayList();
+
+ public synchronized void addConfigured(ProjectHelper projectHelper) {
+ this.projectHelpers.add(projectHelper);
+ }
+
+ @Override
+ public void execute() throws BuildException {
+ ProjectHelperRepository repo = ProjectHelperRepository.getInstance();
+ for (Iterator it = projectHelpers.iterator(); it.hasNext();) {
+ ProjectHelper helper = (ProjectHelper) it.next();
+ repo.registerProjectHelper(helper.getClass());
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Property.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Property.java
new file mode 100644
index 00000000..4aee3d0f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Property.java
@@ -0,0 +1,726 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.property.ResolvePropertyMap;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * <p>Sets a property by name, or set of properties (from file or
+ * resource) in the project. </p>
+ * <p>Properties are immutable: whoever sets a property first freezes it for the
+ * rest of the build; they are most definitely not variable.
+ * <p>There are seven ways to set properties:</p>
+ * <ul>
+ * <li>By supplying both the <i>name</i> and <i>value</i> attribute.</li>
+ * <li>By supplying the <i>name</i> and nested text.</li>
+ * <li>By supplying both the <i>name</i> and <i>refid</i> attribute.</li>
+ * <li>By setting the <i>file</i> attribute with the filename of the property
+ * file to load. This property file has the format as defined by the file used
+ * in the class java.util.Properties.</li>
+ * <li>By setting the <i>url</i> attribute with the url from which to load the
+ * properties. This url must be directed to a file that has the format as defined
+ * by the file used in the class java.util.Properties.</li>
+ * <li>By setting the <i>resource</i> attribute with the resource name of the
+ * property file to load. This property file has the format as defined by the
+ * file used in the class java.util.Properties.</li>
+ * <li>By setting the <i>environment</i> attribute with a prefix to use.
+ * Properties will be defined for every environment variable by
+ * prefixing the supplied name and a period to the name of the variable.</li>
+ * </ul>
+ * <p>Although combinations of these ways are possible, only one should be used
+ * at a time. Problems might occur with the order in which properties are set, for
+ * instance.</p>
+ * <p>The value part of the properties being set, might contain references to other
+ * properties. These references are resolved at the time these properties are set.
+ * This also holds for properties loaded from a property file.</p>
+ * Properties are case sensitive.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.attribute.group name="name" description="One of these, when using the name attribute"
+ * @ant.attribute.group name="noname" description="One of these, when not using the name attribute"
+ * @ant.task category="property"
+ */
+public class Property extends Task {
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected String name;
+ protected String value;
+ protected File file;
+ protected URL url;
+ protected String resource;
+ protected Path classpath;
+ protected String env;
+ protected Reference ref;
+ protected String prefix;
+ private Project fallback;
+ private Object untypedValue;
+ private boolean valueAttributeUsed = false;
+ private boolean relative = false;
+ private File basedir;
+ private boolean prefixValues = false;
+
+ protected boolean userProperty; // set read-only properties
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Constructor for Property.
+ */
+ public Property() {
+ this(false);
+ }
+
+ /**
+ * Constructor for Property.
+ * @param userProperty if true this is a user property
+ * @since Ant 1.5
+ */
+ protected Property(boolean userProperty) {
+ this(userProperty, null);
+ }
+
+ /**
+ * Constructor for Property.
+ * @param userProperty if true this is a user property
+ * @param fallback a project to use to look for references if the reference is
+ * not in the current project
+ * @since Ant 1.5
+ */
+ protected Property(boolean userProperty, Project fallback) {
+ this.userProperty = userProperty;
+ this.fallback = fallback;
+ }
+
+ /**
+ * Sets 'relative' attribute.
+ * @param relative new value
+ * @since Ant 1.8.0
+ */
+ public void setRelative(boolean relative) {
+ this.relative = relative;
+ }
+
+ /**
+ * Sets 'basedir' attribute.
+ * @param basedir new value
+ * @since Ant 1.8.0
+ */
+ public void setBasedir(File basedir) {
+ this.basedir = basedir;
+ }
+
+ /**
+ * The name of the property to set.
+ * @param name property name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the property name.
+ * @return the property name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the property to the absolute filename of the
+ * given file. If the value of this attribute is an absolute path, it
+ * is left unchanged (with / and \ characters converted to the
+ * current platforms conventions). Otherwise it is taken as a path
+ * relative to the project's basedir and expanded.
+ * @param location path to set
+ *
+ * @ant.attribute group="name"
+ */
+ public void setLocation(File location) {
+ if (relative) {
+ internalSetValue(location);
+ } else {
+ setValue(location.getAbsolutePath());
+ }
+ }
+
+ /* the following method is first in source so IH will pick it up first:
+ * Hopefully we'll never get any classes compiled by wise-guy compilers that behave otherwise...
+ */
+
+ /**
+ * Set the value of the property.
+ * @param value the value to use.
+ */
+ public void setValue(Object value) {
+ valueAttributeUsed = true;
+ internalSetValue(value);
+ }
+
+ private void internalSetValue(Object value) {
+ this.untypedValue = value;
+ //preserve protected string value for subclasses :(
+ this.value = value == null ? null : value.toString();
+ }
+
+ /**
+ * Set the value of the property as a String.
+ * @param value value to assign
+ *
+ * @ant.attribute group="name"
+ */
+ public void setValue(String value) {
+ setValue((Object) value);
+ }
+
+ /**
+ * Set a (multiline) property as nested text.
+ * @param msg the text to append to the output text
+ * @since Ant 1.8.0
+ */
+ public void addText(String msg) {
+ if (!valueAttributeUsed) {
+ msg = getProject().replaceProperties(msg);
+ String currentValue = getValue();
+ if (currentValue != null) {
+ msg = currentValue + msg;
+ }
+ internalSetValue(msg);
+ } else if (msg.trim().length() > 0) {
+ throw new BuildException("can't combine nested text with value"
+ + " attribute");
+ }
+ }
+
+ /**
+ * Get the property value.
+ * @return the property value
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * Filename of a property file to load.
+ * @param file filename
+ *
+ * @ant.attribute group="noname"
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * Get the file attribute.
+ * @return the file attribute
+ */
+ public File getFile() {
+ return file;
+ }
+
+ /**
+ * The url from which to load properties.
+ * @param url url string
+ *
+ * @ant.attribute group="noname"
+ */
+ public void setUrl(URL url) {
+ this.url = url;
+ }
+
+ /**
+ * Get the url attribute.
+ * @return the url attribute
+ */
+ public URL getUrl() {
+ return url;
+ }
+
+ /**
+ * Prefix to apply to properties loaded using <code>file</code>
+ * or <code>resource</code>.
+ * A "." is appended to the prefix if not specified.
+ * @param prefix prefix string
+ * @since Ant 1.5
+ */
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ if (prefix != null && !prefix.endsWith(".")) {
+ this.prefix += ".";
+ }
+ }
+
+ /**
+ * Get the prefix attribute.
+ * @return the prefix attribute
+ * @since Ant 1.5
+ */
+ public String getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * Whether to apply the prefix when expanding properties on the
+ * right hand side of a properties file as well.
+ *
+ * @since Ant 1.8.2
+ */
+ public void setPrefixValues(boolean b) {
+ prefixValues = b;
+ }
+
+ /**
+ * Whether to apply the prefix when expanding properties on the
+ * right hand side of a properties file as well.
+ *
+ * @since Ant 1.8.2
+ */
+ public boolean getPrefixValues() {
+ return prefixValues;
+ }
+
+ /**
+ * Sets a reference to an Ant datatype
+ * declared elsewhere.
+ * Only yields reasonable results for references
+ * PATH like structures or properties.
+ * @param ref reference
+ *
+ * @ant.attribute group="name"
+ */
+ public void setRefid(Reference ref) {
+ this.ref = ref;
+ }
+
+ /**
+ * Get the refid attribute.
+ * @return the refid attribute
+ */
+ public Reference getRefid() {
+ return ref;
+ }
+
+ /**
+ * The resource name of a property file to load
+ * @param resource resource on classpath
+ *
+ * @ant.attribute group="noname"
+ */
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ /**
+ * Get the resource attribute.
+ * @return the resource attribute
+ */
+ public String getResource() {
+ return resource;
+ }
+
+ /**
+ * Prefix to use when retrieving environment variables.
+ * Thus if you specify environment=&quot;myenv&quot;
+ * you will be able to access OS-specific
+ * environment variables via property names &quot;myenv.PATH&quot; or
+ * &quot;myenv.TERM&quot;.
+ * <p>
+ * Note that if you supply a property name with a final
+ * &quot;.&quot; it will not be doubled. ie environment=&quot;myenv.&quot; will still
+ * allow access of environment variables through &quot;myenv.PATH&quot; and
+ * &quot;myenv.TERM&quot;. This functionality is currently only implemented
+ * on select platforms. Feel free to send patches to increase the number of platforms
+ * this functionality is supported on ;).<br>
+ * Note also that properties are case sensitive, even if the
+ * environment variables on your operating system are not, e.g. it
+ * will be ${env.Path} not ${env.PATH} on Windows 2000.
+ * @param env prefix
+ *
+ * @ant.attribute group="noname"
+ */
+ public void setEnvironment(String env) {
+ this.env = env;
+ }
+
+ /**
+ * Get the environment attribute.
+ * @return the environment attribute
+ * @since Ant 1.5
+ */
+ public String getEnvironment() {
+ return env;
+ }
+
+ /**
+ * The classpath to use when looking up a resource.
+ * @param classpath to add to any existing classpath
+ */
+ public void setClasspath(Path classpath) {
+ if (this.classpath == null) {
+ this.classpath = classpath;
+ } else {
+ this.classpath.append(classpath);
+ }
+ }
+
+ /**
+ * The classpath to use when looking up a resource.
+ * @return a path to be configured
+ */
+ public Path createClasspath() {
+ if (this.classpath == null) {
+ this.classpath = new Path(getProject());
+ }
+ return this.classpath.createPath();
+ }
+
+ /**
+ * the classpath to use when looking up a resource,
+ * given as reference to a &lt;path&gt; defined elsewhere
+ * @param r a reference to a classpath
+ */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * Get the classpath used when looking up a resource.
+ * @return the classpath
+ * @since Ant 1.5
+ */
+ public Path getClasspath() {
+ return classpath;
+ }
+
+ /**
+ * @param userProperty ignored
+ * @deprecated since 1.5.x.
+ * This was never a supported feature and has been
+ * deprecated without replacement.
+ * @ant.attribute ignore="true"
+ */
+ @Deprecated
+ public void setUserProperty(boolean userProperty) {
+ log("DEPRECATED: Ignoring request to set user property in Property"
+ + " task.", Project.MSG_WARN);
+ }
+
+ /**
+ * get the value of this property
+ * @return the current value or the empty string
+ */
+ @Override
+ public String toString() {
+ return value == null ? "" : value;
+ }
+
+ /**
+ * set the property in the project to the value.
+ * if the task was give a file, resource or env attribute
+ * here is where it is loaded
+ * @throws BuildException on error
+ */
+ @Override
+ public void execute() throws BuildException {
+ if (getProject() == null) {
+ throw new IllegalStateException("project has not been set");
+ }
+
+ if (name != null) {
+ if (untypedValue == null && ref == null) {
+ throw new BuildException("You must specify value, location or "
+ + "refid with the name attribute",
+ getLocation());
+ }
+ } else {
+ if (url == null && file == null && resource == null && env == null) {
+ throw new BuildException("You must specify url, file, resource or "
+ + "environment when not using the "
+ + "name attribute", getLocation());
+ }
+ }
+
+ if (url == null && file == null && resource == null && prefix != null) {
+ throw new BuildException("Prefix is only valid when loading from "
+ + "a url, file or resource", getLocation());
+ }
+
+ if (name != null && untypedValue != null) {
+ if (relative) {
+ try {
+ File from = untypedValue instanceof File ? (File)untypedValue : new File(untypedValue.toString());
+ File to = basedir != null ? basedir : getProject().getBaseDir();
+ String relPath = FileUtils.getRelativePath(to, from);
+ relPath = relPath.replace('/', File.separatorChar);
+ addProperty(name, relPath);
+ } catch (Exception e) {
+ throw new BuildException(e, getLocation());
+ }
+ } else {
+ addProperty(name, untypedValue);
+ }
+ }
+
+ if (file != null) {
+ loadFile(file);
+ }
+
+ if (url != null) {
+ loadUrl(url);
+ }
+
+ if (resource != null) {
+ loadResource(resource);
+ }
+
+ if (env != null) {
+ loadEnvironment(env);
+ }
+
+ if ((name != null) && (ref != null)) {
+ try {
+ addProperty(name,
+ ref.getReferencedObject(getProject()).toString());
+ } catch (BuildException be) {
+ if (fallback != null) {
+ addProperty(name,
+ ref.getReferencedObject(fallback).toString());
+ } else {
+ throw be;
+ }
+ }
+ }
+ }
+
+ /**
+ * load properties from a url
+ * @param url url to load from
+ * @throws BuildException on error
+ */
+ protected void loadUrl(URL url) throws BuildException {
+ Properties props = new Properties();
+ log("Loading " + url, Project.MSG_VERBOSE);
+ try {
+ InputStream is = url.openStream();
+ try {
+ loadProperties(props, is, url.getFile().endsWith(".xml"));
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ addProperties(props);
+ } catch (IOException ex) {
+ throw new BuildException(ex, getLocation());
+ }
+ }
+
+ /**
+ * Loads the properties defined in the InputStream into the given
+ * property. On Java5+ it supports reading from XML based property
+ * definition.
+ * @param props The property object to load into
+ * @param is The input stream from where to load
+ * @param isXml <tt>true</tt> if we should try to load from xml
+ * @throws IOException if something goes wrong
+ * @since 1.8.0
+ * @see "http://java.sun.com/dtd/properties.dtd"
+ * @see java.util.Properties#loadFromXML(InputStream)
+ */
+ private void loadProperties(
+ Properties props, InputStream is, boolean isXml) throws IOException {
+ if (isXml) {
+ // load the xml based property definition
+ props.loadFromXML(is);
+ } else {
+ // load ".properties" format
+ props.load(is);
+ }
+ }
+
+ /**
+ * load properties from a file
+ * @param file file to load
+ * @throws BuildException on error
+ */
+ protected void loadFile(File file) throws BuildException {
+ Properties props = new Properties();
+ log("Loading " + file.getAbsolutePath(), Project.MSG_VERBOSE);
+ try {
+ if (file.exists()) {
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(file);
+ loadProperties(props, fis, file.getName().endsWith(".xml"));
+ } finally {
+ FileUtils.close(fis);
+ }
+ addProperties(props);
+ } else {
+ log("Unable to find property file: " + file.getAbsolutePath(),
+ Project.MSG_VERBOSE);
+ }
+ } catch (IOException ex) {
+ throw new BuildException(ex, getLocation());
+ }
+ }
+
+ /**
+ * load properties from a resource in the current classpath
+ * @param name name of resource to load
+ */
+ protected void loadResource(String name) {
+ Properties props = new Properties();
+ log("Resource Loading " + name, Project.MSG_VERBOSE);
+ InputStream is = null;
+ ClassLoader cL = null;
+ boolean cleanup = false;
+ try {
+ if (classpath != null) {
+ cleanup = true;
+ cL = getProject().createClassLoader(classpath);
+ } else {
+ cL = this.getClass().getClassLoader();
+ }
+
+ if (cL == null) {
+ is = ClassLoader.getSystemResourceAsStream(name);
+ } else {
+ is = cL.getResourceAsStream(name);
+ }
+
+ if (is != null) {
+ loadProperties(props, is, name.endsWith(".xml"));
+ addProperties(props);
+ } else {
+ log("Unable to find resource " + name, Project.MSG_WARN);
+ }
+ } catch (IOException ex) {
+ throw new BuildException(ex, getLocation());
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ if (cleanup && cL != null) {
+ ((AntClassLoader) cL).cleanup();
+ }
+ }
+ }
+
+ /**
+ * load the environment values
+ * @param prefix prefix to place before them
+ */
+ protected void loadEnvironment(String prefix) {
+ Properties props = new Properties();
+ if (!prefix.endsWith(".")) {
+ prefix += ".";
+ }
+ log("Loading Environment " + prefix, Project.MSG_VERBOSE);
+ Map osEnv = Execute.getEnvironmentVariables();
+ for (Iterator e = osEnv.entrySet().iterator(); e.hasNext();) {
+ Map.Entry entry = (Map.Entry) e.next();
+ props.put(prefix + entry.getKey(), entry.getValue());
+ }
+ addProperties(props);
+ }
+
+ /**
+ * iterate through a set of properties,
+ * resolve them then assign them
+ * @param props the properties to iterate over
+ */
+ protected void addProperties(Properties props) {
+ HashMap m = new HashMap(props);
+ resolveAllProperties(m);
+ for (Iterator it = m.keySet().iterator(); it.hasNext();) {
+ Object k = it.next();
+ if (k instanceof String) {
+ String propertyName = (String) k;
+ if (prefix != null) {
+ propertyName = prefix + propertyName;
+ }
+ addProperty(propertyName, m.get(k));
+ }
+ }
+ }
+
+ /**
+ * add a name value pair to the project property set
+ * @param n name of property
+ * @param v value to set
+ */
+ protected void addProperty(String n, String v) {
+ addProperty(n, (Object) v);
+ }
+
+ /**
+ * add a name value pair to the project property set
+ * @param n name of property
+ * @param v value to set
+ * @since Ant 1.8
+ */
+ protected void addProperty(String n, Object v) {
+ PropertyHelper ph = PropertyHelper.getPropertyHelper(getProject());
+ if (userProperty) {
+ if (ph.getUserProperty(n) == null) {
+ ph.setInheritedProperty(n, v);
+ } else {
+ log("Override ignored for " + n, Project.MSG_VERBOSE);
+ }
+ } else {
+ ph.setNewProperty(n, v);
+ }
+ }
+
+ /**
+ * resolve properties inside a properties hashtable
+ * @param props properties object to resolve
+ */
+ private void resolveAllProperties(Map props) throws BuildException {
+ PropertyHelper propertyHelper
+ = PropertyHelper.getPropertyHelper(getProject());
+ new ResolvePropertyMap(
+ getProject(),
+ propertyHelper,
+ propertyHelper.getExpanders())
+ .resolveAllProperties(props, getPrefix(), getPrefixValues());
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java
new file mode 100644
index 00000000..5e8867a7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java
@@ -0,0 +1,145 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.Task;
+
+/**
+ * This task is designed to allow the user to install a different
+ * PropertyHelper on the current Project. This task also allows the
+ * installation of PropertyHelper delegates on either the newly installed
+ * or existing PropertyHelper.
+ * @since Ant 1.8
+ */
+public class PropertyHelperTask extends Task {
+ /**
+ * Nested delegate for refid usage.
+ */
+ public final class DelegateElement {
+ private String refid;
+
+ private DelegateElement() {
+ }
+
+ /**
+ * Get the refid.
+ * @return String
+ */
+ public String getRefid() {
+ return refid;
+ }
+
+ /**
+ * Set the refid.
+ * @param refid the String to set
+ */
+ public void setRefid(String refid) {
+ this.refid = refid;
+ }
+
+ private PropertyHelper.Delegate resolve() {
+ if (refid == null) {
+ throw new BuildException("refid required for generic delegate");
+ }
+ return (PropertyHelper.Delegate) getProject().getReference(refid);
+ }
+ }
+
+ private PropertyHelper propertyHelper;
+ private List delegates;
+
+ /**
+ * Add a new PropertyHelper to be set on the Project.
+ * @param propertyHelper the PropertyHelper to set.
+ */
+ public synchronized void addConfigured(PropertyHelper propertyHelper) {
+ if (this.propertyHelper != null) {
+ throw new BuildException("Only one PropertyHelper can be installed");
+ }
+ this.propertyHelper = propertyHelper;
+ }
+
+ /**
+ * Add a PropertyHelper delegate to the existing or new PropertyHelper.
+ * @param delegate the delegate to add.
+ */
+ public synchronized void addConfigured(PropertyHelper.Delegate delegate) {
+ getAddDelegateList().add(delegate);
+ }
+
+ /**
+ * Add a nested &lt;delegate refid="foo" /&gt; element.
+ * @return DelegateElement
+ */
+ public DelegateElement createDelegate() {
+ DelegateElement result = new DelegateElement();
+ getAddDelegateList().add(result);
+ return result;
+ }
+
+ /**
+ * Execute the task.
+ * @throws BuildException on error.
+ */
+ public void execute() throws BuildException {
+ if (getProject() == null) {
+ throw new BuildException("Project instance not set");
+ }
+ if (propertyHelper == null && delegates == null) {
+ throw new BuildException("Either a new PropertyHelper"
+ + " or one or more PropertyHelper delegates are required");
+ }
+ PropertyHelper ph = propertyHelper;
+ if (ph == null) {
+ ph = PropertyHelper.getPropertyHelper(getProject());
+ } else {
+ ph = propertyHelper;
+ }
+ synchronized (ph) {
+ if (delegates != null) {
+ for (Iterator iter = delegates.iterator(); iter.hasNext();) {
+ Object o = iter.next();
+ PropertyHelper.Delegate delegate = o instanceof DelegateElement
+ ? ((DelegateElement) o).resolve() : (PropertyHelper.Delegate) o;
+ log("Adding PropertyHelper delegate " + delegate, Project.MSG_DEBUG);
+ ph.add(delegate);
+ }
+ }
+ }
+ if (propertyHelper != null) {
+ log("Installing PropertyHelper " + propertyHelper, Project.MSG_DEBUG);
+ // TODO copy existing properties to new PH?
+ getProject().addReference(MagicNames.REFID_PROPERTY_HELPER, propertyHelper);
+ }
+ }
+
+ private synchronized List getAddDelegateList() {
+ if (delegates == null) {
+ delegates = new ArrayList();
+ }
+ return delegates;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PumpStreamHandler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PumpStreamHandler.java
new file mode 100644
index 00000000..42ba0f48
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/PumpStreamHandler.java
@@ -0,0 +1,298 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Copies standard output and error of subprocesses to standard output and
+ * error of the parent process.
+ *
+ * @since Ant 1.2
+ */
+public class PumpStreamHandler implements ExecuteStreamHandler {
+
+ private Thread outputThread;
+ private Thread errorThread;
+ private Thread inputThread;
+
+ private OutputStream out;
+ private OutputStream err;
+ private InputStream input;
+ private final boolean nonBlockingRead;
+
+ /**
+ * Construct a new <code>PumpStreamHandler</code>.
+ * @param out the output <code>OutputStream</code>.
+ * @param err the error <code>OutputStream</code>.
+ * @param input the input <code>InputStream</code>.
+ * @param nonBlockingRead set it to <code>true</code> if the input should be
+ * read with simulated non blocking IO.
+ */
+ public PumpStreamHandler(OutputStream out, OutputStream err,
+ InputStream input, boolean nonBlockingRead) {
+ this.out = out;
+ this.err = err;
+ this.input = input;
+ this.nonBlockingRead = nonBlockingRead;
+ }
+
+ /**
+ * Construct a new <code>PumpStreamHandler</code>.
+ * @param out the output <code>OutputStream</code>.
+ * @param err the error <code>OutputStream</code>.
+ * @param input the input <code>InputStream</code>.
+ */
+ public PumpStreamHandler(OutputStream out, OutputStream err,
+ InputStream input) {
+ this(out, err, input, false);
+ }
+
+ /**
+ * Construct a new <code>PumpStreamHandler</code>.
+ * @param out the output <code>OutputStream</code>.
+ * @param err the error <code>OutputStream</code>.
+ */
+ public PumpStreamHandler(OutputStream out, OutputStream err) {
+ this(out, err, null);
+ }
+
+ /**
+ * Construct a new <code>PumpStreamHandler</code>.
+ * @param outAndErr the output/error <code>OutputStream</code>.
+ */
+ public PumpStreamHandler(OutputStream outAndErr) {
+ this(outAndErr, outAndErr);
+ }
+
+ /**
+ * Construct a new <code>PumpStreamHandler</code>.
+ */
+ public PumpStreamHandler() {
+ this(System.out, System.err);
+ }
+
+ /**
+ * Set the <code>InputStream</code> from which to read the
+ * standard output of the process.
+ * @param is the <code>InputStream</code>.
+ */
+ public void setProcessOutputStream(InputStream is) {
+ createProcessOutputPump(is, out);
+ }
+
+ /**
+ * Set the <code>InputStream</code> from which to read the
+ * standard error of the process.
+ * @param is the <code>InputStream</code>.
+ */
+ public void setProcessErrorStream(InputStream is) {
+ if (err != null) {
+ createProcessErrorPump(is, err);
+ }
+ }
+
+ /**
+ * Set the <code>OutputStream</code> by means of which
+ * input can be sent to the process.
+ * @param os the <code>OutputStream</code>.
+ */
+ public void setProcessInputStream(OutputStream os) {
+ if (input != null) {
+ inputThread = createPump(input, os, true, nonBlockingRead);
+ } else {
+ try {
+ os.close();
+ } catch (IOException e) {
+ //ignore
+ }
+ }
+ }
+
+ /**
+ * Start the <code>Thread</code>s.
+ */
+ public void start() {
+ outputThread.start();
+ errorThread.start();
+ if (inputThread != null) {
+ inputThread.start();
+ }
+ }
+
+ /**
+ * Stop pumping the streams.
+ */
+ public void stop() {
+ finish(inputThread);
+
+ try {
+ err.flush();
+ } catch (IOException e) {
+ // ignore
+ }
+ try {
+ out.flush();
+ } catch (IOException e) {
+ // ignore
+ }
+ finish(outputThread);
+ finish(errorThread);
+ }
+
+ private static final long JOIN_TIMEOUT = 200;
+
+ /**
+ * Waits for a thread to finish while trying to make it finish
+ * quicker by stopping the pumper (if the thread is a {@link
+ * ThreadWithPumper ThreadWithPumper} instance) or interrupting
+ * the thread.
+ *
+ * @since Ant 1.8.0
+ */
+ protected final void finish(Thread t) {
+ if (t == null) {
+ // nothing to terminate
+ return;
+ }
+ try {
+ StreamPumper s = null;
+ if (t instanceof ThreadWithPumper) {
+ s = ((ThreadWithPumper) t).getPumper();
+ }
+ if (s != null && s.isFinished()) {
+ return;
+ }
+ if (!t.isAlive()) {
+ return;
+ }
+
+ if (s != null && !s.isFinished()) {
+ s.stop();
+ }
+ t.join(JOIN_TIMEOUT);
+ while ((s == null || !s.isFinished()) && t.isAlive()) {
+ t.interrupt();
+ t.join(JOIN_TIMEOUT);
+ }
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
+ /**
+ * Get the error stream.
+ * @return <code>OutputStream</code>.
+ */
+ protected OutputStream getErr() {
+ return err;
+ }
+
+ /**
+ * Get the output stream.
+ * @return <code>OutputStream</code>.
+ */
+ protected OutputStream getOut() {
+ return out;
+ }
+
+ /**
+ * Create the pump to handle process output.
+ * @param is the <code>InputStream</code>.
+ * @param os the <code>OutputStream</code>.
+ */
+ protected void createProcessOutputPump(InputStream is, OutputStream os) {
+ outputThread = createPump(is, os);
+ }
+
+ /**
+ * Create the pump to handle error output.
+ * @param is the input stream to copy from.
+ * @param os the output stream to copy to.
+ */
+ protected void createProcessErrorPump(InputStream is, OutputStream os) {
+ errorThread = createPump(is, os);
+ }
+
+ /**
+ * Creates a stream pumper to copy the given input stream to the
+ * given output stream.
+ * @param is the input stream to copy from.
+ * @param os the output stream to copy to.
+ * @return a thread object that does the pumping.
+ */
+ protected Thread createPump(InputStream is, OutputStream os) {
+ return createPump(is, os, false);
+ }
+
+ /**
+ * Creates a stream pumper to copy the given input stream to the
+ * given output stream.
+ * @param is the input stream to copy from.
+ * @param os the output stream to copy to.
+ * @param closeWhenExhausted if true close the inputstream.
+ * @return a thread object that does the pumping, subclasses
+ * should return an instance of {@link ThreadWithPumper
+ * ThreadWithPumper}.
+ */
+ protected Thread createPump(InputStream is, OutputStream os,
+ boolean closeWhenExhausted) {
+ return createPump(is, os, closeWhenExhausted, true);
+ }
+
+ /**
+ * Creates a stream pumper to copy the given input stream to the
+ * given output stream.
+ * @param is the input stream to copy from.
+ * @param os the output stream to copy to.
+ * @param closeWhenExhausted if true close the inputstream.
+ * @param nonBlockingIO set it to <code>true</code> to use simulated non
+ * blocking IO.
+ * @return a thread object that does the pumping, subclasses
+ * should return an instance of {@link ThreadWithPumper
+ * ThreadWithPumper}.
+ * @since Ant 1.8.2
+ */
+ protected Thread createPump(InputStream is, OutputStream os,
+ boolean closeWhenExhausted, boolean nonBlockingIO) {
+ StreamPumper pumper = new StreamPumper(is, os, closeWhenExhausted, nonBlockingIO);
+ pumper.setAutoflush(true);
+ final Thread result = new ThreadWithPumper(pumper);
+ result.setDaemon(true);
+ return result;
+ }
+
+ /**
+ * Specialized subclass that allows access to the running StreamPumper.
+ *
+ * @since Ant 1.8.0
+ */
+ protected static class ThreadWithPumper extends Thread {
+ private final StreamPumper pumper;
+ public ThreadWithPumper(StreamPumper p) {
+ super(p);
+ pumper = p;
+ }
+ protected StreamPumper getPumper() {
+ return pumper;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Recorder.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Recorder.java
new file mode 100644
index 00000000..cc3b432a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Recorder.java
@@ -0,0 +1,324 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.SubBuildListener;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.LogLevel;
+
+/**
+ * Adds a listener to the current build process that records the
+ * output to a file.
+ * <p>Several recorders can exist at the same time. Each recorder is
+ * associated with a file. The filename is used as a unique identifier for
+ * the recorders. The first call to the recorder task with an unused filename
+ * will create a recorder (using the parameters provided) and add it to the
+ * listeners of the build. All subsequent calls to the recorder task using
+ * this filename will modify that recorders state (recording or not) or other
+ * properties (like logging level).</p>
+ * <p>Some technical issues: the file's print stream is flushed for &quot;finished&quot;
+ * events (buildFinished, targetFinished and taskFinished), and is closed on
+ * a buildFinished event.</p>
+ * @see RecorderEntry
+ * @version 0.5
+ * @since Ant 1.4
+ * @ant.task name="record" category="utility"
+ */
+public class Recorder extends Task implements SubBuildListener {
+
+ //////////////////////////////////////////////////////////////////////
+ // ATTRIBUTES
+
+ /** The name of the file to record to. */
+ private String filename = null;
+ /**
+ * Whether or not to append. Need Boolean to record an unset state (null).
+ */
+ private Boolean append = null;
+ /**
+ * Whether to start or stop recording. Need Boolean to record an unset
+ * state (null).
+ */
+ private Boolean start = null;
+ /** The level to log at. A level of -1 means not initialized yet. */
+ private int loglevel = -1;
+ /** Strip task banners if true. */
+ private boolean emacsMode = false;
+ /** The list of recorder entries. */
+ private static Hashtable recorderEntries = new Hashtable();
+
+ //////////////////////////////////////////////////////////////////////
+ // CONSTRUCTORS / INITIALIZERS
+
+ /**
+ * Overridden so we can add the task as build listener.
+ *
+ * @since Ant 1.7
+ */
+ public void init() {
+ getProject().addBuildListener(this);
+ }
+
+ //////////////////////////////////////////////////////////////////////
+ // ACCESSOR METHODS
+
+ /**
+ * Sets the name of the file to log to, and the name of the recorder
+ * entry.
+ *
+ * @param fname File name of logfile.
+ */
+ public void setName(String fname) {
+ filename = fname;
+ }
+
+
+ /**
+ * Sets the action for the associated recorder entry.
+ *
+ * @param action The action for the entry to take: start or stop.
+ */
+ public void setAction(ActionChoices action) {
+ if (action.getValue().equalsIgnoreCase("start")) {
+ start = Boolean.TRUE;
+ } else {
+ start = Boolean.FALSE;
+ }
+ }
+
+
+ /**
+ * Whether or not the logger should append to a previous file.
+ * @param append if true, append to a previous file.
+ */
+ public void setAppend(boolean append) {
+ this.append = (append ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+
+ /**
+ * Set emacs mode.
+ * @param emacsMode if true use emacs mode
+ */
+ public void setEmacsMode(boolean emacsMode) {
+ this.emacsMode = emacsMode;
+ }
+
+
+ /**
+ * Sets the level to which this recorder entry should log to.
+ * @param level the level to set.
+ * @see VerbosityLevelChoices
+ */
+ public void setLoglevel(VerbosityLevelChoices level) {
+ loglevel = level.getLevel();
+ }
+
+ //////////////////////////////////////////////////////////////////////
+ // CORE / MAIN BODY
+
+ /**
+ * The main execution.
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ if (filename == null) {
+ throw new BuildException("No filename specified");
+ }
+
+ getProject().log("setting a recorder for name " + filename,
+ Project.MSG_DEBUG);
+
+ // get the recorder entry
+ RecorderEntry recorder = getRecorder(filename, getProject());
+ // set the values on the recorder
+ recorder.setMessageOutputLevel(loglevel);
+ recorder.setEmacsMode(emacsMode);
+ if (start != null) {
+ if (start.booleanValue()) {
+ recorder.reopenFile();
+ recorder.setRecordState(start);
+ } else {
+ recorder.setRecordState(start);
+ recorder.closeFile();
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////
+ // INNER CLASSES
+
+ /**
+ * A list of possible values for the <code>setAction()</code> method.
+ * Possible values include: start and stop.
+ */
+ public static class ActionChoices extends EnumeratedAttribute {
+ private static final String[] VALUES = {"start", "stop"};
+
+ /**
+ * @see EnumeratedAttribute#getValues()
+ */
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return VALUES;
+ }
+ }
+
+
+ /**
+ * A list of possible values for the <code>setLoglevel()</code> method.
+ * Possible values include: error, warn, info, verbose, debug.
+ */
+ public static class VerbosityLevelChoices extends LogLevel {
+ }
+
+
+ /**
+ * Gets the recorder that's associated with the passed in name. If the
+ * recorder doesn't exist, then a new one is created.
+ * @param name the name of the recorder
+ * @param proj the current project
+ * @return a recorder
+ * @throws BuildException on error
+ */
+ protected RecorderEntry getRecorder(String name, Project proj)
+ throws BuildException {
+ Object o = recorderEntries.get(name);
+ RecorderEntry entry;
+
+ if (o == null) {
+ // create a recorder entry
+ entry = new RecorderEntry(name);
+
+ if (append == null) {
+ entry.openFile(false);
+ } else {
+ entry.openFile(append.booleanValue());
+ }
+ entry.setProject(proj);
+ recorderEntries.put(name, entry);
+ } else {
+ entry = (RecorderEntry) o;
+ }
+ return entry;
+ }
+
+ /**
+ * Empty implementation required by SubBuildListener interface.
+ * @param event ignored.
+ * @since Ant 1.7
+ */
+ public void buildStarted(BuildEvent event) {
+ }
+
+ /**
+ * Empty implementation required by SubBuildListener interface.
+ * @param event ignored.
+ * @since Ant 1.7
+ */
+ public void subBuildStarted(BuildEvent event) {
+ }
+
+ /**
+ * Empty implementation required by SubBuildListener interface.
+ * @param event ignored.
+ * @since Ant 1.7
+ */
+ public void targetStarted(BuildEvent event) {
+ }
+
+ /**
+ * Empty implementation required by SubBuildListener interface.
+ * @param event ignored.
+ * @since Ant 1.7
+ */
+ public void targetFinished(BuildEvent event) {
+ }
+
+ /**
+ * Empty implementation required by SubBuildListener interface.
+ * @param event ignored.
+ * @since Ant 1.7
+ */
+ public void taskStarted(BuildEvent event) {
+ }
+
+ /**
+ * Empty implementation required by SubBuildListener interface.
+ * @param event ignored.
+ * @since Ant 1.7
+ */
+ public void taskFinished(BuildEvent event) {
+ }
+
+ /**
+ * Empty implementation required by SubBuildListener interface.
+ * @param event ignored.
+ * @since Ant 1.7
+ */
+ public void messageLogged(BuildEvent event) {
+ }
+
+ /**
+ * Cleans recorder registry.
+ * @param event ignored.
+ * @since Ant 1.7
+ */
+ public void buildFinished(BuildEvent event) {
+ cleanup();
+ }
+
+ /**
+ * Cleans recorder registry, if this is the subbuild the task has
+ * been created in.
+ * @param event ignored.
+ * @since Ant 1.7
+ */
+ public void subBuildFinished(BuildEvent event) {
+ if (event.getProject() == getProject()) {
+ cleanup();
+ }
+ }
+
+ /**
+ * cleans recorder registry and removes itself from BuildListener list.
+ *
+ * @since Ant 1.7
+ */
+ private void cleanup() {
+ Hashtable entries = (Hashtable) recorderEntries.clone();
+ Iterator itEntries = entries.entrySet().iterator();
+ while (itEntries.hasNext()) {
+ Map.Entry entry = (Map.Entry) itEntries.next();
+ RecorderEntry re = (RecorderEntry) entry.getValue();
+ if (re.getProject() == getProject()) {
+ recorderEntries.remove(entry.getKey());
+ }
+ }
+ getProject().removeBuildListener(this);
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/RecorderEntry.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/RecorderEntry.java
new file mode 100644
index 00000000..dfb8e845
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/RecorderEntry.java
@@ -0,0 +1,367 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildLogger;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.SubBuildListener;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * This is a class that represents a recorder. This is the listener to the
+ * build process.
+ *
+ * @since Ant 1.4
+ */
+public class RecorderEntry implements BuildLogger, SubBuildListener {
+
+ //////////////////////////////////////////////////////////////////////
+ // ATTRIBUTES
+
+ /** The name of the file associated with this recorder entry. */
+ private String filename = null;
+ /** The state of the recorder (recorder on or off). */
+ private boolean record = true;
+ /** The current verbosity level to record at. */
+ private int loglevel = Project.MSG_INFO;
+ /** The output PrintStream to record to. */
+ private PrintStream out = null;
+ /** The start time of the last know target. */
+ private long targetStartTime = 0L;
+ /** Strip task banners if true. */
+ private boolean emacsMode = false;
+ /** project instance the recorder is associated with */
+ private Project project;
+
+ //////////////////////////////////////////////////////////////////////
+ // CONSTRUCTORS / INITIALIZERS
+
+ /**
+ * @param name The name of this recorder (used as the filename).
+ */
+ protected RecorderEntry(String name) {
+ targetStartTime = System.currentTimeMillis();
+ filename = name;
+ }
+
+ //////////////////////////////////////////////////////////////////////
+ // ACCESSOR METHODS
+
+ /**
+ * @return the name of the file the output is sent to.
+ */
+ public String getFilename() {
+ return filename;
+ }
+
+ /**
+ * Turns off or on this recorder.
+ *
+ * @param state true for on, false for off, null for no change.
+ */
+ public void setRecordState(Boolean state) {
+ if (state != null) {
+ flush();
+ record = state.booleanValue();
+ }
+ }
+
+ /**
+ * @see org.apache.tools.ant.BuildListener#buildStarted(BuildEvent)
+ */
+ /** {@inheritDoc}. */
+ public void buildStarted(BuildEvent event) {
+ log("> BUILD STARTED", Project.MSG_DEBUG);
+ }
+
+ /**
+ * @see org.apache.tools.ant.BuildListener#buildFinished(BuildEvent)
+ */
+ /** {@inheritDoc}. */
+ public void buildFinished(BuildEvent event) {
+ log("< BUILD FINISHED", Project.MSG_DEBUG);
+
+ if (record && out != null) {
+ Throwable error = event.getException();
+
+ if (error == null) {
+ out.println(StringUtils.LINE_SEP + "BUILD SUCCESSFUL");
+ } else {
+ out.println(StringUtils.LINE_SEP + "BUILD FAILED"
+ + StringUtils.LINE_SEP);
+ error.printStackTrace(out);
+ }
+ }
+ cleanup();
+ }
+
+ /**
+ * Cleans up any resources held by this recorder entry at the end
+ * of a subbuild if it has been created for the subbuild's project
+ * instance.
+ *
+ * @param event the buildFinished event
+ *
+ * @since Ant 1.6.2
+ */
+ public void subBuildFinished(BuildEvent event) {
+ if (event.getProject() == project) {
+ cleanup();
+ }
+ }
+
+ /**
+ * Empty implementation to satisfy the BuildListener interface.
+ *
+ * @param event the buildStarted event
+ *
+ * @since Ant 1.6.2
+ */
+ public void subBuildStarted(BuildEvent event) {
+ }
+
+ /**
+ * @see org.apache.tools.ant.BuildListener#targetStarted(BuildEvent)
+ */
+ /** {@inheritDoc}. */
+ public void targetStarted(BuildEvent event) {
+ log(">> TARGET STARTED -- " + event.getTarget(), Project.MSG_DEBUG);
+ log(StringUtils.LINE_SEP + event.getTarget().getName() + ":",
+ Project.MSG_INFO);
+ targetStartTime = System.currentTimeMillis();
+ }
+
+ /**
+ * @see org.apache.tools.ant.BuildListener#targetFinished(BuildEvent)
+ */
+ /** {@inheritDoc}. */
+ public void targetFinished(BuildEvent event) {
+ log("<< TARGET FINISHED -- " + event.getTarget(), Project.MSG_DEBUG);
+
+ String time = formatTime(System.currentTimeMillis() - targetStartTime);
+
+ log(event.getTarget() + ": duration " + time, Project.MSG_VERBOSE);
+ flush();
+ }
+
+ /**
+ * @see org.apache.tools.ant.BuildListener#taskStarted(BuildEvent)
+ */
+ /** {@inheritDoc}. */
+ public void taskStarted(BuildEvent event) {
+ log(">>> TASK STARTED -- " + event.getTask(), Project.MSG_DEBUG);
+ }
+
+ /**
+ * @see org.apache.tools.ant.BuildListener#taskFinished(BuildEvent)
+ */
+ /** {@inheritDoc}. */
+ public void taskFinished(BuildEvent event) {
+ log("<<< TASK FINISHED -- " + event.getTask(), Project.MSG_DEBUG);
+ flush();
+ }
+
+ /**
+ * @see org.apache.tools.ant.BuildListener#messageLogged(BuildEvent)
+ */
+ /** {@inheritDoc}. */
+ public void messageLogged(BuildEvent event) {
+ log("--- MESSAGE LOGGED", Project.MSG_DEBUG);
+
+ StringBuffer buf = new StringBuffer();
+
+ if (event.getTask() != null) {
+ String name = event.getTask().getTaskName();
+
+ if (!emacsMode) {
+ String label = "[" + name + "] ";
+ int size = DefaultLogger.LEFT_COLUMN_SIZE - label.length();
+
+ for (int i = 0; i < size; i++) {
+ buf.append(" ");
+ }
+ buf.append(label);
+ }
+ }
+ buf.append(event.getMessage());
+
+ log(buf.toString(), event.getPriority());
+ }
+
+
+ /**
+ * The thing that actually sends the information to the output.
+ *
+ * @param mesg The message to log.
+ * @param level The verbosity level of the message.
+ */
+ private void log(String mesg, int level) {
+ if (record && (level <= loglevel) && out != null) {
+ out.println(mesg);
+ }
+ }
+
+ private void flush() {
+ if (record && out != null) {
+ out.flush();
+ }
+ }
+
+ /**
+ * @see BuildLogger#setMessageOutputLevel(int)
+ */
+ /** {@inheritDoc}. */
+ public void setMessageOutputLevel(int level) {
+ if (level >= Project.MSG_ERR && level <= Project.MSG_DEBUG) {
+ loglevel = level;
+ }
+ }
+
+ /**
+ * @see BuildLogger#setOutputPrintStream(PrintStream)
+ */
+ /** {@inheritDoc}. */
+ public void setOutputPrintStream(PrintStream output) {
+ closeFile();
+ out = output;
+ }
+
+
+ /**
+ * @see BuildLogger#setEmacsMode(boolean)
+ */
+ /** {@inheritDoc}. */
+ public void setEmacsMode(boolean emacsMode) {
+ this.emacsMode = emacsMode;
+ }
+
+
+ /**
+ * @see BuildLogger#setErrorPrintStream(PrintStream)
+ */
+ /** {@inheritDoc}. */
+ public void setErrorPrintStream(PrintStream err) {
+ setOutputPrintStream(err);
+ }
+
+
+ private static String formatTime(long millis) {
+ // CheckStyle:MagicNumber OFF
+ long seconds = millis / 1000;
+ long minutes = seconds / 60;
+
+
+ if (minutes > 0) {
+ return Long.toString(minutes) + " minute"
+ + (minutes == 1 ? " " : "s ")
+ + Long.toString(seconds % 60) + " second"
+ + (seconds % 60 == 1 ? "" : "s");
+ } else {
+ return Long.toString(seconds) + " second"
+ + (seconds % 60 == 1 ? "" : "s");
+ }
+ // CheckStyle:MagicNumber ON
+ }
+
+ /**
+ * Set the project associated with this recorder entry.
+ *
+ * @param project the project instance
+ *
+ * @since 1.6.2
+ */
+ public void setProject(Project project) {
+ this.project = project;
+ if (project != null) {
+ project.addBuildListener(this);
+ }
+ }
+
+ /**
+ * Get the project associated with this recorder entry.
+ *
+ * @since 1.8.0
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * @since 1.6.2
+ */
+ public void cleanup() {
+ closeFile();
+ if (project != null) {
+ project.removeBuildListener(this);
+ }
+ project = null;
+ }
+
+ /**
+ * Initially opens the file associated with this recorder.
+ * Used by Recorder.
+ * @param append Indicates if output must be appended to the logfile or that
+ * the logfile should be overwritten.
+ * @throws BuildException
+ * @since 1.6.3
+ */
+ void openFile(boolean append) throws BuildException {
+ openFileImpl(append);
+ }
+
+ /**
+ * Closes the file associated with this recorder.
+ * Used by Recorder.
+ * @since 1.6.3
+ */
+ void closeFile() {
+ if (out != null) {
+ out.close();
+ out = null;
+ }
+ }
+
+ /**
+ * Re-opens the file associated with this recorder.
+ * Used by Recorder.
+ * @throws BuildException
+ * @since 1.6.3
+ */
+ void reopenFile() throws BuildException {
+ openFileImpl(true);
+ }
+
+ private void openFileImpl(boolean append) throws BuildException {
+ if (out == null) {
+ try {
+ out = new PrintStream(new FileOutputStream(filename, append));
+ } catch (IOException ioe) {
+ throw new BuildException("Problems opening file using a "
+ + "recorder entry", ioe);
+ }
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Redirector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Redirector.java
new file mode 100644
index 00000000..3b35d231
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Redirector.java
@@ -0,0 +1,1022 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PipedOutputStream;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.util.ConcatFileInputStream;
+import org.apache.tools.ant.util.KeepAliveOutputStream;
+import org.apache.tools.ant.util.LazyFileOutputStream;
+import org.apache.tools.ant.util.LeadPipeInputStream;
+import org.apache.tools.ant.util.LineOrientedOutputStreamRedirector;
+import org.apache.tools.ant.util.OutputStreamFunneler;
+import org.apache.tools.ant.util.ReaderInputStream;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.TeeOutputStream;
+
+/**
+ * The Redirector class manages the setup and connection of input and output
+ * redirection for an Ant project component.
+ *
+ * @since Ant 1.6
+ */
+public class Redirector {
+ private static final int STREAMPUMPER_WAIT_INTERVAL = 1000;
+
+ private static final String DEFAULT_ENCODING = System
+ .getProperty("file.encoding");
+
+ private class PropertyOutputStream extends ByteArrayOutputStream {
+ private final String property;
+
+ private boolean closed = false;
+
+ PropertyOutputStream(final String property) {
+ super();
+ this.property = property;
+ }
+
+ @Override
+ public void close() throws IOException {
+ synchronized (outMutex) {
+ if (!closed && !(appendOut && appendProperties)) {
+ setPropertyFromBAOS(this, property);
+ closed = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * The file(s) from which standard input is being taken. If &gt; 1, files'
+ * content will be concatenated in the order received.
+ */
+ private File[] input;
+
+ /**
+ * The file(s) receiving standard output. Will also receive standard error
+ * unless standard error is redirected or logError is true.
+ */
+ private File[] out;
+
+ /**
+ * The file(s) to which standard error is being redirected
+ */
+ private File[] error;
+
+ /**
+ * Indicates if standard error should be logged to Ant's log system rather
+ * than the output. This has no effect if standard error is redirected to a
+ * file or property.
+ */
+ private boolean logError = false;
+
+ /**
+ * Buffer used to capture output for storage into a property
+ */
+ private PropertyOutputStream baos = null;
+
+ /**
+ * Buffer used to capture error output for storage into a property
+ */
+ private PropertyOutputStream errorBaos = null;
+
+ /** The name of the property into which output is to be stored */
+ private String outputProperty;
+
+ /** The name of the property into which error output is to be stored */
+ private String errorProperty;
+
+ /** String from which input is taken */
+ private String inputString;
+
+ /** Flag which indicates if error and output files are to be appended. */
+ private boolean appendOut = false;
+
+ private boolean appendErr = false;
+
+ /** Flag which indicates that output should be always sent to the log */
+ private boolean alwaysLogOut = false;
+
+ private boolean alwaysLogErr = false;
+
+ /** Flag which indicates whether files should be created even when empty. */
+ private boolean createEmptyFilesOut = true;
+
+ private boolean createEmptyFilesErr = true;
+
+ /** The task for which this redirector is working */
+ private final ProjectComponent managingTask;
+
+ /** The stream for output data */
+ private OutputStream outputStream = null;
+
+ /** The stream for error output */
+ private OutputStream errorStream = null;
+
+ /** The stream for input */
+ private InputStream inputStream = null;
+
+ /** Stream which is used for line oriented output */
+ private PrintStream outPrintStream = null;
+
+ /** Stream which is used for line oriented error output */
+ private PrintStream errorPrintStream = null;
+
+ /** The output filter chains */
+ private Vector<FilterChain> outputFilterChains;
+
+ /** The error filter chains */
+ private Vector<FilterChain> errorFilterChains;
+
+ /** The input filter chains */
+ private Vector<FilterChain> inputFilterChains;
+
+ /** The output encoding */
+ private String outputEncoding = DEFAULT_ENCODING;
+
+ /** The error encoding */
+ private String errorEncoding = DEFAULT_ENCODING;
+
+ /** The input encoding */
+ private String inputEncoding = DEFAULT_ENCODING;
+
+ /** Whether to complete properties settings **/
+ private boolean appendProperties = true;
+
+ /** The thread group used for starting <code>StreamPumper</code> threads */
+ private final ThreadGroup threadGroup = new ThreadGroup("redirector");
+
+ /** whether to log the inputstring */
+ private boolean logInputString = true;
+
+ /** Mutex for in */
+ private final Object inMutex = new Object();
+
+ /** Mutex for out */
+ private final Object outMutex = new Object();
+
+ /** Mutex for err */
+ private final Object errMutex = new Object();
+
+ /** Is the output binary or can we safely split it into lines? */
+ private boolean outputIsBinary = false;
+
+ /**
+ * Create a redirector instance for the given task
+ *
+ * @param managingTask
+ * the task for which the redirector is to work
+ */
+ public Redirector(final Task managingTask) {
+ this((ProjectComponent) managingTask);
+ }
+
+ /**
+ * Create a redirector instance for the given task
+ *
+ * @param managingTask
+ * the project component for which the redirector is to work
+ * @since Ant 1.6.3
+ */
+ public Redirector(final ProjectComponent managingTask) {
+ this.managingTask = managingTask;
+ }
+
+ /**
+ * Set the input to use for the task
+ *
+ * @param input
+ * the file from which input is read.
+ */
+ public void setInput(final File input) {
+ setInput((input == null) ? null : new File[] {input});
+ }
+
+ /**
+ * Set the input to use for the task
+ *
+ * @param input
+ * the files from which input is read.
+ */
+ public void setInput(final File[] input) {
+ synchronized (inMutex) {
+ if (input == null) {
+ this.input = null;
+ } else {
+ this.input = input.clone();
+ }
+ }
+ }
+
+ /**
+ * Set the string to use as input
+ *
+ * @param inputString
+ * the string which is used as the input source
+ */
+ public void setInputString(final String inputString) {
+ synchronized (inMutex) {
+ this.inputString = inputString;
+ }
+ }
+
+ /**
+ * Set whether to include the value of the input string in log messages.
+ * Defaults to true.
+ *
+ * @param logInputString
+ * true or false.
+ * @since Ant 1.7
+ */
+ public void setLogInputString(final boolean logInputString) {
+ this.logInputString = logInputString;
+ }
+
+ /**
+ * Set a stream to use as input.
+ *
+ * @param inputStream
+ * the stream from which input will be read
+ * @since Ant 1.6.3
+ */
+ /* public */void setInputStream(final InputStream inputStream) {
+ synchronized (inMutex) {
+ this.inputStream = inputStream;
+ }
+ }
+
+ /**
+ * File the output of the process is redirected to. If error is not
+ * redirected, it too will appear in the output
+ *
+ * @param out
+ * the file to which output stream is written
+ */
+ public void setOutput(final File out) {
+ setOutput((out == null) ? null : new File[] {out});
+ }
+
+ /**
+ * Files the output of the process is redirected to. If error is not
+ * redirected, it too will appear in the output
+ *
+ * @param out
+ * the files to which output stream is written
+ */
+ public void setOutput(final File[] out) {
+ synchronized (outMutex) {
+ if (out == null) {
+ this.out = null;
+ } else {
+ this.out = out.clone();
+ }
+ }
+ }
+
+ /**
+ * Set the output encoding.
+ *
+ * @param outputEncoding
+ * <code>String</code>.
+ */
+ public void setOutputEncoding(final String outputEncoding) {
+ if (outputEncoding == null) {
+ throw new IllegalArgumentException(
+ "outputEncoding must not be null");
+ }
+ synchronized (outMutex) {
+ this.outputEncoding = outputEncoding;
+ }
+ }
+
+ /**
+ * Set the error encoding.
+ *
+ * @param errorEncoding
+ * <code>String</code>.
+ */
+ public void setErrorEncoding(final String errorEncoding) {
+ if (errorEncoding == null) {
+ throw new IllegalArgumentException("errorEncoding must not be null");
+ }
+ synchronized (errMutex) {
+ this.errorEncoding = errorEncoding;
+ }
+ }
+
+ /**
+ * Set the input encoding.
+ *
+ * @param inputEncoding
+ * <code>String</code>.
+ */
+ public void setInputEncoding(final String inputEncoding) {
+ if (inputEncoding == null) {
+ throw new IllegalArgumentException("inputEncoding must not be null");
+ }
+ synchronized (inMutex) {
+ this.inputEncoding = inputEncoding;
+ }
+ }
+
+ /**
+ * Controls whether error output of exec is logged. This is only useful when
+ * output is being redirected and error output is desired in the Ant log
+ *
+ * @param logError
+ * if true the standard error is sent to the Ant log system and
+ * not sent to output.
+ */
+ public void setLogError(final boolean logError) {
+ synchronized (errMutex) {
+ this.logError = logError;
+ }
+ }
+
+ /**
+ * This <code>Redirector</code>'s subordinate
+ * <code>PropertyOutputStream</code>s will not set their respective
+ * properties <code>while (appendProperties &amp;&amp; append)</code>.
+ *
+ * @param appendProperties
+ * whether to append properties.
+ */
+ public void setAppendProperties(final boolean appendProperties) {
+ synchronized (outMutex) {
+ this.appendProperties = appendProperties;
+ }
+ }
+
+ /**
+ * Set the file to which standard error is to be redirected.
+ *
+ * @param error
+ * the file to which error is to be written
+ */
+ public void setError(final File error) {
+ setError((error == null) ? null : new File[] {error});
+ }
+
+ /**
+ * Set the files to which standard error is to be redirected.
+ *
+ * @param error
+ * the file to which error is to be written
+ */
+ public void setError(final File[] error) {
+ synchronized (errMutex) {
+ if (error == null) {
+ this.error = null;
+ } else {
+ this.error = error.clone();
+ }
+ }
+ }
+
+ /**
+ * Property name whose value should be set to the output of the process.
+ *
+ * @param outputProperty
+ * the name of the property to be set with the task's output.
+ */
+ public void setOutputProperty(final String outputProperty) {
+ if (outputProperty == null
+ || !(outputProperty.equals(this.outputProperty))) {
+ synchronized (outMutex) {
+ this.outputProperty = outputProperty;
+ baos = null;
+ }
+ }
+ }
+
+ /**
+ * Whether output should be appended to or overwrite an existing file.
+ * Defaults to false.
+ *
+ * @param append
+ * if true output and error streams are appended to their
+ * respective files, if specified.
+ */
+ public void setAppend(final boolean append) {
+ synchronized (outMutex) {
+ appendOut = append;
+ }
+ synchronized (errMutex) {
+ appendErr = append;
+ }
+ }
+
+ /**
+ * If true, (error and non-error) output will be "teed", redirected as
+ * specified while being sent to Ant's logging mechanism as if no
+ * redirection had taken place. Defaults to false.
+ *
+ * @param alwaysLog
+ * <code>boolean</code>
+ * @since Ant 1.6.3
+ */
+ public void setAlwaysLog(final boolean alwaysLog) {
+ synchronized (outMutex) {
+ alwaysLogOut = alwaysLog;
+ }
+ synchronized (errMutex) {
+ alwaysLogErr = alwaysLog;
+ }
+ }
+
+ /**
+ * Whether output and error files should be created even when empty.
+ * Defaults to true.
+ *
+ * @param createEmptyFiles
+ * <code>boolean</code>.
+ */
+ public void setCreateEmptyFiles(final boolean createEmptyFiles) {
+ synchronized (outMutex) {
+ createEmptyFilesOut = createEmptyFiles;
+ }
+ synchronized (outMutex) {
+ createEmptyFilesErr = createEmptyFiles;
+ }
+ }
+
+ /**
+ * Property name whose value should be set to the error of the process.
+ *
+ * @param errorProperty
+ * the name of the property to be set with the error output.
+ */
+ public void setErrorProperty(final String errorProperty) {
+ synchronized (errMutex) {
+ if (errorProperty == null
+ || !(errorProperty.equals(this.errorProperty))) {
+ this.errorProperty = errorProperty;
+ errorBaos = null;
+ }
+ }
+ }
+
+ /**
+ * Set the input <code>FilterChain</code>s.
+ *
+ * @param inputFilterChains
+ * <code>Vector</code> containing <code>FilterChain</code>.
+ */
+ public void setInputFilterChains(final Vector<FilterChain> inputFilterChains) {
+ synchronized (inMutex) {
+ this.inputFilterChains = inputFilterChains;
+ }
+ }
+
+ /**
+ * Set the output <code>FilterChain</code>s.
+ *
+ * @param outputFilterChains
+ * <code>Vector</code> containing <code>FilterChain</code>.
+ */
+ public void setOutputFilterChains(final Vector<FilterChain> outputFilterChains) {
+ synchronized (outMutex) {
+ this.outputFilterChains = outputFilterChains;
+ }
+ }
+
+ /**
+ * Set the error <code>FilterChain</code>s.
+ *
+ * @param errorFilterChains
+ * <code>Vector</code> containing <code>FilterChain</code>.
+ */
+ public void setErrorFilterChains(final Vector<FilterChain> errorFilterChains) {
+ synchronized (errMutex) {
+ this.errorFilterChains = errorFilterChains;
+ }
+ }
+
+ /**
+ * Whether to consider the output created by the process binary.
+ *
+ * <p>Binary output will not be split into lines which may make
+ * error and normal output look mixed up when they get written to
+ * the same stream.</p>
+ * @since 1.9.4
+ */
+ public void setBinaryOutput(final boolean b) {
+ outputIsBinary = b;
+ }
+
+ /**
+ * Set a property from a ByteArrayOutputStream
+ *
+ * @param baos
+ * contains the property value.
+ * @param propertyName
+ * the property name.
+ *
+ * @exception IOException
+ * if the value cannot be read form the stream.
+ */
+ private void setPropertyFromBAOS(final ByteArrayOutputStream baos,
+ final String propertyName) throws IOException {
+
+ final BufferedReader in = new BufferedReader(new StringReader(Execute
+ .toString(baos)));
+ String line = null;
+ final StringBuffer val = new StringBuffer();
+ while ((line = in.readLine()) != null) {
+ if (val.length() != 0) {
+ val.append(StringUtils.LINE_SEP);
+ }
+ val.append(line);
+ }
+ managingTask.getProject().setNewProperty(propertyName, val.toString());
+ }
+
+ /**
+ * Create the input, error and output streams based on the configuration
+ * options.
+ */
+ public void createStreams() {
+
+ synchronized (outMutex) {
+ outStreams();
+ if (alwaysLogOut || outputStream == null) {
+ final OutputStream outputLog = new LogOutputStream(managingTask,
+ Project.MSG_INFO);
+ outputStream = (outputStream == null) ? outputLog
+ : new TeeOutputStream(outputLog, outputStream);
+ }
+
+ if ((outputFilterChains != null && outputFilterChains.size() > 0)
+ || !(outputEncoding.equalsIgnoreCase(inputEncoding))) {
+ try {
+ final LeadPipeInputStream snk = new LeadPipeInputStream();
+ snk.setManagingComponent(managingTask);
+
+ InputStream outPumpIn = snk;
+
+ Reader reader = new InputStreamReader(outPumpIn,
+ inputEncoding);
+
+ if (outputFilterChains != null
+ && outputFilterChains.size() > 0) {
+ final ChainReaderHelper helper = new ChainReaderHelper();
+ helper.setProject(managingTask.getProject());
+ helper.setPrimaryReader(reader);
+ helper.setFilterChains(outputFilterChains);
+ reader = helper.getAssembledReader();
+ }
+ outPumpIn = new ReaderInputStream(reader, outputEncoding);
+
+ final Thread t = new Thread(threadGroup, new StreamPumper(
+ outPumpIn, outputStream, true), "output pumper");
+ t.setPriority(Thread.MAX_PRIORITY);
+ outputStream = new PipedOutputStream(snk);
+ t.start();
+ } catch (final IOException eyeOhEx) {
+ throw new BuildException("error setting up output stream",
+ eyeOhEx);
+ }
+ }
+ }
+
+ synchronized (errMutex) {
+ errorStreams();
+ if (alwaysLogErr || errorStream == null) {
+ final OutputStream errorLog = new LogOutputStream(managingTask,
+ Project.MSG_WARN);
+ errorStream = (errorStream == null) ? errorLog
+ : new TeeOutputStream(errorLog, errorStream);
+ }
+
+ if ((errorFilterChains != null && errorFilterChains.size() > 0)
+ || !(errorEncoding.equalsIgnoreCase(inputEncoding))) {
+ try {
+ final LeadPipeInputStream snk = new LeadPipeInputStream();
+ snk.setManagingComponent(managingTask);
+
+ InputStream errPumpIn = snk;
+
+ Reader reader = new InputStreamReader(errPumpIn,
+ inputEncoding);
+
+ if (errorFilterChains != null
+ && errorFilterChains.size() > 0) {
+ final ChainReaderHelper helper = new ChainReaderHelper();
+ helper.setProject(managingTask.getProject());
+ helper.setPrimaryReader(reader);
+ helper.setFilterChains(errorFilterChains);
+ reader = helper.getAssembledReader();
+ }
+ errPumpIn = new ReaderInputStream(reader, errorEncoding);
+
+ final Thread t = new Thread(threadGroup, new StreamPumper(
+ errPumpIn, errorStream, true), "error pumper");
+ t.setPriority(Thread.MAX_PRIORITY);
+ errorStream = new PipedOutputStream(snk);
+ t.start();
+ } catch (final IOException eyeOhEx) {
+ throw new BuildException("error setting up error stream",
+ eyeOhEx);
+ }
+ }
+ }
+
+ synchronized (inMutex) {
+ // if input files are specified, inputString and inputStream are
+ // ignored;
+ // classes that work with redirector attributes can enforce
+ // whatever warnings are needed
+ if (input != null && input.length > 0) {
+ managingTask
+ .log("Redirecting input from file"
+ + ((input.length == 1) ? "" : "s"),
+ Project.MSG_VERBOSE);
+ try {
+ inputStream = new ConcatFileInputStream(input);
+ } catch (final IOException eyeOhEx) {
+ throw new BuildException(eyeOhEx);
+ }
+ ((ConcatFileInputStream) inputStream)
+ .setManagingComponent(managingTask);
+ } else if (inputString != null) {
+ final StringBuffer buf = new StringBuffer("Using input ");
+ if (logInputString) {
+ buf.append('"').append(inputString).append('"');
+ } else {
+ buf.append("string");
+ }
+ managingTask.log(buf.toString(), Project.MSG_VERBOSE);
+ inputStream = new ByteArrayInputStream(inputString.getBytes());
+ }
+
+ if (inputStream != null && inputFilterChains != null
+ && inputFilterChains.size() > 0) {
+ final ChainReaderHelper helper = new ChainReaderHelper();
+ helper.setProject(managingTask.getProject());
+ try {
+ helper.setPrimaryReader(new InputStreamReader(inputStream,
+ inputEncoding));
+ } catch (final IOException eyeOhEx) {
+ throw new BuildException("error setting up input stream",
+ eyeOhEx);
+ }
+ helper.setFilterChains(inputFilterChains);
+ inputStream = new ReaderInputStream(
+ helper.getAssembledReader(), inputEncoding);
+ }
+ }
+ }
+
+ /** outStreams */
+ private void outStreams() {
+ if (out != null && out.length > 0) {
+ final String logHead = new StringBuffer("Output ").append(
+ ((appendOut) ? "appended" : "redirected")).append(" to ")
+ .toString();
+ outputStream = foldFiles(out, logHead, Project.MSG_VERBOSE,
+ appendOut, createEmptyFilesOut);
+ }
+ if (outputProperty != null) {
+ if (baos == null) {
+ baos = new PropertyOutputStream(outputProperty);
+ managingTask.log("Output redirected to property: "
+ + outputProperty, Project.MSG_VERBOSE);
+ }
+ // shield it from being closed by a filtering StreamPumper
+ final OutputStream keepAliveOutput = new KeepAliveOutputStream(baos);
+ outputStream = (outputStream == null) ? keepAliveOutput
+ : new TeeOutputStream(outputStream, keepAliveOutput);
+ } else {
+ baos = null;
+ }
+ }
+
+ private void errorStreams() {
+ if (error != null && error.length > 0) {
+ final String logHead = new StringBuffer("Error ").append(
+ ((appendErr) ? "appended" : "redirected")).append(" to ")
+ .toString();
+ errorStream = foldFiles(error, logHead, Project.MSG_VERBOSE,
+ appendErr, createEmptyFilesErr);
+ } else if (!(logError || outputStream == null) && errorProperty == null) {
+ final long funnelTimeout = 0L;
+ final OutputStreamFunneler funneler = new OutputStreamFunneler(
+ outputStream, funnelTimeout);
+ try {
+ outputStream = funneler.getFunnelInstance();
+ errorStream = funneler.getFunnelInstance();
+ if (!outputIsBinary) {
+ outputStream = new LineOrientedOutputStreamRedirector(outputStream);
+ errorStream = new LineOrientedOutputStreamRedirector(errorStream);
+ }
+ } catch (final IOException eyeOhEx) {
+ throw new BuildException(
+ "error splitting output/error streams", eyeOhEx);
+ }
+ }
+ if (errorProperty != null) {
+ if (errorBaos == null) {
+ errorBaos = new PropertyOutputStream(errorProperty);
+ managingTask.log("Error redirected to property: "
+ + errorProperty, Project.MSG_VERBOSE);
+ }
+ // shield it from being closed by a filtering StreamPumper
+ final OutputStream keepAliveError = new KeepAliveOutputStream(errorBaos);
+ errorStream = (error == null || error.length == 0) ? keepAliveError
+ : new TeeOutputStream(errorStream, keepAliveError);
+ } else {
+ errorBaos = null;
+ }
+ }
+
+ /**
+ * Create the StreamHandler to use with our Execute instance.
+ *
+ * @return the execute stream handler to manage the input, output and error
+ * streams.
+ *
+ * @throws BuildException
+ * if the execute stream handler cannot be created.
+ */
+ public ExecuteStreamHandler createHandler() throws BuildException {
+ createStreams();
+ final boolean nonBlockingRead = input == null && inputString == null;
+ return new PumpStreamHandler(getOutputStream(), getErrorStream(),
+ getInputStream(), nonBlockingRead);
+ }
+
+ /**
+ * Pass output sent to System.out to specified output.
+ *
+ * @param output
+ * the data to be output
+ */
+ protected void handleOutput(final String output) {
+ synchronized (outMutex) {
+ if (outPrintStream == null) {
+ outPrintStream = new PrintStream(outputStream);
+ }
+ outPrintStream.print(output);
+ }
+ }
+
+ /**
+ * Handle an input request
+ *
+ * @param buffer
+ * the buffer into which data is to be read.
+ * @param offset
+ * the offset into the buffer at which data is stored.
+ * @param length
+ * the amount of data to read
+ *
+ * @return the number of bytes read
+ *
+ * @exception IOException
+ * if the data cannot be read
+ */
+ protected int handleInput(final byte[] buffer, final int offset, final int length)
+ throws IOException {
+ synchronized (inMutex) {
+ if (inputStream == null) {
+ return managingTask.getProject().defaultInput(buffer, offset,
+ length);
+ }
+ return inputStream.read(buffer, offset, length);
+
+ }
+ }
+
+ /**
+ * Process data due to a flush operation.
+ *
+ * @param output
+ * the data being flushed.
+ */
+ protected void handleFlush(final String output) {
+ synchronized (outMutex) {
+ if (outPrintStream == null) {
+ outPrintStream = new PrintStream(outputStream);
+ }
+ outPrintStream.print(output);
+ outPrintStream.flush();
+ }
+ }
+
+ /**
+ * Process error output
+ *
+ * @param output
+ * the error output data.
+ */
+ protected void handleErrorOutput(final String output) {
+ synchronized (errMutex) {
+ if (errorPrintStream == null) {
+ errorPrintStream = new PrintStream(errorStream);
+ }
+ errorPrintStream.print(output);
+ }
+ }
+
+ /**
+ * Handle a flush operation on the error stream
+ *
+ * @param output
+ * the error information being flushed.
+ */
+ protected void handleErrorFlush(final String output) {
+ synchronized (errMutex) {
+ if (errorPrintStream == null) {
+ errorPrintStream = new PrintStream(errorStream);
+ }
+ errorPrintStream.print(output);
+ errorPrintStream.flush();
+ }
+ }
+
+ /**
+ * Get the output stream for the redirector
+ *
+ * @return the redirector's output stream or null if no output has been
+ * configured
+ */
+ public OutputStream getOutputStream() {
+ synchronized (outMutex) {
+ return outputStream;
+ }
+ }
+
+ /**
+ * Get the error stream for the redirector
+ *
+ * @return the redirector's error stream or null if no output has been
+ * configured
+ */
+ public OutputStream getErrorStream() {
+ synchronized (errMutex) {
+ return errorStream;
+ }
+ }
+
+ /**
+ * Get the input stream for the redirector
+ *
+ * @return the redirector's input stream or null if no output has been
+ * configured
+ */
+ public InputStream getInputStream() {
+ synchronized (inMutex) {
+ return inputStream;
+ }
+ }
+
+ /**
+ * Complete redirection.
+ *
+ * This operation will close any streams and create any specified property
+ * values.
+ *
+ * @throws IOException
+ * if the output properties cannot be read from their output
+ * streams.
+ */
+ public void complete() throws IOException {
+ System.out.flush();
+ System.err.flush();
+
+ synchronized (inMutex) {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+
+ synchronized (outMutex) {
+ outputStream.flush();
+ outputStream.close();
+ }
+
+ synchronized (errMutex) {
+ errorStream.flush();
+ errorStream.close();
+ }
+
+ // wait for the StreamPumpers to finish
+ synchronized (this) {
+ while (threadGroup.activeCount() > 0) {
+ try {
+ managingTask.log("waiting for " + threadGroup.activeCount()
+ + " Threads:", Project.MSG_DEBUG);
+ final Thread[] thread = new Thread[threadGroup.activeCount()];
+ threadGroup.enumerate(thread);
+ for (int i = 0; i < thread.length && thread[i] != null; i++) {
+ try {
+ managingTask.log(thread[i].toString(),
+ Project.MSG_DEBUG);
+ } catch (final NullPointerException enPeaEx) {
+ // Ignore exception
+ }
+ }
+ wait(STREAMPUMPER_WAIT_INTERVAL);
+ } catch (final InterruptedException eyeEx) {
+ final Thread[] thread = new Thread[threadGroup.activeCount()];
+ threadGroup.enumerate(thread);
+ for (int i = 0; i < thread.length && thread[i] != null; i++) {
+ thread[i].interrupt();
+ }
+ }
+ }
+ }
+
+ setProperties();
+
+ synchronized (inMutex) {
+ inputStream = null;
+ }
+ synchronized (outMutex) {
+ outputStream = null;
+ outPrintStream = null;
+ }
+ synchronized (errMutex) {
+ errorStream = null;
+ errorPrintStream = null;
+ }
+ }
+
+ /**
+ * Notify the <code>Redirector</code> that it is now okay to set any output
+ * and/or error properties.
+ */
+ public void setProperties() {
+ synchronized (outMutex) {
+ if (baos != null) {
+ try {
+ baos.close();
+ } catch (final IOException eyeOhEx) {
+ // Ignore exception
+ }
+ }
+ }
+ synchronized (errMutex) {
+ if (errorBaos != null) {
+ try {
+ errorBaos.close();
+ } catch (final IOException eyeOhEx) {
+ // Ignore exception
+ }
+ }
+ }
+ }
+
+ private OutputStream foldFiles(final File[] file, final String logHead, final int loglevel,
+ final boolean append, final boolean createEmptyFiles) {
+ final OutputStream result = new LazyFileOutputStream(file[0], append,
+ createEmptyFiles);
+
+ managingTask.log(logHead + file[0], loglevel);
+ final char[] c = new char[logHead.length()];
+ Arrays.fill(c, ' ');
+ final String indent = new String(c);
+
+ for (int i = 1; i < file.length; i++) {
+ outputStream = new TeeOutputStream(outputStream,
+ new LazyFileOutputStream(file[i], append, createEmptyFiles));
+ managingTask.log(indent + file[i], loglevel);
+ }
+ return result;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Rename.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Rename.java
new file mode 100644
index 00000000..382c2a77
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Rename.java
@@ -0,0 +1,96 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Renames a file.
+ *
+ * @deprecated The rename task is deprecated since Ant 1.2. Use move instead.
+ * @since Ant 1.1
+ */
+public class Rename extends Task {
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private File src;
+ private File dest;
+ private boolean replace = true;
+
+
+ /**
+ * Sets the file to be renamed.
+ * @param src the file to rename
+ */
+ public void setSrc(File src) {
+ this.src = src;
+ }
+
+ /**
+ * Sets the new name of the file.
+ * @param dest the new name of the file.
+ */
+ public void setDest(File dest) {
+ this.dest = dest;
+ }
+
+ /**
+ * Sets whether an existing file should be replaced.
+ * @param replace <code>on</code>, if an existing file should be replaced.
+ */
+ public void setReplace(String replace) {
+ this.replace = Project.toBoolean(replace);
+ }
+
+
+ /**
+ * Renames the file <code>src</code> to <code>dest</code>
+ * @exception org.apache.tools.ant.BuildException The exception is
+ * thrown, if the rename operation fails.
+ */
+ public void execute() throws BuildException {
+ log("DEPRECATED - The rename task is deprecated. Use move instead.");
+
+ if (dest == null) {
+ throw new BuildException("dest attribute is required", getLocation());
+ }
+
+ if (src == null) {
+ throw new BuildException("src attribute is required", getLocation());
+ }
+
+ if (!replace && dest.exists()) {
+ throw new BuildException(dest + " already exists.");
+ }
+
+ try {
+ FILE_UTILS.rename(src, dest);
+ } catch (IOException e) {
+ throw new BuildException("Unable to rename " + src + " to "
+ + dest, e, getLocation());
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Replace.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Replace.java
new file mode 100644
index 00000000..9aa9fe8e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Replace.java
@@ -0,0 +1,955 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Replaces all occurrences of one or more string tokens with given
+ * values in the indicated files. Each value can be either a string
+ * or the value of a property available in a designated property file.
+ * If you want to replace a text that crosses line boundaries, you
+ * must use a nested <code>&lt;replacetoken&gt;</code> element.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="filesystem"
+ */
+public class Replace extends MatchingTask {
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private File sourceFile = null;
+ private NestedString token = null;
+ private NestedString value = new NestedString();
+
+ private Resource propertyResource = null;
+ private Resource replaceFilterResource = null;
+ private Properties properties = null;
+ private ArrayList replacefilters = new ArrayList();
+
+ private File dir = null;
+
+ private int fileCount;
+ private int replaceCount;
+ private boolean summary = false;
+
+ /** The encoding used to read and write files - if null, uses default */
+ private String encoding = null;
+
+ private Union resources;
+
+ private boolean preserveLastModified = false;
+ private boolean failOnNoReplacements = false;
+
+ /**
+ * An inline string to use as the replacement text.
+ */
+ public class NestedString {
+
+ private boolean expandProperties = false;
+ private StringBuffer buf = new StringBuffer();
+
+ /**
+ * Whether properties should be expanded in nested test.
+ *
+ * <p>If you use this class via its Java interface the text
+ * you add via {@link #addText addText} has most likely been
+ * expanded already so you do <b>not</b> want to set this to
+ * true.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public void setExpandProperties(boolean b) {
+ expandProperties = b;
+ }
+
+ /**
+ * The text of the element.
+ *
+ * @param val the string to add
+ */
+ public void addText(String val) {
+ buf.append(val);
+ }
+
+ /**
+ * @return the text
+ */
+ public String getText() {
+ String s = buf.toString();
+ return expandProperties ? getProject().replaceProperties(s) : s;
+ }
+ }
+
+ /**
+ * A filter to apply.
+ */
+ public class Replacefilter {
+ private NestedString token;
+ private NestedString value;
+ private String replaceValue;
+ private String property;
+
+ private StringBuffer inputBuffer;
+ private StringBuffer outputBuffer = new StringBuffer();
+
+ /**
+ * Validate the filter's configuration.
+ * @throws BuildException if any part is invalid.
+ */
+ public void validate() throws BuildException {
+ //Validate mandatory attributes
+ if (token == null) {
+ String message = "token is a mandatory for replacefilter.";
+ throw new BuildException(message);
+ }
+
+ if ("".equals(token.getText())) {
+ String message = "The token must not be an empty "
+ + "string.";
+ throw new BuildException(message);
+ }
+
+ //value and property are mutually exclusive attributes
+ if ((value != null) && (property != null)) {
+ String message = "Either value or property "
+ + "can be specified, but a replacefilter "
+ + "element cannot have both.";
+ throw new BuildException(message);
+ }
+
+ if ((property != null)) {
+ //the property attribute must have access to a property file
+ if (propertyResource == null) {
+ String message = "The replacefilter's property attribute "
+ + "can only be used with the replacetask's "
+ + "propertyFile/Resource attribute.";
+ throw new BuildException(message);
+ }
+
+ //Make sure property exists in property file
+ if (properties == null
+ || properties.getProperty(property) == null) {
+ String message = "property \"" + property
+ + "\" was not found in " + propertyResource.getName();
+ throw new BuildException(message);
+ }
+ }
+
+ replaceValue = getReplaceValue();
+ }
+
+ /**
+ * Get the replacement value for this filter token.
+ * @return the replacement value
+ */
+ public String getReplaceValue() {
+ if (property != null) {
+ return properties.getProperty(property);
+ } else if (value != null) {
+ return value.getText();
+ } else if (Replace.this.value != null) {
+ return Replace.this.value.getText();
+ } else {
+ //Default is empty string
+ return "";
+ }
+ }
+
+ /**
+ * Set the token to replace.
+ * @param t <code>String</code> token.
+ */
+ public void setToken(String t) {
+ createReplaceToken().addText(t);
+ }
+
+ /**
+ * Get the string to search for.
+ * @return current <code>String</code> token.
+ */
+ public String getToken() {
+ return token.getText();
+ }
+
+ /**
+ * The replacement string; required if <code>property</code>
+ * is not set.
+ * @param value <code>String</code> value to replace.
+ */
+ public void setValue(String value) {
+ createReplaceValue().addText(value);
+ }
+
+ /**
+ * Get replacement <code>String</code>.
+ * @return replacement or null.
+ */
+ public String getValue() {
+ return value.getText();
+ }
+
+ /**
+ * Set the name of the property whose value is to serve as
+ * the replacement value; required if <code>value</code> is not set.
+ * @param property property name.
+ */
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ /**
+ * Get the name of the property whose value is to serve as
+ * the replacement value.
+ * @return property or null.
+ */
+ public String getProperty() {
+ return property;
+ }
+
+ /**
+ * Create a token to filter as the text of a nested element.
+ * @return nested token <code>NestedString</code> to configure.
+ * @since Ant 1.8.0
+ */
+ public NestedString createReplaceToken() {
+ if (token == null) {
+ token = new NestedString();
+ }
+ return token;
+ }
+
+ /**
+ * Create a string to replace the token as the text of a nested element.
+ * @return replacement value <code>NestedString</code> to configure.
+ * @since Ant 1.8.0
+ */
+ public NestedString createReplaceValue() {
+ if (value == null) {
+ value = new NestedString();
+ }
+ return value;
+ }
+
+ /**
+ * Retrieves the output buffer of this filter. The filter guarantees
+ * that data is only appended to the end of this StringBuffer.
+ * @return The StringBuffer containing the output of this filter.
+ */
+ StringBuffer getOutputBuffer() {
+ return outputBuffer;
+ }
+
+ /**
+ * Sets the input buffer for this filter.
+ * The filter expects from the component providing the input that data
+ * is only added by that component to the end of this StringBuffer.
+ * This StringBuffer will be modified by this filter, and expects that
+ * another component will only apped to this StringBuffer.
+ * @param input The input for this filter.
+ */
+ void setInputBuffer(StringBuffer input) {
+ inputBuffer = input;
+ }
+
+ /**
+ * Processes the buffer as far as possible. Takes into account that
+ * appended data may make it possible to replace the end of the already
+ * received data, when the token is split over the "old" and the "new"
+ * part.
+ * @return true if some data has been made available in the
+ * output buffer.
+ */
+ boolean process() {
+ String t = getToken();
+ if (inputBuffer.length() > t.length()) {
+ int pos = replace();
+ pos = Math.max((inputBuffer.length() - t.length()), pos);
+ outputBuffer.append(inputBuffer.substring(0, pos));
+ inputBuffer.delete(0, pos);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Processes the buffer to the end. Does not take into account that
+ * appended data may make it possible to replace the end of the already
+ * received data.
+ */
+ void flush() {
+ replace();
+ outputBuffer.append(inputBuffer);
+ inputBuffer.delete(0, inputBuffer.length());
+ }
+
+ /**
+ * Performs the replace operation.
+ * @return The position of the last character that was inserted as
+ * replacement.
+ */
+ private int replace() {
+ String t = getToken();
+ int found = inputBuffer.indexOf(t);
+ int pos = -1;
+ final int tokenLength = t.length();
+ final int replaceValueLength = replaceValue.length();
+ while (found >= 0) {
+ inputBuffer.replace(found, found + tokenLength, replaceValue);
+ pos = found + replaceValueLength;
+ found = inputBuffer.indexOf(t, pos);
+ ++replaceCount;
+ }
+ return pos;
+ }
+ }
+
+ /**
+ * Class reading a file in small chunks, and presenting these chunks in
+ * a StringBuffer. Compatible with the Replacefilter.
+ * @since 1.7
+ */
+ private class FileInput /* JDK 5: implements Closeable */ {
+ private StringBuffer outputBuffer;
+ private final InputStream is;
+ private Reader reader;
+ private char[] buffer;
+ private static final int BUFF_SIZE = 4096;
+
+ /**
+ * Constructs the input component. Opens the file for reading.
+ * @param source The file to read from.
+ * @throws IOException When the file cannot be read from.
+ */
+ FileInput(File source) throws IOException {
+ outputBuffer = new StringBuffer();
+ buffer = new char[BUFF_SIZE];
+ is = new FileInputStream(source);
+ try {
+ reader = new BufferedReader(encoding != null ? new InputStreamReader(is, encoding) : new InputStreamReader(is));
+ } finally {
+ if (reader == null) {
+ is.close();
+ }
+ }
+ }
+
+ /**
+ * Retrieves the output buffer of this filter. The component guarantees
+ * that data is only appended to the end of this StringBuffer.
+ * @return The StringBuffer containing the output of this filter.
+ */
+ StringBuffer getOutputBuffer() {
+ return outputBuffer;
+ }
+
+ /**
+ * Reads some data from the file.
+ * @return true when the end of the file has not been reached.
+ * @throws IOException When the file cannot be read from.
+ */
+ boolean readChunk() throws IOException {
+ int bufferLength = 0;
+ bufferLength = reader.read(buffer);
+ if (bufferLength < 0) {
+ return false;
+ }
+ outputBuffer.append(new String(buffer, 0, bufferLength));
+ return true;
+ }
+
+ /**
+ * Closes the file.
+ * @throws IOException When the file cannot be closed.
+ */
+ public void close() throws IOException {
+ is.close();
+ }
+
+ }
+
+ /**
+ * Component writing a file in chunks, taking the chunks from the
+ * Replacefilter.
+ * @since 1.7
+ */
+ private class FileOutput /* JDK 5: implements Closeable */ {
+ private StringBuffer inputBuffer;
+ private final OutputStream os;
+ private Writer writer;
+
+ /**
+ * Constructs the output component. Opens the file for writing.
+ * @param out The file to read to.
+ * @throws IOException When the file cannot be read from.
+ */
+ FileOutput(File out) throws IOException {
+ os = new FileOutputStream(out);
+ try {
+ writer = new BufferedWriter(encoding != null ? new OutputStreamWriter(os, encoding) : new OutputStreamWriter(os));
+ } finally {
+ if (writer == null) {
+ os.close();
+ }
+ }
+ }
+
+ /**
+ * Sets the input buffer for this component.
+ * The filter expects from the component providing the input that data
+ * is only added by that component to the end of this StringBuffer.
+ * This StringBuffer will be modified by this filter, and expects that
+ * another component will only append to this StringBuffer.
+ * @param input The input for this filter.
+ */
+ void setInputBuffer(StringBuffer input) {
+ inputBuffer = input;
+ }
+
+ /**
+ * Writes the buffer as far as possible.
+ * @return false to be inline with the Replacefilter.
+ * (Yes defining an interface crossed my mind, but would publish the
+ * internal behavior.)
+ * @throws IOException when the output cannot be written.
+ */
+ boolean process() throws IOException {
+ writer.write(inputBuffer.toString());
+ inputBuffer.delete(0, inputBuffer.length());
+ return false;
+ }
+
+ /**
+ * Processes the buffer to the end.
+ * @throws IOException when the output cannot be flushed.
+ */
+ void flush() throws IOException {
+ process();
+ writer.flush();
+ }
+
+ /**
+ * Closes the file.
+ * @throws IOException When the file cannot be closed.
+ */
+ public void close() throws IOException {
+ os.close();
+ }
+
+ }
+
+ /**
+ * Do the execution.
+ * @throws BuildException if we can't build
+ */
+ public void execute() throws BuildException {
+
+ ArrayList savedFilters = (ArrayList) replacefilters.clone();
+ Properties savedProperties =
+ properties == null ? null : (Properties) properties.clone();
+
+ if (token != null) {
+ // line separators in values and tokens are "\n"
+ // in order to compare with the file contents, replace them
+ // as needed
+ StringBuffer val = new StringBuffer(value.getText());
+ stringReplace(val, "\r\n", "\n");
+ stringReplace(val, "\n", StringUtils.LINE_SEP);
+ StringBuffer tok = new StringBuffer(token.getText());
+ stringReplace(tok, "\r\n", "\n");
+ stringReplace(tok, "\n", StringUtils.LINE_SEP);
+ Replacefilter firstFilter = createPrimaryfilter();
+ firstFilter.setToken(tok.toString());
+ firstFilter.setValue(val.toString());
+ }
+
+ try {
+ if (replaceFilterResource != null) {
+ Properties props = getProperties(replaceFilterResource);
+ Iterator e = props.keySet().iterator();
+ while (e.hasNext()) {
+ String tok = e.next().toString();
+ Replacefilter replaceFilter = createReplacefilter();
+ replaceFilter.setToken(tok);
+ replaceFilter.setValue(props.getProperty(tok));
+ }
+ }
+
+ validateAttributes();
+
+ if (propertyResource != null) {
+ properties = getProperties(propertyResource);
+ }
+
+ validateReplacefilters();
+ fileCount = 0;
+ replaceCount = 0;
+
+ if (sourceFile != null) {
+ processFile(sourceFile);
+ }
+
+ if (dir != null) {
+ DirectoryScanner ds = super.getDirectoryScanner(dir);
+ String[] srcs = ds.getIncludedFiles();
+
+ for (int i = 0; i < srcs.length; i++) {
+ File file = new File(dir, srcs[i]);
+ processFile(file);
+ }
+ }
+
+ if (resources != null) {
+ for (Resource r : resources) {
+ FileProvider fp =
+ r.as(FileProvider.class);
+ processFile(fp.getFile());
+ }
+ }
+
+ if (summary) {
+ log("Replaced " + replaceCount + " occurrences in "
+ + fileCount + " files.", Project.MSG_INFO);
+ }
+ if (failOnNoReplacements && replaceCount == 0) {
+ throw new BuildException("didn't replace anything");
+ }
+ } finally {
+ replacefilters = savedFilters;
+ properties = savedProperties;
+ } // end of finally
+
+ }
+
+ /**
+ * Validate attributes provided for this task in .xml build file.
+ *
+ * @exception BuildException if any supplied attribute is invalid or any
+ * mandatory attribute is missing.
+ */
+ public void validateAttributes() throws BuildException {
+ if (sourceFile == null && dir == null && resources == null) {
+ String message = "Either the file or the dir attribute "
+ + "or nested resources must be specified";
+ throw new BuildException(message, getLocation());
+ }
+ if (propertyResource != null && !propertyResource.isExists()) {
+ String message = "Property file " + propertyResource.getName()
+ + " does not exist.";
+ throw new BuildException(message, getLocation());
+ }
+ if (token == null && replacefilters.size() == 0) {
+ String message = "Either token or a nested replacefilter "
+ + "must be specified";
+ throw new BuildException(message, getLocation());
+ }
+ if (token != null && "".equals(token.getText())) {
+ String message = "The token attribute must not be an empty string.";
+ throw new BuildException(message, getLocation());
+ }
+ }
+
+ /**
+ * Validate nested elements.
+ *
+ * @exception BuildException if any supplied attribute is invalid or any
+ * mandatory attribute is missing.
+ */
+ public void validateReplacefilters()
+ throws BuildException {
+ final int size = replacefilters.size();
+ for (int i = 0; i < size; i++) {
+ Replacefilter element =
+ (Replacefilter) replacefilters.get(i);
+ element.validate();
+ }
+ }
+
+ /**
+ * Load a properties file.
+ * @param propertyFile the file to load the properties from.
+ * @return loaded <code>Properties</code> object.
+ * @throws BuildException if the file could not be found or read.
+ */
+ public Properties getProperties(File propertyFile) throws BuildException {
+ return getProperties(new FileResource(getProject(), propertyFile));
+ }
+
+ /**
+ * Load a properties resource.
+ * @param propertyResource the resource to load the properties from.
+ * @return loaded <code>Properties</code> object.
+ * @throws BuildException if the resource could not be found or read.
+ * @since Ant 1.8.0
+ */
+ public Properties getProperties(Resource propertyResource)
+ throws BuildException {
+ Properties props = new Properties();
+
+ InputStream in = null;
+ try {
+ in = propertyResource.getInputStream();
+ props.load(in);
+ } catch (IOException e) {
+ String message = "Property resource (" + propertyResource.getName()
+ + ") cannot be loaded.";
+ throw new BuildException(message);
+ } finally {
+ FileUtils.close(in);
+ }
+
+ return props;
+ }
+
+ /**
+ * Perform the replacement on the given file.
+ *
+ * The replacement is performed on a temporary file which then
+ * replaces the original file.
+ *
+ * @param src the source <code>File</code>.
+ */
+ private void processFile(File src) throws BuildException {
+ if (!src.exists()) {
+ throw new BuildException("Replace: source file " + src.getPath()
+ + " doesn't exist", getLocation());
+ }
+
+ int repCountStart = replaceCount;
+ logFilterChain(src.getPath());
+
+ try {
+ File temp = FILE_UTILS.createTempFile("rep", ".tmp",
+ src.getParentFile(), false, true);
+ try {
+ FileInput in = new FileInput(src);
+ try {
+ FileOutput out = new FileOutput(temp);
+ try {
+ out.setInputBuffer(buildFilterChain(in.getOutputBuffer()));
+
+ while (in.readChunk()) {
+ if (processFilterChain()) {
+ out.process();
+ }
+ }
+
+ flushFilterChain();
+
+ out.flush();
+ } finally {
+ out.close();
+ }
+ } finally {
+ in.close();
+ }
+ boolean changes = (replaceCount != repCountStart);
+ if (changes) {
+ fileCount++;
+ long origLastModified = src.lastModified();
+ FILE_UTILS.rename(temp, src);
+ if (preserveLastModified) {
+ FILE_UTILS.setFileLastModified(src, origLastModified);
+ }
+ }
+ } finally {
+ if (temp.isFile() && !temp.delete()) {
+ temp.deleteOnExit();
+ }
+ }
+ } catch (IOException ioe) {
+ throw new BuildException("IOException in " + src + " - "
+ + ioe.getClass().getName() + ":"
+ + ioe.getMessage(), ioe, getLocation());
+ }
+ }
+
+ /**
+ * Flushes all filters.
+ */
+ private void flushFilterChain() {
+ final int size = replacefilters.size();
+ for (int i = 0; i < size; i++) {
+ Replacefilter filter = (Replacefilter) replacefilters.get(i);
+ filter.flush();
+ }
+ }
+
+ /**
+ * Performs the normal processing of the filters.
+ * @return true if the filter chain produced new output.
+ */
+ private boolean processFilterChain() {
+ final int size = replacefilters.size();
+ for (int i = 0; i < size; i++) {
+ Replacefilter filter = (Replacefilter) replacefilters.get(i);
+ if (!filter.process()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Creates the chain of filters to operate.
+ * @param inputBuffer <code>StringBuffer</code> containing the input for the
+ * first filter.
+ * @return <code>StringBuffer</code> containing the output of the last filter.
+ */
+ private StringBuffer buildFilterChain(StringBuffer inputBuffer) {
+ StringBuffer buf = inputBuffer;
+ final int size = replacefilters.size();
+ for (int i = 0; i < size; i++) {
+ Replacefilter filter = (Replacefilter) replacefilters.get(i);
+ filter.setInputBuffer(buf);
+ buf = filter.getOutputBuffer();
+ }
+ return buf;
+ }
+
+ /**
+ * Logs the chain of filters to operate on the file.
+ * @param filename <code>String</code>.
+ */
+ private void logFilterChain(String filename) {
+ final int size = replacefilters.size();
+ for (int i = 0; i < size; i++) {
+ Replacefilter filter = (Replacefilter) replacefilters.get(i);
+ log("Replacing in " + filename + ": " + filter.getToken()
+ + " --> " + filter.getReplaceValue(), Project.MSG_VERBOSE);
+ }
+ }
+ /**
+ * Set the source file; required unless <code>dir</code> is set.
+ * @param file source <code>File</code>.
+ */
+ public void setFile(File file) {
+ this.sourceFile = file;
+ }
+
+ /**
+ * Indicates whether a summary of the replace operation should be
+ * produced, detailing how many token occurrences and files were
+ * processed; optional, default=<code>false</code>.
+ *
+ * @param summary <code>boolean</code> whether a summary of the
+ * replace operation should be logged.
+ */
+ public void setSummary(boolean summary) {
+ this.summary = summary;
+ }
+
+
+ /**
+ * Sets the name of a property file containing filters; optional.
+ * Each property will be treated as a replacefilter where token is the name
+ * of the property and value is the value of the property.
+ * @param replaceFilterFile <code>File</code> to load.
+ */
+ public void setReplaceFilterFile(File replaceFilterFile) {
+ setReplaceFilterResource(new FileResource(getProject(),
+ replaceFilterFile));
+ }
+
+ /**
+ * Sets the name of a resource containing filters; optional.
+ * Each property will be treated as a replacefilter where token is the name
+ * of the property and value is the value of the property.
+ * @param replaceFilter <code>Resource</code> to load.
+ * @since Ant 1.8.0
+ */
+ public void setReplaceFilterResource(Resource replaceFilter) {
+ this.replaceFilterResource = replaceFilter;
+ }
+
+ /**
+ * The base directory to use when replacing a token in multiple files;
+ * required if <code>file</code> is not defined.
+ * @param dir <code>File</code> representing the base directory.
+ */
+ public void setDir(File dir) {
+ this.dir = dir;
+ }
+
+ /**
+ * Set the string token to replace; required unless a nested
+ * <code>replacetoken</code> element or the
+ * <code>replacefilterresource</code> attribute is used.
+ * @param token token <code>String</code>.
+ */
+ public void setToken(String token) {
+ createReplaceToken().addText(token);
+ }
+
+ /**
+ * Set the string value to use as token replacement;
+ * optional, default is the empty string "".
+ * @param value replacement value.
+ */
+ public void setValue(String value) {
+ createReplaceValue().addText(value);
+ }
+
+ /**
+ * Set the file encoding to use on the files read and written by the task;
+ * optional, defaults to default JVM encoding.
+ *
+ * @param encoding the encoding to use on the files.
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Create a token to filter as the text of a nested element.
+ * @return nested token <code>NestedString</code> to configure.
+ */
+ public NestedString createReplaceToken() {
+ if (token == null) {
+ token = new NestedString();
+ }
+ return token;
+ }
+
+ /**
+ * Create a string to replace the token as the text of a nested element.
+ * @return replacement value <code>NestedString</code> to configure.
+ */
+ public NestedString createReplaceValue() {
+ return value;
+ }
+
+ /**
+ * The name of a property file from which properties specified using nested
+ * <code>&lt;replacefilter&gt;</code> elements are drawn; required only if
+ * the <i>property</i> attribute of <code>&lt;replacefilter&gt;</code> is used.
+ * @param propertyFile <code>File</code> to load.
+ */
+ public void setPropertyFile(File propertyFile) {
+ setPropertyResource(new FileResource(propertyFile));
+ }
+
+ /**
+ * A resource from which properties specified using nested
+ * <code>&lt;replacefilter&gt;</code> elements are drawn; required
+ * only if the <i>property</i> attribute of
+ * <code>&lt;replacefilter&gt;</code> is used.
+ * @param propertyResource <code>Resource</code> to load.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setPropertyResource(Resource propertyResource) {
+ this.propertyResource = propertyResource;
+ }
+
+ /**
+ * Add a nested &lt;replacefilter&gt; element.
+ * @return a nested <code>Replacefilter</code> object to be configured.
+ */
+ public Replacefilter createReplacefilter() {
+ Replacefilter filter = new Replacefilter();
+ replacefilters.add(filter);
+ return filter;
+ }
+
+ /**
+ * Support arbitrary file system based resource collections.
+ *
+ * @since Ant 1.8.0
+ */
+ public void addConfigured(ResourceCollection rc) {
+ if (!rc.isFilesystemOnly()) {
+ throw new BuildException("only filesystem resources are supported");
+ }
+ if (resources == null) {
+ resources = new Union();
+ }
+ resources.add(rc);
+ }
+
+ /**
+ * Whether the file timestamp shall be preserved even if the file
+ * is modified.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setPreserveLastModified(boolean b) {
+ preserveLastModified = b;
+ }
+
+ /**
+ * Whether the build should fail if nothing has been replaced.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setFailOnNoReplacements(boolean b) {
+ failOnNoReplacements = b;
+ }
+
+ /**
+ * Adds the token and value as first &lt;replacefilter&gt; element.
+ * The token and value are always processed first.
+ * @return a nested <code>Replacefilter</code> object to be configured.
+ */
+ private Replacefilter createPrimaryfilter() {
+ Replacefilter filter = new Replacefilter();
+ replacefilters.add(0, filter);
+ return filter;
+ }
+
+ /**
+ * Replace occurrences of str1 in StringBuffer str with str2.
+ */
+ private void stringReplace(StringBuffer str, String str1, String str2) {
+ int found = str.indexOf(str1);
+ final int str1Length = str1.length();
+ final int str2Length = str2.length();
+ while (found >= 0) {
+ str.replace(found, found + str1Length, str2);
+ found = str.indexOf(str1, found + str2Length);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ResourceCount.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ResourceCount.java
new file mode 100644
index 00000000..b29b57b6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ResourceCount.java
@@ -0,0 +1,124 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.types.Comparison;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * Count resources from a ResourceCollection, storing to a property or
+ * writing to the log. Can also be used as a Condition.
+ * @since Ant 1.7
+ */
+public class ResourceCount extends Task implements Condition {
+
+ private static final String ONE_NESTED_MESSAGE
+ = "ResourceCount can count resources from exactly one nested ResourceCollection.";
+
+ private static final String COUNT_REQUIRED
+ = "Use of the ResourceCount condition requires that the count attribute be set.";
+
+ private ResourceCollection rc;
+ private Comparison when = Comparison.EQUAL;
+ private Integer count;
+ private String property;
+
+ /**
+ * Add the ResourceCollection to count.
+ * @param r the ResourceCollection to count.
+ * @throws BuildException if already set.
+ */
+ public void add(ResourceCollection r) {
+ if (rc != null) {
+ throw new BuildException(ONE_NESTED_MESSAGE);
+ }
+ rc = r;
+ }
+
+ /**
+ * Set the ResourceCollection reference.
+ * @param r the Reference.
+ */
+ public void setRefid(Reference r) {
+ Object o = r.getReferencedObject();
+ if (!(o instanceof ResourceCollection)) {
+ throw new BuildException(r.getRefId()
+ + " doesn\'t denote a ResourceCollection");
+ }
+ add((ResourceCollection) o);
+ }
+
+ /**
+ * Execute as a Task.
+ */
+ public void execute() {
+ if (rc == null) {
+ throw new BuildException(ONE_NESTED_MESSAGE);
+ }
+ if (property == null) {
+ log("resource count = " + rc.size());
+ } else {
+ getProject().setNewProperty(property, Integer.toString(rc.size()));
+ }
+ }
+
+ /**
+ * Fulfill the condition contract.
+ * @return true if the specified ResourceCollection satisfies the set criteria.
+ * @throws BuildException if an error occurs.
+ */
+ public boolean eval() {
+ if (rc == null) {
+ throw new BuildException(ONE_NESTED_MESSAGE);
+ }
+ if (count == null) {
+ throw new BuildException(COUNT_REQUIRED);
+ }
+ return when.evaluate(new Integer(rc.size()).compareTo(count));
+ }
+
+ /**
+ * Set the target count number for use as a Condition.
+ * @param c number of Resources as int.
+ */
+ public void setCount(int c) {
+ count = new Integer(c);
+ }
+
+ /**
+ * Set the comparison for use as a Condition.
+ * @param c Comparison (an EnumeratedAttribute) When.
+ * @see org.apache.tools.ant.types.Comparison
+ */
+ public void setWhen(Comparison c) {
+ when = c;
+ }
+
+ /**
+ * Set the name of the property to set in task mode.
+ * @param p the property name to set.
+ */
+ public void setProperty(String p) {
+ property = p;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Retry.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Retry.java
new file mode 100644
index 00000000..1a00fa4c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Retry.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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Retries the nested task a set number of times
+ * @since Ant 1.7.1
+ */
+public class Retry extends Task implements TaskContainer {
+
+ /**
+ * task to execute n times
+ */
+ private Task nestedTask;
+
+ /**
+ * set retryCount to 1 by default
+ */
+ private int retryCount = 1;
+
+ /**
+ * The time to wait between retries in milliseconds, default to 0.
+ */
+ private int retryDelay = 0;
+
+ /**
+ * set the task
+ * @param t the task to retry.
+ */
+ public synchronized void addTask(Task t) {
+ if (nestedTask != null) {
+ throw new BuildException(
+ "The retry task container accepts a single nested task"
+ + " (which may be a sequential task container)");
+ }
+ nestedTask = t;
+ }
+
+ /**
+ * set the number of times to retry the task
+ * @param n the number to use.
+ */
+ public void setRetryCount(int n) {
+ retryCount = n;
+ }
+
+ /**
+ * set the delay between retries (in milliseconds)
+ * @param retryDelay the time between retries.
+ * @since Ant 1.8.3
+ */
+ public void setRetryDelay(int retryDelay) {
+ if (retryDelay < 0) {
+ throw new BuildException("retryDelay must be a non-negative number");
+ }
+ this.retryDelay = retryDelay;
+ }
+
+ /**
+ * perform the work
+ * @throws BuildException if there is an error.
+ */
+ public void execute() throws BuildException {
+ StringBuffer errorMessages = new StringBuffer();
+ for (int i = 0; i <= retryCount; i++) {
+ try {
+ nestedTask.perform();
+ break;
+ } catch (Exception e) {
+ errorMessages.append(e.getMessage());
+ if (i >= retryCount) {
+ StringBuffer exceptionMessage = new StringBuffer();
+ exceptionMessage.append("Task [").append(nestedTask.getTaskName());
+ exceptionMessage.append("] failed after [").append(retryCount);
+ exceptionMessage.append("] attempts; giving up.").append(StringUtils.LINE_SEP);
+ exceptionMessage.append("Error messages:").append(StringUtils.LINE_SEP);
+ exceptionMessage.append(errorMessages);
+ throw new BuildException(exceptionMessage.toString(), getLocation());
+ }
+ String msg;
+ if (retryDelay > 0) {
+ msg = "Attempt [" + i + "]: error occurred; retrying after " + retryDelay + " ms...";
+ } else {
+ msg = "Attempt [" + i + "]: error occurred; retrying...";
+ }
+ log(msg, e, Project.MSG_INFO);
+ errorMessages.append(StringUtils.LINE_SEP);
+ if (retryDelay > 0) {
+ try {
+ Thread.sleep(retryDelay);
+ } catch (InterruptedException ie) {
+ // Ignore Exception
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Rmic.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Rmic.java
new file mode 100644
index 00000000..6935f9e3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Rmic.java
@@ -0,0 +1,853 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.rmi.Remote;
+import java.util.Vector;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.rmic.RmicAdapter;
+import org.apache.tools.ant.taskdefs.rmic.RmicAdapterFactory;
+import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.SourceFileScanner;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.facade.FacadeTaskHelper;
+
+/**
+ * <p>Runs the rmic compiler against classes.</p>
+ *
+ * <p>Rmic can be run on a single class (as specified with the classname
+ * attribute) or a number of classes at once (all classes below base that
+ * are neither _Stub nor _Skel classes). If you want to rmic a single
+ * class and this class is a class nested into another class, you have to
+ * specify the classname in the form <code>Outer$$Inner</code> instead of
+ * <code>Outer.Inner</code>.</p>
+ *
+ * <p>It is possible to refine the set of files that are being rmiced. This can
+ * be done with the <i>includes</i>, <i>includesfile</i>, <i>excludes</i>,
+ * <i>excludesfile</i> and <i>defaultexcludes</i>
+ * attributes. With the <i>includes</i> or <i>includesfile</i> attribute you
+ * specify the files you want to have included by using patterns. The
+ * <i>exclude</i> or <i>excludesfile</i> attribute is used to specify
+ * the files you want to have excluded. This is also done with patterns. And
+ * finally with the <i>defaultexcludes</i> attribute, you can specify whether
+ * you want to use default exclusions or not. See the section on
+ * directory based tasks, on how the
+ * inclusion/exclusion of files works, and how to write patterns.</p>
+ *
+ * <p>This task forms an implicit FileSet and
+ * supports all attributes of <code>&lt;fileset&gt;</code>
+ * (<code>dir</code> becomes <code>base</code>) as well as the nested
+ * <code>&lt;include&gt;</code>, <code>&lt;exclude&gt;</code> and
+ * <code>&lt;patternset&gt;</code> elements.</p>
+ *
+ * <p>It is possible to use different compilers. This can be selected
+ * with the &quot;build.rmic&quot; property or the <code>compiler</code>
+ * attribute. <a name="compilervalues">There are three choices</a>:</p>
+ *
+ * <ul>
+ * <li>sun (the standard compiler of the JDK)</li>
+ * <li>kaffe (the standard compiler of
+ * {@link <a href="http://www.kaffe.org">Kaffe</a>})</li>
+ * <li>weblogic</li>
+ * </ul>
+ *
+ * <p> The <a href="http://dione.zcu.cz/~toman40/miniRMI/">miniRMI</a>
+ * project contains a compiler implementation for this task as well,
+ * please consult miniRMI's documentation to learn how to use it.</p>
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="java"
+ */
+
+public class Rmic extends MatchingTask {
+
+ /** rmic failed message */
+ public static final String ERROR_RMIC_FAILED
+ = "Rmic failed; see the compiler error output for details.";
+
+ private File baseDir;
+ private File destDir;
+ private String classname;
+ private File sourceBase;
+ private String stubVersion;
+ private Path compileClasspath;
+ private Path extDirs;
+ private boolean verify = false;
+ private boolean filtering = false;
+
+ private boolean iiop = false;
+ private String iiopOpts;
+ private boolean idl = false;
+ private String idlOpts;
+ private boolean debug = false;
+ private boolean includeAntRuntime = true;
+ private boolean includeJavaRuntime = false;
+
+ private Vector compileList = new Vector();
+
+ private AntClassLoader loader = null;
+
+ private FacadeTaskHelper facade;
+ /** unable to verify message */
+ public static final String ERROR_UNABLE_TO_VERIFY_CLASS = "Unable to verify class ";
+ /** could not be found message */
+ public static final String ERROR_NOT_FOUND = ". It could not be found.";
+ /** not defined message */
+ public static final String ERROR_NOT_DEFINED = ". It is not defined.";
+ /** loaded error message */
+ public static final String ERROR_LOADING_CAUSED_EXCEPTION = ". Loading caused Exception: ";
+ /** base not exists message */
+ public static final String ERROR_NO_BASE_EXISTS = "base or destdir does not exist: ";
+ /** base not a directory message */
+ public static final String ERROR_NOT_A_DIR = "base or destdir is not a directory:";
+ /** base attribute not set message */
+ public static final String ERROR_BASE_NOT_SET = "base or destdir attribute must be set!";
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private String executable = null;
+
+ private boolean listFiles = false;
+
+ private RmicAdapter nestedAdapter = null;
+
+ /**
+ * Constructor for Rmic.
+ */
+ public Rmic() {
+ facade = new FacadeTaskHelper(RmicAdapterFactory.DEFAULT_COMPILER);
+ }
+
+ /**
+ * Sets the location to store the compiled files; required
+ * @param base the location to store the compiled files
+ */
+ public void setBase(File base) {
+ this.baseDir = base;
+ }
+
+ /**
+ * Sets the base directory to output the generated files.
+ * @param destdir the base directory to output the generated files.
+ * @since Ant 1.8.0
+ */
+ public void setDestdir(File destdir) {
+ this.destDir = destdir;
+ }
+
+ /**
+ * Gets the base directory to output the generated files.
+ * @return the base directory to output the generated files.
+ * @since Ant 1.8.0
+ */
+ public File getDestdir() {
+ return this.destDir;
+ }
+
+ /**
+ * Gets the base directory to output the generated files,
+ * favoring destdir if set, otherwise defaulting to basedir.
+ * @return the actual directory to output to (either destdir or basedir)
+ * @since Ant 1.8.0
+ */
+ public File getOutputDir() {
+ if (getDestdir() != null) {
+ return getDestdir();
+ }
+ return getBase();
+ }
+
+ /**
+ * Gets the base directory to output generated class.
+ * @return the location of the compiled files
+ */
+ public File getBase() {
+ return this.baseDir;
+ }
+
+ /**
+ * Sets the class to run <code>rmic</code> against;
+ * optional
+ * @param classname the name of the class for rmic to create code for
+ */
+ public void setClassname(String classname) {
+ this.classname = classname;
+ }
+
+ /**
+ * Gets the class name to compile.
+ * @return the name of the class to compile
+ */
+ public String getClassname() {
+ return classname;
+ }
+
+ /**
+ * optional directory to save generated source files to.
+ * @param sourceBase the directory to save source files to.
+ */
+ public void setSourceBase(File sourceBase) {
+ this.sourceBase = sourceBase;
+ }
+
+ /**
+ * Gets the source dirs to find the source java files.
+ * @return sourceBase the directory containing the source files.
+ */
+ public File getSourceBase() {
+ return sourceBase;
+ }
+
+ /**
+ * Specify the JDK version for the generated stub code.
+ * Specify &quot;1.1&quot; to pass the &quot;-v1.1&quot; option to rmic.
+ * @param stubVersion the JDK version
+ */
+ public void setStubVersion(String stubVersion) {
+ this.stubVersion = stubVersion;
+ }
+
+ /**
+ * Gets the JDK version for the generated stub code.
+ * @return stubVersion
+ */
+ public String getStubVersion() {
+ return stubVersion;
+ }
+
+ /**
+ * Sets token filtering [optional], default=false
+ * @param filter turn on token filtering
+ */
+ public void setFiltering(boolean filter) {
+ this.filtering = filter;
+ }
+
+ /**
+ * Gets whether token filtering is set
+ * @return filtering
+ */
+ public boolean getFiltering() {
+ return filtering;
+ }
+
+ /**
+ * Generate debug info (passes -g to rmic);
+ * optional, defaults to false
+ * @param debug turn on debug info
+ */
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ }
+
+ /**
+ * Gets the debug flag.
+ * @return debug
+ */
+ public boolean getDebug() {
+ return debug;
+ }
+
+ /**
+ * Set the classpath to be used for this compilation.
+ * @param classpath the classpath used for this compilation
+ */
+ public synchronized void setClasspath(Path classpath) {
+ if (compileClasspath == null) {
+ compileClasspath = classpath;
+ } else {
+ compileClasspath.append(classpath);
+ }
+ }
+
+ /**
+ * Creates a nested classpath element.
+ * @return classpath
+ */
+ public synchronized Path createClasspath() {
+ if (compileClasspath == null) {
+ compileClasspath = new Path(getProject());
+ }
+ return compileClasspath.createPath();
+ }
+
+ /**
+ * Adds to the classpath a reference to
+ * a &lt;path&gt; defined elsewhere.
+ * @param pathRef the reference to add to the classpath
+ */
+ public void setClasspathRef(Reference pathRef) {
+ createClasspath().setRefid(pathRef);
+ }
+
+ /**
+ * Gets the classpath.
+ * @return the classpath
+ */
+ public Path getClasspath() {
+ return compileClasspath;
+ }
+
+ /**
+ * Flag to enable verification so that the classes
+ * found by the directory match are
+ * checked to see if they implement java.rmi.Remote.
+ * optional; This defaults to false if not set.
+ * @param verify turn on verification for classes
+ */
+ public void setVerify(boolean verify) {
+ this.verify = verify;
+ }
+
+ /**
+ * Get verify flag.
+ * @return verify
+ */
+ public boolean getVerify() {
+ return verify;
+ }
+
+ /**
+ * Indicates that IIOP compatible stubs should
+ * be generated; optional, defaults to false
+ * if not set.
+ * @param iiop generate IIOP compatible stubs
+ */
+ public void setIiop(boolean iiop) {
+ this.iiop = iiop;
+ }
+
+ /**
+ * Gets iiop flags.
+ * @return iiop
+ */
+ public boolean getIiop() {
+ return iiop;
+ }
+
+ /**
+ * Set additional arguments for iiop
+ * @param iiopOpts additional arguments for iiop
+ */
+ public void setIiopopts(String iiopOpts) {
+ this.iiopOpts = iiopOpts;
+ }
+
+ /**
+ * Gets additional arguments for iiop.
+ * @return iiopOpts
+ */
+ public String getIiopopts() {
+ return iiopOpts;
+ }
+
+ /**
+ * Indicates that IDL output should be
+ * generated. This defaults to false
+ * if not set.
+ * @param idl generate IDL output
+ */
+ public void setIdl(boolean idl) {
+ this.idl = idl;
+ }
+
+ /**
+ * Gets IDL flags.
+ * @return the idl flag
+ */
+ public boolean getIdl() {
+ return idl;
+ }
+
+ /**
+ * pass additional arguments for IDL compile
+ * @param idlOpts additional IDL arguments
+ */
+ public void setIdlopts(String idlOpts) {
+ this.idlOpts = idlOpts;
+ }
+
+ /**
+ * Gets additional arguments for idl compile.
+ * @return the idl options
+ */
+ public String getIdlopts() {
+ return idlOpts;
+ }
+
+ /**
+ * Gets file list to compile.
+ * @return the list of files to compile.
+ */
+ public Vector getFileList() {
+ return compileList;
+ }
+
+ /**
+ * Sets whether or not to include ant's own classpath in this task's
+ * classpath.
+ * Optional; default is <code>true</code>.
+ * @param include if true include ant's classpath
+ */
+ public void setIncludeantruntime(boolean include) {
+ includeAntRuntime = include;
+ }
+
+ /**
+ * Gets whether or not the ant classpath is to be included in the
+ * task's classpath.
+ * @return true if ant's classpath is to be included
+ */
+ public boolean getIncludeantruntime() {
+ return includeAntRuntime;
+ }
+
+ /**
+ * task's classpath.
+ * Enables or disables including the default run-time
+ * libraries from the executing VM; optional,
+ * defaults to false
+ * @param include if true include default run-time libraries
+ */
+ public void setIncludejavaruntime(boolean include) {
+ includeJavaRuntime = include;
+ }
+
+ /**
+ * Gets whether or not the java runtime should be included in this
+ * task's classpath.
+ * @return true if default run-time libraries are included
+ */
+ public boolean getIncludejavaruntime() {
+ return includeJavaRuntime;
+ }
+
+ /**
+ * Sets the extension directories that will be used during the
+ * compilation; optional.
+ * @param extDirs the extension directories to be used
+ */
+ public synchronized void setExtdirs(Path extDirs) {
+ if (this.extDirs == null) {
+ this.extDirs = extDirs;
+ } else {
+ this.extDirs.append(extDirs);
+ }
+ }
+
+ /**
+ * Maybe creates a nested extdirs element.
+ * @return path object to be configured with the extension directories
+ */
+ public synchronized Path createExtdirs() {
+ if (extDirs == null) {
+ extDirs = new Path(getProject());
+ }
+ return extDirs.createPath();
+ }
+
+ /**
+ * Gets the extension directories that will be used during the
+ * compilation.
+ * @return the extension directories to be used
+ */
+ public Path getExtdirs() {
+ return extDirs;
+ }
+
+ /**
+ * @return the compile list.
+ */
+ public Vector getCompileList() {
+ return compileList;
+ }
+
+ /**
+ * Sets the compiler implementation to use; optional,
+ * defaults to the value of the <code>build.rmic</code> property,
+ * or failing that, default compiler for the current VM
+ * @param compiler the compiler implementation to use
+ * @since Ant 1.5
+ */
+ public void setCompiler(String compiler) {
+ if (compiler.length() > 0) {
+ facade.setImplementation(compiler);
+ }
+ }
+
+ /**
+ * get the name of the current compiler
+ * @return the name of the compiler
+ * @since Ant 1.5
+ */
+ public String getCompiler() {
+ facade.setMagicValue(getProject().getProperty("build.rmic"));
+ return facade.getImplementation();
+ }
+
+ /**
+ * Adds an implementation specific command line argument.
+ * @return an object to be configured with a command line argument
+ * @since Ant 1.5
+ */
+ public ImplementationSpecificArgument createCompilerArg() {
+ ImplementationSpecificArgument arg = new ImplementationSpecificArgument();
+ facade.addImplementationArgument(arg);
+ return arg;
+ }
+
+ /**
+ * Get the additional implementation specific command line arguments.
+ * @return array of command line arguments, guaranteed to be non-null.
+ * @since Ant 1.5
+ */
+ public String[] getCurrentCompilerArgs() {
+ getCompiler();
+ return facade.getArgs();
+ }
+
+ /**
+ * Name of the executable to use when forking.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setExecutable(String ex) {
+ executable = ex;
+ }
+
+ /**
+ * Explicitly specified name of the executable to use when forking
+ * - if any.
+ *
+ * @since Ant 1.8.0
+ */
+ public String getExecutable() {
+ return executable;
+ }
+
+ /**
+ * The classpath to use when loading the compiler implementation
+ * if it is not a built-in one.
+ *
+ * @since Ant 1.8.0
+ */
+ public Path createCompilerClasspath() {
+ return facade.getImplementationClasspath(getProject());
+ }
+
+ /**
+ * If true, list the source files being handed off to the compiler.
+ * @param list if true list the source files
+ * @since Ant 1.8.0
+ */
+ public void setListfiles(boolean list) {
+ listFiles = list;
+ }
+
+ /**
+ * Set the compiler adapter explicitly.
+ * @since Ant 1.8.0
+ */
+ public void add(RmicAdapter adapter) {
+ if (nestedAdapter != null) {
+ throw new BuildException("Can't have more than one rmic adapter");
+ }
+ nestedAdapter = adapter;
+ }
+
+ /**
+ * execute by creating an instance of an implementation
+ * class and getting to do the work
+ * @throws org.apache.tools.ant.BuildException
+ * if there's a problem with baseDir or RMIC
+ */
+ @Override
+ public void execute() throws BuildException {
+ try {
+ compileList.clear();
+
+ File outputDir = getOutputDir();
+ if (outputDir == null) {
+ throw new BuildException(ERROR_BASE_NOT_SET, getLocation());
+ }
+ if (!outputDir.exists()) {
+ throw new BuildException(ERROR_NO_BASE_EXISTS + outputDir,
+ getLocation());
+ }
+ if (!outputDir.isDirectory()) {
+ throw new BuildException(ERROR_NOT_A_DIR + outputDir, getLocation());
+ }
+ if (verify) {
+ log("Verify has been turned on.", Project.MSG_VERBOSE);
+ }
+ RmicAdapter adapter =
+ nestedAdapter != null ? nestedAdapter :
+ RmicAdapterFactory.getRmic(getCompiler(), this,
+ createCompilerClasspath());
+
+ // now we need to populate the compiler adapter
+ adapter.setRmic(this);
+
+ Path classpath = adapter.getClasspath();
+ loader = getProject().createClassLoader(classpath);
+
+ // scan base dirs to build up compile lists only if a
+ // specific classname is not given
+ if (classname == null) {
+ DirectoryScanner ds = this.getDirectoryScanner(baseDir);
+ String[] files = ds.getIncludedFiles();
+ scanDir(baseDir, files, adapter.getMapper());
+ } else {
+ // otherwise perform a timestamp comparison - at least
+ String path = classname.replace('.', File.separatorChar)
+ + ".class";
+ File f = new File(baseDir, path);
+ if (f.isFile()) {
+ scanDir(baseDir, new String[] {path}, adapter.getMapper());
+ } else {
+ // Does not exist, so checking whether it is up to
+ // date makes no sense. Compilation will fail
+ // later anyway, but tests expect a certain
+ // output.
+ compileList.add(classname);
+ }
+ }
+ int fileCount = compileList.size();
+ if (fileCount > 0) {
+ log("RMI Compiling " + fileCount + " class"
+ + (fileCount > 1 ? "es" : "") + " to "
+ + outputDir, Project.MSG_INFO);
+
+ if (listFiles) {
+ for (int i = 0; i < fileCount; i++) {
+ log(compileList.get(i).toString());
+ }
+ }
+
+ // finally, lets execute the compiler!!
+ if (!adapter.execute()) {
+ throw new BuildException(ERROR_RMIC_FAILED, getLocation());
+ }
+ }
+ /*
+ * Move the generated source file to the base directory. If
+ * base directory and sourcebase are the same, the generated
+ * sources are already in place.
+ */
+ if (null != sourceBase && !outputDir.equals(sourceBase)
+ && fileCount > 0) {
+ if (idl) {
+ log("Cannot determine sourcefiles in idl mode, ",
+ Project.MSG_WARN);
+ log("sourcebase attribute will be ignored.",
+ Project.MSG_WARN);
+ } else {
+ for (int j = 0; j < fileCount; j++) {
+ moveGeneratedFile(outputDir, sourceBase,
+ (String) compileList.elementAt(j),
+ adapter);
+ }
+ }
+ }
+ } finally {
+ cleanup();
+ }
+ }
+
+ /**
+ * Cleans up resources.
+ *
+ * @since Ant 1.8.0
+ */
+ protected void cleanup() {
+ if (loader != null) {
+ loader.cleanup();
+ loader = null;
+ }
+ }
+
+ /**
+ * Move the generated source file(s) to the base directory
+ *
+ * @throws org.apache.tools.ant.BuildException When error
+ * copying/removing files.
+ */
+ private void moveGeneratedFile(File baseDir, File sourceBaseFile, String classname,
+ RmicAdapter adapter) throws BuildException {
+ String classFileName = classname.replace('.', File.separatorChar)
+ + ".class";
+ String[] generatedFiles = adapter.getMapper().mapFileName(classFileName);
+
+ for (int i = 0; i < generatedFiles.length; i++) {
+ final String generatedFile = generatedFiles[i];
+ if (!generatedFile.endsWith(".class")) {
+ // don't know how to handle that - a IDL file doesn't
+ // have a corresponding Java source for example.
+ continue;
+ }
+ String sourceFileName = StringUtils.removeSuffix(generatedFile,
+ ".class")
+ + ".java";
+
+ File oldFile = new File(baseDir, sourceFileName);
+ if (!oldFile.exists()) {
+ // no source file generated, nothing to move
+ continue;
+ }
+
+ File newFile = new File(sourceBaseFile, sourceFileName);
+ try {
+ if (filtering) {
+ FILE_UTILS.copyFile(oldFile, newFile,
+ new FilterSetCollection(getProject()
+ .getGlobalFilterSet()));
+ } else {
+ FILE_UTILS.copyFile(oldFile, newFile);
+ }
+ oldFile.delete();
+ } catch (IOException ioe) {
+ String msg = "Failed to copy " + oldFile + " to " + newFile
+ + " due to "
+ + ioe.getMessage();
+ throw new BuildException(msg, ioe, getLocation());
+ }
+ }
+ }
+
+ /**
+ * Scans the directory looking for class files to be compiled.
+ * The result is returned in the class variable compileList.
+ * @param baseDir the base direction
+ * @param files the list of files to scan
+ * @param mapper the mapper of files to target files
+ */
+ protected void scanDir(File baseDir, String[] files, FileNameMapper mapper) {
+ String[] newFiles = files;
+ if (idl) {
+ log("will leave uptodate test to rmic implementation in idl mode.",
+ Project.MSG_VERBOSE);
+ } else if (iiop && iiopOpts != null && iiopOpts.indexOf("-always") > -1) {
+ log("no uptodate test as -always option has been specified",
+ Project.MSG_VERBOSE);
+ } else {
+ SourceFileScanner sfs = new SourceFileScanner(this);
+ newFiles = sfs.restrict(files, baseDir, getOutputDir(), mapper);
+ }
+ for (int i = 0; i < newFiles.length; i++) {
+ String name = newFiles[i].replace(File.separatorChar, '.');
+ name = name.substring(0, name.lastIndexOf(".class"));
+ compileList.addElement(name);
+ }
+ }
+
+ /**
+ * Load named class and test whether it can be rmic'ed
+ * @param classname the name of the class to be tested
+ * @return true if the class can be rmic'ed
+ */
+ public boolean isValidRmiRemote(String classname) {
+ try {
+ Class testClass = loader.loadClass(classname);
+ // One cannot RMIC an interface for "classic" RMI (JRMP)
+ if (testClass.isInterface() && !iiop && !idl) {
+ return false;
+ }
+ return isValidRmiRemote(testClass);
+ } catch (ClassNotFoundException e) {
+ log(ERROR_UNABLE_TO_VERIFY_CLASS + classname + ERROR_NOT_FOUND,
+ Project.MSG_WARN);
+ } catch (NoClassDefFoundError e) {
+ log(ERROR_UNABLE_TO_VERIFY_CLASS + classname + ERROR_NOT_DEFINED,
+ Project.MSG_WARN);
+ } catch (Throwable t) {
+ log(ERROR_UNABLE_TO_VERIFY_CLASS + classname
+ + ERROR_LOADING_CAUSED_EXCEPTION + t.getMessage(),
+ Project.MSG_WARN);
+ }
+ // we only get here if an exception has been thrown
+ return false;
+ }
+
+ /**
+ * Returns the topmost interface that extends Remote for a given
+ * class - if one exists.
+ * @param testClass the class to be tested
+ * @return the topmost interface that extends Remote, or null if there
+ * is none.
+ */
+ public Class getRemoteInterface(Class testClass) {
+ if (Remote.class.isAssignableFrom(testClass)) {
+ Class [] interfaces = testClass.getInterfaces();
+ if (interfaces != null) {
+ for (int i = 0; i < interfaces.length; i++) {
+ if (Remote.class.isAssignableFrom(interfaces[i])) {
+ return interfaces[i];
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Check to see if the class or (super)interfaces implement
+ * java.rmi.Remote.
+ */
+ private boolean isValidRmiRemote (Class testClass) {
+ return getRemoteInterface(testClass) != null;
+ }
+
+ /**
+ * Classloader for the user-specified classpath.
+ * @return the classloader
+ */
+ public ClassLoader getLoader() {
+ return loader;
+ }
+
+ /**
+ * Adds an "compiler" attribute to Commandline$Attribute used to
+ * filter command line attributes based on the current
+ * implementation.
+ */
+ public class ImplementationSpecificArgument extends
+ org.apache.tools.ant.util.facade.ImplementationSpecificArgument {
+ /**
+ * Only pass the specified argument if the
+ * chosen compiler implementation matches the
+ * value of this attribute. Legal values are
+ * the same as those in the above list of
+ * valid compilers.)
+ * @param impl the compiler to be used.
+ */
+ public void setCompiler(String impl) {
+ super.setImplementation(impl);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SQLExec.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SQLExec.java
new file mode 100644
index 00000000..6d1e5148
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SQLExec.java
@@ -0,0 +1,1162 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.sql.Blob;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.Appendable;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.KeepAliveOutputStream;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Executes a series of SQL statements on a database using JDBC.
+ *
+ * <p>Statements can
+ * either be read in from a text file using the <i>src</i> attribute or from
+ * between the enclosing SQL tags.</p>
+ *
+ * <p>Multiple statements can be provided, separated by semicolons (or the
+ * defined <i>delimiter</i>). Individual lines within the statements can be
+ * commented using either --, // or REM at the start of the line.</p>
+ *
+ * <p>The <i>autocommit</i> attribute specifies whether auto-commit should be
+ * turned on or off whilst executing the statements. If auto-commit is turned
+ * on each statement will be executed and committed. If it is turned off the
+ * statements will all be executed as one transaction.</p>
+ *
+ * <p>The <i>onerror</i> attribute specifies how to proceed when an error occurs
+ * during the execution of one of the statements.
+ * The possible values are: <b>continue</b> execution, only show the error;
+ * <b>stop</b> execution and commit transaction;
+ * and <b>abort</b> execution and transaction and fail task.</p>
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task name="sql" category="database"
+ */
+public class SQLExec extends JDBCTask {
+
+ /**
+ * delimiters we support, "normal" and "row"
+ */
+ public static class DelimiterType extends EnumeratedAttribute {
+ /** The enumerated strings */
+ public static final String NORMAL = "normal", ROW = "row";
+ /** @return the enumerated strings */
+ @Override
+ public String[] getValues() {
+ return new String[] {NORMAL, ROW};
+ }
+ }
+
+ private int goodSql = 0;
+
+ private int totalSql = 0;
+
+ /**
+ * Database connection
+ */
+ private Connection conn = null;
+
+ /**
+ * files to load
+ */
+ private Union resources;
+
+ /**
+ * SQL statement
+ */
+ private Statement statement = null;
+
+ /**
+ * SQL input file
+ */
+ private File srcFile = null;
+
+ /**
+ * SQL input command
+ */
+ private String sqlCommand = "";
+
+ /**
+ * SQL transactions to perform
+ */
+ private Vector transactions = new Vector();
+
+ /**
+ * SQL Statement delimiter
+ */
+ private String delimiter = ";";
+
+ /**
+ * The delimiter type indicating whether the delimiter will
+ * only be recognized on a line by itself
+ */
+ private String delimiterType = DelimiterType.NORMAL;
+
+ /**
+ * Print SQL results.
+ */
+ private boolean print = false;
+
+ /**
+ * Print header columns.
+ */
+ private boolean showheaders = true;
+
+ /**
+ * Print SQL stats (rows affected)
+ */
+ private boolean showtrailers = true;
+
+ /**
+ * Results Output Resource.
+ */
+ private Resource output = null;
+
+ /**
+ * Output encoding.
+ */
+ private String outputEncoding = null;
+
+ /**
+ * Action to perform if an error is found
+ */
+ private String onError = "abort";
+
+ /**
+ * Encoding to use when reading SQL statements from a file
+ */
+ private String encoding = null;
+
+ /**
+ * Append to an existing file or overwrite it?
+ */
+ private boolean append = false;
+
+ /**
+ * Keep the format of a sql block?
+ */
+ private boolean keepformat = false;
+
+ /**
+ * Argument to Statement.setEscapeProcessing
+ *
+ * @since Ant 1.6
+ */
+ private boolean escapeProcessing = true;
+
+ /**
+ * should properties be expanded in text?
+ * false for backwards compatibility
+ *
+ * @since Ant 1.7
+ */
+ private boolean expandProperties = true;
+
+ /**
+ * should we print raw BLOB data?
+ * @since Ant 1.7.1
+ */
+ private boolean rawBlobs;
+
+ /**
+ * delimiters must match in case and whitespace is significant.
+ * @since Ant 1.8.0
+ */
+ private boolean strictDelimiterMatching = true;
+
+ /**
+ * whether to show SQLWarnings as WARN messages.
+ * @since Ant 1.8.0
+ */
+ private boolean showWarnings = false;
+
+ /**
+ * The column separator used when printing the results.
+ *
+ * <p>Defaults to ","</p>
+ *
+ * @since Ant 1.8.0
+ */
+ private String csvColumnSep = ",";
+
+ /**
+ * The character used to quote column values.
+ *
+ * <p>If set, columns that contain either the column separator or
+ * the quote character itself will be surrounded by the quote
+ * character. The quote character itself will be doubled if it
+ * appears inside of the column's value.</p>
+ *
+ * <p>If this value is not set (the default), no column values
+ * will be quoted, not even if they contain the column
+ * separator.</p>
+ *
+ * <p><b>Note:<b> BLOB values will never be quoted.</p>
+ *
+ * <p>Defaults to "not set"</p>
+ *
+ * @since Ant 1.8.0
+ */
+ private String csvQuoteChar = null;
+
+ /**
+ * Whether a warning is an error - in which case onError applies.
+ * @since Ant 1.8.0
+ */
+ private boolean treatWarningsAsErrors = false;
+
+ /**
+ * The name of the property to set in the event of an error
+ * @since Ant 1.8.0
+ */
+ private String errorProperty = null;
+
+ /**
+ * The name of the property to set in the event of a warning
+ * @since Ant 1.8.0
+ */
+ private String warningProperty = null;
+
+ /**
+ * The name of the property that receives the number of rows
+ * returned
+ * @since Ant 1.8.0
+ */
+ private String rowCountProperty = null;
+
+ /**
+ * Set the name of the SQL file to be run.
+ * Required unless statements are enclosed in the build file
+ * @param srcFile the file containing the SQL command.
+ */
+ public void setSrc(File srcFile) {
+ this.srcFile = srcFile;
+ }
+
+ /**
+ * Enable property expansion inside nested text
+ *
+ * @param expandProperties if true expand properties.
+ * @since Ant 1.7
+ */
+ public void setExpandProperties(boolean expandProperties) {
+ this.expandProperties = expandProperties;
+ }
+
+ /**
+ * is property expansion inside inline text enabled?
+ *
+ * @return true if properties are to be expanded.
+ * @since Ant 1.7
+ */
+ public boolean getExpandProperties() {
+ return expandProperties;
+ }
+
+ /**
+ * Set an inline SQL command to execute.
+ * NB: Properties are not expanded in this text unless {@link #expandProperties}
+ * is set.
+ * @param sql an inline string containing the SQL command.
+ */
+ public void addText(String sql) {
+ //there is no need to expand properties here as that happens when Transaction.addText is
+ //called; to do so here would be an error.
+ this.sqlCommand += sql;
+ }
+
+ /**
+ * Adds a set of files (nested fileset attribute).
+ * @param set a set of files contains SQL commands, each File is run in
+ * a separate transaction.
+ */
+ public void addFileset(FileSet set) {
+ add(set);
+ }
+
+ /**
+ * Adds a collection of resources (nested element).
+ * @param rc a collection of resources containing SQL commands,
+ * each resource is run in a separate transaction.
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection rc) {
+ if (rc == null) {
+ throw new BuildException("Cannot add null ResourceCollection");
+ }
+ synchronized (this) {
+ if (resources == null) {
+ resources = new Union();
+ }
+ }
+ resources.add(rc);
+ }
+
+ /**
+ * Add a SQL transaction to execute
+ * @return a Transaction to be configured.
+ */
+ public Transaction createTransaction() {
+ Transaction t = new Transaction();
+ transactions.addElement(t);
+ return t;
+ }
+
+ /**
+ * Set the file encoding to use on the SQL files read in
+ *
+ * @param encoding the encoding to use on the files
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Set the delimiter that separates SQL statements. Defaults to &quot;;&quot;;
+ * optional
+ *
+ * <p>For example, set this to "go" and delimitertype to "ROW" for
+ * Sybase ASE or MS SQL Server.</p>
+ * @param delimiter the separator.
+ */
+ public void setDelimiter(String delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ /**
+ * Set the delimiter type: "normal" or "row" (default "normal").
+ *
+ * <p>The delimiter type takes two values - normal and row. Normal
+ * means that any occurrence of the delimiter terminate the SQL
+ * command whereas with row, only a line containing just the
+ * delimiter is recognized as the end of the command.</p>
+ * @param delimiterType the type of delimiter - "normal" or "row".
+ */
+ public void setDelimiterType(DelimiterType delimiterType) {
+ this.delimiterType = delimiterType.getValue();
+ }
+
+ /**
+ * Print result sets from the statements;
+ * optional, default false
+ * @param print if true print result sets.
+ */
+ public void setPrint(boolean print) {
+ this.print = print;
+ }
+
+ /**
+ * Print headers for result sets from the
+ * statements; optional, default true.
+ * @param showheaders if true print headers of result sets.
+ */
+ public void setShowheaders(boolean showheaders) {
+ this.showheaders = showheaders;
+ }
+
+ /**
+ * Print trailing info (rows affected) for the SQL
+ * Addresses Bug/Request #27446
+ * @param showtrailers if true prints the SQL rows affected
+ * @since Ant 1.7
+ */
+ public void setShowtrailers(boolean showtrailers) {
+ this.showtrailers = showtrailers;
+ }
+
+ /**
+ * Set the output file;
+ * optional, defaults to the Ant log.
+ * @param output the output file to use for logging messages.
+ */
+ public void setOutput(File output) {
+ setOutput(new FileResource(getProject(), output));
+ }
+
+ /**
+ * Set the output Resource;
+ * optional, defaults to the Ant log.
+ * @param output the output Resource to store results.
+ * @since Ant 1.8
+ */
+ public void setOutput(Resource output) {
+ this.output = output;
+ }
+
+ /**
+ * The encoding to use when writing the result to a resource.
+ * <p>Default's to the platform's default encoding</p>
+ * @param outputEncoding the name of the encoding or null for the
+ * platform's default encoding
+ * @since Ant 1.9.4
+ */
+ public void setOutputEncoding(String outputEncoding) {
+ this.outputEncoding = outputEncoding;
+ }
+
+ /**
+ * whether output should be appended to or overwrite
+ * an existing file. Defaults to false.
+ *
+ * @since Ant 1.5
+ * @param append if true append to an existing file.
+ */
+ public void setAppend(boolean append) {
+ this.append = append;
+ }
+
+
+ /**
+ * Action to perform when statement fails: continue, stop, or abort
+ * optional; default &quot;abort&quot;
+ * @param action the action to perform on statement failure.
+ */
+ public void setOnerror(OnError action) {
+ this.onError = action.getValue();
+ }
+
+ /**
+ * whether or not format should be preserved.
+ * Defaults to false.
+ *
+ * @param keepformat The keepformat to set
+ */
+ public void setKeepformat(boolean keepformat) {
+ this.keepformat = keepformat;
+ }
+
+ /**
+ * Set escape processing for statements.
+ * @param enable if true enable escape processing, default is true.
+ * @since Ant 1.6
+ */
+ public void setEscapeProcessing(boolean enable) {
+ escapeProcessing = enable;
+ }
+
+ /**
+ * Set whether to print raw BLOBs rather than their string (hex) representations.
+ * @param rawBlobs whether to print raw BLOBs.
+ * @since Ant 1.7.1
+ */
+ public void setRawBlobs(boolean rawBlobs) {
+ this.rawBlobs = rawBlobs;
+ }
+
+ /**
+ * If false, delimiters will be searched for in a case-insensitive
+ * manner (i.e. delimiter="go" matches "GO") and surrounding
+ * whitespace will be ignored (delimiter="go" matches "GO ").
+ * @since Ant 1.8.0
+ */
+ public void setStrictDelimiterMatching(boolean b) {
+ strictDelimiterMatching = b;
+ }
+
+ /**
+ * whether to show SQLWarnings as WARN messages.
+ * @since Ant 1.8.0
+ */
+ public void setShowWarnings(boolean b) {
+ showWarnings = b;
+ }
+
+ /**
+ * Whether a warning is an error - in which case onError applies.
+ * @since Ant 1.8.0
+ */
+ public void setTreatWarningsAsErrors(boolean b) {
+ treatWarningsAsErrors = b;
+ }
+
+ /**
+ * The column separator used when printing the results.
+ *
+ * <p>Defaults to ","</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public void setCsvColumnSeparator(String s) {
+ csvColumnSep = s;
+ }
+
+ /**
+ * The character used to quote column values.
+ *
+ * <p>If set, columns that contain either the column separator or
+ * the quote character itself will be surrounded by the quote
+ * character. The quote character itself will be doubled if it
+ * appears inside of the column's value.</p>
+ *
+ * <p>If this value is not set (the default), no column values
+ * will be quoted, not even if they contain the column
+ * separator.</p>
+ *
+ * <p><b>Note:</b> BLOB values will never be quoted.</p>
+ *
+ * <p>Defaults to "not set"</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public void setCsvQuoteCharacter(String s) {
+ if (s != null && s.length() > 1) {
+ throw new BuildException("The quote character must be a single"
+ + " character.");
+ }
+ csvQuoteChar = s;
+ }
+
+ /**
+ * Property to set to "true" if a statement throws an error.
+ *
+ * @param errorProperty the name of the property to set in the
+ * event of an error.
+ * @since Ant 1.8.0
+ */
+ public void setErrorProperty(String errorProperty) {
+ this.errorProperty = errorProperty;
+ }
+
+ /**
+ * Property to set to "true" if a statement produces a warning.
+ *
+ * @param warningProperty the name of the property to set in the
+ * event of a warning.
+ * @since Ant 1.8.0
+ */
+ public void setWarningProperty(String warningProperty) {
+ this.warningProperty = warningProperty;
+ }
+
+ /**
+ * Sets a given property to the number of rows in the first
+ * statement that returned a row count.
+ * @since Ant 1.8.0
+ */
+ public void setRowCountProperty(String rowCountProperty) {
+ this.rowCountProperty = rowCountProperty;
+ }
+
+ /**
+ * Load the sql file and then execute it
+ * @throws BuildException on error.
+ */
+ @Override
+ public void execute() throws BuildException {
+ Vector savedTransaction = (Vector) transactions.clone();
+ String savedSqlCommand = sqlCommand;
+
+ sqlCommand = sqlCommand.trim();
+
+ try {
+ if (srcFile == null && sqlCommand.length() == 0 && resources == null) {
+ if (transactions.size() == 0) {
+ throw new BuildException("Source file or resource collection, "
+ + "transactions or sql statement "
+ + "must be set!", getLocation());
+ }
+ }
+
+ if (srcFile != null && !srcFile.isFile()) {
+ throw new BuildException("Source file " + srcFile
+ + " is not a file!", getLocation());
+ }
+
+ if (resources != null) {
+ // deal with the resources
+ for (Resource r : resources) {
+ // Make a transaction for each resource
+ Transaction t = createTransaction();
+ t.setSrcResource(r);
+ }
+ }
+
+ // Make a transaction group for the outer command
+ Transaction t = createTransaction();
+ t.setSrc(srcFile);
+ t.addText(sqlCommand);
+
+ if (getConnection() == null) {
+ // not a valid rdbms
+ return;
+ }
+
+ try {
+ PrintStream out = KeepAliveOutputStream.wrapSystemOut();
+ try {
+ if (output != null) {
+ log("Opening PrintStream to output Resource " + output, Project.MSG_VERBOSE);
+ OutputStream os = null;
+ FileProvider fp =
+ output.as(FileProvider.class);
+ if (fp != null) {
+ os = new FileOutputStream(fp.getFile(), append);
+ } else {
+ if (append) {
+ Appendable a =
+ output.as(Appendable.class);
+ if (a != null) {
+ os = a.getAppendOutputStream();
+ }
+ }
+ if (os == null) {
+ os = output.getOutputStream();
+ if (append) {
+ log("Ignoring append=true for non-appendable"
+ + " resource " + output,
+ Project.MSG_WARN);
+ }
+ }
+ }
+ if (outputEncoding != null) {
+ out = new PrintStream(new BufferedOutputStream(os),
+ false, outputEncoding);
+ } else {
+ out = new PrintStream(new BufferedOutputStream(os));
+ }
+ }
+
+ // Process all transactions
+ for (Enumeration e = transactions.elements();
+ e.hasMoreElements();) {
+
+ ((Transaction) e.nextElement()).runTransaction(out);
+ if (!isAutocommit()) {
+ log("Committing transaction", Project.MSG_VERBOSE);
+ getConnection().commit();
+ }
+ }
+ } finally {
+ FileUtils.close(out);
+ }
+ } catch (IOException e) {
+ closeQuietly();
+ setErrorProperty();
+ if (onError.equals("abort")) {
+ throw new BuildException(e, getLocation());
+ }
+ } catch (SQLException e) {
+ closeQuietly();
+ setErrorProperty();
+ if (onError.equals("abort")) {
+ throw new BuildException(e, getLocation());
+ }
+ } finally {
+ try {
+ if (getStatement() != null) {
+ getStatement().close();
+ }
+ } catch (SQLException ex) {
+ // ignore
+ }
+ try {
+ if (getConnection() != null) {
+ getConnection().close();
+ }
+ } catch (SQLException ex) {
+ // ignore
+ }
+ }
+
+ log(goodSql + " of " + totalSql + " SQL statements executed successfully");
+ } finally {
+ transactions = savedTransaction;
+ sqlCommand = savedSqlCommand;
+ }
+ }
+
+ /**
+ * read in lines and execute them
+ * @param reader the reader contains sql lines.
+ * @param out the place to output results.
+ * @throws SQLException on sql problems
+ * @throws IOException on io problems
+ */
+ protected void runStatements(Reader reader, PrintStream out)
+ throws SQLException, IOException {
+ StringBuffer sql = new StringBuffer();
+ String line;
+
+ BufferedReader in = new BufferedReader(reader);
+
+ while ((line = in.readLine()) != null) {
+ if (!keepformat) {
+ line = line.trim();
+ }
+ if (expandProperties) {
+ line = getProject().replaceProperties(line);
+ }
+ if (!keepformat) {
+ if (line.startsWith("//")) {
+ continue;
+ }
+ if (line.startsWith("--")) {
+ continue;
+ }
+ StringTokenizer st = new StringTokenizer(line);
+ if (st.hasMoreTokens()) {
+ String token = st.nextToken();
+ if ("REM".equalsIgnoreCase(token)) {
+ continue;
+ }
+ }
+ }
+
+ sql.append(keepformat ? "\n" : " ").append(line);
+
+ // SQL defines "--" as a comment to EOL
+ // and in Oracle it may contain a hint
+ // so we cannot just remove it, instead we must end it
+ if (!keepformat && line.indexOf("--") >= 0) {
+ sql.append("\n");
+ }
+ int lastDelimPos = lastDelimiterPosition(sql, line);
+ if (lastDelimPos > -1) {
+ execSQL(sql.substring(0, lastDelimPos), out);
+ sql.replace(0, sql.length(), "");
+ }
+ }
+ // Catch any statements not followed by ;
+ if (sql.length() > 0) {
+ execSQL(sql.toString(), out);
+ }
+ }
+
+ /**
+ * Exec the sql statement.
+ * @param sql the SQL statement to execute
+ * @param out the place to put output
+ * @throws SQLException on SQL problems
+ */
+ protected void execSQL(String sql, PrintStream out) throws SQLException {
+ // Check and ignore empty statements
+ if ("".equals(sql.trim())) {
+ return;
+ }
+
+ ResultSet resultSet = null;
+ try {
+ totalSql++;
+ log("SQL: " + sql, Project.MSG_VERBOSE);
+
+ boolean ret;
+ int updateCount = 0, updateCountTotal = 0;
+
+ ret = getStatement().execute(sql);
+ updateCount = getStatement().getUpdateCount();
+ do {
+ if (updateCount != -1) {
+ updateCountTotal += updateCount;
+ }
+ if (ret) {
+ resultSet = getStatement().getResultSet();
+ printWarnings(resultSet.getWarnings(), false);
+ resultSet.clearWarnings();
+ if (print) {
+ printResults(resultSet, out);
+ }
+ }
+ ret = getStatement().getMoreResults();
+ updateCount = getStatement().getUpdateCount();
+ } while (ret || updateCount != -1);
+
+ printWarnings(getStatement().getWarnings(), false);
+ getStatement().clearWarnings();
+
+ log(updateCountTotal + " rows affected", Project.MSG_VERBOSE);
+ if (updateCountTotal != -1) {
+ setRowCountProperty(updateCountTotal);
+ }
+
+ if (print && showtrailers) {
+ out.println(updateCountTotal + " rows affected");
+ }
+ SQLWarning warning = getConnection().getWarnings();
+ printWarnings(warning, true);
+ getConnection().clearWarnings();
+ goodSql++;
+ } catch (SQLException e) {
+ log("Failed to execute: " + sql, Project.MSG_ERR);
+ setErrorProperty();
+ if (!onError.equals("abort")) {
+ log(e.toString(), Project.MSG_ERR);
+ }
+ if (!onError.equals("continue")) {
+ throw e;
+ }
+ } finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ } catch (SQLException e) {
+ //ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * print any results in the statement
+ * @deprecated since 1.6.x.
+ * Use {@link #printResults(java.sql.ResultSet, java.io.PrintStream)
+ * the two arg version} instead.
+ * @param out the place to print results
+ * @throws SQLException on SQL problems.
+ */
+ @Deprecated
+ protected void printResults(PrintStream out) throws SQLException {
+ ResultSet rs = getStatement().getResultSet();
+ try {
+ printResults(rs, out);
+ } finally {
+ if (rs != null) {
+ rs.close();
+ }
+ }
+ }
+
+ /**
+ * print any results in the result set.
+ * @param rs the resultset to print information about
+ * @param out the place to print results
+ * @throws SQLException on SQL problems.
+ * @since Ant 1.6.3
+ */
+ protected void printResults(ResultSet rs, PrintStream out) throws SQLException {
+ if (rs != null) {
+ log("Processing new result set.", Project.MSG_VERBOSE);
+ ResultSetMetaData md = rs.getMetaData();
+ int columnCount = md.getColumnCount();
+ if (columnCount > 0) {
+ if (showheaders) {
+ out.print(md.getColumnName(1));
+ for (int col = 2; col <= columnCount; col++) {
+ out.print(csvColumnSep);
+ out.print(maybeQuote(md.getColumnName(col)));
+ }
+ out.println();
+ }
+ while (rs.next()) {
+ printValue(rs, 1, out);
+ for (int col = 2; col <= columnCount; col++) {
+ out.print(csvColumnSep);
+ printValue(rs, col, out);
+ }
+ out.println();
+ printWarnings(rs.getWarnings(), false);
+ }
+ }
+ }
+ out.println();
+ }
+
+ private void printValue(ResultSet rs, int col, PrintStream out)
+ throws SQLException {
+ if (rawBlobs && rs.getMetaData().getColumnType(col) == Types.BLOB) {
+ Blob blob = rs.getBlob(col);
+ if (blob != null) {
+ new StreamPumper(rs.getBlob(col).getBinaryStream(), out).run();
+ }
+ } else {
+ out.print(maybeQuote(rs.getString(col)));
+ }
+ }
+
+ private String maybeQuote(String s) {
+ if (csvQuoteChar == null || s == null
+ || (s.indexOf(csvColumnSep) == -1 && s.indexOf(csvQuoteChar) == -1)
+ ) {
+ return s;
+ }
+ StringBuffer sb = new StringBuffer(csvQuoteChar);
+ int len = s.length();
+ char q = csvQuoteChar.charAt(0);
+ for (int i = 0; i < len; i++) {
+ char c = s.charAt(i);
+ if (c == q) {
+ sb.append(q);
+ }
+ sb.append(c);
+ }
+ return sb.append(csvQuoteChar).toString();
+ }
+
+ /*
+ * Closes an unused connection after an error and doesn't rethrow
+ * a possible SQLException
+ * @since Ant 1.7
+ */
+ private void closeQuietly() {
+ if (!isAutocommit() && getConnection() != null && onError.equals("abort")) {
+ try {
+ getConnection().rollback();
+ } catch (SQLException ex) {
+ // ignore
+ }
+ }
+ }
+
+
+ /**
+ * Caches the connection returned by the base class's getConnection method.
+ *
+ * <p>Subclasses that need to provide a different connection than
+ * the base class would, should override this method but keep in
+ * mind that this class expects to get the same connection
+ * instance on consecutive calls.</p>
+ *
+ * <p>returns null if the connection does not connect to the
+ * expected RDBMS.</p>
+ */
+ @Override
+ protected Connection getConnection() {
+ if (conn == null) {
+ conn = super.getConnection();
+ if (!isValidRdbms(conn)) {
+ conn = null;
+ }
+ }
+ return conn;
+ }
+
+ /**
+ * Creates and configures a Statement instance which is then
+ * cached for subsequent calls.
+ *
+ * <p>Subclasses that want to provide different Statement
+ * instances, should override this method but keep in mind that
+ * this class expects to get the same connection instance on
+ * consecutive calls.</p>
+ */
+ protected Statement getStatement() throws SQLException {
+ if (statement == null) {
+ statement = getConnection().createStatement();
+ statement.setEscapeProcessing(escapeProcessing);
+ }
+
+ return statement;
+ }
+
+ /**
+ * The action a task should perform on an error,
+ * one of "continue", "stop" and "abort"
+ */
+ public static class OnError extends EnumeratedAttribute {
+ /** @return the enumerated values */
+ @Override
+ public String[] getValues() {
+ return new String[] {"continue", "stop", "abort"};
+ }
+ }
+
+ /**
+ * Contains the definition of a new transaction element.
+ * Transactions allow several files or blocks of statements
+ * to be executed using the same JDBC connection and commit
+ * operation in between.
+ */
+ public class Transaction {
+ private Resource tSrcResource = null;
+ private String tSqlCommand = "";
+
+ /**
+ * Set the source file attribute.
+ * @param src the source file
+ */
+ public void setSrc(File src) {
+ //there are places (in this file, and perhaps elsewhere, where it is assumed
+ //that null is an acceptable parameter.
+ if (src != null) {
+ setSrcResource(new FileResource(src));
+ }
+ }
+
+ /**
+ * Set the source resource attribute.
+ * @param src the source file
+ * @since Ant 1.7
+ */
+ public void setSrcResource(Resource src) {
+ if (tSrcResource != null) {
+ throw new BuildException("only one resource per transaction");
+ }
+ tSrcResource = src;
+ }
+
+ /**
+ * Set inline text
+ * @param sql the inline text
+ */
+ public void addText(String sql) {
+ if (sql != null) {
+ this.tSqlCommand += sql;
+ }
+ }
+
+ /**
+ * Set the source resource.
+ * @param a the source resource collection.
+ * @since Ant 1.7
+ */
+ public void addConfigured(ResourceCollection a) {
+ if (a.size() != 1) {
+ throw new BuildException("only single argument resource "
+ + "collections are supported.");
+ }
+ setSrcResource(a.iterator().next());
+ }
+
+ /**
+ *
+ */
+ private void runTransaction(PrintStream out)
+ throws IOException, SQLException {
+ if (tSqlCommand.length() != 0) {
+ log("Executing commands", Project.MSG_INFO);
+ runStatements(new StringReader(tSqlCommand), out);
+ }
+
+ if (tSrcResource != null) {
+ log("Executing resource: " + tSrcResource.toString(),
+ Project.MSG_INFO);
+ InputStream is = null;
+ Reader reader = null;
+ try {
+ is = tSrcResource.getInputStream();
+ reader = (encoding == null) ? new InputStreamReader(is)
+ : new InputStreamReader(is, encoding);
+ runStatements(reader, out);
+ } finally {
+ FileUtils.close(is);
+ FileUtils.close(reader);
+ }
+ }
+ }
+ }
+
+ public int lastDelimiterPosition(StringBuffer buf, String currentLine) {
+ if (strictDelimiterMatching) {
+ if ((delimiterType.equals(DelimiterType.NORMAL)
+ && StringUtils.endsWith(buf, delimiter)) ||
+ (delimiterType.equals(DelimiterType.ROW)
+ && currentLine.equals(delimiter))) {
+ return buf.length() - delimiter.length();
+ }
+ // no match
+ return -1;
+ } else {
+ String d = delimiter.trim().toLowerCase(Locale.ENGLISH);
+ if (delimiterType.equals(DelimiterType.NORMAL)) {
+ // still trying to avoid wasteful copying, see
+ // StringUtils.endsWith
+ int endIndex = delimiter.length() - 1;
+ int bufferIndex = buf.length() - 1;
+ while (bufferIndex >= 0
+ && Character.isWhitespace(buf.charAt(bufferIndex))) {
+ --bufferIndex;
+ }
+ if (bufferIndex < endIndex) {
+ return -1;
+ }
+ while (endIndex >= 0) {
+ if (buf.substring(bufferIndex, bufferIndex + 1)
+ .toLowerCase(Locale.ENGLISH).charAt(0)
+ != d.charAt(endIndex)) {
+ return -1;
+ }
+ bufferIndex--;
+ endIndex--;
+ }
+ return bufferIndex + 1;
+ } else {
+ return currentLine.trim().toLowerCase(Locale.ENGLISH).equals(d)
+ ? buf.length() - currentLine.length() : -1;
+ }
+ }
+ }
+
+ private void printWarnings(SQLWarning warning, boolean force)
+ throws SQLException {
+ SQLWarning initialWarning = warning;
+ if (showWarnings || force) {
+ while (warning != null) {
+ log(warning + " sql warning",
+ showWarnings ? Project.MSG_WARN : Project.MSG_VERBOSE);
+ warning = warning.getNextWarning();
+ }
+ }
+ if (initialWarning != null) {
+ setWarningProperty();
+ }
+ if (treatWarningsAsErrors && initialWarning != null) {
+ throw initialWarning;
+ }
+ }
+
+ protected final void setErrorProperty() {
+ setProperty(errorProperty, "true");
+ }
+
+ protected final void setWarningProperty() {
+ setProperty(warningProperty, "true");
+ }
+
+ protected final void setRowCountProperty(int rowCount) {
+ setProperty(rowCountProperty, Integer.toString(rowCount));
+ }
+
+ private void setProperty(String name, String value) {
+ if (name != null) {
+ getProject().setNewProperty(name, value);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SendEmail.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SendEmail.java
new file mode 100644
index 00000000..58d5cdc0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SendEmail.java
@@ -0,0 +1,45 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.taskdefs.email.EmailTask;
+
+/**
+ * A task to send SMTP email.
+ * This task can send mail using either plain
+ * text, UU encoding or Mime format mail depending on what is available.
+ * Attachments may be sent using nested FileSet
+ * elements.
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task name="mail" category="network"
+ */
+public class SendEmail extends EmailTask {
+ /**
+ * Sets the mailport parameter of this build task.
+ * @param value mail port name.
+ *
+ * @deprecated since 1.5.x.
+ * Use {@link #setMailport(int)} instead.
+ */
+ public void setMailport(Integer value) {
+ setMailport(value.intValue());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sequential.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sequential.java
new file mode 100644
index 00000000..468ac148
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sequential.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.tools.ant.taskdefs;
+
+import java.util.Iterator;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.property.LocalProperties;
+
+/**
+ * Sequential is a container task - it can contain other Ant tasks. The nested
+ * tasks are simply executed in sequence. Sequential's primary use is to support
+ * the sequential execution of a subset of tasks within the {@link Parallel Parallel Task}
+
+ * <p>
+ * The sequential task has no attributes and does not support any nested
+ * elements apart from Ant tasks. Any valid Ant task may be embedded within the
+ * sequential task.</p>
+ *
+ * @since Ant 1.4
+ * @ant.task category="control"
+ */
+public class Sequential extends Task implements TaskContainer {
+
+ /** Optional Vector holding the nested tasks */
+ private Vector nestedTasks = new Vector();
+
+ /**
+ * Add a nested task to Sequential.
+ * <p>
+ * @param nestedTask Nested task to execute Sequential
+ * <p>
+ */
+ public void addTask(Task nestedTask) {
+ nestedTasks.addElement(nestedTask);
+ }
+
+ /**
+ * Execute all nestedTasks.
+ *
+ * @throws BuildException if one of the nested tasks fails.
+ */
+ public void execute() throws BuildException {
+ LocalProperties localProperties
+ = LocalProperties.get(getProject());
+ localProperties.enterScope();
+ try {
+ for (Iterator i = nestedTasks.iterator(); i.hasNext();) {
+ Task nestedTask = (Task) i.next();
+ nestedTask.perform();
+ }
+ } finally {
+ localProperties.exitScope();
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SignJar.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SignJar.java
new file mode 100644
index 00000000..fc31b1d3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SignJar.java
@@ -0,0 +1,645 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.IsSigned;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.ant.util.ResourceUtils;
+
+/**
+ * Signs JAR or ZIP files with the javasign command line tool. The tool detailed
+ * dependency checking: files are only signed if they are not signed. The
+ * <tt>signjar</tt> attribute can point to the file to generate; if this file
+ * exists then its modification date is used as a cue as to whether to resign
+ * any JAR file.
+ *
+ * Timestamp driven signing is based on the unstable and inadequately documented
+ * information in the Java1.5 docs
+ * @see <a href="http://java.sun.com/j2se/1.5.0/docs/guide/security/time-of-signing-beta1.html">
+ * beta documentation</a>
+ * @ant.task category="java"
+ * @since Ant 1.1
+ */
+public class SignJar extends AbstractJarSignerTask {
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * name to a signature file
+ */
+ protected String sigfile;
+
+ /**
+ * name of a single jar
+ */
+ protected File signedjar;
+
+ /**
+ * flag for internal sf signing
+ */
+ protected boolean internalsf;
+
+ /**
+ * sign sections only?
+ */
+ protected boolean sectionsonly;
+
+ /**
+ * flag to preserve timestamp on modified files
+ */
+ private boolean preserveLastModified;
+
+ /**
+ * Whether to assume a jar which has an appropriate .SF file in is already
+ * signed.
+ */
+ protected boolean lazy;
+
+ /**
+ * the output directory when using paths.
+ */
+ protected File destDir;
+
+ /**
+ * mapper for todir work
+ */
+ private FileNameMapper mapper;
+
+ /**
+ * URL for a tsa; null implies no tsa support
+ */
+ protected String tsaurl;
+
+ /**
+ * Proxy host to be used when connecting to TSA server
+ */
+ protected String tsaproxyhost;
+
+ /**
+ * Proxy port to be used when connecting to TSA server
+ */
+ protected String tsaproxyport;
+
+ /**
+ * alias for the TSA in the keystore
+ */
+ protected String tsacert;
+
+ /**
+ * force signing even if the jar is already signed.
+ */
+ private boolean force = false;
+
+ /**
+ * signature algorithm
+ */
+ private String sigAlg;
+
+ /**
+ * digest algorithm
+ */
+ private String digestAlg;
+
+ /**
+ * error string for unit test verification: {@value}
+ */
+ public static final String ERROR_TODIR_AND_SIGNEDJAR
+ = "'destdir' and 'signedjar' cannot both be set";
+ /**
+ * error string for unit test verification: {@value}
+ */
+ public static final String ERROR_TOO_MANY_MAPPERS = "Too many mappers";
+ /**
+ * error string for unit test verification {@value}
+ */
+ public static final String ERROR_SIGNEDJAR_AND_PATHS
+ = "You cannot specify the signed JAR when using paths or filesets";
+ /**
+ * error string for unit test verification: {@value}
+ */
+ public static final String ERROR_BAD_MAP = "Cannot map source file to anything sensible: ";
+ /**
+ * error string for unit test verification: {@value}
+ */
+ public static final String ERROR_MAPPER_WITHOUT_DEST
+ = "The destDir attribute is required if a mapper is set";
+ /**
+ * error string for unit test verification: {@value}
+ */
+ public static final String ERROR_NO_ALIAS = "alias attribute must be set";
+ /**
+ * error string for unit test verification: {@value}
+ */
+ public static final String ERROR_NO_STOREPASS = "storepass attribute must be set";
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * name of .SF/.DSA file; optional
+ *
+ * @param sigfile the name of the .SF/.DSA file
+ */
+ public void setSigfile(final String sigfile) {
+ this.sigfile = sigfile;
+ }
+
+ /**
+ * name of signed JAR file; optional
+ *
+ * @param signedjar the name of the signed jar file
+ */
+ public void setSignedjar(final File signedjar) {
+ this.signedjar = signedjar;
+ }
+
+ /**
+ * Flag to include the .SF file inside the signature; optional; default
+ * false
+ *
+ * @param internalsf if true include the .SF file inside the signature
+ */
+ public void setInternalsf(final boolean internalsf) {
+ this.internalsf = internalsf;
+ }
+
+ /**
+ * flag to compute hash of entire manifest; optional, default false
+ *
+ * @param sectionsonly flag to compute hash of entire manifest
+ */
+ public void setSectionsonly(final boolean sectionsonly) {
+ this.sectionsonly = sectionsonly;
+ }
+
+ /**
+ * flag to control whether the presence of a signature file means a JAR is
+ * signed; optional, default false
+ *
+ * @param lazy flag to control whether the presence of a signature
+ */
+ public void setLazy(final boolean lazy) {
+ this.lazy = lazy;
+ }
+
+ /**
+ * Optionally sets the output directory to be used.
+ *
+ * @param destDir the directory in which to place signed jars
+ * @since Ant 1.7
+ */
+ public void setDestDir(File destDir) {
+ this.destDir = destDir;
+ }
+
+
+ /**
+ * add a mapper to determine file naming policy. Only used with toDir
+ * processing.
+ *
+ * @param newMapper the mapper to add.
+ * @since Ant 1.7
+ */
+ public void add(FileNameMapper newMapper) {
+ if (mapper != null) {
+ throw new BuildException(ERROR_TOO_MANY_MAPPERS);
+ }
+ mapper = newMapper;
+ }
+
+ /**
+ * get the active mapper; may be null
+ * @return mapper or null
+ * @since Ant 1.7
+ */
+ public FileNameMapper getMapper() {
+ return mapper;
+ }
+
+ /**
+ * get the -tsaurl url
+ * @return url or null
+ * @since Ant 1.7
+ */
+ public String getTsaurl() {
+ return tsaurl;
+ }
+
+ /**
+ *
+ * @param tsaurl the tsa url.
+ * @since Ant 1.7
+ */
+ public void setTsaurl(String tsaurl) {
+ this.tsaurl = tsaurl;
+ }
+
+ /**
+ * Get the proxy host to be used when connecting to the TSA url
+ * @return url or null
+ * @since Ant 1.9.5
+ */
+ public String getTsaproxyhost() {
+ return tsaproxyhost;
+ }
+
+ /**
+ *
+ * @param tsaproxyhost the proxy host to be used when connecting to the TSA.
+ * @since Ant 1.9.5
+ */
+ public void setTsaproxyhost(String tsaproxyhost) {
+ this.tsaproxyhost = tsaproxyhost;
+ }
+
+ /**
+ * Get the proxy host to be used when connecting to the TSA url
+ * @return url or null
+ * @since Ant 1.9.5
+ */
+ public String getTsaproxyport() {
+ return tsaproxyport;
+ }
+
+ /**
+ *
+ * @param tsaproxyport the proxy port to be used when connecting to the TSA.
+ * @since Ant 1.9.5
+ */
+ public void setTsaproxyport(String tsaproxyport) {
+ this.tsaproxyport = tsaproxyport;
+ }
+
+ /**
+ * get the -tsacert option
+ * @since Ant 1.7
+ * @return a certificate alias or null
+ */
+ public String getTsacert() {
+ return tsacert;
+ }
+
+ /**
+ * set the alias in the keystore of the TSA to use;
+ * @param tsacert the cert alias.
+ */
+ public void setTsacert(String tsacert) {
+ this.tsacert = tsacert;
+ }
+
+ /**
+ * Whether to force signing of a jar even it is already signed.
+ * @since Ant 1.8.0
+ */
+ public void setForce(boolean b) {
+ force = b;
+ }
+
+ /**
+ * Should the task force signing of a jar even it is already
+ * signed?
+ * @since Ant 1.8.0
+ */
+ public boolean isForce() {
+ return force;
+ }
+
+ /**
+ * Signature Algorithm; optional
+ *
+ * @param sigAlg the signature algorithm
+ */
+ public void setSigAlg(String sigAlg) {
+ this.sigAlg = sigAlg;
+ }
+
+ /**
+ * Signature Algorithm; optional
+ */
+ public String getSigAlg() {
+ return sigAlg;
+ }
+
+ /**
+ * Digest Algorithm; optional
+ *
+ * @param digestAlg the digest algorithm
+ */
+ public void setDigestAlg(String digestAlg) {
+ this.digestAlg = digestAlg;
+ }
+
+ /**
+ * Digest Algorithm; optional
+ */
+ public String getDigestAlg() {
+ return digestAlg;
+ }
+
+ /**
+ * sign the jar(s)
+ *
+ * @throws BuildException on errors
+ */
+ @Override
+ public void execute() throws BuildException {
+ //validation logic
+ final boolean hasJar = jar != null;
+ final boolean hasSignedJar = signedjar != null;
+ final boolean hasDestDir = destDir != null;
+ final boolean hasMapper = mapper != null;
+
+ if (!hasJar && !hasResources()) {
+ throw new BuildException(ERROR_NO_SOURCE);
+ }
+ if (null == alias) {
+ throw new BuildException(ERROR_NO_ALIAS);
+ }
+
+ if (null == storepass) {
+ throw new BuildException(ERROR_NO_STOREPASS);
+ }
+
+ if (hasDestDir && hasSignedJar) {
+ throw new BuildException(ERROR_TODIR_AND_SIGNEDJAR);
+ }
+
+
+ if (hasResources() && hasSignedJar) {
+ throw new BuildException(ERROR_SIGNEDJAR_AND_PATHS);
+ }
+
+ //this isn't strictly needed, but by being fussy now,
+ //we can change implementation details later
+ if (!hasDestDir && hasMapper) {
+ throw new BuildException(ERROR_MAPPER_WITHOUT_DEST);
+ }
+
+ beginExecution();
+
+
+ try {
+ //special case single jar handling with signedjar attribute set
+ if (hasJar && hasSignedJar) {
+ // single jar processing
+ signOneJar(jar, signedjar);
+ //return here.
+ return;
+ }
+
+ //the rest of the method treats single jar like
+ //a nested path with one file
+
+ Path sources = createUnifiedSourcePath();
+ //set up our mapping policy
+ FileNameMapper destMapper;
+ if (hasMapper) {
+ destMapper = mapper;
+ } else {
+ //no mapper? use the identity policy
+ destMapper = new IdentityMapper();
+ }
+
+
+ //at this point the paths are set up with lists of files,
+ //and the mapper is ready to map from source dirs to dest files
+ //now we iterate through every JAR giving source and dest names
+ // deal with the paths
+ for (Resource r : sources) {
+ FileResource fr = ResourceUtils
+ .asFileResource(r.as(FileProvider.class));
+
+ //calculate our destination directory; it is either the destDir
+ //attribute, or the base dir of the fileset (for in situ updates)
+ File toDir = hasDestDir ? destDir : fr.getBaseDir();
+
+ //determine the destination filename via the mapper
+ String[] destFilenames = destMapper.mapFileName(fr.getName());
+ if (destFilenames == null || destFilenames.length != 1) {
+ //we only like simple mappers.
+ throw new BuildException(ERROR_BAD_MAP + fr.getFile());
+ }
+ File destFile = new File(toDir, destFilenames[0]);
+ signOneJar(fr.getFile(), destFile);
+ }
+ } finally {
+ endExecution();
+ }
+ }
+
+ /**
+ * Sign one jar.
+ * <p/>
+ * The signing only takes place if {@link #isUpToDate(File, File)} indicates
+ * that it is needed.
+ *
+ * @param jarSource source to sign
+ * @param jarTarget target; may be null
+ * @throws BuildException
+ */
+ private void signOneJar(File jarSource, File jarTarget)
+ throws BuildException {
+
+
+ File targetFile = jarTarget;
+ if (targetFile == null) {
+ targetFile = jarSource;
+ }
+ if (isUpToDate(jarSource, targetFile)) {
+ return;
+ }
+
+ long lastModified = jarSource.lastModified();
+ final ExecTask cmd = createJarSigner();
+
+ setCommonOptions(cmd);
+
+ bindToKeystore(cmd);
+ if (null != sigfile) {
+ addValue(cmd, "-sigfile");
+ String value = this.sigfile;
+ addValue(cmd, value);
+ }
+
+ try {
+ //DO NOT SET THE -signedjar OPTION if source==dest
+ //unless you like fielding hotspot crash reports
+ if (!FILE_UTILS.areSame(jarSource, targetFile)) {
+ addValue(cmd, "-signedjar");
+ addValue(cmd, targetFile.getPath());
+ }
+ } catch (IOException ioex) {
+ throw new BuildException(ioex);
+ }
+
+ if (internalsf) {
+ addValue(cmd, "-internalsf");
+ }
+
+ if (sectionsonly) {
+ addValue(cmd, "-sectionsonly");
+ }
+
+ if (sigAlg != null) {
+ addValue(cmd, "-sigalg");
+ addValue(cmd, sigAlg);
+ }
+
+ if (digestAlg != null) {
+ addValue(cmd, "-digestalg");
+ addValue(cmd, digestAlg);
+ }
+
+ //add -tsa operations if declared
+ addTimestampAuthorityCommands(cmd);
+
+ //JAR source is required
+ addValue(cmd, jarSource.getPath());
+
+ //alias is required for signing
+ addValue(cmd, alias);
+
+ log("Signing JAR: "
+ + jarSource.getAbsolutePath()
+ + " to "
+ + targetFile.getAbsolutePath()
+ + " as " + alias);
+
+ cmd.execute();
+
+ // restore the lastModified attribute
+ if (preserveLastModified) {
+ FILE_UTILS.setFileLastModified(targetFile, lastModified);
+ }
+ }
+
+ /**
+ * If the tsa parameters are set, this passes them to the command.
+ * There is no validation of java version, as third party JDKs
+ * may implement this on earlier/later jarsigner implementations.
+ * @param cmd the exec task.
+ */
+ private void addTimestampAuthorityCommands(final ExecTask cmd) {
+ if (tsaurl != null) {
+ addValue(cmd, "-tsa");
+ addValue(cmd, tsaurl);
+ }
+
+ if (tsacert != null) {
+ addValue(cmd, "-tsacert");
+ addValue(cmd, tsacert);
+ }
+
+ if (tsaproxyhost != null) {
+ if (tsaurl == null || tsaurl.startsWith("https")) {
+ addProxyFor(cmd, "https");
+ }
+ if (tsaurl == null || !tsaurl.startsWith("https")) {
+ addProxyFor(cmd, "http");
+ }
+ }
+ }
+
+ /**
+ * <p>Compare a jar file with its corresponding signed jar. The logic for this
+ * is complex, and best explained in the source itself. Essentially if
+ * either file doesn't exist, or the destfile has an out of date timestamp,
+ * then the return value is false.</p>
+ *
+ * <p>If we are signing ourself, the check {@link #isSigned(File)} is used to
+ * trigger the process.</p>
+ *
+ * @param jarFile the unsigned jar file
+ * @param signedjarFile the result signed jar file
+ * @return true if the signedjarFile is considered up to date
+ */
+ protected boolean isUpToDate(File jarFile, File signedjarFile) {
+ if (isForce() || null == jarFile || !jarFile.exists()) {
+ //these are pathological cases, but retained in case somebody
+ //subclassed us.
+ return false;
+ }
+
+ //we normally compare destination with source
+ File destFile = signedjarFile;
+ if (destFile == null) {
+ //but if no dest is specified, compare source to source
+ destFile = jarFile;
+ }
+
+ //if, by any means, the destfile and source match,
+ if (jarFile.equals(destFile)) {
+ if (lazy) {
+ //we check the presence of signatures on lazy signing
+ return isSigned(jarFile);
+ }
+ //unsigned or non-lazy self signings are always false
+ return false;
+ }
+
+ //if they are different, the timestamps are used
+ return FILE_UTILS.isUpToDate(jarFile, destFile);
+ }
+
+ /**
+ * test for a file being signed, by looking for a signature in the META-INF
+ * directory with our alias/sigfile.
+ *
+ * @param file the file to be checked
+ * @return true if the file is signed
+ * @see IsSigned#isSigned(File, String)
+ */
+ protected boolean isSigned(File file) {
+ try {
+ return IsSigned.isSigned(file, sigfile == null ? alias : sigfile);
+ } catch (IOException e) {
+ //just log this
+ log(e.toString(), Project.MSG_VERBOSE);
+ return false;
+ }
+ }
+
+ /**
+ * true to indicate that the signed jar modification date remains the same
+ * as the original. Defaults to false
+ *
+ * @param preserveLastModified if true preserve the last modified time
+ */
+ public void setPreserveLastModified(boolean preserveLastModified) {
+ this.preserveLastModified = preserveLastModified;
+ }
+
+ private void addProxyFor(final ExecTask cmd, final String scheme) {
+ addValue(cmd, "-J-D" + scheme + ".proxyHost=" + tsaproxyhost);
+
+ if (tsaproxyport != null) {
+ addValue(cmd, "-J-D" + scheme + ".proxyPort=" + tsaproxyport);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sleep.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sleep.java
new file mode 100644
index 00000000..6468c390
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sleep.java
@@ -0,0 +1,194 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Sleep, or pause, for a period of time.
+ *
+ * <p>A task for sleeping a short period of time, useful when a
+ * build or deployment process requires an interval between tasks.</p>
+ *
+ * <p>A negative value can be supplied to any of attributes provided the total sleep time
+ * is positive, pending fundamental changes in physics and JVM
+ * execution times</p>
+ *
+ * <p>Note that sleep times are always hints to be interpreted by the OS how it feels
+ * small times may either be ignored or rounded up to a minimum timeslice. Note
+ * also that the system clocks often have a fairly low granularity too, which complicates
+ * measuring how long a sleep actually took.</p>
+ *
+ * @since Ant 1.4
+ * @ant.task category="utility"
+ */
+public class Sleep extends Task {
+ /**
+ * failure flag
+ */
+ private boolean failOnError = true;
+
+ /**
+ * sleep seconds
+ */
+ private int seconds = 0;
+
+ /**
+ * sleep hours
+ */
+ private int hours = 0;
+ /**
+ * sleep minutes
+ */
+ private int minutes = 0;
+
+ /**
+ * sleep milliseconds
+ */
+ private int milliseconds = 0;
+
+
+
+ /**
+ * Creates new instance
+ */
+ public Sleep() {
+ }
+
+
+ /**
+ * seconds to add to the sleep time
+ *
+ * @param seconds The new Seconds value
+ */
+ public void setSeconds(int seconds) {
+ this.seconds = seconds;
+ }
+
+
+ /**
+ * hours to add to the sleep time.
+ *
+ * @param hours The new Hours value
+ */
+ public void setHours(int hours) {
+ this.hours = hours;
+ }
+
+
+ /**
+ * minutes to add to the sleep time
+ *
+ * @param minutes The new Minutes value
+ */
+ public void setMinutes(int minutes) {
+ this.minutes = minutes;
+ }
+
+
+ /**
+ * milliseconds to add to the sleep time
+ *
+ * @param milliseconds The new Milliseconds value
+ */
+ public void setMilliseconds(int milliseconds) {
+ this.milliseconds = milliseconds;
+ }
+
+
+ /**
+ * sleep for a period of time
+ *
+ * @param millis time to sleep
+ */
+ public void doSleep(long millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException ie) {
+ // Ignore Exception
+ }
+ }
+
+
+ /**
+ * flag controlling whether to break the build on an error.
+ *
+ * @param failOnError The new FailOnError value
+ */
+ public void setFailOnError(boolean failOnError) {
+ this.failOnError = failOnError;
+ }
+
+
+ /**
+ * return time to sleep
+ *
+ * @return sleep time. if below 0 then there is an error
+ */
+
+ private long getSleepTime() {
+ // CheckStyle:MagicNumber OFF
+ return ((((long) hours * 60) + minutes) * 60 + seconds) * 1000
+ + milliseconds;
+ // CheckStyle:MagicNumber ON
+ }
+
+
+ /**
+ * verify parameters
+ *
+ * @throws BuildException if something is invalid
+ */
+ public void validate()
+ throws BuildException {
+ if (getSleepTime() < 0) {
+ throw new BuildException("Negative sleep periods are not "
+ + "supported");
+ }
+ }
+
+
+ /**
+ * Executes this build task. Throws org.apache.tools.ant.BuildException
+ * if there is an error during task execution.
+ *
+ * @exception BuildException Description of Exception
+ */
+ @Override
+ public void execute()
+ throws BuildException {
+ try {
+ validate();
+ long sleepTime = getSleepTime();
+ log("sleeping for " + sleepTime + " milliseconds",
+ Project.MSG_VERBOSE);
+ doSleep(sleepTime);
+ } catch (Exception e) {
+ if (failOnError) {
+ throw new BuildException(e);
+ } else {
+ String text = e.toString();
+ log(text, Project.MSG_ERR);
+ }
+ }
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/StreamPumper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/StreamPumper.java
new file mode 100644
index 00000000..df95eedb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/StreamPumper.java
@@ -0,0 +1,252 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Copies all data from an input stream to an output stream.
+ *
+ * @since Ant 1.2
+ */
+public class StreamPumper implements Runnable {
+
+ private static final int SMALL_BUFFER_SIZE = 128;
+
+ private final InputStream is;
+ private final OutputStream os;
+ private volatile boolean finish;
+ private volatile boolean finished;
+ private final boolean closeWhenExhausted;
+ private boolean autoflush = false;
+ private Exception exception = null;
+ private int bufferSize = SMALL_BUFFER_SIZE;
+ private boolean started = false;
+ private final boolean useAvailable;
+
+ /**
+ * Create a new StreamPumper.
+ *
+ * @param is input stream to read data from
+ * @param os output stream to write data to.
+ * @param closeWhenExhausted if true, the output stream will be closed when
+ * the input is exhausted.
+ */
+ public StreamPumper(InputStream is, OutputStream os, boolean closeWhenExhausted) {
+ this(is, os, closeWhenExhausted, false);
+ }
+
+
+ /**
+ * Create a new StreamPumper.
+ *
+ * <p><b>Note:</b> If you set useAvailable to true, you must
+ * explicitly invoke {@link #stop stop} or interrupt the
+ * corresponding Thread when you are done or the run method will
+ * never finish on some JVMs (namely those where available returns
+ * 0 on a closed stream). Setting it to true may also impact
+ * performance negatively. This flag should only be set to true
+ * if you intend to stop the pumper before the input stream gets
+ * closed.</p>
+ *
+ * @param is input stream to read data from
+ * @param os output stream to write data to.
+ * @param closeWhenExhausted if true, the output stream will be closed when
+ * the input is exhausted.
+ * @param useAvailable whether the pumper should use {@link
+ * java.io.InputStream#available available} to determine
+ * whether input is ready, thus trying to emulate
+ * non-blocking behavior.
+ *
+ * @since Ant 1.8.0
+ */
+ public StreamPumper(InputStream is, OutputStream os,
+ boolean closeWhenExhausted,
+ boolean useAvailable) {
+ this.is = is;
+ this.os = os;
+ this.closeWhenExhausted = closeWhenExhausted;
+ this.useAvailable = useAvailable;
+ }
+
+ /**
+ * Create a new StreamPumper.
+ *
+ * @param is input stream to read data from
+ * @param os output stream to write data to.
+ */
+ public StreamPumper(InputStream is, OutputStream os) {
+ this(is, os, false);
+ }
+
+ /**
+ * Set whether data should be flushed through to the output stream.
+ * @param autoflush if true, push through data; if false, let it be buffered
+ * @since Ant 1.6.3
+ */
+ /*package*/ void setAutoflush(boolean autoflush) {
+ this.autoflush = autoflush;
+ }
+
+ /**
+ * Copies data from the input stream to the output stream.
+ *
+ * Terminates as soon as the input stream is closed or an error occurs.
+ */
+ public void run() {
+ synchronized (this) {
+ started = true;
+ }
+ finished = false;
+
+ final byte[] buf = new byte[bufferSize];
+
+ int length;
+ try {
+ while (true) {
+ waitForInput(is);
+
+ if (finish || Thread.interrupted()) {
+ break;
+ }
+
+ length = is.read(buf);
+ if (length <= 0 || Thread.interrupted()) {
+ break;
+ }
+ os.write(buf, 0, length);
+ if (autoflush) {
+ os.flush();
+ }
+ if (finish) {
+ break;
+ }
+ }
+ // On completion, drain any available data (which might be the first data available for quick executions)
+ if (finish) {
+ while((length = is.available()) > 0) {
+ if (Thread.interrupted()) {
+ break;
+ }
+ length = is.read(buf, 0, Math.min(length, buf.length));
+ if (length <= 0) {
+ break;
+ }
+ os.write(buf, 0, length);
+ }
+ }
+ os.flush();
+ } catch (InterruptedException ie) {
+ // likely PumpStreamHandler trying to stop us
+ } catch (Exception e) {
+ synchronized (this) {
+ exception = e;
+ }
+ } finally {
+ if (closeWhenExhausted) {
+ FileUtils.close(os);
+ }
+ finished = true;
+ finish = false;
+ synchronized (this) {
+ notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Tells whether the end of the stream has been reached.
+ * @return true is the stream has been exhausted.
+ */
+ public boolean isFinished() {
+ return finished;
+ }
+
+ /**
+ * This method blocks until the StreamPumper finishes.
+ * @throws InterruptedException if interrupted.
+ * @see #isFinished()
+ */
+ public synchronized void waitFor() throws InterruptedException {
+ while (!isFinished()) {
+ wait();
+ }
+ }
+
+ /**
+ * Set the size in bytes of the read buffer.
+ * @param bufferSize the buffer size to use.
+ * @throws IllegalStateException if the StreamPumper is already running.
+ */
+ public synchronized void setBufferSize(int bufferSize) {
+ if (started) {
+ throw new IllegalStateException("Cannot set buffer size on a running StreamPumper");
+ }
+ this.bufferSize = bufferSize;
+ }
+
+ /**
+ * Get the size in bytes of the read buffer.
+ * @return the int size of the read buffer.
+ */
+ public synchronized int getBufferSize() {
+ return bufferSize;
+ }
+
+ /**
+ * Get the exception encountered, if any.
+ * @return the Exception encountered.
+ */
+ public synchronized Exception getException() {
+ return exception;
+ }
+
+ /**
+ * Stop the pumper as soon as possible.
+ * Note that it may continue to block on the input stream
+ * but it will really stop the thread as soon as it gets EOF
+ * or any byte, and it will be marked as finished.
+ * @since Ant 1.6.3
+ */
+ /*package*/ synchronized void stop() {
+ finish = true;
+ notifyAll();
+ }
+
+ private static final long POLL_INTERVAL = 100;
+
+ private void waitForInput(InputStream is)
+ throws IOException, InterruptedException {
+ if (useAvailable) {
+ while (!finish && is.available() == 0) {
+ if (Thread.interrupted()) {
+ throw new InterruptedException();
+ }
+
+ synchronized (this) {
+ this.wait(POLL_INTERVAL);
+ }
+ }
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SubAnt.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SubAnt.java
new file mode 100644
index 00000000..35109faa
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/SubAnt.java
@@ -0,0 +1,642 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Main;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Ant.TargetElement;
+import org.apache.tools.ant.types.DirSet;
+import org.apache.tools.ant.types.FileList;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PropertySet;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.ResourceCollection;
+
+
+/**
+ * Calls a given target for all defined sub-builds. This is an extension
+ * of ant for bulk project execution.
+ * <p>
+ * <h2> Use with directories </h2>
+ * <p>
+ * subant can be used with directory sets to execute a build from different directories.
+ * 2 different options are offered
+ * </p>
+ * <ul>
+ * <li>
+ * run the same build file /somepath/otherpath/mybuild.xml
+ * with different base directories use the genericantfile attribute
+ * </li>
+ * <li>if you want to run directory1/build.xml, directory2/build.xml, ....
+ * use the antfile attribute. The base directory does not get set by the subant task in this case,
+ * because you can specify it in each build file.
+ * </li>
+ * </ul>
+ * @since Ant1.6
+ * @ant.task name="subant" category="control"
+ */
+public class SubAnt extends Task {
+
+ private Path buildpath;
+
+ private Ant ant = null;
+ private String subTarget = null;
+ private String antfile = getDefaultBuildFile();
+ private File genericantfile = null;
+ private boolean verbose = false;
+ private boolean inheritAll = false;
+ private boolean inheritRefs = false;
+ private boolean failOnError = true;
+ private String output = null;
+
+ private Vector properties = new Vector();
+ private Vector references = new Vector();
+ private Vector propertySets = new Vector();
+
+ /** the targets to call on the new project */
+ private Vector/*<TargetElement>*/ targets = new Vector();
+
+ /**
+ * Get the default build file name to use when launching the task.
+ * <p>
+ * This function may be overrided by providers of custom ProjectHelper so they can implement easily their sub
+ * launcher.
+ *
+ * @return the name of the default file
+ * @since Ant 1.8.0
+ */
+ protected String getDefaultBuildFile() {
+ return Main.DEFAULT_BUILD_FILENAME;
+ }
+
+ /**
+ * Pass output sent to System.out to the new project.
+ *
+ * @param output a line of output
+ * @since Ant 1.6.2
+ */
+ @Override
+ public void handleOutput(String output) {
+ if (ant != null) {
+ ant.handleOutput(output);
+ } else {
+ super.handleOutput(output);
+ }
+ }
+
+ /**
+ * Process input into the ant task
+ *
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read
+ *
+ * @return the number of bytes read
+ *
+ * @exception IOException if the data cannot be read
+ *
+ * @see Task#handleInput(byte[], int, int)
+ *
+ * @since Ant 1.6.2
+ */
+ @Override
+ public int handleInput(byte[] buffer, int offset, int length)
+ throws IOException {
+ if (ant != null) {
+ return ant.handleInput(buffer, offset, length);
+ } else {
+ return super.handleInput(buffer, offset, length);
+ }
+ }
+
+ /**
+ * Pass output sent to System.out to the new project.
+ *
+ * @param output The output to log. Should not be <code>null</code>.
+ *
+ * @since Ant 1.6.2
+ */
+ @Override
+ public void handleFlush(String output) {
+ if (ant != null) {
+ ant.handleFlush(output);
+ } else {
+ super.handleFlush(output);
+ }
+ }
+
+ /**
+ * Pass output sent to System.err to the new project.
+ *
+ * @param output The error output to log. Should not be <code>null</code>.
+ *
+ * @since Ant 1.6.2
+ */
+ @Override
+ public void handleErrorOutput(String output) {
+ if (ant != null) {
+ ant.handleErrorOutput(output);
+ } else {
+ super.handleErrorOutput(output);
+ }
+ }
+
+ /**
+ * Pass output sent to System.err to the new project.
+ *
+ * @param output The error output to log. Should not be <code>null</code>.
+ *
+ * @since Ant 1.6.2
+ */
+ @Override
+ public void handleErrorFlush(String output) {
+ if (ant != null) {
+ ant.handleErrorFlush(output);
+ } else {
+ super.handleErrorFlush(output);
+ }
+ }
+
+ /**
+ * Runs the various sub-builds.
+ */
+ @Override
+ public void execute() {
+ if (buildpath == null) {
+ throw new BuildException("No buildpath specified");
+ }
+
+ final String[] filenames = buildpath.list();
+ final int count = filenames.length;
+ if (count < 1) {
+ log("No sub-builds to iterate on", Project.MSG_WARN);
+ return;
+ }
+/*
+ //REVISIT: there must be cleaner way of doing this, if it is merited at all
+ if (subTarget == null) {
+ subTarget = getOwningTarget().getName();
+ }
+*/
+ BuildException buildException = null;
+ for (int i = 0; i < count; ++i) {
+ File file = null;
+ String subdirPath = null;
+ Throwable thrownException = null;
+ try {
+ File directory = null;
+ file = new File(filenames[i]);
+ if (file.isDirectory()) {
+ if (verbose) {
+ subdirPath = file.getPath();
+ log("Entering directory: " + subdirPath + "\n", Project.MSG_INFO);
+ }
+ if (genericantfile != null) {
+ directory = file;
+ file = genericantfile;
+ } else {
+ file = new File(file, antfile);
+ }
+ }
+ execute(file, directory);
+ if (verbose && subdirPath != null) {
+ log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
+ }
+ } catch (RuntimeException ex) {
+ if (!(getProject().isKeepGoingMode())) {
+ if (verbose && subdirPath != null) {
+ log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
+ }
+ throw ex; // throw further
+ }
+ thrownException = ex;
+ } catch (Throwable ex) {
+ if (!(getProject().isKeepGoingMode())) {
+ if (verbose && subdirPath != null) {
+ log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
+ }
+ throw new BuildException(ex);
+ }
+ thrownException = ex;
+ }
+ if (thrownException != null) {
+ if (thrownException instanceof BuildException) {
+ log("File '" + file
+ + "' failed with message '"
+ + thrownException.getMessage() + "'.", Project.MSG_ERR);
+ // only the first build exception is reported
+ if (buildException == null) {
+ buildException = (BuildException) thrownException;
+ }
+ } else {
+ log("Target '" + file
+ + "' failed with message '"
+ + thrownException.getMessage() + "'.", Project.MSG_ERR);
+ thrownException.printStackTrace(System.err);
+ if (buildException == null) {
+ buildException =
+ new BuildException(thrownException);
+ }
+ }
+ if (verbose && subdirPath != null) {
+ log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
+ }
+ }
+ }
+ // check if one of the builds failed in keep going mode
+ if (buildException != null) {
+ throw buildException;
+ }
+ }
+
+ /**
+ * Runs the given target on the provided build file.
+ *
+ * @param file the build file to execute
+ * @param directory the directory of the current iteration
+ * @throws BuildException is the file cannot be found, read, is
+ * a directory, or the target called failed, but only if
+ * <code>failOnError</code> is <code>true</code>. Otherwise,
+ * a warning log message is simply output.
+ */
+ private void execute(File file, File directory)
+ throws BuildException {
+ if (!file.exists() || file.isDirectory() || !file.canRead()) {
+ String msg = "Invalid file: " + file;
+ if (failOnError) {
+ throw new BuildException(msg);
+ }
+ log(msg, Project.MSG_WARN);
+ return;
+ }
+
+ ant = createAntTask(directory);
+ String antfilename = file.getAbsolutePath();
+ ant.setAntfile(antfilename);
+ final int size = targets.size();
+ for (int i = 0; i < size; i++) {
+ TargetElement targetElement = (TargetElement) targets.get(i);
+ ant.addConfiguredTarget(targetElement);
+ }
+
+ try {
+ if (verbose) {
+ log("Executing: " + antfilename, Project.MSG_INFO);
+ }
+ ant.execute();
+ } catch (BuildException e) {
+ if (failOnError || isHardError(e)) {
+ throw e;
+ }
+ log("Failure for target '" + subTarget
+ + "' of: " + antfilename + "\n"
+ + e.getMessage(), Project.MSG_WARN);
+ } catch (Throwable e) {
+ if (failOnError || isHardError(e)) {
+ throw new BuildException(e);
+ }
+ log("Failure for target '" + subTarget
+ + "' of: " + antfilename + "\n"
+ + e.toString(),
+ Project.MSG_WARN);
+ } finally {
+ ant = null;
+ }
+ }
+
+ /** whether we should even try to continue after this error */
+ private boolean isHardError(Throwable t) {
+ if (t instanceof BuildException) {
+ return isHardError(t.getCause());
+ } else if (t instanceof OutOfMemoryError) {
+ return true;
+ } else if (t instanceof ThreadDeath) {
+ return true;
+ } else { // incl. t == null
+ return false;
+ }
+ }
+
+ /**
+ * This method builds the file name to use in conjunction with directories.
+ *
+ * <p>Defaults to "build.xml".
+ * If <code>genericantfile</code> is set, this attribute is ignored.</p>
+ *
+ * @param antfile the short build file name. Defaults to "build.xml".
+ */
+ public void setAntfile(String antfile) {
+ this.antfile = antfile;
+ }
+
+ /**
+ * This method builds a file path to use in conjunction with directories.
+ *
+ * <p>Use <code>genericantfile</code>, in order to run the same build file
+ * with different basedirs.</p>
+ * If this attribute is set, <code>antfile</code> is ignored.
+ *
+ * @param afile (path of the generic ant file, absolute or relative to
+ * project base directory)
+ * */
+ public void setGenericAntfile(File afile) {
+ this.genericantfile = afile;
+ }
+
+ /**
+ * Sets whether to fail with a build exception on error, or go on.
+ *
+ * @param failOnError the new value for this boolean flag.
+ */
+ public void setFailonerror(boolean failOnError) {
+ this.failOnError = failOnError;
+ }
+
+ /**
+ * The target to call on the different sub-builds. Set to "" to execute
+ * the default target.
+ * @param target the target
+ * <p>
+ */
+ // REVISIT: Defaults to the target name that contains this task if not specified.
+ public void setTarget(String target) {
+ this.subTarget = target;
+ }
+
+ /**
+ * Add a target to this Ant invocation.
+ * @param t the <code>TargetElement</code> to add.
+ * @since Ant 1.7
+ */
+ public void addConfiguredTarget(TargetElement t) {
+ String name = t.getName();
+ if ("".equals(name)) {
+ throw new BuildException("target name must not be empty");
+ }
+ targets.add(t);
+ }
+
+ /**
+ * Enable/ disable verbose log messages showing when each sub-build path is entered/ exited.
+ * The default value is "false".
+ * @param on true to enable verbose mode, false otherwise (default).
+ */
+ public void setVerbose(boolean on) {
+ this.verbose = on;
+ }
+
+ /**
+ * Corresponds to <code>&lt;ant&gt;</code>'s
+ * <code>output</code> attribute.
+ *
+ * @param s the filename to write the output to.
+ */
+ public void setOutput(String s) {
+ this.output = s;
+ }
+
+ /**
+ * Corresponds to <code>&lt;ant&gt;</code>'s
+ * <code>inheritall</code> attribute.
+ *
+ * @param b the new value for this boolean flag.
+ */
+ public void setInheritall(boolean b) {
+ this.inheritAll = b;
+ }
+
+ /**
+ * Corresponds to <code>&lt;ant&gt;</code>'s
+ * <code>inheritrefs</code> attribute.
+ *
+ * @param b the new value for this boolean flag.
+ */
+ public void setInheritrefs(boolean b) {
+ this.inheritRefs = b;
+ }
+
+ /**
+ * Corresponds to <code>&lt;ant&gt;</code>'s
+ * nested <code>&lt;property&gt;</code> element.
+ *
+ * @param p the property to pass on explicitly to the sub-build.
+ */
+ public void addProperty(Property p) {
+ properties.addElement(p);
+ }
+
+ /**
+ * Corresponds to <code>&lt;ant&gt;</code>'s
+ * nested <code>&lt;reference&gt;</code> element.
+ *
+ * @param r the reference to pass on explicitly to the sub-build.
+ */
+ public void addReference(Ant.Reference r) {
+ references.addElement(r);
+ }
+
+ /**
+ * Corresponds to <code>&lt;ant&gt;</code>'s
+ * nested <code>&lt;propertyset&gt;</code> element.
+ * @param ps the propertyset
+ */
+ public void addPropertyset(PropertySet ps) {
+ propertySets.addElement(ps);
+ }
+
+ /**
+ * Adds a directory set to the implicit build path.
+ * <p>
+ * <em>Note that the directories will be added to the build path
+ * in no particular order, so if order is significant, one should
+ * use a file list instead!</em>
+ *
+ * @param set the directory set to add.
+ */
+ public void addDirset(DirSet set) {
+ add(set);
+ }
+
+ /**
+ * Adds a file set to the implicit build path.
+ * <p>
+ * <em>Note that the directories will be added to the build path
+ * in no particular order, so if order is significant, one should
+ * use a file list instead!</em>
+ *
+ * @param set the file set to add.
+ */
+ public void addFileset(FileSet set) {
+ add(set);
+ }
+
+ /**
+ * Adds an ordered file list to the implicit build path.
+ * <p>
+ * <em>Note that contrary to file and directory sets, file lists
+ * can reference non-existent files or directories!</em>
+ *
+ * @param list the file list to add.
+ */
+ public void addFilelist(FileList list) {
+ add(list);
+ }
+
+ /**
+ * Adds a resource collection to the implicit build path.
+ *
+ * @param rc the resource collection to add.
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection rc) {
+ getBuildpath().add(rc);
+ }
+
+ /**
+ * Set the buildpath to be used to find sub-projects.
+ *
+ * @param s an Ant Path object containing the buildpath.
+ */
+ public void setBuildpath(Path s) {
+ getBuildpath().append(s);
+ }
+
+ /**
+ * Creates a nested build path, and add it to the implicit build path.
+ *
+ * @return the newly created nested build path.
+ */
+ public Path createBuildpath() {
+ return getBuildpath().createPath();
+ }
+
+ /**
+ * Creates a nested <code>&lt;buildpathelement&gt;</code>,
+ * and add it to the implicit build path.
+ *
+ * @return the newly created nested build path element.
+ */
+ public Path.PathElement createBuildpathElement() {
+ return getBuildpath().createPathElement();
+ }
+
+ /**
+ * Gets the implicit build path, creating it if <code>null</code>.
+ *
+ * @return the implicit build path.
+ */
+ private Path getBuildpath() {
+ if (buildpath == null) {
+ buildpath = new Path(getProject());
+ }
+ return buildpath;
+ }
+
+ /**
+ * Buildpath to use, by reference.
+ *
+ * @param r a reference to an Ant Path object containing the buildpath.
+ */
+ public void setBuildpathRef(Reference r) {
+ createBuildpath().setRefid(r);
+ }
+
+ /**
+ * Creates the &lt;ant&gt; task configured to run a specific target.
+ *
+ * @param directory : if not null the directory where the build should run
+ *
+ * @return the ant task, configured with the explicit properties and
+ * references necessary to run the sub-build.
+ */
+ private Ant createAntTask(File directory) {
+ Ant antTask = new Ant(this);
+ antTask.init();
+ if (subTarget != null && subTarget.length() > 0) {
+ antTask.setTarget(subTarget);
+ }
+
+
+ if (output != null) {
+ antTask.setOutput(output);
+ }
+
+ if (directory != null) {
+ antTask.setDir(directory);
+ } else {
+ antTask.setUseNativeBasedir(true);
+ }
+
+ antTask.setInheritAll(inheritAll);
+ for (Enumeration i = properties.elements(); i.hasMoreElements();) {
+ copyProperty(antTask.createProperty(), (Property) i.nextElement());
+ }
+
+ for (Enumeration i = propertySets.elements(); i.hasMoreElements();) {
+ antTask.addPropertyset((PropertySet) i.nextElement());
+ }
+
+ antTask.setInheritRefs(inheritRefs);
+ for (Enumeration i = references.elements(); i.hasMoreElements();) {
+ antTask.addReference((Ant.Reference) i.nextElement());
+ }
+
+ return antTask;
+ }
+
+ /**
+ * Assigns an Ant property to another.
+ *
+ * @param to the destination property whose content is modified.
+ * @param from the source property whose content is copied.
+ */
+ private static void copyProperty(Property to, Property from) {
+ to.setName(from.getName());
+
+ if (from.getValue() != null) {
+ to.setValue(from.getValue());
+ }
+ if (from.getFile() != null) {
+ to.setFile(from.getFile());
+ }
+ if (from.getResource() != null) {
+ to.setResource(from.getResource());
+ }
+ if (from.getPrefix() != null) {
+ to.setPrefix(from.getPrefix());
+ }
+ if (from.getRefid() != null) {
+ to.setRefid(from.getRefid());
+ }
+ if (from.getEnvironment() != null) {
+ to.setEnvironment(from.getEnvironment());
+ }
+ if (from.getClasspath() != null) {
+ to.setClasspath(from.getClasspath());
+ }
+ }
+
+} // END class SubAnt
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sync.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sync.java
new file mode 100644
index 00000000..c1c75529
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sync.java
@@ -0,0 +1,606 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.AbstractFileSet;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.Resources;
+import org.apache.tools.ant.types.resources.Restrict;
+import org.apache.tools.ant.types.resources.selectors.Exists;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.NoneSelector;
+
+/**
+ * Synchronize a local target directory from the files defined
+ * in one or more filesets.
+ *
+ * <p>Uses a &lt;copy&gt; task internally, but forbidding the use of
+ * mappers and filter chains. Files of the destination directory not
+ * present in any of the source fileset are removed.</p>
+ *
+ * @since Ant 1.6
+ *
+ * revised by <a href="mailto:daniel.armbrust@mayo.edu">Dan Armbrust</a>
+ * to remove orphaned directories.
+ *
+ * @ant.task category="filesystem"
+ */
+public class Sync extends Task {
+
+ // Same as regular <copy> task... see at end-of-file!
+ private MyCopy myCopy;
+
+ // Similar to a fileset, but doesn't allow dir attribute to be set
+ private SyncTarget syncTarget;
+
+ private Resources resources = null;
+
+ // Override Task#init
+ /**
+ * Initialize the sync task.
+ * @throws BuildException if there is a problem.
+ * @see Task#init()
+ */
+ @Override
+ public void init()
+ throws BuildException {
+ // Instantiate it
+ myCopy = new MyCopy();
+ configureTask(myCopy);
+
+ // Default config of <mycopy> for our purposes.
+ myCopy.setFiltering(false);
+ myCopy.setIncludeEmptyDirs(false);
+ myCopy.setPreserveLastModified(true);
+ }
+
+ private void configureTask(Task helper) {
+ helper.setProject(getProject());
+ helper.setTaskName(getTaskName());
+ helper.setOwningTarget(getOwningTarget());
+ helper.init();
+ }
+
+ // Override Task#execute
+ /**
+ * Execute the sync task.
+ * @throws BuildException if there is an error.
+ * @see Task#execute()
+ */
+ @Override
+ public void execute()
+ throws BuildException {
+ // The destination of the files to copy
+ File toDir = myCopy.getToDir();
+
+ // The complete list of files to copy
+ Set allFiles = myCopy.nonOrphans;
+
+ // If the destination directory didn't already exist,
+ // or was empty, then no previous file removal is necessary!
+ boolean noRemovalNecessary = !toDir.exists() || toDir.list().length < 1;
+
+ // Copy all the necessary out-of-date files
+ log("PASS#1: Copying files to " + toDir, Project.MSG_DEBUG);
+ myCopy.execute();
+
+ // Do we need to perform further processing?
+ if (noRemovalNecessary) {
+ log("NO removing necessary in " + toDir, Project.MSG_DEBUG);
+ return; // nope ;-)
+ }
+
+ // will hold the directories matched by SyncTarget in reversed
+ // lexicographic order (order is important, that's why we use
+ // a LinkedHashSet
+ Set preservedDirectories = new LinkedHashSet();
+
+ // Get rid of all files not listed in the source filesets.
+ log("PASS#2: Removing orphan files from " + toDir, Project.MSG_DEBUG);
+ int[] removedFileCount = removeOrphanFiles(allFiles, toDir,
+ preservedDirectories);
+ logRemovedCount(removedFileCount[0], "dangling director", "y", "ies");
+ logRemovedCount(removedFileCount[1], "dangling file", "", "s");
+
+ // Get rid of empty directories on the destination side
+ if (!myCopy.getIncludeEmptyDirs()
+ || getExplicitPreserveEmptyDirs() == Boolean.FALSE) {
+ log("PASS#3: Removing empty directories from " + toDir,
+ Project.MSG_DEBUG);
+
+ int removedDirCount = 0;
+ if (!myCopy.getIncludeEmptyDirs()) {
+ removedDirCount =
+ removeEmptyDirectories(toDir, false, preservedDirectories);
+ } else { // must be syncTarget.preserveEmptydirs == FALSE
+ removedDirCount =
+ removeEmptyDirectories(preservedDirectories);
+ }
+ logRemovedCount(removedDirCount, "empty director", "y", "ies");
+ }
+ }
+
+ private void logRemovedCount(int count, String prefix,
+ String singularSuffix, String pluralSuffix) {
+ File toDir = myCopy.getToDir();
+
+ String what = (prefix == null) ? "" : prefix;
+ what += (count < 2) ? singularSuffix : pluralSuffix;
+
+ if (count > 0) {
+ log("Removed " + count + " " + what + " from " + toDir,
+ Project.MSG_INFO);
+ } else {
+ log("NO " + what + " to remove from " + toDir,
+ Project.MSG_VERBOSE);
+ }
+ }
+
+ /**
+ * Removes all files and folders not found as keys of a table
+ * (used as a set!).
+ *
+ * <p>If the provided file is a directory, it is recursively
+ * scanned for orphaned files which will be removed as well.</p>
+ *
+ * <p>If the directory is an orphan, it will also be removed.</p>
+ *
+ * @param nonOrphans the table of all non-orphan <code>File</code>s.
+ * @param file the initial file or directory to scan or test.
+ * @param preservedDirectories will be filled with the directories
+ * matched by preserveInTarget - if any. Will not be
+ * filled unless preserveEmptyDirs and includeEmptyDirs
+ * conflict.
+ * @return the number of orphaned files and directories actually removed.
+ * Position 0 of the array is the number of orphaned directories.
+ * Position 1 of the array is the number or orphaned files.
+ */
+ private int[] removeOrphanFiles(Set nonOrphans, File toDir,
+ Set preservedDirectories) {
+ int[] removedCount = new int[] {0, 0};
+ String[] excls =
+ (String[]) nonOrphans.toArray(new String[nonOrphans.size() + 1]);
+ // want to keep toDir itself
+ excls[nonOrphans.size()] = "";
+
+ DirectoryScanner ds = null;
+ if (syncTarget != null) {
+ FileSet fs = syncTarget.toFileSet(false);
+ fs.setDir(toDir);
+
+ // preserveInTarget would find all files we want to keep,
+ // but we need to find all that we want to delete - so the
+ // meaning of all patterns and selectors must be inverted
+ PatternSet ps = syncTarget.mergePatterns(getProject());
+ fs.appendExcludes(ps.getIncludePatterns(getProject()));
+ fs.appendIncludes(ps.getExcludePatterns(getProject()));
+ fs.setDefaultexcludes(!syncTarget.getDefaultexcludes());
+
+ // selectors are implicitly ANDed in DirectoryScanner. To
+ // revert their logic we wrap them into a <none> selector
+ // instead.
+ FileSelector[] s = syncTarget.getSelectors(getProject());
+ if (s.length > 0) {
+ NoneSelector ns = new NoneSelector();
+ for (int i = 0; i < s.length; i++) {
+ ns.appendSelector(s[i]);
+ }
+ fs.appendSelector(ns);
+ }
+ ds = fs.getDirectoryScanner(getProject());
+ } else {
+ ds = new DirectoryScanner();
+ ds.setBasedir(toDir);
+ }
+ ds.addExcludes(excls);
+
+ ds.scan();
+ String[] files = ds.getIncludedFiles();
+ for (int i = 0; i < files.length; i++) {
+ File f = new File(toDir, files[i]);
+ log("Removing orphan file: " + f, Project.MSG_DEBUG);
+ f.delete();
+ ++removedCount[1];
+ }
+ String[] dirs = ds.getIncludedDirectories();
+ // ds returns the directories in lexicographic order.
+ // iterating through the array backwards means we are deleting
+ // leaves before their parent nodes - thus making sure (well,
+ // more likely) that the directories are empty when we try to
+ // delete them.
+ for (int i = dirs.length - 1; i >= 0; --i) {
+ File f = new File(toDir, dirs[i]);
+ String[] children = f.list();
+ if (children == null || children.length < 1) {
+ log("Removing orphan directory: " + f, Project.MSG_DEBUG);
+ f.delete();
+ ++removedCount[0];
+ }
+ }
+
+ Boolean ped = getExplicitPreserveEmptyDirs();
+ if (ped != null && ped.booleanValue() != myCopy.getIncludeEmptyDirs()) {
+ FileSet fs = syncTarget.toFileSet(true);
+ fs.setDir(toDir);
+ String[] preservedDirs =
+ fs.getDirectoryScanner(getProject()).getIncludedDirectories();
+ for (int i = preservedDirs.length - 1; i >= 0; --i) {
+ preservedDirectories.add(new File(toDir, preservedDirs[i]));
+ }
+ }
+
+ return removedCount;
+ }
+
+ /**
+ * Removes all empty directories from a directory.
+ *
+ * <p><em>Note that a directory that contains only empty
+ * directories, directly or not, will be removed!</em></p>
+ *
+ * <p>Recurses depth-first to find the leaf directories
+ * which are empty and removes them, then unwinds the
+ * recursion stack, removing directories which have
+ * become empty themselves, etc...</p>
+ *
+ * @param dir the root directory to scan for empty directories.
+ * @param removeIfEmpty whether to remove the root directory
+ * itself if it becomes empty.
+ * @param preservedEmptyDirectories directories matched by
+ * syncTarget
+ * @return the number of empty directories actually removed.
+ */
+ private int removeEmptyDirectories(File dir, boolean removeIfEmpty,
+ Set preservedEmptyDirectories) {
+ int removedCount = 0;
+ if (dir.isDirectory()) {
+ File[] children = dir.listFiles();
+ for (int i = 0; i < children.length; ++i) {
+ File file = children[i];
+ // Test here again to avoid method call for non-directories!
+ if (file.isDirectory()) {
+ removedCount +=
+ removeEmptyDirectories(file, true,
+ preservedEmptyDirectories);
+ }
+ }
+ if (children.length > 0) {
+ // This directory may have become empty...
+ // We need to re-query its children list!
+ children = dir.listFiles();
+ }
+ if (children.length < 1 && removeIfEmpty
+ && !preservedEmptyDirectories.contains(dir)) {
+ log("Removing empty directory: " + dir, Project.MSG_DEBUG);
+ dir.delete();
+ ++removedCount;
+ }
+ }
+ return removedCount;
+ }
+
+ /**
+ * Removes all empty directories preserved by preserveInTarget in
+ * the preserveEmptyDirs == FALSE case.
+ *
+ * <p>Relies on the set to be ordered in reversed lexicographic
+ * order so that directories will be removed depth-first.</p>
+ *
+ * @param preservedEmptyDirectories directories matched by
+ * syncTarget
+ * @return the number of empty directories actually removed.
+ *
+ * @since Ant 1.8.0
+ */
+ private int removeEmptyDirectories(Set preservedEmptyDirectories) {
+ int removedCount = 0;
+ for (Iterator iter = preservedEmptyDirectories.iterator();
+ iter.hasNext();) {
+ File f = (File) iter.next();
+ String[] s = f.list();
+ if (s == null || s.length == 0) {
+ log("Removing empty directory: " + f, Project.MSG_DEBUG);
+ f.delete();
+ ++removedCount;
+ }
+ }
+ return removedCount;
+ }
+
+ //
+ // Various copy attributes/subelements of <copy> passed thru to <mycopy>
+ //
+
+ /**
+ * Sets the destination directory.
+ * @param destDir the destination directory
+ */
+ public void setTodir(File destDir) {
+ myCopy.setTodir(destDir);
+ }
+
+ /**
+ * Used to force listing of all names of copied files.
+ * @param verbose if true force listing of all names of copied files.
+ */
+ public void setVerbose(boolean verbose) {
+ myCopy.setVerbose(verbose);
+ }
+
+ /**
+ * Overwrite any existing destination file(s).
+ * @param overwrite if true overwrite any existing destination file(s).
+ */
+ public void setOverwrite(boolean overwrite) {
+ myCopy.setOverwrite(overwrite);
+ }
+
+ /**
+ * Used to copy empty directories.
+ * @param includeEmpty If true copy empty directories.
+ */
+ public void setIncludeEmptyDirs(boolean includeEmpty) {
+ myCopy.setIncludeEmptyDirs(includeEmpty);
+ }
+
+ /**
+ * If false, note errors to the output but keep going.
+ * @param failonerror true or false
+ */
+ public void setFailOnError(boolean failonerror) {
+ myCopy.setFailOnError(failonerror);
+ }
+
+ /**
+ * Adds a set of files to copy.
+ * @param set a fileset
+ */
+ public void addFileset(FileSet set) {
+ add(set);
+ }
+
+ /**
+ * Adds a collection of filesystem resources to copy.
+ * @param rc a resource collection
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection rc) {
+ if (rc instanceof FileSet && rc.isFilesystemOnly()) {
+ // receives special treatment in copy that this task relies on
+ myCopy.add(rc);
+ } else {
+ if (resources == null) {
+ Restrict r = new Restrict();
+ r.add(new Exists());
+ r.add(resources = new Resources());
+ myCopy.add(r);
+ }
+ resources.add(rc);
+ }
+ }
+
+ /**
+ * The number of milliseconds leeway to give before deciding a
+ * target is out of date.
+ *
+ * <p>Default is 0 milliseconds, or 2 seconds on DOS systems.</p>
+ * @param granularity a <code>long</code> value
+ * @since Ant 1.6.2
+ */
+ public void setGranularity(long granularity) {
+ myCopy.setGranularity(granularity);
+ }
+
+ /**
+ * A container for patterns and selectors that can be used to
+ * specify files that should be kept in the target even if they
+ * are not present in any source directory.
+ *
+ * <p>You must not invoke this method more than once.</p>
+ * @param s a preserveintarget nested element
+ * @since Ant 1.7
+ */
+ public void addPreserveInTarget(SyncTarget s) {
+ if (syncTarget != null) {
+ throw new BuildException("you must not specify multiple "
+ + "preserveintarget elements.");
+ }
+ syncTarget = s;
+ }
+
+ /**
+ * The value of preserveTarget's preserveEmptyDirs attribute if
+ * specified and preserveTarget has been used in the first place.
+ *
+ * @since Ant 1.8.0.
+ */
+ private Boolean getExplicitPreserveEmptyDirs() {
+ return syncTarget == null ? null : syncTarget.getPreserveEmptyDirs();
+ }
+
+ /**
+ * Subclass Copy in order to access it's file/dir maps.
+ */
+ public static class MyCopy extends Copy {
+
+ // List of files that must be copied, irrelevant from the
+ // fact that they are newer or not than the destination.
+ private Set nonOrphans = new HashSet();
+
+ /** Constructor for MyCopy. */
+ public MyCopy() {
+ }
+
+ /**
+ * @see Copy#scan(File, File, String[], String[])
+ */
+ /** {@inheritDoc} */
+ @Override
+ protected void scan(File fromDir, File toDir, String[] files,
+ String[] dirs) {
+ assertTrue("No mapper", mapperElement == null);
+
+ super.scan(fromDir, toDir, files, dirs);
+
+ for (int i = 0; i < files.length; ++i) {
+ nonOrphans.add(files[i]);
+ }
+ for (int i = 0; i < dirs.length; ++i) {
+ nonOrphans.add(dirs[i]);
+ }
+ }
+
+ /**
+ * @see Copy#scan(Resource[], File)
+ */
+ /** {@inheritDoc} */
+ @Override
+ protected Map scan(Resource[] resources, File toDir) {
+ assertTrue("No mapper", mapperElement == null);
+
+ for (int i = 0; i < resources.length; i++) {
+ nonOrphans.add(resources[i].getName());
+ }
+ return super.scan(resources, toDir);
+ }
+
+ /**
+ * Get the destination directory.
+ * @return the destination directory
+ */
+ public File getToDir() {
+ return destDir;
+ }
+
+ /**
+ * Get the includeEmptyDirs attribute.
+ * @return true if emptyDirs are to be included
+ */
+ public boolean getIncludeEmptyDirs() {
+ return includeEmpty;
+ }
+
+ /**
+ * Yes, we can.
+ * @return true always.
+ * @since Ant 1.7
+ */
+ @Override
+ protected boolean supportsNonFileResources() {
+ return true;
+ }
+ }
+
+ /**
+ * Inner class used to hold exclude patterns and selectors to save
+ * stuff that happens to live in the target directory but should
+ * not get removed.
+ *
+ * @since Ant 1.7
+ */
+ public static class SyncTarget extends AbstractFileSet {
+
+ private Boolean preserveEmptyDirs;
+
+ /**
+ * Constructor for SyncTarget.
+ * This just changes the default value of "defaultexcludes" from
+ * true to false.
+ */
+ public SyncTarget() {
+ super();
+ }
+
+ /**
+ * Override AbstractFileSet#setDir(File) to disallow
+ * setting the directory.
+ * @param dir ignored
+ * @throws BuildException always
+ */
+ @Override
+ public void setDir(File dir) throws BuildException {
+ throw new BuildException("preserveintarget doesn't support the dir "
+ + "attribute");
+ }
+
+ /**
+ * Whether empty directories matched by this fileset should be
+ * preserved.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setPreserveEmptyDirs(boolean b) {
+ preserveEmptyDirs = Boolean.valueOf(b);
+ }
+
+ /**
+ * Whether empty directories matched by this fileset should be
+ * preserved.
+ *
+ * @since Ant 1.8.0
+ */
+ public Boolean getPreserveEmptyDirs() {
+ return preserveEmptyDirs;
+ }
+
+ private FileSet toFileSet(boolean withPatterns) {
+ FileSet fs = new FileSet();
+ fs.setCaseSensitive(isCaseSensitive());
+ fs.setFollowSymlinks(isFollowSymlinks());
+ fs.setMaxLevelsOfSymlinks(getMaxLevelsOfSymlinks());
+ fs.setProject(getProject());
+
+ if (withPatterns) {
+ PatternSet ps = mergePatterns(getProject());
+ fs.appendIncludes(ps.getIncludePatterns(getProject()));
+ fs.appendExcludes(ps.getExcludePatterns(getProject()));
+ for (Enumeration e = selectorElements(); e.hasMoreElements();) {
+ fs.appendSelector((FileSelector) e.nextElement());
+ }
+ fs.setDefaultexcludes(getDefaultexcludes());
+ }
+ return fs;
+ }
+ }
+
+ /**
+ * Pseudo-assert method.
+ */
+ private static void assertTrue(String message, boolean condition) {
+ if (!condition) {
+ throw new BuildException("Assertion Error: " + message);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Tar.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Tar.java
new file mode 100644
index 00000000..97547022
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Tar.java
@@ -0,0 +1,1016 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+import java.util.zip.GZIPOutputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.ArchiveFileSet;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.ArchiveResource;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.TarResource;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.MergingMapper;
+import org.apache.tools.ant.util.ResourceUtils;
+import org.apache.tools.ant.util.SourceFileScanner;
+import org.apache.tools.bzip2.CBZip2OutputStream;
+import org.apache.tools.tar.TarConstants;
+import org.apache.tools.tar.TarEntry;
+import org.apache.tools.tar.TarOutputStream;
+
+/**
+ * Creates a tar archive.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ */
+public class Tar extends MatchingTask {
+ private static final int BUFFER_SIZE = 8 * 1024;
+
+ /**
+ * @deprecated since 1.5.x.
+ * Tar.WARN is deprecated and is replaced with
+ * Tar.TarLongFileMode.WARN
+ */
+ @Deprecated
+ public static final String WARN = "warn";
+ /**
+ * @deprecated since 1.5.x.
+ * Tar.FAIL is deprecated and is replaced with
+ * Tar.TarLongFileMode.FAIL
+ */
+ @Deprecated
+ public static final String FAIL = "fail";
+ /**
+ * @deprecated since 1.5.x.
+ * Tar.TRUNCATE is deprecated and is replaced with
+ * Tar.TarLongFileMode.TRUNCATE
+ */
+ @Deprecated
+ public static final String TRUNCATE = "truncate";
+ /**
+ * @deprecated since 1.5.x.
+ * Tar.GNU is deprecated and is replaced with
+ * Tar.TarLongFileMode.GNU
+ */
+ @Deprecated
+ public static final String GNU = "gnu";
+ /**
+ * @deprecated since 1.5.x.
+ * Tar.OMIT is deprecated and is replaced with
+ * Tar.TarLongFileMode.OMIT
+ */
+ @Deprecated
+ public static final String OMIT = "omit";
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ File tarFile;
+ File baseDir;
+
+ private TarLongFileMode longFileMode = new TarLongFileMode();
+
+ // need to keep the package private version for backwards compatibility
+ Vector<TarFileSet> filesets = new Vector<TarFileSet>();
+ // we must keep two lists since other classes may modify the
+ // filesets Vector (it is package private) without us noticing
+ private final Vector<ResourceCollection> resourceCollections = new Vector<ResourceCollection>();
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Indicates whether the user has been warned about long files already.
+ */
+ private boolean longWarningGiven = false;
+
+ private TarCompressionMethod compression = new TarCompressionMethod();
+
+ /**
+ * Encoding to use for filenames, defaults to the platform's
+ * default encoding.
+ */
+ private String encoding;
+
+ /**
+ * Add a new fileset with the option to specify permissions
+ * @return the tar fileset to be used as the nested element.
+ */
+ public TarFileSet createTarFileSet() {
+ final TarFileSet fs = new TarFileSet();
+ fs.setProject(getProject());
+ filesets.addElement(fs);
+ return fs;
+ }
+
+ /**
+ * Add a collection of resources to archive.
+ * @param res a resource collection to archive.
+ * @since Ant 1.7
+ */
+ public void add(final ResourceCollection res) {
+ resourceCollections.add(res);
+ }
+
+ /**
+ * Set is the name/location of where to create the tar file.
+ * @param tarFile the location of the tar file.
+ * @deprecated since 1.5.x.
+ * For consistency with other tasks, please use setDestFile().
+ */
+ @Deprecated
+ public void setTarfile(final File tarFile) {
+ this.tarFile = tarFile;
+ }
+
+ /**
+ * Set is the name/location of where to create the tar file.
+ * @since Ant 1.5
+ * @param destFile The output of the tar
+ */
+ public void setDestFile(final File destFile) {
+ this.tarFile = destFile;
+ }
+
+ /**
+ * This is the base directory to look in for things to tar.
+ * @param baseDir the base directory.
+ */
+ public void setBasedir(final File baseDir) {
+ this.baseDir = baseDir;
+ }
+
+ /**
+ * Set how to handle long files, those with a path&gt;100 chars.
+ * Optional, default=warn.
+ * <p>
+ * Allowable values are
+ * <ul>
+ * <li> truncate - paths are truncated to the maximum length
+ * <li> fail - paths greater than the maximum cause a build exception
+ * <li> warn - paths greater than the maximum cause a warning and GNU is used
+ * <li> gnu - GNU extensions are used for any paths greater than the maximum.
+ * <li> omit - paths greater than the maximum are omitted from the archive
+ * </ul>
+ * @param mode the mode string to handle long files.
+ * @deprecated since 1.5.x.
+ * setLongFile(String) is deprecated and is replaced with
+ * setLongFile(Tar.TarLongFileMode) to make Ant's Introspection
+ * mechanism do the work and also to encapsulate operations on
+ * the mode in its own class.
+ */
+ @Deprecated
+ public void setLongfile(final String mode) {
+ log("DEPRECATED - The setLongfile(String) method has been deprecated."
+ + " Use setLongfile(Tar.TarLongFileMode) instead.");
+ this.longFileMode = new TarLongFileMode();
+ longFileMode.setValue(mode);
+ }
+
+ /**
+ * Set how to handle long files, those with a path&gt;100 chars.
+ * Optional, default=warn.
+ * <p>
+ * Allowable values are
+ * <ul>
+ * <li> truncate - paths are truncated to the maximum length
+ * <li> fail - paths greater than the maximum cause a build exception
+ * <li> warn - paths greater than the maximum cause a warning and GNU is used
+ * <li> gnu - extensions used by older versions of GNU tar are used for any paths greater than the maximum.
+ * <li> posix - use POSIX PAX extension headers for any paths greater than the maximum. Supported by all modern tar implementations.
+ * <li> omit - paths greater than the maximum are omitted from the archive
+ * </ul>
+ * @param mode the mode to handle long file names.
+ */
+ public void setLongfile(final TarLongFileMode mode) {
+ this.longFileMode = mode;
+ }
+
+ /**
+ * Set compression method.
+ * Allowable values are
+ * <ul>
+ * <li> none - no compression
+ * <li> gzip - Gzip compression
+ * <li> bzip2 - Bzip2 compression
+ * </ul>
+ * @param mode the compression method.
+ */
+ public void setCompression(final TarCompressionMethod mode) {
+ this.compression = mode;
+ }
+
+ /**
+ * Encoding to use for filenames, defaults to the platform's
+ * default encoding.
+ *
+ * <p>For a list of possible values see <a
+ * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>.</p>
+ * @param encoding the encoding name
+ *
+ * @since Ant 1.9.5
+ */
+ public void setEncoding(final String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * do the business
+ * @throws BuildException on error
+ */
+ @Override
+ public void execute() throws BuildException {
+ if (tarFile == null) {
+ throw new BuildException("tarfile attribute must be set!",
+ getLocation());
+ }
+
+ if (tarFile.exists() && tarFile.isDirectory()) {
+ throw new BuildException("tarfile is a directory!",
+ getLocation());
+ }
+
+ if (tarFile.exists() && !tarFile.canWrite()) {
+ throw new BuildException("Can not write to the specified tarfile!",
+ getLocation());
+ }
+
+ @SuppressWarnings("unchecked")
+ final Vector<TarFileSet> savedFileSets = (Vector<TarFileSet>) filesets.clone();
+ try {
+ if (baseDir != null) {
+ if (!baseDir.exists()) {
+ throw new BuildException("basedir does not exist!",
+ getLocation());
+ }
+
+ // add the main fileset to the list of filesets to process.
+ final TarFileSet mainFileSet = new TarFileSet(fileset);
+ mainFileSet.setDir(baseDir);
+ filesets.addElement(mainFileSet);
+ }
+
+ if (filesets.size() == 0 && resourceCollections.size() == 0) {
+ throw new BuildException("You must supply either a basedir "
+ + "attribute or some nested resource"
+ + " collections.",
+ getLocation());
+ }
+
+ // check if tar is out of date with respect to each
+ // fileset
+ boolean upToDate = true;
+ for(final TarFileSet tfs : filesets) {
+ upToDate &= check(tfs);
+ }
+ for(final ResourceCollection rcol : resourceCollections) {
+ upToDate &= check(rcol);
+ }
+
+ if (upToDate) {
+ log("Nothing to do: " + tarFile.getAbsolutePath()
+ + " is up to date.", Project.MSG_INFO);
+ return;
+ }
+
+ final File parent = tarFile.getParentFile();
+ if (parent != null && !parent.isDirectory()
+ && !(parent.mkdirs() || parent.isDirectory())) {
+ throw new BuildException("Failed to create missing parent"
+ + " directory for " + tarFile);
+ }
+
+ log("Building tar: " + tarFile.getAbsolutePath(), Project.MSG_INFO);
+
+ TarOutputStream tOut = null;
+ try {
+ tOut = new TarOutputStream(
+ compression.compress(
+ new BufferedOutputStream(
+ new FileOutputStream(tarFile))),
+ encoding);
+ tOut.setDebug(true);
+ if (longFileMode.isTruncateMode()) {
+ tOut.setLongFileMode(TarOutputStream.LONGFILE_TRUNCATE);
+ } else if (longFileMode.isFailMode()
+ || longFileMode.isOmitMode()) {
+ tOut.setLongFileMode(TarOutputStream.LONGFILE_ERROR);
+ } else if (longFileMode.isPosixMode()) {
+ tOut.setLongFileMode(TarOutputStream.LONGFILE_POSIX);
+ } else {
+ // warn or GNU
+ tOut.setLongFileMode(TarOutputStream.LONGFILE_GNU);
+ }
+
+ longWarningGiven = false;
+ for (final TarFileSet tfs : filesets) {
+ tar(tfs, tOut);
+ }
+ for (final ResourceCollection rcol : resourceCollections) {
+ tar(rcol, tOut);
+ }
+ } catch (final IOException ioe) {
+ final String msg = "Problem creating TAR: " + ioe.getMessage();
+ throw new BuildException(msg, ioe, getLocation());
+ } finally {
+ FileUtils.close(tOut);
+ }
+ } finally {
+ filesets = savedFileSets;
+ }
+ }
+
+ /**
+ * tar a file
+ * @param file the file to tar
+ * @param tOut the output stream
+ * @param vPath the path name of the file to tar
+ * @param tarFileSet the fileset that the file came from.
+ * @throws IOException on error
+ */
+ protected void tarFile(final File file, final TarOutputStream tOut, final String vPath,
+ final TarFileSet tarFileSet)
+ throws IOException {
+ if (file.equals(tarFile)) {
+ // If the archive is built for the first time and it is
+ // matched by a resource collection, then it hasn't been
+ // found in check (it hasn't been there) but will be
+ // included now.
+ //
+ // for some strange reason the old code would simply skip
+ // the entry and not fail, do the same now for backwards
+ // compatibility reasons. Without this, the which4j build
+ // fails in Gump
+ return;
+ }
+ tarResource(new FileResource(file), tOut, vPath, tarFileSet);
+ }
+
+ /**
+ * tar a resource
+ * @param r the resource to tar
+ * @param tOut the output stream
+ * @param vPath the path name of the file to tar
+ * @param tarFileSet the fileset that the file came from, may be null.
+ * @throws IOException on error
+ * @since Ant 1.7
+ */
+ protected void tarResource(final Resource r, final TarOutputStream tOut, String vPath,
+ final TarFileSet tarFileSet)
+ throws IOException {
+
+ if (!r.isExists()) {
+ return;
+ }
+
+ boolean preserveLeadingSlashes = false;
+
+ if (tarFileSet != null) {
+ final String fullpath = tarFileSet.getFullpath(this.getProject());
+ if (fullpath.length() > 0) {
+ vPath = fullpath;
+ } else {
+ // don't add "" to the archive
+ if (vPath.length() <= 0) {
+ return;
+ }
+
+ String prefix = tarFileSet.getPrefix(this.getProject());
+ // '/' is appended for compatibility with the zip task.
+ if (prefix.length() > 0 && !prefix.endsWith("/")) {
+ prefix = prefix + "/";
+ }
+ vPath = prefix + vPath;
+ }
+
+ preserveLeadingSlashes = tarFileSet.getPreserveLeadingSlashes();
+
+ if (vPath.startsWith("/") && !preserveLeadingSlashes) {
+ final int l = vPath.length();
+ if (l <= 1) {
+ // we would end up adding "" to the archive
+ return;
+ }
+ vPath = vPath.substring(1, l);
+ }
+ }
+
+ if (r.isDirectory() && !vPath.endsWith("/")) {
+ vPath += "/";
+ }
+
+ if (vPath.length() >= TarConstants.NAMELEN) {
+ if (longFileMode.isOmitMode()) {
+ log("Omitting: " + vPath, Project.MSG_INFO);
+ return;
+ } else if (longFileMode.isWarnMode()) {
+ log("Entry: " + vPath + " longer than "
+ + TarConstants.NAMELEN + " characters.",
+ Project.MSG_WARN);
+ if (!longWarningGiven) {
+ log("Resulting tar file can only be processed "
+ + "successfully by GNU compatible tar commands",
+ Project.MSG_WARN);
+ longWarningGiven = true;
+ }
+ } else if (longFileMode.isFailMode()) {
+ throw new BuildException("Entry: " + vPath
+ + " longer than " + TarConstants.NAMELEN
+ + "characters.", getLocation());
+ }
+ }
+
+ final TarEntry te = new TarEntry(vPath, preserveLeadingSlashes);
+ te.setModTime(r.getLastModified());
+ // preserve permissions
+ if (r instanceof ArchiveResource) {
+ final ArchiveResource ar = (ArchiveResource) r;
+ te.setMode(ar.getMode());
+ if (r instanceof TarResource) {
+ final TarResource tr = (TarResource) r;
+ te.setUserName(tr.getUserName());
+ te.setUserId(tr.getUid());
+ te.setGroupName(tr.getGroup());
+ te.setGroupId(tr.getGid());
+ }
+ }
+
+ if (!r.isDirectory()) {
+ if (r.size() > TarConstants.MAXSIZE) {
+ throw new BuildException(
+ "Resource: " + r + " larger than "
+ + TarConstants.MAXSIZE + " bytes.");
+ }
+ te.setSize(r.getSize());
+ // override permissions if set explicitly
+ if (tarFileSet != null && tarFileSet.hasFileModeBeenSet()) {
+ te.setMode(tarFileSet.getMode());
+ }
+ } else if (tarFileSet != null && tarFileSet.hasDirModeBeenSet()) {
+ // override permissions if set explicitly
+ te.setMode(tarFileSet.getDirMode(this.getProject()));
+ }
+
+ if (tarFileSet != null) {
+ // only override permissions if set explicitly
+ if (tarFileSet.hasUserNameBeenSet()) {
+ te.setUserName(tarFileSet.getUserName());
+ }
+ if (tarFileSet.hasGroupBeenSet()) {
+ te.setGroupName(tarFileSet.getGroup());
+ }
+ if (tarFileSet.hasUserIdBeenSet()) {
+ te.setUserId(tarFileSet.getUid());
+ }
+ if (tarFileSet.hasGroupIdBeenSet()) {
+ te.setGroupId(tarFileSet.getGid());
+ }
+ }
+
+ InputStream in = null;
+ try {
+ tOut.putNextEntry(te);
+
+ if (!r.isDirectory()) {
+ in = r.getInputStream();
+
+ final byte[] buffer = new byte[BUFFER_SIZE];
+ int count = 0;
+ do {
+ tOut.write(buffer, 0, count);
+ count = in.read(buffer, 0, buffer.length);
+ } while (count != -1);
+ }
+
+ tOut.closeEntry();
+ } finally {
+ FileUtils.close(in);
+ }
+ }
+
+ /**
+ * Is the archive up to date in relationship to a list of files.
+ * @param files the files to check
+ * @return true if the archive is up to date.
+ * @deprecated since 1.5.x.
+ * use the two-arg version instead.
+ */
+ @Deprecated
+ protected boolean archiveIsUpToDate(final String[] files) {
+ return archiveIsUpToDate(files, baseDir);
+ }
+
+ /**
+ * Is the archive up to date in relationship to a list of files.
+ * @param files the files to check
+ * @param dir the base directory for the files.
+ * @return true if the archive is up to date.
+ * @since Ant 1.5.2
+ */
+ protected boolean archiveIsUpToDate(final String[] files, final File dir) {
+ final SourceFileScanner sfs = new SourceFileScanner(this);
+ final MergingMapper mm = new MergingMapper();
+ mm.setTo(tarFile.getAbsolutePath());
+ return sfs.restrict(files, dir, null, mm).length == 0;
+ }
+
+ /**
+ * Is the archive up to date in relationship to a list of files.
+ * @param r the files to check
+ * @return true if the archive is up to date.
+ * @since Ant 1.7
+ */
+ protected boolean archiveIsUpToDate(final Resource r) {
+ return SelectorUtils.isOutOfDate(new FileResource(tarFile), r,
+ FileUtils.getFileUtils()
+ .getFileTimestampGranularity());
+ }
+
+ /**
+ * Whether this task can deal with non-file resources.
+ *
+ * <p>This implementation returns true only if this task is
+ * &lt;tar&gt;. Any subclass of this class that also wants to
+ * support non-file resources needs to override this method. We
+ * need to do so for backwards compatibility reasons since we
+ * can't expect subclasses to support resources.</p>
+ * @return true for this task.
+ * @since Ant 1.7
+ */
+ protected boolean supportsNonFileResources() {
+ return getClass().equals(Tar.class);
+ }
+
+ /**
+ * Checks whether the archive is out-of-date with respect to the resources
+ * of the given collection.
+ *
+ * <p>Also checks that either all collections only contain file
+ * resources or this class supports non-file collections.</p>
+ *
+ * <p>And - in case of file-collections - ensures that the archive won't
+ * contain itself.</p>
+ *
+ * @param rc the resource collection to check
+ * @return whether the archive is up-to-date
+ * @since Ant 1.7
+ */
+ protected boolean check(final ResourceCollection rc) {
+ boolean upToDate = true;
+ if (isFileFileSet(rc)) {
+ final FileSet fs = (FileSet) rc;
+ upToDate = check(fs.getDir(getProject()), getFileNames(fs));
+ } else if (!rc.isFilesystemOnly() && !supportsNonFileResources()) {
+ throw new BuildException("only filesystem resources are supported");
+ } else if (rc.isFilesystemOnly()) {
+ final Set<File> basedirs = new HashSet<File>();
+ final Map<File, List<String>> basedirToFilesMap = new HashMap<File, List<String>>();
+ for (final Resource res : rc) {
+ final FileResource r = ResourceUtils
+ .asFileResource(res.as(FileProvider.class));
+ File base = r.getBaseDir();
+ if (base == null) {
+ base = Copy.NULL_FILE_PLACEHOLDER;
+ }
+ basedirs.add(base);
+ List<String> files = basedirToFilesMap.get(base);
+ if (files == null) {
+ files = new Vector<String>();
+ basedirToFilesMap.put(base, files);
+ }
+ if (base == Copy.NULL_FILE_PLACEHOLDER) {
+ files.add(r.getFile().getAbsolutePath());
+ } else {
+ files.add(r.getName());
+ }
+ }
+ for (final File base : basedirs) {
+ final File tmpBase = base == Copy.NULL_FILE_PLACEHOLDER ? null : base;
+ final List<String> files = basedirToFilesMap.get(base);
+ upToDate &= check(tmpBase, files);
+ }
+ } else { // non-file resources
+ final Iterator<Resource> iter = rc.iterator();
+ while (upToDate && iter.hasNext()) {
+ final Resource r = iter.next();
+ upToDate = archiveIsUpToDate(r);
+ }
+ }
+ return upToDate;
+ }
+
+ /**
+ * <p>Checks whether the archive is out-of-date with respect to the
+ * given files, ensures that the archive won't contain itself.</p>
+ *
+ * @param basedir base directory for file names
+ * @param files array of relative file names
+ * @return whether the archive is up-to-date
+ * @since Ant 1.7
+ */
+ protected boolean check(final File basedir, final String[] files) {
+ boolean upToDate = true;
+ if (!archiveIsUpToDate(files, basedir)) {
+ upToDate = false;
+ }
+
+ for (int i = 0; i < files.length; ++i) {
+ if (tarFile.equals(new File(basedir, files[i]))) {
+ throw new BuildException("A tar file cannot include "
+ + "itself", getLocation());
+ }
+ }
+ return upToDate;
+ }
+
+ /**
+ * <p>Checks whether the archive is out-of-date with respect to the
+ * given files, ensures that the archive won't contain itself.</p>
+ *
+ * @param basedir base directory for file names
+ * @param files array of relative file names
+ * @return whether the archive is up-to-date
+ * @see #check(File, String[])
+ * @since Ant 1.9.5
+ */
+ protected boolean check(final File basedir, final Collection<String> files) {
+ return check(basedir, files.toArray(new String[files.size()]));
+ }
+
+ /**
+ * Adds the resources contained in this collection to the archive.
+ *
+ * <p>Uses the file based methods for file resources for backwards
+ * compatibility.</p>
+ *
+ * @param rc the collection containing resources to add
+ * @param tOut stream writing to the archive.
+ * @throws IOException on error.
+ * @since Ant 1.7
+ */
+ protected void tar(final ResourceCollection rc, final TarOutputStream tOut)
+ throws IOException {
+ ArchiveFileSet afs = null;
+ if (rc instanceof ArchiveFileSet) {
+ afs = (ArchiveFileSet) rc;
+ }
+ if (afs != null && afs.size() > 1
+ && afs.getFullpath(this.getProject()).length() > 0) {
+ throw new BuildException("fullpath attribute may only "
+ + "be specified for "
+ + "filesets that specify a "
+ + "single file.");
+ }
+ final TarFileSet tfs = asTarFileSet(afs);
+
+ if (isFileFileSet(rc)) {
+ final FileSet fs = (FileSet) rc;
+ final String[] files = getFileNames(fs);
+ for (int i = 0; i < files.length; i++) {
+ final File f = new File(fs.getDir(getProject()), files[i]);
+ final String name = files[i].replace(File.separatorChar, '/');
+ tarFile(f, tOut, name, tfs);
+ }
+ } else if (rc.isFilesystemOnly()) {
+ for (final Resource r : rc) {
+ final File f = r.as(FileProvider.class).getFile();
+ tarFile(f, tOut, f.getName(), tfs);
+ }
+ } else { // non-file resources
+ for (final Resource r : rc) {
+ tarResource(r, tOut, r.getName(), tfs);
+ }
+ }
+ }
+
+ /**
+ * whether the given resource collection is a (subclass of)
+ * FileSet that only contains file system resources.
+ * @param rc the resource collection to check.
+ * @return true if the collection is a fileset.
+ * @since Ant 1.7
+ */
+ protected static boolean isFileFileSet(final ResourceCollection rc) {
+ return rc instanceof FileSet && rc.isFilesystemOnly();
+ }
+
+ /**
+ * Grabs all included files and directors from the FileSet and
+ * returns them as an array of (relative) file names.
+ * @param fs the fileset to operate on.
+ * @return a list of the filenames.
+ * @since Ant 1.7
+ */
+ protected static String[] getFileNames(final FileSet fs) {
+ final DirectoryScanner ds = fs.getDirectoryScanner(fs.getProject());
+ final String[] directories = ds.getIncludedDirectories();
+ final String[] filesPerSe = ds.getIncludedFiles();
+ final String[] files = new String [directories.length + filesPerSe.length];
+ System.arraycopy(directories, 0, files, 0, directories.length);
+ System.arraycopy(filesPerSe, 0, files, directories.length,
+ filesPerSe.length);
+ return files;
+ }
+
+ /**
+ * Copies fullpath, prefix and permission attributes from the
+ * ArchiveFileSet to a new TarFileSet (or returns it unchanged if
+ * it already is a TarFileSet).
+ *
+ * @param archiveFileSet fileset to copy attributes from, may be null
+ * @return a new TarFileSet.
+ * @since Ant 1.7
+ */
+ protected TarFileSet asTarFileSet(final ArchiveFileSet archiveFileSet) {
+ TarFileSet tfs = null;
+ if (archiveFileSet != null && archiveFileSet instanceof TarFileSet) {
+ tfs = (TarFileSet) archiveFileSet;
+ } else {
+ tfs = new TarFileSet();
+ tfs.setProject(getProject());
+ if (archiveFileSet != null) {
+ tfs.setPrefix(archiveFileSet.getPrefix(getProject()));
+ tfs.setFullpath(archiveFileSet.getFullpath(getProject()));
+ if (archiveFileSet.hasFileModeBeenSet()) {
+ tfs.integerSetFileMode(archiveFileSet
+ .getFileMode(getProject()));
+ }
+ if (archiveFileSet.hasDirModeBeenSet()) {
+ tfs.integerSetDirMode(archiveFileSet
+ .getDirMode(getProject()));
+ }
+
+ if (archiveFileSet
+ instanceof org.apache.tools.ant.types.TarFileSet) {
+ final org.apache.tools.ant.types.TarFileSet t =
+ (org.apache.tools.ant.types.TarFileSet) archiveFileSet;
+ if (t.hasUserNameBeenSet()) {
+ tfs.setUserName(t.getUserName());
+ }
+ if (t.hasGroupBeenSet()) {
+ tfs.setGroup(t.getGroup());
+ }
+ if (t.hasUserIdBeenSet()) {
+ tfs.setUid(t.getUid());
+ }
+ if (t.hasGroupIdBeenSet()) {
+ tfs.setGid(t.getGid());
+ }
+ }
+ }
+ }
+ return tfs;
+ }
+
+ /**
+ * This is a FileSet with the option to specify permissions
+ * and other attributes.
+ */
+ public static class TarFileSet
+ extends org.apache.tools.ant.types.TarFileSet {
+ private String[] files = null;
+
+ private boolean preserveLeadingSlashes = false;
+
+ /**
+ * Creates a new <code>TarFileSet</code> instance.
+ * Using a fileset as a constructor argument.
+ *
+ * @param fileset a <code>FileSet</code> value
+ */
+ public TarFileSet(final FileSet fileset) {
+ super(fileset);
+ }
+
+ /**
+ * Creates a new <code>TarFileSet</code> instance.
+ *
+ */
+ public TarFileSet() {
+ super();
+ }
+
+ /**
+ * Get a list of files and directories specified in the fileset.
+ * @param p the current project.
+ * @return a list of file and directory names, relative to
+ * the baseDir for the project.
+ */
+ public String[] getFiles(final Project p) {
+ if (files == null) {
+ files = getFileNames(this);
+ }
+
+ return files;
+ }
+
+ /**
+ * A 3 digit octal string, specify the user, group and
+ * other modes in the standard Unix fashion;
+ * optional, default=0644
+ * @param octalString a 3 digit octal string.
+ */
+ public void setMode(final String octalString) {
+ setFileMode(octalString);
+ }
+
+ /**
+ * @return the current mode.
+ */
+ public int getMode() {
+ return getFileMode(this.getProject());
+ }
+
+ /**
+ * Flag to indicates whether leading `/'s should
+ * be preserved in the file names.
+ * Optional, default is <code>false</code>.
+ * @param b the leading slashes flag.
+ */
+ public void setPreserveLeadingSlashes(final boolean b) {
+ this.preserveLeadingSlashes = b;
+ }
+
+ /**
+ * @return the leading slashes flag.
+ */
+ public boolean getPreserveLeadingSlashes() {
+ return preserveLeadingSlashes;
+ }
+ }
+
+ /**
+ * Set of options for long file handling in the task.
+ *
+ */
+ public static class TarLongFileMode extends EnumeratedAttribute {
+
+ /** permissible values for longfile attribute */
+ public static final String
+ WARN = "warn",
+ FAIL = "fail",
+ TRUNCATE = "truncate",
+ GNU = "gnu",
+ POSIX = "posix",
+ OMIT = "omit";
+
+ private final String[] validModes = {
+ WARN, FAIL, TRUNCATE, GNU, POSIX, OMIT
+ };
+
+ /** Constructor, defaults to "warn" */
+ public TarLongFileMode() {
+ super();
+ setValue(WARN);
+ }
+
+ /**
+ * @return the possible values for this enumerated type.
+ */
+ @Override
+ public String[] getValues() {
+ return validModes;
+ }
+
+ /**
+ * @return true if value is "truncate".
+ */
+ public boolean isTruncateMode() {
+ return TRUNCATE.equalsIgnoreCase(getValue());
+ }
+
+ /**
+ * @return true if value is "warn".
+ */
+ public boolean isWarnMode() {
+ return WARN.equalsIgnoreCase(getValue());
+ }
+
+ /**
+ * @return true if value is "gnu".
+ */
+ public boolean isGnuMode() {
+ return GNU.equalsIgnoreCase(getValue());
+ }
+
+ /**
+ * @return true if value is "fail".
+ */
+ public boolean isFailMode() {
+ return FAIL.equalsIgnoreCase(getValue());
+ }
+
+ /**
+ * @return true if value is "omit".
+ */
+ public boolean isOmitMode() {
+ return OMIT.equalsIgnoreCase(getValue());
+ }
+
+ /**
+ * @return true if value is "posix".
+ */
+ public boolean isPosixMode() {
+ return POSIX.equalsIgnoreCase(getValue());
+ }
+ }
+
+ /**
+ * Valid Modes for Compression attribute to Tar Task
+ *
+ */
+ public static final class TarCompressionMethod extends EnumeratedAttribute {
+
+ // permissible values for compression attribute
+ /**
+ * No compression
+ */
+ private static final String NONE = "none";
+ /**
+ * GZIP compression
+ */
+ private static final String GZIP = "gzip";
+ /**
+ * BZIP2 compression
+ */
+ private static final String BZIP2 = "bzip2";
+
+
+ /**
+ * Default constructor
+ */
+ public TarCompressionMethod() {
+ super();
+ setValue(NONE);
+ }
+
+ /**
+ * Get valid enumeration values.
+ * @return valid enumeration values
+ */
+ @Override
+ public String[] getValues() {
+ return new String[] {NONE, GZIP, BZIP2 };
+ }
+
+ /**
+ * This method wraps the output stream with the
+ * corresponding compression method
+ *
+ * @param ostream output stream
+ * @return output stream with on-the-fly compression
+ * @exception IOException thrown if file is not writable
+ */
+ private OutputStream compress(final OutputStream ostream)
+ throws IOException {
+ final String v = getValue();
+ if (GZIP.equals(v)) {
+ return new GZIPOutputStream(ostream);
+ } else {
+ if (BZIP2.equals(v)) {
+ ostream.write('B');
+ ostream.write('Z');
+ return new CBZip2OutputStream(ostream);
+ }
+ }
+ return ostream;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/TaskOutputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/TaskOutputStream.java
new file mode 100644
index 00000000..0fdeeff6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/TaskOutputStream.java
@@ -0,0 +1,96 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.Task;
+
+/**
+ * Redirects text written to a stream thru the standard
+ * ant logging mechanism. This class is useful for integrating
+ * with tools that write to System.out and System.err. For example,
+ * the following will cause all text written to System.out to be
+ * logged with "info" priority:
+ * <pre>System.setOut(new PrintStream(new TaskOutputStream(project, Project.MSG_INFO)));</pre>
+ *
+ * <p><strong>As of Ant 1.2, this class is considered to be dead code
+ * by the Ant developers and is unmaintained. Don't use
+ * it.</strong></p>
+ *
+ * @deprecated since 1.2.x.
+ * Use LogOutputStream instead.
+ */
+
+public class TaskOutputStream extends OutputStream {
+
+ private Task task;
+ private StringBuffer line;
+ private int msgOutputLevel;
+
+ /**
+ * Constructs a new JavacOutputStream with the given project
+ * as the output source for messages.
+ */
+
+ TaskOutputStream(Task task, int msgOutputLevel) {
+ System.err.println("As of Ant 1.2 released in October 2000, the "
+ + "TaskOutputStream class");
+ System.err.println("is considered to be dead code by the Ant "
+ + "developers and is unmaintained.");
+ System.err.println("Don\'t use it!");
+
+ this.task = task;
+ this.msgOutputLevel = msgOutputLevel;
+
+ line = new StringBuffer();
+ }
+
+ /**
+ * Write a character to the output stream. This method looks
+ * to make sure that there isn't an error being reported and
+ * will flush each line of input out to the project's log stream.
+ * @param c the character to write
+ * @throws IOException on error
+ */
+
+ public void write(int c) throws IOException {
+ char cc = (char) c;
+ if (cc == '\r' || cc == '\n') {
+ // line feed
+ if (line.length() > 0) {
+ processLine();
+ }
+ } else {
+ line.append(cc);
+ }
+ }
+
+ /**
+ * Processes a line of input and determines if an error occurred.
+ */
+
+ private void processLine() {
+ String s = line.toString();
+ task.log(s, msgOutputLevel);
+ line = new StringBuffer();
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Taskdef.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Taskdef.java
new file mode 100644
index 00000000..a7d0b71c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Taskdef.java
@@ -0,0 +1,51 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskAdapter;
+
+/**
+ * <p>Adds a task definition to the current project, such that this new task can be
+ * used in the current project. Two attributes are needed, the name that identifies
+ * this task uniquely, and the full name of the class (including the packages) that
+ * implements this task.</p>
+ * <p>You can also define a group of tasks at once using the file or
+ * resource attributes. These attributes point to files in the format of
+ * Java property files. Each line defines a single task in the format:</p>
+ * <pre>
+ * taskname=fully.qualified.java.classname
+ * </pre>
+ * @since Ant 1.1
+ * @ant.task category="internal"
+ */
+public class Taskdef extends Typedef {
+
+ /**
+ * Default constructor.
+ * Creates a new Taskdef instance.
+ * This sets the adapter and the adaptto classes to
+ * TaskAdapter and Task.
+ */
+
+ public Taskdef() {
+ setAdapterClass(TaskAdapter.class);
+ setAdaptToClass(Task.class);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/TempFile.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/TempFile.java
new file mode 100644
index 00000000..5f55a376
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/TempFile.java
@@ -0,0 +1,163 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * This task sets a property to the name of a temporary file.
+ * Unlike {@link File#createTempFile}, this task does not (by default) actually create the
+ * temporary file, but it does guarantee that the file did not
+ * exist when the task was executed.
+ * <p>
+ * Examples
+ * <pre>&lt;tempfile property="temp.file" /&gt;</pre>
+ * create a temporary file
+ * <pre>&lt;tempfile property="temp.file" suffix=".xml" /&gt;</pre>
+ * create a temporary file with the .xml suffix.
+ * <pre>&lt;tempfile property="temp.file" destDir="build"/&gt;</pre>
+ * create a temp file in the build subdir
+ * @since Ant 1.5
+ * @ant.task
+ */
+
+public class TempFile extends Task {
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Name of property to set.
+ */
+ private String property;
+
+ /**
+ * Directory to create the file in. Can be null.
+ */
+ private File destDir = null;
+
+ /**
+ * Prefix for the file.
+ */
+ private String prefix;
+
+ /**
+ * Suffix for the file.
+ */
+ private String suffix = "";
+
+ /** deleteOnExit flag */
+ private boolean deleteOnExit;
+
+ /** createFile flag */
+ private boolean createFile;
+
+ /**
+ * Sets the property you wish to assign the temporary file to.
+ *
+ * @param property The property to set
+ * @ant.attribute group="required"
+ */
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+
+ /**
+ * Sets the destination directory. If not set,
+ * the basedir directory is used instead.
+ *
+ * @param destDir The new destDir value
+ */
+ public void setDestDir(File destDir) {
+ this.destDir = destDir;
+ }
+
+
+ /**
+ * Sets the optional prefix string for the temp file.
+ *
+ * @param prefix string to prepend to generated string
+ */
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+
+ /**
+ * Sets the optional suffix string for the temp file.
+ *
+ * @param suffix suffix including any "." , e.g ".xml"
+ */
+ public void setSuffix(String suffix) {
+ this.suffix = suffix;
+ }
+
+ /**
+ * Set whether the tempfile created by this task should be set
+ * for deletion on normal VM exit.
+ * @param deleteOnExit boolean flag.
+ */
+ public void setDeleteOnExit(boolean deleteOnExit) {
+ this.deleteOnExit = deleteOnExit;
+ }
+
+ /**
+ * Learn whether deleteOnExit is set for this tempfile task.
+ * @return boolean deleteOnExit flag.
+ */
+ public boolean isDeleteOnExit() {
+ return deleteOnExit;
+ }
+
+ /**
+ * If set the file is actually created, if not just a name is created.
+ * @param createFile boolean flag.
+ */
+ public void setCreateFile(boolean createFile) {
+ this.createFile = createFile;
+ }
+
+ /**
+ * Learn whether createFile flag is set for this tempfile task.
+ * @return the createFile flag.
+ */
+ public boolean isCreateFile() {
+ return createFile;
+ }
+
+ /**
+ * Creates the temporary file.
+ *
+ * @exception BuildException if something goes wrong with the build
+ */
+ public void execute() throws BuildException {
+ if (property == null || property.length() == 0) {
+ throw new BuildException("no property specified");
+ }
+ if (destDir == null) {
+ destDir = getProject().resolveFile(".");
+ }
+ File tfile = FILE_UTILS.createTempFile(prefix, suffix, destDir,
+ deleteOnExit, createFile);
+ getProject().setNewProperty(property, tfile.toString());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Touch.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Touch.java
new file mode 100644
index 00000000..4a4118ce
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Touch.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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileList;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.Touchable;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Touch a file and/or fileset(s) and/or filelist(s);
+ * corresponds to the Unix touch command.
+ *
+ * <p>If the file to touch doesn't exist, an empty one is created.</p>
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="filesystem"
+ */
+public class Touch extends Task {
+
+ public interface DateFormatFactory {
+ DateFormat getPrimaryFormat();
+ DateFormat getFallbackFormat();
+ }
+
+ public static final DateFormatFactory DEFAULT_DF_FACTORY
+ = new DateFormatFactory() {
+ /*
+ * The initial version used DateFormat.SHORT for the
+ * time format, which ignores seconds. If we want
+ * seconds as well, we need DateFormat.MEDIUM, which
+ * in turn would break all old build files.
+ *
+ * First try to parse with DateFormat.SHORT and if
+ * that fails with MEDIUM - throw an exception if both
+ * fail.
+ */
+ public DateFormat getPrimaryFormat() {
+ return DateFormat.getDateTimeInstance(DateFormat.SHORT,
+ DateFormat.SHORT, Locale.US);
+ }
+ public DateFormat getFallbackFormat() {
+ return DateFormat.getDateTimeInstance(DateFormat.SHORT,
+ DateFormat.MEDIUM, Locale.US);
+ }
+ };
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private File file;
+ private long millis = -1;
+ private String dateTime;
+ private Vector filesets = new Vector();
+ private Union resources;
+ private boolean dateTimeConfigured;
+ private boolean mkdirs;
+ private boolean verbose = true;
+ private FileNameMapper fileNameMapper = null;
+ private DateFormatFactory dfFactory = DEFAULT_DF_FACTORY;
+
+ /**
+ * Construct a new <code>Touch</code> task.
+ */
+ public Touch() {
+ }
+
+ /**
+ * Sets a single source file to touch. If the file does not exist
+ * an empty file will be created.
+ * @param file the <code>File</code> to touch.
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * Set the new modification time of file(s) touched
+ * in milliseconds since midnight Jan 1 1970. Optional, default=now.
+ * @param millis the <code>long</code> timestamp to use.
+ */
+ public void setMillis(long millis) {
+ this.millis = millis;
+ }
+
+ /**
+ * Set the new modification time of file(s) touched
+ * in the format &quot;MM/DD/YYYY HH:MM AM <i>or</i> PM&quot;
+ * or &quot;MM/DD/YYYY HH:MM:SS AM <i>or</i> PM&quot;.
+ * Optional, default=now.
+ * @param dateTime the <code>String</code> date in the specified format.
+ */
+ public void setDatetime(String dateTime) {
+ if (this.dateTime != null) {
+ log("Resetting datetime attribute to " + dateTime, Project.MSG_VERBOSE);
+ }
+ this.dateTime = dateTime;
+ dateTimeConfigured = false;
+ }
+
+ /**
+ * Set whether nonexistent parent directories should be created
+ * when touching new files.
+ * @param mkdirs <code>boolean</code> whether to create parent directories.
+ * @since Ant 1.6.3
+ */
+ public void setMkdirs(boolean mkdirs) {
+ this.mkdirs = mkdirs;
+ }
+
+ /**
+ * Set whether the touch task will report every file it creates;
+ * defaults to <code>true</code>.
+ * @param verbose <code>boolean</code> flag.
+ * @since Ant 1.6.3
+ */
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ /**
+ * Set the format of the datetime attribute.
+ * @param pattern the <code>SimpleDateFormat</code>-compatible format pattern.
+ * @since Ant 1.6.3
+ */
+ public void setPattern(final String pattern) {
+ dfFactory = new DateFormatFactory() {
+ public DateFormat getPrimaryFormat() {
+ return new SimpleDateFormat(pattern);
+ }
+ public DateFormat getFallbackFormat() {
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Add a <code>Mapper</code>.
+ * @param mapper the <code>Mapper</code> to add.
+ * @since Ant 1.6.3
+ */
+ public void addConfiguredMapper(Mapper mapper) {
+ add(mapper.getImplementation());
+ }
+
+ /**
+ * Add a <code>FileNameMapper</code>.
+ * @param fileNameMapper the <code>FileNameMapper</code> to add.
+ * @since Ant 1.6.3
+ * @throws BuildException if multiple mappers are added.
+ */
+ public void add(FileNameMapper fileNameMapper) throws BuildException {
+ if (this.fileNameMapper != null) {
+ throw new BuildException("Only one mapper may be added to the "
+ + getTaskName() + " task.");
+ }
+ this.fileNameMapper = fileNameMapper;
+ }
+
+ /**
+ * Add a set of files to touch.
+ * @param set the <code>Fileset</code> to add.
+ */
+ public void addFileset(FileSet set) {
+ filesets.add(set);
+ add(set);
+ }
+
+ /**
+ * Add a filelist to touch.
+ * @param list the <code>Filelist</code> to add.
+ */
+ public void addFilelist(FileList list) {
+ add(list);
+ }
+
+ /**
+ * Add a collection of resources to touch.
+ * @param rc the collection to add.
+ * @since Ant 1.7
+ */
+ public synchronized void add(ResourceCollection rc) {
+ resources = resources == null ? new Union() : resources;
+ resources.add(rc);
+ }
+
+ /**
+ * Check that this task has been configured properly.
+ * @throws BuildException if configuration errors are detected.
+ * @since Ant 1.6.3
+ */
+ protected synchronized void checkConfiguration() throws BuildException {
+ if (file == null && resources == null) {
+ throw new BuildException("Specify at least one source"
+ + "--a file or resource collection.");
+ }
+ if (file != null && file.exists() && file.isDirectory()) {
+ throw new BuildException("Use a resource collection to touch directories.");
+ }
+ if (dateTime != null && !dateTimeConfigured) {
+ long workmillis = millis;
+ if ("now".equalsIgnoreCase(dateTime)) {
+ workmillis = System.currentTimeMillis();
+ } else {
+ DateFormat df = dfFactory.getPrimaryFormat();
+ ParseException pe = null;
+ try {
+ workmillis = df.parse(dateTime).getTime();
+ } catch (ParseException peOne) {
+ df = dfFactory.getFallbackFormat();
+ if (df == null) {
+ pe = peOne;
+ } else {
+ try {
+ workmillis = df.parse(dateTime).getTime();
+ } catch (ParseException peTwo) {
+ pe = peTwo;
+ }
+ }
+ }
+ if (pe != null) {
+ throw new BuildException(pe.getMessage(), pe, getLocation());
+ }
+ if (workmillis < 0) {
+ throw new BuildException("Date of " + dateTime
+ + " results in negative " + "milliseconds value "
+ + "relative to epoch " + "(January 1, 1970, "
+ + "00:00:00 GMT).");
+ }
+ }
+ log("Setting millis to " + workmillis + " from datetime attribute",
+ ((millis < 0) ? Project.MSG_DEBUG : Project.MSG_VERBOSE));
+ setMillis(workmillis);
+ // only set if successful to this point:
+ dateTimeConfigured = true;
+ }
+ }
+
+ /**
+ * Execute the touch operation.
+ *
+ * @throws BuildException
+ * if an error occurs.
+ */
+ public void execute() throws BuildException {
+ checkConfiguration();
+ touch();
+ }
+
+ /**
+ * Does the actual work; assumes everything has been checked by now.
+ * @throws BuildException if an error occurs.
+ */
+ protected void touch() throws BuildException {
+ long defaultTimestamp = getTimestamp();
+
+ if (file != null) {
+ touch(new FileResource(file.getParentFile(), file.getName()),
+ defaultTimestamp);
+ }
+ if (resources == null) {
+ return;
+ }
+ // deal with the resource collections
+ for (Resource r : resources) {
+ Touchable t = r.as(Touchable.class);
+ if (t == null) {
+ throw new BuildException("Can't touch " + r);
+ }
+ touch(r, defaultTimestamp);
+ }
+
+ // deal with filesets in a special way since the task
+ // originally also used the directories and Union won't return
+ // them.
+ final int size = filesets.size();
+ for (int i = 0; i < size; i++) {
+ FileSet fs = (FileSet) filesets.elementAt(i);
+ DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+ File fromDir = fs.getDir(getProject());
+
+ String[] srcDirs = ds.getIncludedDirectories();
+
+ for (int j = 0; j < srcDirs.length; j++) {
+ touch(new FileResource(fromDir, srcDirs[j]), defaultTimestamp);
+ }
+ }
+ }
+
+ /**
+ * Touch a single file with the current timestamp (this.millis). This method
+ * does not interact with any nested mappers and remains for reasons of
+ * backwards-compatibility only.
+ * @param file file to touch
+ * @throws BuildException on error
+ * @deprecated since 1.6.x.
+ */
+ protected void touch(File file) {
+ touch(file, getTimestamp());
+ }
+
+ private long getTimestamp() {
+ return (millis < 0) ? System.currentTimeMillis() : millis;
+ }
+
+ private void touch(Resource r, long defaultTimestamp) {
+ if (fileNameMapper == null) {
+ FileProvider fp = r.as(FileProvider.class);
+ if (fp != null) {
+ // use this to create file and deal with non-writable files
+ touch(fp.getFile(), defaultTimestamp);
+ } else {
+ r.as(Touchable.class).touch(defaultTimestamp);
+ }
+ } else {
+ String[] mapped = fileNameMapper.mapFileName(r.getName());
+ if (mapped != null && mapped.length > 0) {
+ long modTime = defaultTimestamp;
+ if (millis < 0 && r.isExists()) {
+ modTime = r.getLastModified();
+ }
+ for (int i = 0; i < mapped.length; i++) {
+ touch(getProject().resolveFile(mapped[i]), modTime);
+ }
+ }
+ }
+ }
+
+ private void touch(File file, long modTime) {
+ if (!file.exists()) {
+ log("Creating " + file,
+ ((verbose) ? Project.MSG_INFO : Project.MSG_VERBOSE));
+ try {
+ FILE_UTILS.createNewFile(file, mkdirs);
+ } catch (IOException ioe) {
+ throw new BuildException("Could not create " + file, ioe,
+ getLocation());
+ }
+ }
+ if (!file.canWrite()) {
+ throw new BuildException("Can not change modification date of "
+ + "read-only file " + file);
+ }
+ FILE_UTILS.setFileLastModified(file, modTime);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Transform.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Transform.java
new file mode 100644
index 00000000..9fd15d1f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Transform.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.tools.ant.taskdefs;
+
+/**
+ * Has been merged into ExecuteOn, empty class for backwards compatibility.
+ * We leave that in case that external programs access this class directly,
+ * for example via
+ * <tt> Transform tr = (Transform) getProject().createTask("apply") </tt>
+ *
+ * @ant.task ignore="true"
+ */
+public class Transform extends ExecuteOn {
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Truncate.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Truncate.java
new file mode 100644
index 00000000..fe5e9a7a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Truncate.java
@@ -0,0 +1,205 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Set the length of one or more files, as the intermittently available
+ * <code>truncate</code> Unix utility/function.
+ * @since Ant 1.7.1
+ */
+public class Truncate extends Task {
+
+ private static final int BUFFER_SIZE = 1024;
+
+ private static final Long ZERO = new Long(0L);
+
+ private static final String NO_CHILD = "No files specified.";
+
+ private static final String INVALID_LENGTH = "Cannot truncate to length ";
+
+ private static final String READ_WRITE = "rw";
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private static final byte[] FILL_BUFFER = new byte[BUFFER_SIZE];
+
+ private Path path;
+ private boolean create = true;
+ private boolean mkdirs = false;
+
+ private Long length;
+ private Long adjust;
+
+ /**
+ * Set a single target File.
+ * @param f the single File
+ */
+ public void setFile(File f) {
+ add(new FileResource(f));
+ }
+
+ /**
+ * Add a nested (filesystem-only) ResourceCollection.
+ * @param rc the ResourceCollection to add.
+ */
+ public void add(ResourceCollection rc) {
+ getPath().add(rc);
+ }
+
+ /**
+ * Set the amount by which files' lengths should be adjusted.
+ * It is permissible to append K / M / G / T / P.
+ * @param adjust (positive or negative) adjustment amount.
+ */
+ public void setAdjust(Long adjust) {
+ this.adjust = adjust;
+ }
+
+ /**
+ * Set the length to which files should be set.
+ * It is permissible to append K / M / G / T / P.
+ * @param length (positive) adjustment amount.
+ */
+ public void setLength(Long length) {
+ this.length = length;
+ if (length != null && length.longValue() < 0) {
+ throw new BuildException(INVALID_LENGTH + length);
+ }
+ }
+
+ /**
+ * Set whether to create nonexistent files.
+ * @param create boolean, default <code>true</code>.
+ */
+ public void setCreate(boolean create) {
+ this.create = create;
+ }
+
+ /**
+ * Set whether, when creating nonexistent files, nonexistent directories
+ * should also be created.
+ * @param mkdirs boolean, default <code>false</code>.
+ */
+ public void setMkdirs(boolean mkdirs) {
+ this.mkdirs = mkdirs;
+ }
+
+ /** {@inheritDoc}. */
+ public void execute() {
+ if (length != null && adjust != null) {
+ throw new BuildException(
+ "length and adjust are mutually exclusive options");
+ }
+ if (length == null && adjust == null) {
+ length = ZERO;
+ }
+ if (path == null) {
+ throw new BuildException(NO_CHILD);
+ }
+ for (Resource r : path) {
+ File f = r.as(FileProvider.class).getFile();
+ if (shouldProcess(f)) {
+ process(f);
+ }
+ }
+ }
+
+ private boolean shouldProcess(File f) {
+ if (f.isFile()) {
+ return true;
+ }
+ if (!create) {
+ return false;
+ }
+ Exception exception = null;
+ try {
+ if (FILE_UTILS.createNewFile(f, mkdirs)) {
+ return true;
+ }
+ } catch (IOException e) {
+ exception = e;
+ }
+ String msg = "Unable to create " + f;
+ if (exception == null) {
+ log(msg, Project.MSG_WARN);
+ return false;
+ }
+ throw new BuildException(msg, exception);
+ }
+
+ private void process(File f) {
+ long len = f.length();
+ long newLength = length == null
+ ? len + adjust.longValue() : length.longValue();
+
+ if (len == newLength) {
+ //nothing to do!
+ return;
+ }
+ RandomAccessFile raf = null;
+ try {
+ raf = new RandomAccessFile(f, READ_WRITE);
+ } catch (Exception e) {
+ throw new BuildException("Could not open " + f + " for writing", e);
+ }
+ try {
+ if (newLength > len) {
+ long pos = len;
+ raf.seek(pos);
+ while (pos < newLength) {
+ long writeCount = Math.min(FILL_BUFFER.length,
+ newLength - pos);
+ raf.write(FILL_BUFFER, 0, (int) writeCount);
+ pos += writeCount;
+ }
+ } else {
+ raf.setLength(newLength);
+ }
+ } catch (IOException e) {
+ throw new BuildException("Exception working with " + raf, e);
+ } finally {
+ try {
+ raf.close();
+ } catch (IOException e) {
+ log("Caught " + e + " closing " + raf, Project.MSG_WARN);
+ }
+ }
+ }
+
+ private synchronized Path getPath() {
+ if (path == null) {
+ path = new Path(getProject());
+ }
+ return path;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Tstamp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Tstamp.java
new file mode 100644
index 00000000..805427ab
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Tstamp.java
@@ -0,0 +1,341 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ * Sets properties to the current time, or offsets from the current time.
+ * The default properties are TSTAMP, DSTAMP and TODAY;
+ *
+ * @since Ant 1.1
+ * @ant.task category="utility"
+ */
+public class Tstamp extends Task {
+
+ private Vector customFormats = new Vector();
+ private String prefix = "";
+
+ /**
+ * Set a prefix for the properties. If the prefix does not end with a "."
+ * one is automatically added.
+ * @param prefix the prefix to use.
+ * @since Ant 1.5
+ */
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ if (!this.prefix.endsWith(".")) {
+ this.prefix += ".";
+ }
+ }
+
+ /**
+ * create the timestamps. Custom ones are done before
+ * the standard ones, to get their retaliation in early.
+ * @throws BuildException on error.
+ */
+ public void execute() throws BuildException {
+ try {
+ Date d = new Date();
+
+ Enumeration i = customFormats.elements();
+ while (i.hasMoreElements()) {
+ CustomFormat cts = (CustomFormat) i.nextElement();
+ cts.execute(getProject(), d, getLocation());
+ }
+
+ SimpleDateFormat dstamp = new SimpleDateFormat ("yyyyMMdd");
+ setProperty("DSTAMP", dstamp.format(d));
+
+ SimpleDateFormat tstamp = new SimpleDateFormat ("HHmm");
+ setProperty("TSTAMP", tstamp.format(d));
+
+ SimpleDateFormat today
+ = new SimpleDateFormat ("MMMM d yyyy", Locale.US);
+ setProperty("TODAY", today.format(d));
+
+ } catch (Exception e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * create a custom format with the current prefix.
+ * @return a ready to fill-in format
+ */
+ public CustomFormat createFormat() {
+ CustomFormat cts = new CustomFormat();
+ customFormats.addElement(cts);
+ return cts;
+ }
+
+ /**
+ * helper that encapsulates prefix logic and property setting
+ * policy (i.e. we use setNewProperty instead of setProperty).
+ */
+ private void setProperty(String name, String value) {
+ getProject().setNewProperty(prefix + name, value);
+ }
+
+ /**
+ * This nested element that allows a property to be set
+ * to the current date and time in a given format.
+ * The date/time patterns are as defined in the
+ * Java SimpleDateFormat class.
+ * The format element also allows offsets to be applied to
+ * the time to generate different time values.
+ * @todo consider refactoring out into a re-usable element.
+ */
+ public class CustomFormat {
+ private TimeZone timeZone;
+ private String propertyName;
+ private String pattern;
+ private String language;
+ private String country;
+ private String variant;
+ private int offset = 0;
+ private int field = Calendar.DATE;
+
+ /**
+ * Create a format
+ */
+ public CustomFormat() {
+ }
+
+ /**
+ * The property to receive the date/time string in the given pattern
+ * @param propertyName the name of the property.
+ */
+ public void setProperty(String propertyName) {
+ this.propertyName = propertyName;
+ }
+
+ /**
+ * The date/time pattern to be used. The values are as
+ * defined by the Java SimpleDateFormat class.
+ * @param pattern the pattern to use.
+ * @see java.text.SimpleDateFormat
+ */
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+
+ /**
+ * The locale used to create date/time string.
+ * The general form is "language, country, variant" but
+ * either variant or variant and country may be omitted.
+ * For more information please refer to documentation
+ * for the java.util.Locale class.
+ * @param locale the locale to use.
+ * @see java.util.Locale
+ */
+ public void setLocale(String locale) {
+ StringTokenizer st = new StringTokenizer(locale, " \t\n\r\f,");
+ try {
+ language = st.nextToken();
+ if (st.hasMoreElements()) {
+ country = st.nextToken();
+ if (st.hasMoreElements()) {
+ variant = st.nextToken();
+ if (st.hasMoreElements()) {
+ throw new BuildException("bad locale format",
+ getLocation());
+ }
+ }
+ } else {
+ country = "";
+ }
+ } catch (NoSuchElementException e) {
+ throw new BuildException("bad locale format", e,
+ getLocation());
+ }
+ }
+
+ /**
+ * The timezone to use for displaying time.
+ * The values are as defined by the Java TimeZone class.
+ * @param id the timezone value.
+ * @see java.util.TimeZone
+ */
+ public void setTimezone(String id) {
+ timeZone = TimeZone.getTimeZone(id);
+ }
+
+ /**
+ * The numeric offset to the current time.
+ * @param offset the offset to use.
+ */
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ /**
+ * Set the unit type (using String).
+ * @param unit the unit to use.
+ * @deprecated since 1.5.x.
+ * setUnit(String) is deprecated and is replaced with
+ * setUnit(Tstamp.Unit) to make Ant's
+ * Introspection mechanism do the work and also to
+ * encapsulate operations on the unit in its own
+ * class.
+ */
+ public void setUnit(String unit) {
+ log("DEPRECATED - The setUnit(String) method has been deprecated."
+ + " Use setUnit(Tstamp.Unit) instead.");
+ Unit u = new Unit();
+ u.setValue(unit);
+ field = u.getCalendarField();
+ }
+
+ /**
+ * The unit of the offset to be applied to the current time.
+ * Valid Values are
+ * <ul>
+ * <li>millisecond</li>
+ * <li>second</li>
+ * <li>minute</li>
+ * <li>hour</li>
+ * <li>day</li>
+ * <li>week</li>
+ * <li>month</li>
+ * <li>year</li>
+ * </ul>
+ * The default unit is day.
+ * @param unit the unit to use.
+ */
+ public void setUnit(Unit unit) {
+ field = unit.getCalendarField();
+ }
+
+ /**
+ * validate parameter and execute the format.
+ * @param project project to set property in.
+ * @param date date to use as a starting point.
+ * @param location line in file (for errors)
+ */
+ public void execute(Project project, Date date, Location location) {
+ if (propertyName == null) {
+ throw new BuildException("property attribute must be provided",
+ location);
+ }
+
+ if (pattern == null) {
+ throw new BuildException("pattern attribute must be provided",
+ location);
+ }
+
+ SimpleDateFormat sdf;
+ if (language == null) {
+ sdf = new SimpleDateFormat(pattern);
+ } else if (variant == null) {
+ sdf = new SimpleDateFormat(pattern,
+ new Locale(language, country));
+ } else {
+ sdf = new SimpleDateFormat(pattern,
+ new Locale(language, country,
+ variant));
+ }
+ if (offset != 0) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(date);
+ calendar.add(field, offset);
+ date = calendar.getTime();
+ }
+ if (timeZone != null) {
+ sdf.setTimeZone(timeZone);
+ }
+ Tstamp.this.setProperty(propertyName, sdf.format(date));
+ }
+ }
+
+ /**
+ * set of valid units to use for time offsets.
+ */
+ public static class Unit extends EnumeratedAttribute {
+
+ private static final String MILLISECOND = "millisecond";
+ private static final String SECOND = "second";
+ private static final String MINUTE = "minute";
+ private static final String HOUR = "hour";
+ private static final String DAY = "day";
+ private static final String WEEK = "week";
+ private static final String MONTH = "month";
+ private static final String YEAR = "year";
+
+ private static final String[] UNITS = {
+ MILLISECOND,
+ SECOND,
+ MINUTE,
+ HOUR,
+ DAY,
+ WEEK,
+ MONTH,
+ YEAR
+ };
+
+ private Map calendarFields = new HashMap();
+
+ /** Constructor for Unit enumerated type. */
+ public Unit() {
+ calendarFields.put(MILLISECOND,
+ new Integer(Calendar.MILLISECOND));
+ calendarFields.put(SECOND, new Integer(Calendar.SECOND));
+ calendarFields.put(MINUTE, new Integer(Calendar.MINUTE));
+ calendarFields.put(HOUR, new Integer(Calendar.HOUR_OF_DAY));
+ calendarFields.put(DAY, new Integer(Calendar.DATE));
+ calendarFields.put(WEEK, new Integer(Calendar.WEEK_OF_YEAR));
+ calendarFields.put(MONTH, new Integer(Calendar.MONTH));
+ calendarFields.put(YEAR, new Integer(Calendar.YEAR));
+ }
+
+ /**
+ * Convert the value to int unit value.
+ * @return an int value.
+ */
+ public int getCalendarField() {
+ String key = getValue().toLowerCase(Locale.ENGLISH);
+ Integer i = (Integer) calendarFields.get(key);
+ return i.intValue();
+ }
+
+ /**
+ * Get the valid values.
+ * @return the value values.
+ */
+ public String[] getValues() {
+ return UNITS;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Typedef.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Typedef.java
new file mode 100644
index 00000000..87276e2e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Typedef.java
@@ -0,0 +1,46 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+
+
+/**
+ *
+ * Adds a data type definition to the current project.
+ * Two attributes are
+ * needed, the name that identifies this data type uniquely, and the full
+ * name of the class (including the packages) that implements this
+ * type.
+ * <p>You can also define a group of data types at once using the file or
+ * resource attributes. These attributes point to files in the format of
+ * Java property files. Each line defines a single data type in the
+ * format:</p>
+ * <pre>
+ * typename=fully.qualified.java.classname
+ * </pre>
+ * <p>Typedef should be used to add your own types to the system. Data
+ * types are things likepaths or filesets that can be defined at
+ * the project level and referenced via their ID attribute.</p>
+ * <p>Custom data types usually need custom tasks to put them to good use.</p>
+ *
+ * @since Ant 1.4
+ * @ant.task category="internal"
+ */
+public class Typedef extends Definer {
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Unpack.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Unpack.java
new file mode 100644
index 00000000..8640f8fb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Unpack.java
@@ -0,0 +1,195 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+
+/**
+ * Abstract Base class for unpack tasks.
+ *
+ * @since Ant 1.5
+ */
+
+public abstract class Unpack extends Task {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected File source;
+ protected File dest;
+ protected Resource srcResource;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * @deprecated since 1.5.x.
+ * setSrc(String) is deprecated and is replaced with
+ * setSrc(File) to make Ant's Introspection
+ * mechanism do the work and also to encapsulate operations on
+ * the type in its own class.
+ * @ant.attribute ignore="true"
+ * @param src a <code>String</code> value
+ */
+ public void setSrc(String src) {
+ log("DEPRECATED - The setSrc(String) method has been deprecated."
+ + " Use setSrc(File) instead.");
+ setSrc(getProject().resolveFile(src));
+ }
+
+ /**
+ * @deprecated since 1.5.x.
+ * setDest(String) is deprecated and is replaced with
+ * setDest(File) to make Ant's Introspection
+ * mechanism do the work and also to encapsulate operations on
+ * the type in its own class.
+ * @ant.attribute ignore="true"
+ * @param dest a <code>String</code> value
+ */
+ public void setDest(String dest) {
+ log("DEPRECATED - The setDest(String) method has been deprecated."
+ + " Use setDest(File) instead.");
+ setDest(getProject().resolveFile(dest));
+ }
+
+ /**
+ * The file to expand; required.
+ * @param src file to expand
+ */
+ public void setSrc(File src) {
+ setSrcResource(new FileResource(src));
+ }
+
+ /**
+ * The resource to expand; required.
+ * @param src resource to expand
+ */
+ public void setSrcResource(Resource src) {
+ if (!src.isExists()) {
+ throw new BuildException(
+ "the archive " + src.getName() + " doesn't exist");
+ }
+ if (src.isDirectory()) {
+ throw new BuildException(
+ "the archive " + src.getName() + " can't be a directory");
+ }
+ FileProvider fp = src.as(FileProvider.class);
+ if (fp != null) {
+ source = fp.getFile();
+ } else if (!supportsNonFileResources()) {
+ throw new BuildException(
+ "The source " + src.getName()
+ + " is not a FileSystem "
+ + "Only FileSystem resources are"
+ + " supported.");
+ }
+ srcResource = src;
+ }
+
+ /**
+ * Set the source Archive resource.
+ * @param a the archive as a single element Resource collection.
+ */
+ public void addConfigured(ResourceCollection a) {
+ if (a.size() != 1) {
+ throw new BuildException("only single argument resource collections"
+ + " are supported as archives");
+ }
+ setSrcResource(a.iterator().next());
+ }
+
+ /**
+ * The destination file or directory; optional.
+ * @param dest destination file or directory
+ */
+ public void setDest(File dest) {
+ this.dest = dest;
+ }
+
+ private void validate() throws BuildException {
+ if (srcResource == null) {
+ throw new BuildException("No Src specified", getLocation());
+ }
+
+ if (dest == null) {
+ dest = new File(source.getParent());
+ }
+
+ if (dest.isDirectory()) {
+ String defaultExtension = getDefaultExtension();
+ createDestFile(defaultExtension);
+ }
+ }
+
+ private void createDestFile(String defaultExtension) {
+ String sourceName = source.getName();
+ int len = sourceName.length();
+ if (defaultExtension != null
+ && len > defaultExtension.length()
+ && defaultExtension.equalsIgnoreCase(
+ sourceName.substring(len - defaultExtension.length()))) {
+ dest = new File(dest, sourceName.substring(0,
+ len - defaultExtension.length()));
+ } else {
+ dest = new File(dest, sourceName);
+ }
+ }
+
+ /**
+ * Execute the task.
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ File savedDest = dest; // may be altered in validate
+ try {
+ validate();
+ extract();
+ } finally {
+ dest = savedDest;
+ }
+ }
+
+ /**
+ * Get the extension.
+ * This is to be overridden by subclasses.
+ * @return the default extension.
+ */
+ protected abstract String getDefaultExtension();
+
+ /**
+ * Do the uncompressing.
+ * This is to be overridden by subclasses.
+ */
+ protected abstract void extract();
+
+ /**
+ * Whether this task can deal with non-file resources.
+ *
+ * <p>This implementation returns false.</p>
+ * @return false for this task.
+ * @since Ant 1.7
+ */
+ protected boolean supportsNonFileResources() {
+ return false;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Untar.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Untar.java
new file mode 100644
index 00000000..8343aec5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Untar.java
@@ -0,0 +1,244 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.bzip2.CBZip2InputStream;
+import org.apache.tools.tar.TarEntry;
+import org.apache.tools.tar.TarInputStream;
+
+
+
+/**
+ * Untar a file.
+ * <p>PatternSets are used to select files to extract
+ * <I>from</I> the archive. If no patternset is used, all files are extracted.
+ * </p>
+ * <p>FileSets may be used to select archived files
+ * to perform unarchival upon.
+ * </p>
+ * <p>File permissions will not be restored on extracted files.</p>
+ * <p>The untar task recognizes the long pathname entries used by GNU tar.<p>
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ */
+public class Untar extends Expand {
+ /**
+ * compression method
+ */
+ private UntarCompressionMethod compression = new UntarCompressionMethod();
+
+ public Untar() {
+ super(null);
+ }
+
+ /**
+ * Set decompression algorithm to use; default=none.
+ *
+ * Allowable values are
+ * <ul>
+ * <li>none - no compression
+ * <li>gzip - Gzip compression
+ * <li>bzip2 - Bzip2 compression
+ * </ul>
+ *
+ * @param method compression method
+ */
+ public void setCompression(UntarCompressionMethod method) {
+ compression = method;
+ }
+
+ /**
+ * No unicode extra fields in tar.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setScanForUnicodeExtraFields(boolean b) {
+ throw new BuildException("The " + getTaskName()
+ + " task doesn't support the encoding"
+ + " attribute", getLocation());
+ }
+
+ /**
+ * @see Expand#expandFile(FileUtils, File, File)
+ */
+ /** {@inheritDoc} */
+ protected void expandFile(FileUtils fileUtils, File srcF, File dir) {
+ FileInputStream fis = null;
+ if (!srcF.exists()) {
+ throw new BuildException("Unable to untar "
+ + srcF
+ + " as the file does not exist",
+ getLocation());
+ }
+ try {
+ fis = new FileInputStream(srcF);
+ expandStream(srcF.getPath(), fis, dir);
+ } catch (IOException ioe) {
+ throw new BuildException("Error while expanding " + srcF.getPath()
+ + "\n" + ioe.toString(),
+ ioe, getLocation());
+ } finally {
+ FileUtils.close(fis);
+ }
+ }
+
+ /**
+ * This method is to be overridden by extending unarchival tasks.
+ *
+ * @param srcR the source resource
+ * @param dir the destination directory
+ * @since Ant 1.7
+ */
+ protected void expandResource(Resource srcR, File dir) {
+ if (!srcR.isExists()) {
+ throw new BuildException("Unable to untar "
+ + srcR.getName()
+ + " as the it does not exist",
+ getLocation());
+ }
+
+ InputStream i = null;
+ try {
+ i = srcR.getInputStream();
+ expandStream(srcR.getName(), i, dir);
+ } catch (IOException ioe) {
+ throw new BuildException("Error while expanding " + srcR.getName(),
+ ioe, getLocation());
+ } finally {
+ FileUtils.close(i);
+ }
+ }
+
+ /**
+ * @since Ant 1.7
+ */
+ private void expandStream(String name, InputStream stream, File dir)
+ throws IOException {
+ TarInputStream tis = null;
+ try {
+ tis =
+ new TarInputStream(compression.decompress(name,
+ new BufferedInputStream(stream)),
+ getEncoding());
+ log("Expanding: " + name + " into " + dir, Project.MSG_INFO);
+ TarEntry te = null;
+ boolean empty = true;
+ FileNameMapper mapper = getMapper();
+ while ((te = tis.getNextEntry()) != null) {
+ empty = false;
+ extractFile(FileUtils.getFileUtils(), null, dir, tis,
+ te.getName(), te.getModTime(),
+ te.isDirectory(), mapper);
+ }
+ if (empty && getFailOnEmptyArchive()) {
+ throw new BuildException("archive '" + name + "' is empty");
+ }
+ log("expand complete", Project.MSG_VERBOSE);
+ } finally {
+ FileUtils.close(tis);
+ }
+ }
+
+ /**
+ * Valid Modes for Compression attribute to Untar Task
+ *
+ */
+ public static final class UntarCompressionMethod
+ extends EnumeratedAttribute {
+
+ // permissible values for compression attribute
+ /**
+ * No compression
+ */
+ private static final String NONE = "none";
+ /**
+ * GZIP compression
+ */
+ private static final String GZIP = "gzip";
+ /**
+ * BZIP2 compression
+ */
+ private static final String BZIP2 = "bzip2";
+
+
+ /**
+ * Constructor
+ */
+ public UntarCompressionMethod() {
+ super();
+ setValue(NONE);
+ }
+
+ /**
+ * Get valid enumeration values
+ *
+ * @return valid values
+ */
+ public String[] getValues() {
+ return new String[] {NONE, GZIP, BZIP2};
+ }
+
+ /**
+ * This method wraps the input stream with the
+ * corresponding decompression method
+ *
+ * @param name provides location information for BuildException
+ * @param istream input stream
+ * @return input stream with on-the-fly decompression
+ * @exception IOException thrown by GZIPInputStream constructor
+ * @exception BuildException thrown if bzip stream does not
+ * start with expected magic values
+ */
+ public InputStream decompress(final String name,
+ final InputStream istream)
+ throws IOException, BuildException {
+ final String v = getValue();
+ if (GZIP.equals(v)) {
+ return new GZIPInputStream(istream);
+ } else {
+ if (BZIP2.equals(v)) {
+ final char[] magic = new char[] {'B', 'Z'};
+ for (int i = 0; i < magic.length; i++) {
+ if (istream.read() != magic[i]) {
+ throw new BuildException(
+ "Invalid bz2 file." + name);
+ }
+ }
+ return new CBZip2InputStream(istream);
+ }
+ }
+ return istream;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/UpToDate.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/UpToDate.java
new file mode 100644
index 00000000..e5c85ce1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/UpToDate.java
@@ -0,0 +1,277 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.MergingMapper;
+import org.apache.tools.ant.util.ResourceUtils;
+import org.apache.tools.ant.util.SourceFileScanner;
+
+/**
+ * Sets the given property if the specified target has a timestamp
+ * greater than all of the source files.
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="control"
+ */
+
+public class UpToDate extends Task implements Condition {
+
+ private String property;
+ private String value;
+ private File sourceFile;
+ private File targetFile;
+ private Vector sourceFileSets = new Vector();
+ private Union sourceResources = new Union();
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected Mapper mapperElement = null;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * The property to set if the target file is more up-to-date than
+ * (each of) the source file(s).
+ *
+ * @param property the name of the property to set if Target is up-to-date.
+ */
+ public void setProperty(final String property) {
+ this.property = property;
+ }
+
+ /**
+ * The value to set the named property to if the target file is more
+ * up-to-date than (each of) the source file(s). Defaults to 'true'.
+ *
+ * @param value the value to set the property to if Target is up-to-date
+ */
+ public void setValue(final String value) {
+ this.value = value;
+ }
+
+ /**
+ * Returns the value, or "true" if a specific value wasn't provided.
+ */
+ private String getValue() {
+ return (value != null) ? value : "true";
+ }
+
+ /**
+ * The file which must be more up-to-date than (each of) the source file(s)
+ * if the property is to be set.
+ *
+ * @param file the file we are checking against.
+ */
+ public void setTargetFile(final File file) {
+ this.targetFile = file;
+ }
+
+ /**
+ * The file that must be older than the target file
+ * if the property is to be set.
+ *
+ * @param file the file we are checking against the target file.
+ */
+ public void setSrcfile(final File file) {
+ this.sourceFile = file;
+ }
+
+ /**
+ * Nested &lt;srcfiles&gt; element.
+ * @param fs the source files
+ */
+ public void addSrcfiles(final FileSet fs) {
+ sourceFileSets.addElement(fs);
+ }
+
+ /**
+ * Nested resource collections as sources.
+ * @return the source resources to configure.
+ * @since Ant 1.7
+ */
+ public Union createSrcResources() {
+ return sourceResources;
+ }
+
+ /**
+ * Defines the FileNameMapper to use (nested mapper element).
+ * @return a mapper to be configured
+ * @throws BuildException if more than one mapper is defined
+ */
+ public Mapper createMapper() throws BuildException {
+ if (mapperElement != null) {
+ throw new BuildException("Cannot define more than one mapper",
+ getLocation());
+ }
+ mapperElement = new Mapper(getProject());
+ return mapperElement;
+ }
+
+ /**
+ * A nested filenamemapper
+ * @param fileNameMapper the mapper to add
+ * @since Ant 1.6.3
+ */
+ public void add(FileNameMapper fileNameMapper) {
+ createMapper().add(fileNameMapper);
+ }
+
+ /**
+ * Evaluate (all) target and source file(s) to
+ * see if the target(s) is/are up-to-date.
+ * @return true if the target(s) is/are up-to-date
+ */
+ public boolean eval() {
+ if (sourceFileSets.size() == 0 && sourceResources.size() == 0
+ && sourceFile == null) {
+ throw new BuildException("At least one srcfile or a nested "
+ + "<srcfiles> or <srcresources> element "
+ + "must be set.");
+ }
+
+ if ((sourceFileSets.size() > 0 || sourceResources.size() > 0)
+ && sourceFile != null) {
+ throw new BuildException("Cannot specify both the srcfile "
+ + "attribute and a nested <srcfiles> "
+ + "or <srcresources> element.");
+ }
+
+ if (targetFile == null && mapperElement == null) {
+ throw new BuildException("The targetfile attribute or a nested "
+ + "mapper element must be set.");
+ }
+
+ // if the target file is not there, then it can't be up-to-date
+ if (targetFile != null && !targetFile.exists()) {
+ log("The targetfile \"" + targetFile.getAbsolutePath()
+ + "\" does not exist.", Project.MSG_VERBOSE);
+ return false;
+ }
+
+ // if the source file isn't there, throw an exception
+ if (sourceFile != null && !sourceFile.exists()) {
+ throw new BuildException(sourceFile.getAbsolutePath()
+ + " not found.");
+ }
+
+ boolean upToDate = true;
+ if (sourceFile != null) {
+ if (mapperElement == null) {
+ upToDate = targetFile.lastModified() >= sourceFile.lastModified();
+ } else {
+ SourceFileScanner sfs = new SourceFileScanner(this);
+ upToDate = sfs.restrict(new String[] {sourceFile.getAbsolutePath()},
+ null, null,
+ mapperElement.getImplementation()).length == 0;
+ }
+ if (!upToDate) {
+ log(sourceFile.getAbsolutePath()
+ + " is newer than (one of) its target(s).",
+ Project.MSG_VERBOSE);
+ }
+ }
+
+ // filesets are separate from the rest for performance
+ // reasons. If we use the code for union below, we'll always
+ // scan all filesets, even if we know the target is out of
+ // date after the first test.
+ Enumeration e = sourceFileSets.elements();
+ while (upToDate && e.hasMoreElements()) {
+ FileSet fs = (FileSet) e.nextElement();
+ DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+ upToDate = scanDir(fs.getDir(getProject()),
+ ds.getIncludedFiles());
+ }
+
+ if (upToDate) {
+ Resource[] r = sourceResources.listResources();
+ if (r.length > 0) {
+ upToDate = ResourceUtils.selectOutOfDateSources(
+ this, r, getMapper(), getProject()).length == 0;
+ }
+ }
+
+ return upToDate;
+ }
+
+
+ /**
+ * Sets property to true if target file(s) have a more recent timestamp
+ * than (each of) the corresponding source file(s).
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ if (property == null) {
+ throw new BuildException("property attribute is required.",
+ getLocation());
+ }
+ boolean upToDate = eval();
+ if (upToDate) {
+ getProject().setNewProperty(property, getValue());
+ if (mapperElement == null) {
+ log("File \"" + targetFile.getAbsolutePath()
+ + "\" is up-to-date.", Project.MSG_VERBOSE);
+ } else {
+ log("All target files are up-to-date.",
+ Project.MSG_VERBOSE);
+ }
+ }
+ }
+
+ /**
+ * Scan a directory for files to check for "up to date"ness
+ * @param srcDir the directory
+ * @param files the files to scan for
+ * @return true if the files are up to date
+ */
+ protected boolean scanDir(File srcDir, String[] files) {
+ SourceFileScanner sfs = new SourceFileScanner(this);
+ FileNameMapper mapper = getMapper();
+ File dir = srcDir;
+ if (mapperElement == null) {
+ dir = null;
+ }
+ return sfs.restrict(files, srcDir, dir, mapper).length == 0;
+ }
+
+ private FileNameMapper getMapper() {
+ FileNameMapper mapper = null;
+ if (mapperElement == null) {
+ MergingMapper mm = new MergingMapper();
+ mm.setTo(targetFile.getAbsolutePath());
+ mapper = mm;
+ } else {
+ mapper = mapperElement.getImplementation();
+ }
+ return mapper;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/VerifyJar.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/VerifyJar.java
new file mode 100644
index 00000000..4ab21428
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/VerifyJar.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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.filters.ChainableReader;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.RedirectorElement;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileProvider;
+
+/**
+ * JAR verification task.
+ * For every JAR passed in, we fork jarsigner to verify
+ * that it is correctly signed. This is more rigorous than just checking for
+ * the existence of a signature; the entire certification chain is tested
+ * @since Ant 1.7
+ */
+
+public class VerifyJar extends AbstractJarSignerTask {
+ /**
+ * no file message {@value}
+ */
+ public static final String ERROR_NO_FILE = "Not found :";
+
+ /**
+ * The string we look for in the text to indicate direct verification
+ */
+ private static final String VERIFIED_TEXT = "jar verified.";
+
+ /**
+ * certification flag
+ */
+ private boolean certificates = false;
+ private BufferingOutputFilter outputCache = new BufferingOutputFilter();
+ /** Error output if there is a failure to verify the jar. */
+ public static final String ERROR_NO_VERIFY = "Failed to verify ";
+
+ /**
+ * Ask for certificate information to be printed
+ * @param certificates if true print certificates.
+ */
+ public void setCertificates(boolean certificates) {
+ this.certificates = certificates;
+ }
+
+ /**
+ * verify our jar files
+ * @throws BuildException on error.
+ */
+ public void execute() throws BuildException {
+ //validation logic
+ final boolean hasJar = jar != null;
+
+ if (!hasJar && !hasResources()) {
+ throw new BuildException(ERROR_NO_SOURCE);
+ }
+
+ beginExecution();
+
+ //patch the redirector to save output to a file
+ RedirectorElement redirector = getRedirector();
+ redirector.setAlwaysLog(true);
+ FilterChain outputFilterChain = redirector.createOutputFilterChain();
+ outputFilterChain.add(outputCache);
+
+ try {
+ Path sources = createUnifiedSourcePath();
+ for (Resource r : sources) {
+ FileProvider fr = r.as(FileProvider.class);
+ verifyOneJar(fr.getFile());
+ }
+
+ } finally {
+ endExecution();
+ }
+
+ }
+
+ /**
+ * verify a JAR.
+ * @param jar the jar to verify.
+ * @throws BuildException if the file could not be verified
+ */
+ private void verifyOneJar(File jar) {
+ if (!jar.exists()) {
+ throw new BuildException(ERROR_NO_FILE + jar);
+ }
+ final ExecTask cmd = createJarSigner();
+
+ setCommonOptions(cmd);
+ bindToKeystore(cmd);
+
+ //verify special operations
+ addValue(cmd, "-verify");
+
+ if (certificates) {
+ addValue(cmd, "-certs");
+ }
+
+ //JAR is required
+ addValue(cmd, jar.getPath());
+
+ log("Verifying JAR: " + jar.getAbsolutePath());
+ outputCache.clear();
+ BuildException ex = null;
+ try {
+ cmd.execute();
+ } catch (BuildException e) {
+ ex = e;
+ }
+ String results = outputCache.toString();
+ //deal with jdk1.4.2 bug:
+ if (ex != null) {
+ if (results.indexOf("zip file closed") >= 0) {
+ log("You are running " + JARSIGNER_COMMAND + " against a JVM with"
+ + " a known bug that manifests as an IllegalStateException.",
+ Project.MSG_WARN);
+ } else {
+ throw ex;
+ }
+ }
+ if (results.indexOf(VERIFIED_TEXT) < 0) {
+ throw new BuildException(ERROR_NO_VERIFY + jar);
+ }
+ }
+
+ /**
+ * we are not thread safe here. Do not use on multiple threads at the same time.
+ */
+ private static class BufferingOutputFilter implements ChainableReader {
+
+ private BufferingOutputFilterReader buffer;
+
+ public Reader chain(Reader rdr) {
+ buffer = new BufferingOutputFilterReader(rdr);
+ return buffer;
+ }
+
+ public String toString() {
+ return buffer.toString();
+ }
+
+ public void clear() {
+ if (buffer != null) {
+ buffer.clear();
+ }
+ }
+ }
+
+ /**
+ * catch the output of the buffer
+ */
+ private static class BufferingOutputFilterReader extends Reader {
+
+ private Reader next;
+
+ private StringBuffer buffer = new StringBuffer();
+
+ public BufferingOutputFilterReader(Reader next) {
+ this.next = next;
+ }
+
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ //hand down
+ int result = next.read(cbuf, off, len);
+ //cache
+ buffer.append(cbuf, off, len);
+ //return
+ return result;
+ }
+
+ public void close() throws IOException {
+ next.close();
+ }
+
+ public String toString() {
+ return buffer.toString();
+ }
+
+ public void clear() {
+ buffer = new StringBuffer();
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/WaitFor.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/WaitFor.java
new file mode 100644
index 00000000..d23beabc
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/WaitFor.java
@@ -0,0 +1,277 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.taskdefs.condition.ConditionBase;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ * Wait for an external event to occur.
+ *
+ * Wait for an external process to start or to complete some
+ * task. This is useful with the <code>parallel</code> task to
+ * synchronize the execution of tests with server startup.
+ *
+ * The following attributes can be specified on a waitfor task:
+ * <ul>
+ * <li>maxwait - maximum length of time to wait before giving up</li>
+ * <li>maxwaitunit - The unit to be used to interpret maxwait attribute</li>
+ * <li>checkevery - amount of time to sleep between each check</li>
+ * <li>checkeveryunit - The unit to be used to interpret checkevery attribute</li>
+ * <li>timeoutproperty - name of a property to set if maxwait has been exceeded.</li>
+ * </ul>
+ *
+ * The maxwaitunit and checkeveryunit are allowed to have the following values:
+ * millisecond, second, minute, hour, day and week. The default is millisecond.
+ *
+ * For programmatic use/subclassing, there are two methods that may be overridden,
+ * <code>processSuccess</code> and <code>processTimeout</code>
+ * @since Ant 1.5
+ *
+ * @ant.task category="control"
+ */
+public class WaitFor extends ConditionBase {
+ /** a millisecond */
+ public static final long ONE_MILLISECOND = 1L;
+ /** a second in milliseconds */
+ public static final long ONE_SECOND = 1000L;
+ /** a minute in milliseconds */
+ public static final long ONE_MINUTE = ONE_SECOND * 60L;
+ /** an hour in milliseconds */
+ public static final long ONE_HOUR = ONE_MINUTE * 60L;
+ /** a day in milliseconds */
+ public static final long ONE_DAY = ONE_HOUR * 24L;
+ /** a week in milliseconds */
+ public static final long ONE_WEEK = ONE_DAY * 7L;
+
+ /** default wait time */
+ public static final long DEFAULT_MAX_WAIT_MILLIS = ONE_MINUTE * 3L;
+ /** default check time */
+ public static final long DEFAULT_CHECK_MILLIS = 500L;
+
+ /** default max wait time in the current unit*/
+ private long maxWait = DEFAULT_MAX_WAIT_MILLIS;
+ private long maxWaitMultiplier = ONE_MILLISECOND;
+ /**
+ * check time in the current unit
+ */
+ private long checkEvery = DEFAULT_CHECK_MILLIS;
+ private long checkEveryMultiplier = ONE_MILLISECOND;
+ private String timeoutProperty;
+
+ /**
+ * Constructor, names this task "waitfor".
+ */
+ public WaitFor() {
+ super("waitfor");
+ }
+
+
+ /**
+ * Constructor that takes the name of the task in the task name.
+ *
+ * @param taskName the name of the task.
+ * @since Ant 1.8
+ */
+ public WaitFor(String taskName) {
+ super(taskName);
+ }
+
+ /**
+ * Set the maximum length of time to wait.
+ * @param time a <code>long</code> value
+ */
+ public void setMaxWait(long time) {
+ maxWait = time;
+ }
+
+
+ /**
+ * Set the max wait time unit
+ * @param unit an enumerated <code>Unit</code> value
+ */
+ public void setMaxWaitUnit(Unit unit) {
+ maxWaitMultiplier = unit.getMultiplier();
+ }
+
+
+
+ /**
+ * Set the time between each check
+ * @param time a <code>long</code> value
+ */
+ public void setCheckEvery(long time) {
+ checkEvery = time;
+ }
+
+ /**
+ * Set the check every time unit
+ * @param unit an enumerated <code>Unit</code> value
+ */
+ public void setCheckEveryUnit(Unit unit) {
+ checkEveryMultiplier = unit.getMultiplier();
+ }
+
+ /**
+ * Name the property to set after a timeout.
+ * @param p the property name
+ */
+ public void setTimeoutProperty(String p) {
+ timeoutProperty = p;
+ }
+
+ /**
+ * Check repeatedly for the specified conditions until they become
+ * true or the timeout expires.
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ if (countConditions() > 1) {
+ throw new BuildException("You must not nest more than one "
+ + "condition into "
+ + getTaskName());
+ }
+ if (countConditions() < 1) {
+ throw new BuildException("You must nest a condition into "
+ + getTaskName());
+ }
+ Condition c = (Condition) getConditions().nextElement();
+ try {
+ long maxWaitMillis = calculateMaxWaitMillis();
+ long checkEveryMillis = calculateCheckEveryMillis();
+ long start = System.currentTimeMillis();
+ long end = start + maxWaitMillis;
+
+ while (System.currentTimeMillis() < end) {
+ if (c.eval()) {
+ processSuccess();
+ return;
+ }
+ Thread.sleep(checkEveryMillis);
+ }
+ } catch (InterruptedException e) {
+ log("Task " + getTaskName()
+ + " interrupted, treating as timed out.");
+ }
+ processTimeout();
+ }
+
+ /**
+ * Get the check wait time, in milliseconds.
+ * @since Ant 1.8
+ * @return how long to wait between checks
+ */
+ public long calculateCheckEveryMillis() {
+ return checkEvery * checkEveryMultiplier;
+ }
+
+ /**
+ * Get the maximum wait time, in milliseconds.
+ * @since Ant 1.8
+ * @return how long to wait before timing out
+ */
+ public long calculateMaxWaitMillis() {
+ return maxWait * maxWaitMultiplier;
+ }
+
+ /**
+ * Actions to be taken on a successful waitfor.
+ * This is an override point. The base implementation does nothing.
+ * @since Ant1.7
+ */
+ protected void processSuccess() {
+ log(getTaskName() + ": condition was met", Project.MSG_VERBOSE);
+ }
+
+ /**
+ * Actions to be taken on an unsuccessful wait.
+ * This is an override point. It is where the timeout processing takes place.
+ * The base implementation sets the timeoutproperty if there was a timeout
+ * and the property was defined.
+ * @since Ant1.7
+ */
+ protected void processTimeout() {
+ log(getTaskName() + ": timeout", Project.MSG_VERBOSE);
+ if (timeoutProperty != null) {
+ getProject().setNewProperty(timeoutProperty, "true");
+ }
+ }
+
+ /**
+ * The enumeration of units:
+ * millisecond, second, minute, hour, day, week
+ * @todo we use timestamps in many places, why not factor this out
+ */
+ public static class Unit extends EnumeratedAttribute {
+
+ /** millisecond string */
+ public static final String MILLISECOND = "millisecond";
+ /** second string */
+ public static final String SECOND = "second";
+ /** minute string */
+ public static final String MINUTE = "minute";
+ /** hour string */
+ public static final String HOUR = "hour";
+ /** day string */
+ public static final String DAY = "day";
+ /** week string */
+ public static final String WEEK = "week";
+
+ private static final String[] UNITS = {
+ MILLISECOND, SECOND, MINUTE, HOUR, DAY, WEEK
+ };
+
+ private Map timeTable = new HashMap();
+
+ /** Constructor the Unit enumerated type. */
+ public Unit() {
+ timeTable.put(MILLISECOND, new Long(1L));
+ timeTable.put(SECOND, new Long(ONE_SECOND));
+ timeTable.put(MINUTE, new Long(ONE_MINUTE));
+ timeTable.put(HOUR, new Long(ONE_HOUR));
+ timeTable.put(DAY, new Long(ONE_DAY));
+ timeTable.put(WEEK, new Long(ONE_WEEK));
+ }
+
+ /**
+ * Convert the value to a multipler (millisecond to unit).
+ * @return a multipler (a long value)
+ */
+ public long getMultiplier() {
+ String key = getValue().toLowerCase(Locale.ENGLISH);
+ Long l = (Long) timeTable.get(key);
+ return l.longValue();
+ }
+
+ /**
+ * @see EnumeratedAttribute#getValues()
+ */
+ /** {@inheritDoc} */
+ public String[] getValues() {
+ return UNITS;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/War.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/War.java
new file mode 100644
index 00000000..7daf08ef
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/War.java
@@ -0,0 +1,233 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.ZipFileSet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.zip.ZipOutputStream;
+
+
+/**
+ * <p>An extension of &lt;jar&gt; to create a WAR archive.
+ * Contains special treatment for files that should end up in the
+ * <code>WEB-INF/lib</code>, <code>WEB-INF/classes</code> or
+ * <code>WEB-INF</code> directories of the Web Application Archive.</p>
+ *
+ * <p>(The War task is a shortcut for specifying the particular layout of a WAR file.
+ * The same thing can be accomplished by using the <i>prefix</i> and <i>fullpath</i>
+ * attributes of zipfilesets in a Zip or Jar task.)</p>
+ *
+ * <p>The extended zipfileset element from the zip task
+ * (with attributes <i>prefix</i>, <i>fullpath</i>, and <i>src</i>)
+ * is available in the War task.</p>
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="packaging"
+ * @see Jar
+ */
+public class War extends Jar {
+
+ /**
+ * our web.xml deployment descriptor
+ */
+ private File deploymentDescriptor;
+
+ /**
+ * flag set if the descriptor is added
+ */
+ private boolean needxmlfile = true;
+ private File addedWebXmlFile;
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+ /** path to web.xml file */
+ private static final String XML_DESCRIPTOR_PATH = "WEB-INF/web.xml";
+
+ /** Constructor for the War Task. */
+ public War() {
+ super();
+ archiveType = "war";
+ emptyBehavior = "create";
+ }
+
+ /**
+ * <i>Deprecated</i> name of the file to create
+ * -use <tt>destfile</tt> instead.
+ * @param warFile the destination file
+ * @deprecated since 1.5.x.
+ * Use setDestFile(File) instead
+ * @ant.attribute ignore="true"
+ */
+ @Deprecated
+ public void setWarfile(File warFile) {
+ setDestFile(warFile);
+ }
+
+ /**
+ * set the deployment descriptor to use (WEB-INF/web.xml);
+ * required unless <tt>update=true</tt>
+ * @param descr the deployment descriptor file
+ */
+ public void setWebxml(File descr) {
+ deploymentDescriptor = descr;
+ if (!deploymentDescriptor.exists()) {
+ throw new BuildException("Deployment descriptor: "
+ + deploymentDescriptor
+ + " does not exist.");
+ }
+
+ // Create a ZipFileSet for this file, and pass it up.
+ ZipFileSet fs = new ZipFileSet();
+ fs.setFile(deploymentDescriptor);
+ fs.setFullpath(XML_DESCRIPTOR_PATH);
+ super.addFileset(fs);
+ }
+
+
+ /**
+ * Set the policy on the web.xml file, that is, whether or not it is needed
+ * @param needxmlfile whether a web.xml file is needed. Default: true
+ */
+ public void setNeedxmlfile(boolean needxmlfile) {
+ this.needxmlfile = needxmlfile;
+ }
+
+ /**
+ * add files under WEB-INF/lib/
+ * @param fs the zip file set to add
+ */
+
+ public void addLib(ZipFileSet fs) {
+ // We just set the prefix for this fileset, and pass it up.
+ fs.setPrefix("WEB-INF/lib/");
+ super.addFileset(fs);
+ }
+
+ /**
+ * add files under WEB-INF/classes
+ * @param fs the zip file set to add
+ */
+ public void addClasses(ZipFileSet fs) {
+ // We just set the prefix for this fileset, and pass it up.
+ fs.setPrefix("WEB-INF/classes/");
+ super.addFileset(fs);
+ }
+
+ /**
+ * files to add under WEB-INF;
+ * @param fs the zip file set to add
+ */
+ public void addWebinf(ZipFileSet fs) {
+ // We just set the prefix for this fileset, and pass it up.
+ fs.setPrefix("WEB-INF/");
+ super.addFileset(fs);
+ }
+
+ /**
+ * override of parent; validates configuration
+ * before initializing the output stream.
+ * @param zOut the zip output stream
+ * @throws IOException on output error
+ * @throws BuildException if invalid configuration
+ */
+ @Override
+ protected void initZipOutputStream(ZipOutputStream zOut)
+ throws IOException, BuildException {
+ super.initZipOutputStream(zOut);
+ }
+
+ /**
+ * Overridden from Zip class to deal with web.xml
+ *
+ * Here are cases that can arise
+ * -not a web.xml file : add
+ * -first web.xml : add, remember we added it
+ * -same web.xml again: skip
+ * -alternate web.xml : warn and skip
+ *
+ * @param file the file to add to the archive
+ * @param zOut the stream to write to
+ * @param vPath the name this entry shall have in the archive
+ * @param mode the Unix permissions to set.
+ * @throws IOException on output error
+ */
+ @Override
+ protected void zipFile(File file, ZipOutputStream zOut, String vPath,
+ int mode)
+ throws IOException {
+ // If the file being added is WEB-INF/web.xml, we warn if it's
+ // not the one specified in the "webxml" attribute - or if
+ // it's being added twice, meaning the same file is specified
+ // by the "webxml" attribute and in a <fileset> element.
+ //by default, we add the file.
+ boolean addFile = true;
+ if (XML_DESCRIPTOR_PATH.equalsIgnoreCase(vPath)) {
+ //a web.xml file was found. See if it is a duplicate or not
+ if (addedWebXmlFile != null) {
+ //a second web.xml file, so skip it
+ addFile = false;
+ //check to see if we warn or not
+ if (!FILE_UTILS.fileNameEquals(addedWebXmlFile, file)) {
+ logWhenWriting("Warning: selected " + archiveType
+ + " files include a second "
+ + XML_DESCRIPTOR_PATH
+ + " which will be ignored.\n"
+ + "The duplicate entry is at " + file + '\n'
+ + "The file that will be used is "
+ + addedWebXmlFile,
+ Project.MSG_WARN);
+ }
+ } else {
+ //no added file, yet
+ addedWebXmlFile = file;
+ //there is no web.xml file, so add it
+ addFile = true;
+ //and remember that we did
+ deploymentDescriptor = file;
+ }
+ }
+ if (addFile) {
+ super.zipFile(file, zOut, vPath, mode);
+ }
+ }
+
+
+ /**
+ * Make sure we don't think we already have a web.xml next time this task
+ * gets executed.
+ */
+ @Override
+ protected void cleanUp() {
+ if (addedWebXmlFile == null
+ && deploymentDescriptor == null
+ && needxmlfile
+ && !isInUpdateMode()
+ && hasUpdatedFile()) {
+ throw new BuildException("No WEB-INF/web.xml file was added.\n"
+ + "If this is your intent, set needxmlfile='false' ");
+ }
+ addedWebXmlFile = null;
+ super.cleanUp();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/WhichResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/WhichResource.java
new file mode 100644
index 00000000..3f315a8d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/WhichResource.java
@@ -0,0 +1,199 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.net.URL;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Find a class or resource on the supplied classpath, or the
+ * system classpath if none is supplied. The named property is set if
+ * the item can be found. For example
+ * <pre>
+ * &lt;whichresource resource="/log4j.properties"
+ * property="log4j.url" &gt;
+ * </pre>
+ * @since Ant 1.6
+ * @ant.attribute.group name="oneof" description="Exactly one of these two"
+ */
+public class WhichResource extends Task {
+ /**
+ * our classpath
+ */
+ private Path classpath;
+
+ /**
+ * class to look for
+ */
+ private String classname;
+
+ /**
+ * resource to look for
+ */
+ private String resource;
+
+ /**
+ * property to set
+ */
+ private String property;
+
+
+ /**
+ * Set the classpath to be used for this compilation.
+ * @param cp the classpath to be used.
+ */
+ public void setClasspath(Path cp) {
+ if (classpath == null) {
+ classpath = cp;
+ } else {
+ classpath.append(cp);
+ }
+ }
+
+ /**
+ * Adds a path to the classpath.
+ * @return a classpath to be configured.
+ */
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(getProject());
+ }
+ return classpath.createPath();
+ }
+
+ /**
+ * Set the classpath to use by reference.
+ *
+ * @param r a reference to an existing classpath.
+ * @since Ant 1.7.1
+ */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * validate
+ */
+ private void validate() {
+ int setcount = 0;
+ if (classname != null) {
+ setcount++;
+ }
+ if (resource != null) {
+ setcount++;
+ }
+
+
+ if (setcount == 0) {
+ throw new BuildException("One of classname or resource must"
+ + " be specified");
+ }
+ if (setcount > 1) {
+ throw new BuildException("Only one of classname or resource can"
+ + " be specified");
+ }
+ if (property == null) {
+ throw new BuildException("No property defined");
+ }
+ }
+
+ /**
+ * execute it
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ validate();
+ if (classpath != null) {
+ classpath = classpath.concatSystemClasspath("ignore");
+ getProject().log("using user supplied classpath: " + classpath,
+ Project.MSG_DEBUG);
+ } else {
+ classpath = new Path(getProject());
+ classpath = classpath.concatSystemClasspath("only");
+ getProject().log("using system classpath: " + classpath,
+ Project.MSG_DEBUG);
+ }
+ AntClassLoader loader = null;
+ try {
+ loader = AntClassLoader.newAntClassLoader(getProject().getCoreLoader(),
+ getProject(),
+ classpath, false);
+ String loc = null;
+ if (classname != null) {
+ //convert a class name into a resource
+ resource = classname.replace('.', '/') + ".class";
+ }
+
+ if (resource == null) {
+ throw new BuildException("One of class or resource is required");
+ }
+
+ if (resource.startsWith("/")) {
+ resource = resource.substring(1);
+ }
+
+ log("Searching for " + resource, Project.MSG_VERBOSE);
+ URL url;
+ url = loader.getResource(resource);
+ if (url != null) {
+ //set the property
+ loc = url.toExternalForm();
+ getProject().setNewProperty(property, loc);
+ }
+ } finally {
+ if (loader != null) {
+ loader.cleanup();
+ }
+ }
+ }
+
+ /**
+ * name the resource to look for
+ * @param resource the name of the resource to look for.
+ * @ant.attribute group="oneof"
+ */
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ /**
+ * name the class to look for
+ * @param classname the name of the class to look for.
+ * @ant.attribute group="oneof"
+ */
+ public void setClass(String classname) {
+ this.classname = classname;
+ }
+
+ /**
+ * the property to fill with the URL of the resource or class
+ * @param property the property to be set.
+ * @ant.attribute group="required"
+ */
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison.java
new file mode 100644
index 00000000..304995c6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison.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.tools.ant.taskdefs;
+
+import java.io.File;
+
+/**
+ * Proxy interface for XSLT processors.
+ *
+ * @see XSLTProcess
+ * @since Ant 1.1
+ */
+public interface XSLTLiaison {
+
+ /**
+ * the file protocol prefix for systemid.
+ * This file protocol must be appended to an absolute path.
+ * Typically: <tt>FILE_PROTOCOL_PREFIX + file.getAbsolutePath()</tt>
+ * Note that on Windows, an extra '/' must be appended to the
+ * protocol prefix so that there is always 3 consecutive slashes.
+ * @since Ant 1.4
+ */
+ String FILE_PROTOCOL_PREFIX = "file://";
+
+ /**
+ * set the stylesheet to use for the transformation.
+ * @param stylesheet the stylesheet to be used for transformation.
+ * @throws Exception thrown if any problems happens.
+ * @since Ant 1.4
+ */
+ void setStylesheet(File stylesheet) throws Exception;
+
+ /**
+ * Add a parameter to be set during the XSL transformation.
+ * @param name the parameter name.
+ * @param expression the parameter value as an expression string.
+ * @throws Exception thrown if any problems happens.
+ * @see XSLTLiaison4#addParam(java.lang.String, java.lang.Object)
+ * @since Ant 1.3
+ */
+ void addParam(String name, String expression) throws Exception;
+
+ /**
+ * Perform the transformation of a file into another.
+ * @param infile the input file, probably an XML one. :-)
+ * @param outfile the output file resulting from the transformation
+ * @throws Exception thrown if any problems happens.
+ * @see #setStylesheet(File)
+ * @since Ant 1.4
+ */
+ void transform(File infile, File outfile) throws Exception;
+
+} //-- XSLTLiaison
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison2.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison2.java
new file mode 100644
index 00000000..f41f9151
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison2.java
@@ -0,0 +1,33 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+/**
+ * Extended Proxy interface for XSLT processors.
+ *
+ * @see XSLTProcess
+ * @since Ant 1.6
+ */
+public interface XSLTLiaison2 extends XSLTLiaison {
+ /**
+ * Configure the liaision from the XSLTProcess task
+ * @param xsltTask the XSLTProcess task
+ */
+ void configure(XSLTProcess xsltTask);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison3.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison3.java
new file mode 100644
index 00000000..963093ff
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison3.java
@@ -0,0 +1,35 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Extends Proxy interface for XSLT processors.
+ *
+ * @see XSLTProcess
+ * @since Ant 1.7
+ */
+public interface XSLTLiaison3 extends XSLTLiaison2 {
+ /**
+ * sets the stylesheet to use as a resource
+ * @param stylesheet the stylesheet to use as a resource
+ * @throws Exception if the stylesheet cannot be loaded
+ */
+ void setStylesheet(Resource stylesheet) throws Exception;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison4.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison4.java
new file mode 100644
index 00000000..6e16e623
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison4.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.tools.ant.taskdefs;
+
+
+/**
+ * Extends Proxy interface for XSLT processors: adds support for XSLT parameters
+ * of various types (not only String)
+ *
+ *
+ * @see XSLTProcess
+ * @author Frantisek Kucera (xkucf03)
+ * @since Ant 1.9.3
+ */
+public interface XSLTLiaison4 extends XSLTLiaison3 {
+
+ /**
+ * Add a parameter to be set during the XSL transformation.
+ *
+ * @param name the parameter name.
+ * @param value the parameter value as String, Boolean, int, etc.
+ * @throws Exception thrown if any problems happens.
+ * @since Ant 1.9.3
+ * @see javax.xml.transform.Transformer#setParameter(java.lang.String, java.lang.Object)
+ */
+ void addParam(String name, Object value) throws Exception;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLogger.java
new file mode 100644
index 00000000..4dadebc7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLogger.java
@@ -0,0 +1,31 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+/**
+ * Interface to log messages for XSLT
+ * @since Ant 1.5
+ */
+public interface XSLTLogger {
+ /**
+ * Log a message.
+ * @param msg the message to log
+ */
+ void log(String msg);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLoggerAware.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLoggerAware.java
new file mode 100644
index 00000000..edf0fce8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTLoggerAware.java
@@ -0,0 +1,31 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+/**
+ * Interface for a class that one can set an XSLTLogger on.
+ * @since Ant 1.5
+ */
+public interface XSLTLoggerAware {
+ /**
+ * Set the logger for this class.
+ * @param l the logger
+ */
+ void setLogger(XSLTLogger l);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java
new file mode 100644
index 00000000..4fe31260
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java
@@ -0,0 +1,1689 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+import javax.xml.xpath.XPathVariableResolver;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.DynamicConfigurator;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PropertySet;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.XMLCatalog;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.Resources;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.ResourceUtils;
+
+/**
+ * Processes a set of XML documents via XSLT. This is
+ * useful for building views of XML based documentation.
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task name="xslt" category="xml"
+ */
+
+public class XSLTProcess extends MatchingTask implements XSLTLogger {
+ /** destination directory */
+ private File destDir = null;
+
+ /** where to find the source XML file, default is the project's basedir */
+ private File baseDir = null;
+
+ /** XSL stylesheet as a filename */
+ private String xslFile = null;
+
+ /** XSL stylesheet as a {@link org.apache.tools.ant.types.Resource} */
+ private Resource xslResource = null;
+
+ /** extension of the files produced by XSL processing */
+ private String targetExtension = ".html";
+
+ /** name for XSL parameter containing the filename */
+ private String fileNameParameter = null;
+
+ /** name for XSL parameter containing the file directory */
+ private String fileDirParameter = null;
+
+ /** additional parameters to be passed to the stylesheets */
+ private final List<Param> params = new ArrayList<Param>();
+
+ /** Input XML document to be used */
+ private File inFile = null;
+
+ /** Output file */
+ private File outFile = null;
+
+ /** The name of the XSL processor to use */
+ private String processor;
+
+ /** Classpath to use when trying to load the XSL processor */
+ private Path classpath = null;
+
+ /** The Liaison implementation to use to communicate with the XSL
+ * processor */
+ private XSLTLiaison liaison;
+
+ /** Flag which indicates if the stylesheet has been loaded into
+ * the processor */
+ private boolean stylesheetLoaded = false;
+
+ /** force output of target files even if they already exist */
+ private boolean force = false;
+
+ /** XSL output properties to be used */
+ private final Vector outputProperties = new Vector();
+
+ /** for resolving entities such as dtds */
+ private final XMLCatalog xmlCatalog = new XMLCatalog();
+
+ /** Utilities used for file operations */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Whether to style all files in the included directories as well.
+ *
+ * @since Ant 1.5
+ */
+ private boolean performDirectoryScan = true;
+
+ /**
+ * factory element for TraX processors only
+ * @since Ant 1.6
+ */
+ private Factory factory = null;
+
+ /**
+ * whether to reuse Transformer if transforming multiple files.
+ * @since 1.5.2
+ */
+ private boolean reuseLoadedStylesheet = true;
+
+ /**
+ * AntClassLoader for the nested &lt;classpath&gt; - if set.
+ *
+ * <p>We keep this here in order to reset the context classloader
+ * in execute. We can't use liaison.getClass().getClassLoader()
+ * since the actual liaison class may have been loaded by a loader
+ * higher up (system classloader, for example).</p>
+ *
+ * @since Ant 1.6.2
+ */
+ private AntClassLoader loader = null;
+
+ /**
+ * Mapper to use when a set of files gets processed.
+ *
+ * @since Ant 1.6.2
+ */
+ private Mapper mapperElement = null;
+
+ /**
+ * Additional resource collections to process.
+ *
+ * @since Ant 1.7
+ */
+ private final Union resources = new Union();
+
+ /**
+ * Whether to use the implicit fileset.
+ *
+ * @since Ant 1.7
+ */
+ private boolean useImplicitFileset = true;
+
+ /**
+ * The default processor is trax
+ * @since Ant 1.7
+ */
+ public static final String PROCESSOR_TRAX = "trax";
+
+ /**
+ * whether to suppress warnings.
+ *
+ * @since Ant 1.8.0
+ */
+ private boolean suppressWarnings = false;
+
+ /**
+ * whether to fail the build if an error occurs during transformation.
+ *
+ * @since Ant 1.8.0
+ */
+ private boolean failOnTransformationError = true;
+
+ /**
+ * whether to fail the build if an error occurs.
+ *
+ * @since Ant 1.8.0
+ */
+ private boolean failOnError = true;
+
+ /**
+ * Whether the build should fail if the nested resource collection
+ * is empty.
+ *
+ * @since Ant 1.8.0
+ */
+ private boolean failOnNoResources = true;
+
+ /**
+ * For evaluating template params
+ *
+ * @since Ant 1.9.3
+ */
+ private XPathFactory xpathFactory;
+ /**
+ * For evaluating template params
+ *
+ * @since Ant 1.9.3
+ */
+ private XPath xpath;
+
+ /**
+ * System properties to set during transformation.
+ *
+ * @since Ant 1.8.0
+ */
+ private final CommandlineJava.SysProperties sysProperties =
+ new CommandlineJava.SysProperties();
+
+ /**
+ * Trace configuration for Xalan2.
+ *
+ * @since Ant 1.8.0
+ */
+ private TraceConfiguration traceConfiguration;
+
+ /**
+ * Creates a new XSLTProcess Task.
+ */
+ public XSLTProcess() {
+ } //-- XSLTProcess
+
+ /**
+ * Whether to style all files in the included directories as well;
+ * optional, default is true.
+ *
+ * @param b true if files in included directories are processed.
+ * @since Ant 1.5
+ */
+ public void setScanIncludedDirectories(final boolean b) {
+ performDirectoryScan = b;
+ }
+
+ /**
+ * Controls whether the stylesheet is reloaded for every transform.
+ *
+ * <p>Setting this to true may get around a bug in certain
+ * Xalan-J versions, default is false.</p>
+ * @param b a <code>boolean</code> value
+ * @since Ant 1.5.2
+ */
+ public void setReloadStylesheet(final boolean b) {
+ reuseLoadedStylesheet = !b;
+ }
+
+ /**
+ * Defines the mapper to map source to destination files.
+ * @param mapper the mapper to use
+ * @exception BuildException if more than one mapper is defined
+ * @since Ant 1.6.2
+ */
+ public void addMapper(final Mapper mapper) {
+ if (mapperElement != null) {
+ handleError("Cannot define more than one mapper");
+ } else {
+ mapperElement = mapper;
+ }
+ }
+
+ /**
+ * Adds a collection of resources to style in addition to the
+ * given file or the implicit fileset.
+ *
+ * @param rc the collection of resources to style
+ * @since Ant 1.7
+ */
+ public void add(final ResourceCollection rc) {
+ resources.add(rc);
+ }
+
+ /**
+ * Add a nested &lt;style&gt; element.
+ * @param rc the configured Resources object represented as &lt;style&gt;.
+ * @since Ant 1.7
+ */
+ public void addConfiguredStyle(final Resources rc) {
+ if (rc.size() != 1) {
+ handleError("The style element must be specified with exactly one"
+ + " nested resource.");
+ } else {
+ setXslResource(rc.iterator().next());
+ }
+ }
+
+ /**
+ * API method to set the XSL Resource.
+ * @param xslResource Resource to set as the stylesheet.
+ * @since Ant 1.7
+ */
+ public void setXslResource(final Resource xslResource) {
+ this.xslResource = xslResource;
+ }
+
+ /**
+ * Adds a nested filenamemapper.
+ * @param fileNameMapper the mapper to add
+ * @exception BuildException if more than one mapper is defined
+ * @since Ant 1.7.0
+ */
+ public void add(final FileNameMapper fileNameMapper) throws BuildException {
+ final Mapper mapper = new Mapper(getProject());
+ mapper.add(fileNameMapper);
+ addMapper(mapper);
+ }
+
+ /**
+ * Executes the task.
+ *
+ * @exception BuildException if there is an execution problem.
+ * @todo validate that if either in or out is defined, then both are
+ */
+ @Override
+ public void execute() throws BuildException {
+ if ("style".equals(getTaskType())) {
+ log("Warning: the task name <style> is deprecated. Use <xslt> instead.",
+ Project.MSG_WARN);
+ }
+ final File savedBaseDir = baseDir;
+
+ DirectoryScanner scanner;
+ String[] list;
+ String[] dirs;
+
+ final String baseMessage =
+ "specify the stylesheet either as a filename in style attribute "
+ + "or as a nested resource";
+
+ if (xslResource == null && xslFile == null) {
+ handleError(baseMessage);
+ return;
+ }
+ if (xslResource != null && xslFile != null) {
+ handleError(baseMessage + " but not as both");
+ return;
+ }
+ if (inFile != null && !inFile.exists()) {
+ handleError("input file " + inFile + " does not exist");
+ return;
+ }
+ try {
+ setupLoader();
+
+ if (sysProperties.size() > 0) {
+ sysProperties.setSystem();
+ }
+
+ Resource styleResource;
+ if (baseDir == null) {
+ baseDir = getProject().getBaseDir();
+ }
+ liaison = getLiaison();
+
+ // check if liaison wants to log errors using us as logger
+ if (liaison instanceof XSLTLoggerAware) {
+ ((XSLTLoggerAware) liaison).setLogger(this);
+ }
+ log("Using " + liaison.getClass().toString(), Project.MSG_VERBOSE);
+
+ if (xslFile != null) {
+ // If we enter here, it means that the stylesheet is supplied
+ // via style attribute
+ File stylesheet = getProject().resolveFile(xslFile);
+ if (!stylesheet.exists()) {
+ final File alternative = FILE_UTILS.resolveFile(baseDir, xslFile);
+ /*
+ * shouldn't throw out deprecation warnings before we know,
+ * the wrong version has been used.
+ */
+ if (alternative.exists()) {
+ log("DEPRECATED - the 'style' attribute should be "
+ + "relative to the project's");
+ log(" basedir, not the tasks's basedir.");
+ stylesheet = alternative;
+ }
+ }
+ final FileResource fr = new FileResource();
+ fr.setProject(getProject());
+ fr.setFile(stylesheet);
+ styleResource = fr;
+ } else {
+ styleResource = xslResource;
+ }
+
+ if (!styleResource.isExists()) {
+ handleError("stylesheet " + styleResource + " doesn't exist.");
+ return;
+ }
+
+ // if we have an in file and out then process them
+ if (inFile != null && outFile != null) {
+ process(inFile, outFile, styleResource);
+ return;
+ }
+ /*
+ * if we get here, in and out have not been specified, we are
+ * in batch processing mode.
+ */
+
+ //-- make sure destination directory exists...
+ checkDest();
+
+ if (useImplicitFileset) {
+ scanner = getDirectoryScanner(baseDir);
+ log("Transforming into " + destDir, Project.MSG_INFO);
+
+ // Process all the files marked for styling
+ list = scanner.getIncludedFiles();
+ for (int i = 0; i < list.length; ++i) {
+ process(baseDir, list[i], destDir, styleResource);
+ }
+ if (performDirectoryScan) {
+ // Process all the directories marked for styling
+ dirs = scanner.getIncludedDirectories();
+ for (int j = 0; j < dirs.length; ++j) {
+ list = new File(baseDir, dirs[j]).list();
+ for (int i = 0; i < list.length; ++i) {
+ process(baseDir, dirs[j] + File.separator + list[i], destDir,
+ styleResource);
+ }
+ }
+ }
+ } else { // only resource collections, there better be some
+ if (resources.size() == 0) {
+ if (failOnNoResources) {
+ handleError("no resources specified");
+ }
+ return;
+ }
+ }
+ processResources(styleResource);
+ } finally {
+ if (loader != null) {
+ loader.resetThreadContextLoader();
+ loader.cleanup();
+ loader = null;
+ }
+ if (sysProperties.size() > 0) {
+ sysProperties.restoreSystem();
+ }
+ liaison = null;
+ stylesheetLoaded = false;
+ baseDir = savedBaseDir;
+ }
+ }
+
+ /**
+ * Set whether to check dependencies, or always generate;
+ * optional, default is false.
+ *
+ * @param force true if always generate.
+ */
+ public void setForce(final boolean force) {
+ this.force = force;
+ }
+
+ /**
+ * Set the base directory;
+ * optional, default is the project's basedir.
+ *
+ * @param dir the base directory
+ **/
+ public void setBasedir(final File dir) {
+ baseDir = dir;
+ }
+
+ /**
+ * Set the destination directory into which the XSL result
+ * files should be copied to;
+ * required, unless <tt>in</tt> and <tt>out</tt> are
+ * specified.
+ * @param dir the name of the destination directory
+ **/
+ public void setDestdir(final File dir) {
+ destDir = dir;
+ }
+
+ /**
+ * Set the desired file extension to be used for the target;
+ * optional, default is html.
+ * @param name the extension to use
+ **/
+ public void setExtension(final String name) {
+ targetExtension = name;
+ }
+
+ /**
+ * Name of the stylesheet to use - given either relative
+ * to the project's basedir or as an absolute path; required.
+ *
+ * @param xslFile the stylesheet to use
+ */
+ public void setStyle(final String xslFile) {
+ this.xslFile = xslFile;
+ }
+
+ /**
+ * Set the optional classpath to the XSL processor
+ *
+ * @param classpath the classpath to use when loading the XSL processor
+ */
+ public void setClasspath(final Path classpath) {
+ createClasspath().append(classpath);
+ }
+
+ /**
+ * Set the optional classpath to the XSL processor
+ *
+ * @return a path instance to be configured by the Ant core.
+ */
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(getProject());
+ }
+ return classpath.createPath();
+ }
+
+ /**
+ * Set the reference to an optional classpath to the XSL processor
+ *
+ * @param r the id of the Ant path instance to act as the classpath
+ * for loading the XSL processor
+ */
+ public void setClasspathRef(final Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * Set the name of the XSL processor to use; optional, default trax.
+ *
+ * @param processor the name of the XSL processor
+ */
+ public void setProcessor(final String processor) {
+ this.processor = processor;
+ }
+
+ /**
+ * Whether to use the implicit fileset.
+ *
+ * <p>Set this to false if you want explicit control with nested
+ * resource collections.</p>
+ * @param useimplicitfileset set to true if you want to use implicit fileset
+ * @since Ant 1.7
+ */
+ public void setUseImplicitFileset(final boolean useimplicitfileset) {
+ useImplicitFileset = useimplicitfileset;
+ }
+
+ /**
+ * Add the catalog to our internal catalog
+ *
+ * @param xmlCatalog the XMLCatalog instance to use to look up DTDs
+ */
+ public void addConfiguredXMLCatalog(final XMLCatalog xmlCatalog) {
+ this.xmlCatalog.addConfiguredXMLCatalog(xmlCatalog);
+ }
+
+ /**
+ * Pass the filename of the current processed file as a xsl parameter
+ * to the transformation. This value sets the name of that xsl parameter.
+ *
+ * @param fileNameParameter name of the xsl parameter retrieving the
+ * current file name
+ */
+ public void setFileNameParameter(final String fileNameParameter) {
+ this.fileNameParameter = fileNameParameter;
+ }
+
+ /**
+ * Pass the directory name of the current processed file as a xsl parameter
+ * to the transformation. This value sets the name of that xsl parameter.
+ *
+ * @param fileDirParameter name of the xsl parameter retrieving the
+ * current file directory
+ */
+ public void setFileDirParameter(final String fileDirParameter) {
+ this.fileDirParameter = fileDirParameter;
+ }
+
+ /**
+ * Whether to suppress warning messages of the processor.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setSuppressWarnings(final boolean b) {
+ suppressWarnings = b;
+ }
+
+ /**
+ * Whether to suppress warning messages of the processor.
+ *
+ * @since Ant 1.8.0
+ */
+ public boolean getSuppressWarnings() {
+ return suppressWarnings;
+ }
+
+ /**
+ * Whether transformation errors should make the build fail.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setFailOnTransformationError(final boolean b) {
+ failOnTransformationError = b;
+ }
+
+ /**
+ * Whether any errors should make the build fail.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setFailOnError(final boolean b) {
+ failOnError = b;
+ }
+
+ /**
+ * Whether the build should fail if the nested resource collection is empty.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setFailOnNoResources(final boolean b) {
+ failOnNoResources = b;
+ }
+
+ /**
+ * A system property to set during transformation.
+ *
+ * @since Ant 1.8.0
+ */
+ public void addSysproperty(final Environment.Variable sysp) {
+ sysProperties.addVariable(sysp);
+ }
+
+ /**
+ * A set of system properties to set during transformation.
+ *
+ * @since Ant 1.8.0
+ */
+ public void addSyspropertyset(final PropertySet sysp) {
+ sysProperties.addSyspropertyset(sysp);
+ }
+
+ /**
+ * Enables Xalan2 traces and uses the given configuration.
+ *
+ * <p>Note that this element doesn't have any effect with a
+ * processor other than trax or if the Transformer is not Xalan2's
+ * transformer implementation.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public TraceConfiguration createTrace() {
+ if (traceConfiguration != null) {
+ throw new BuildException("can't have more than one trace"
+ + " configuration");
+ }
+ traceConfiguration = new TraceConfiguration();
+ return traceConfiguration;
+ }
+
+ /**
+ * Configuration for Xalan2 traces.
+ *
+ * @since Ant 1.8.0
+ */
+ public TraceConfiguration getTraceConfiguration() {
+ return traceConfiguration;
+ }
+
+ /**
+ * Load processor here instead of in setProcessor - this will be
+ * called from within execute, so we have access to the latest
+ * classpath.
+ *
+ * @param proc the name of the processor to load.
+ * @exception Exception if the processor cannot be loaded.
+ */
+ private void resolveProcessor(final String proc) throws Exception {
+ if (proc.equals(PROCESSOR_TRAX)) {
+ liaison = new org.apache.tools.ant.taskdefs.optional.TraXLiaison();
+ } else {
+ //anything else is a classname
+ final Class clazz = loadClass(proc);
+ liaison = (XSLTLiaison) clazz.newInstance();
+ }
+ }
+
+ /**
+ * Load named class either via the system classloader or a given
+ * custom classloader.
+ *
+ * As a side effect, the loader is set as the thread context classloader
+ * @param classname the name of the class to load.
+ * @return the requested class.
+ * @exception Exception if the class could not be loaded.
+ */
+ private Class loadClass(final String classname) throws Exception {
+ setupLoader();
+ if (loader == null) {
+ return Class.forName(classname);
+ }
+ return Class.forName(classname, true, loader);
+ }
+
+ /**
+ * If a custom classpath has been defined but no loader created
+ * yet, create the classloader and set it as the context
+ * classloader.
+ */
+ private void setupLoader() {
+ if (classpath != null && loader == null) {
+ loader = getProject().createClassLoader(classpath);
+ loader.setThreadContextLoader();
+ }
+ }
+
+ /**
+ * Specifies the output name for the styled result from the
+ * <tt>in</tt> attribute; required if <tt>in</tt> is set
+ *
+ * @param outFile the output File instance.
+ */
+ public void setOut(final File outFile) {
+ this.outFile = outFile;
+ }
+
+ /**
+ * specifies a single XML document to be styled. Should be used
+ * with the <tt>out</tt> attribute; ; required if <tt>out</tt> is set
+ *
+ * @param inFile the input file
+ */
+ public void setIn(final File inFile) {
+ this.inFile = inFile;
+ }
+
+ /**
+ * Throws a BuildException if the destination directory hasn't
+ * been specified.
+ * @since Ant 1.7
+ */
+ private void checkDest() {
+ if (destDir == null) {
+ handleError("destdir attributes must be set!");
+ }
+ }
+
+ /**
+ * Styles all existing resources.
+ *
+ * @param stylesheet style sheet to use
+ * @since Ant 1.7
+ */
+ private void processResources(final Resource stylesheet) {
+ for (final Resource r : resources) {
+ if (!r.isExists()) {
+ continue;
+ }
+ File base = baseDir;
+ String name = r.getName();
+ final FileProvider fp = r.as(FileProvider.class);
+ if (fp != null) {
+ final FileResource f = ResourceUtils.asFileResource(fp);
+ base = f.getBaseDir();
+ if (base == null) {
+ name = f.getFile().getAbsolutePath();
+ }
+ }
+ process(base, name, destDir, stylesheet);
+ }
+ }
+
+ /**
+ * Processes the given input XML file and stores the result
+ * in the given resultFile.
+ *
+ * @param baseDir the base directory for resolving files.
+ * @param xmlFile the input file
+ * @param destDir the destination directory
+ * @param stylesheet the stylesheet to use.
+ * @exception BuildException if the processing fails.
+ */
+ private void process(final File baseDir, final String xmlFile, final File destDir, final Resource stylesheet)
+ throws BuildException {
+
+ File outF = null;
+ File inF = null;
+
+ try {
+ final long styleSheetLastModified = stylesheet.getLastModified();
+ inF = new File(baseDir, xmlFile);
+
+ if (inF.isDirectory()) {
+ log("Skipping " + inF + " it is a directory.", Project.MSG_VERBOSE);
+ return;
+ }
+ FileNameMapper mapper = null;
+ if (mapperElement != null) {
+ mapper = mapperElement.getImplementation();
+ } else {
+ mapper = new StyleMapper();
+ }
+
+ final String[] outFileName = mapper.mapFileName(xmlFile);
+ if (outFileName == null || outFileName.length == 0) {
+ log("Skipping " + inFile + " it cannot get mapped to output.", Project.MSG_VERBOSE);
+ return;
+ } else if (outFileName == null || outFileName.length > 1) {
+ log("Skipping " + inFile + " its mapping is ambiguos.", Project.MSG_VERBOSE);
+ return;
+ }
+ outF = new File(destDir, outFileName[0]);
+
+ if (force || inF.lastModified() > outF.lastModified()
+ || styleSheetLastModified > outF.lastModified()) {
+ ensureDirectoryFor(outF);
+ log("Processing " + inF + " to " + outF);
+ configureLiaison(stylesheet);
+ setLiaisonDynamicFileParameters(liaison, inF);
+ liaison.transform(inF, outF);
+ }
+ } catch (final Exception ex) {
+ // If failed to process document, must delete target document,
+ // or it will not attempt to process it the second time
+ log("Failed to process " + inFile, Project.MSG_INFO);
+ if (outF != null) {
+ outF.delete();
+ }
+ handleTransformationError(ex);
+ }
+
+ } //-- processXML
+
+ /**
+ * Process the input file to the output file with the given stylesheet.
+ *
+ * @param inFile the input file to process.
+ * @param outFile the destination file.
+ * @param stylesheet the stylesheet to use.
+ * @exception BuildException if the processing fails.
+ */
+ private void process(final File inFile, final File outFile, final Resource stylesheet) throws BuildException {
+ try {
+ final long styleSheetLastModified = stylesheet.getLastModified();
+ log("In file " + inFile + " time: " + inFile.lastModified(), Project.MSG_DEBUG);
+ log("Out file " + outFile + " time: " + outFile.lastModified(), Project.MSG_DEBUG);
+ log("Style file " + xslFile + " time: " + styleSheetLastModified, Project.MSG_DEBUG);
+ if (force || inFile.lastModified() >= outFile.lastModified()
+ || styleSheetLastModified >= outFile.lastModified()) {
+ ensureDirectoryFor(outFile);
+ log("Processing " + inFile + " to " + outFile, Project.MSG_INFO);
+ configureLiaison(stylesheet);
+ setLiaisonDynamicFileParameters(liaison, inFile);
+ liaison.transform(inFile, outFile);
+ } else {
+ log("Skipping input file " + inFile + " because it is older than output file "
+ + outFile + " and so is the stylesheet " + stylesheet, Project.MSG_DEBUG);
+ }
+ } catch (final Exception ex) {
+ log("Failed to process " + inFile, Project.MSG_INFO);
+ if (outFile != null) {
+ outFile.delete();
+ }
+ handleTransformationError(ex);
+ }
+ }
+
+ /**
+ * Ensure the directory exists for a given file
+ *
+ * @param targetFile the file for which the directories are required.
+ * @exception BuildException if the directories cannot be created.
+ */
+ private void ensureDirectoryFor(final File targetFile) throws BuildException {
+ final File directory = targetFile.getParentFile();
+ if (!directory.exists()) {
+ if (!(directory.mkdirs() || directory.isDirectory())) {
+ handleError("Unable to create directory: "
+ + directory.getAbsolutePath());
+ }
+ }
+ }
+
+ /**
+ * Get the factory instance configured for this processor
+ *
+ * @return the factory instance in use
+ */
+ public Factory getFactory() {
+ return factory;
+ }
+
+ /**
+ * Get the XML catalog containing entity definitions
+ *
+ * @return the XML catalog for the task.
+ */
+ public XMLCatalog getXMLCatalog() {
+ xmlCatalog.setProject(getProject());
+ return xmlCatalog;
+ }
+
+ /**
+ * Get an enumeration on the outputproperties.
+ * @return the outputproperties
+ */
+ public Enumeration getOutputProperties() {
+ return outputProperties.elements();
+ }
+
+ /**
+ * Get the Liaison implementation to use in processing.
+ *
+ * @return an instance of the XSLTLiaison interface.
+ */
+ protected XSLTLiaison getLiaison() {
+ // if processor wasn't specified, use TraX.
+ if (liaison == null) {
+ if (processor != null) {
+ try {
+ resolveProcessor(processor);
+ } catch (final Exception e) {
+ handleError(e);
+ }
+ } else {
+ try {
+ resolveProcessor(PROCESSOR_TRAX);
+ } catch (final Throwable e1) {
+ e1.printStackTrace();
+ handleError(e1);
+ }
+ }
+ }
+ return liaison;
+ }
+
+ /**
+ * Create an instance of an XSL parameter for configuration by Ant.
+ *
+ * @return an instance of the Param class to be configured.
+ */
+ public Param createParam() {
+ final Param p = new Param();
+ params.add(p);
+ return p;
+ }
+
+ /**
+ * The Param inner class used to store XSL parameters
+ */
+ public static class Param {
+ /** The parameter name */
+ private String name = null;
+
+ /** The parameter's value */
+ private String expression = null;
+
+ /**
+ * Type of the expression.
+ * @see ParamType
+ */
+ private String type;
+
+ private Object ifCond;
+ private Object unlessCond;
+ private Project project;
+
+ /**
+ * Set the current project
+ *
+ * @param project the current project
+ */
+ public void setProject(final Project project) {
+ this.project = project;
+ }
+
+ /**
+ * Set the parameter name.
+ *
+ * @param name the name of the parameter.
+ */
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * The parameter value -
+ * can be a primitive type value or an XPath expression.
+ * @param expression the parameter's value/expression.
+ * @see #setType(java.lang.String)
+ */
+ public void setExpression(final String expression) {
+ this.expression = expression;
+ }
+
+ /**
+ * @see ParamType
+ * @since Ant 1.9.3
+ */
+ public void setType(final String type) {
+ this.type = type;
+ }
+
+ /**
+ * Get the parameter name
+ *
+ * @return the parameter name
+ * @exception BuildException if the name is not set.
+ */
+ public String getName() throws BuildException {
+ if (name == null) {
+ throw new BuildException("Name attribute is missing.");
+ }
+ return name;
+ }
+
+ /**
+ * Get the parameter's value
+ *
+ * @return the parameter value
+ * @exception BuildException if the value is not set.
+ * @see #getType()
+ */
+ public String getExpression() throws BuildException {
+ if (expression == null) {
+ throw new BuildException("Expression attribute is missing.");
+ }
+ return expression;
+ }
+
+ /**
+ * @see ParamType
+ * @since Ant 1.9.3
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Set whether this param should be used. It will be used if
+ * the expression evaluates to true or the name of a property
+ * which has been set, otherwise it won't.
+ * @param ifCond evaluated expression
+ * @since Ant 1.8.0
+ */
+ public void setIf(final Object ifCond) {
+ this.ifCond = ifCond;
+ }
+
+ /**
+ * Set whether this param should be used. It will be used if
+ * the expression evaluates to true or the name of a property
+ * which has been set, otherwise it won't.
+ * @param ifProperty evaluated expression
+ */
+ public void setIf(final String ifProperty) {
+ setIf((Object) ifProperty);
+ }
+
+ /**
+ * Set whether this param should NOT be used. It will not be
+ * used if the expression evaluates to true or the name of a
+ * property which has been set, otherwise it will be used.
+ * @param unlessCond evaluated expression
+ * @since Ant 1.8.0
+ */
+ public void setUnless(final Object unlessCond) {
+ this.unlessCond = unlessCond;
+ }
+
+ /**
+ * Set whether this param should NOT be used. It will not be
+ * used if the expression evaluates to true or the name of a
+ * property which has been set, otherwise it will be used.
+ * @param unlessProperty evaluated expression
+ */
+ public void setUnless(final String unlessProperty) {
+ setUnless((Object) unlessProperty);
+ }
+
+ /**
+ * Ensures that the param passes the conditions placed
+ * on it with <code>if</code> and <code>unless</code> properties.
+ * @return true if the task passes the "if" and "unless" parameters
+ */
+ public boolean shouldUse() {
+ final PropertyHelper ph = PropertyHelper.getPropertyHelper(project);
+ return ph.testIfCondition(ifCond)
+ && ph.testUnlessCondition(unlessCond);
+ }
+ } // Param
+
+ /**
+ * Enum for types of the parameter expression.
+ *
+ * <p>The expression can be:</p>
+ * <ul>
+ * <li>primitive type that will be parsed from the string value e.g.
+ * {@linkplain Integer#parseInt(java.lang.String)}</li>
+ * <li>XPath expression that will be evaluated (outside of the transformed
+ * document - on empty one) and casted to given type. Inside XPath
+ * expressions the Ant variables (properties) can be used (as XPath
+ * variables - e.g. $variable123). n.b. placeholders in form of
+ * ${variable123} will be substituted with their values before evaluating the
+ * XPath expression (so it can be used for dynamic XPath function names and
+ * other hacks).</li>
+ * </ul>
+ * <p>The parameter will be then passed to the XSLT template.</p>
+ *
+ * <p>Default type (if omitted) is primitive String. So if the expression is e.g
+ * "true" with no type, in XSLT it will be only a text string, not true
+ * boolean.</p>
+ *
+ * @see Param#setType(java.lang.String)
+ * @see Param#setExpression(java.lang.String)
+ * @since Ant 1.9.3
+ */
+ public enum ParamType {
+
+ STRING,
+ BOOLEAN,
+ INT,
+ LONG,
+ DOUBLE,
+ XPATH_STRING,
+ XPATH_BOOLEAN,
+ XPATH_NUMBER,
+ XPATH_NODE,
+ XPATH_NODESET;
+
+ public static final Map<ParamType, QName> XPATH_TYPES;
+
+ static {
+ final Map<ParamType, QName> m = new EnumMap<ParamType, QName>(ParamType.class);
+ m.put(XPATH_STRING, XPathConstants.STRING);
+ m.put(XPATH_BOOLEAN, XPathConstants.BOOLEAN);
+ m.put(XPATH_NUMBER, XPathConstants.NUMBER);
+ m.put(XPATH_NODE, XPathConstants.NODE);
+ m.put(XPATH_NODESET, XPathConstants.NODESET);
+ XPATH_TYPES = Collections.unmodifiableMap(m);
+ }
+ }
+
+ /**
+ * Create an instance of an output property to be configured.
+ * @return the newly created output property.
+ * @since Ant 1.5
+ */
+ public OutputProperty createOutputProperty() {
+ final OutputProperty p = new OutputProperty();
+ outputProperties.addElement(p);
+ return p;
+ }
+
+ /**
+ * Specify how the result tree should be output as specified
+ * in the <a href="http://www.w3.org/TR/xslt#output">
+ * specification</a>.
+ * @since Ant 1.5
+ */
+ public static class OutputProperty {
+ /** output property name */
+ private String name;
+
+ /** output property value */
+ private String value;
+
+ /**
+ * @return the output property name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * set the name for this property
+ * @param name A non-null String that specifies an
+ * output property name, which may be namespace qualified.
+ */
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return the output property value.
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * set the value for this property
+ * @param value The non-null string value of the output property.
+ */
+ public void setValue(final String value) {
+ this.value = value;
+ }
+ }
+
+ /**
+ * Initialize internal instance of XMLCatalog.
+ * Initialize XPath for parameter evaluation.
+ * @throws BuildException on error
+ */
+ @Override
+ public void init() throws BuildException {
+ super.init();
+ xmlCatalog.setProject(getProject());
+
+ xpathFactory = XPathFactory.newInstance();
+ xpath = xpathFactory.newXPath();
+ xpath.setXPathVariableResolver(new XPathVariableResolver() {
+ public Object resolveVariable(final QName variableName) {
+ return getProject().getProperty(variableName.toString());
+ }
+ });
+ }
+
+ /**
+ * Loads the stylesheet and set xsl:param parameters.
+ *
+ * @param stylesheet the file from which to load the stylesheet.
+ * @exception BuildException if the stylesheet cannot be loaded.
+ * @deprecated since Ant 1.7
+ */
+ @Deprecated
+ protected void configureLiaison(final File stylesheet) throws BuildException {
+ final FileResource fr = new FileResource();
+ fr.setProject(getProject());
+ fr.setFile(stylesheet);
+ configureLiaison(fr);
+ }
+
+ /**
+ * Loads the stylesheet and set xsl:param parameters.
+ *
+ * @param stylesheet the resource from which to load the stylesheet.
+ * @exception BuildException if the stylesheet cannot be loaded.
+ * @since Ant 1.7
+ */
+ protected void configureLiaison(final Resource stylesheet) throws BuildException {
+ if (stylesheetLoaded && reuseLoadedStylesheet) {
+ return;
+ }
+ stylesheetLoaded = true;
+
+ try {
+ log("Loading stylesheet " + stylesheet, Project.MSG_INFO);
+ // We call liaison.configure() and then liaison.setStylesheet()
+ // so that the internal variables of liaison can be set up
+ if (liaison instanceof XSLTLiaison2) {
+ ((XSLTLiaison2) liaison).configure(this);
+ }
+ if (liaison instanceof XSLTLiaison3) {
+ // If we are here we can set the stylesheet as a
+ // resource
+ ((XSLTLiaison3) liaison).setStylesheet(stylesheet);
+ } else {
+ // If we are here we cannot set the stylesheet as
+ // a resource, but we can set it as a file. So,
+ // we make an attempt to get it as a file
+ final FileProvider fp =
+ stylesheet.as(FileProvider.class);
+ if (fp != null) {
+ liaison.setStylesheet(fp.getFile());
+ } else {
+ handleError(liaison.getClass().toString()
+ + " accepts the stylesheet only as a file");
+ return;
+ }
+ }
+ for (final Param p : params) {
+ if (p.shouldUse()) {
+ final Object evaluatedParam = evaluateParam(p);
+ if (liaison instanceof XSLTLiaison4) {
+ ((XSLTLiaison4)liaison).addParam(p.getName(), evaluatedParam);
+ } else {
+ if (evaluatedParam == null || evaluatedParam instanceof String) {
+ liaison.addParam(p.getName(), (String)evaluatedParam);
+ } else {
+ log("XSLTLiaison '" + liaison.getClass().getName()
+ + "' supports only String parameters. Converting parameter '" + p.getName()
+ + "' to its String value '" + evaluatedParam, Project.MSG_WARN);
+ liaison.addParam(p.getName(), String.valueOf(evaluatedParam));
+ }
+ }
+ }
+ }
+ } catch (final Exception ex) {
+ log("Failed to transform using stylesheet " + stylesheet, Project.MSG_INFO);
+ handleTransformationError(ex);
+ }
+ }
+
+ /**
+ * Evaluates parameter expression according to its type.
+ *
+ * @param param parameter from Ant build file
+ * @return value to be passed to XSLT as parameter
+ * @throws IllegalArgumentException if param type is unsupported
+ * @throws NumberFormatException if expression of numeric type is not
+ * desired numeric type
+ * @throws XPathExpressionException if XPath expression can not be compiled
+ * @since Ant 1.9.3
+ */
+ private Object evaluateParam(final Param param) throws XPathExpressionException {
+ final String typeName = param.getType();
+ final String expression = param.getExpression();
+
+ ParamType type;
+
+ if (typeName == null || "".equals(typeName)) {
+ type = ParamType.STRING; // String is default
+ } else {
+ try {
+ type = ParamType.valueOf(typeName);
+ } catch (final IllegalArgumentException e) {
+ throw new IllegalArgumentException("Invalid XSLT parameter type: " + typeName, e);
+ }
+ }
+
+ switch (type) {
+ case STRING:
+ return expression;
+ case BOOLEAN:
+ return Boolean.parseBoolean(expression);
+ case DOUBLE:
+ return Double.parseDouble(expression);
+ case INT:
+ return Integer.parseInt(expression);
+ case LONG:
+ return Long.parseLong(expression);
+ default: // XPath expression
+ final QName xpathType = ParamType.XPATH_TYPES.get(type);
+ if (xpathType == null) {
+ throw new IllegalArgumentException("Invalid XSLT parameter type: " + typeName);
+ } else {
+ final XPathExpression xpe = xpath.compile(expression);
+ // null = evaluate XPath on empty XML document
+ return xpe.evaluate((Object) null, xpathType);
+ }
+ }
+ }
+
+ /**
+ * Sets file parameter(s) for directory and filename if the attribute
+ * 'filenameparameter' or 'filedirparameter' are set in the task.
+ *
+ * @param liaison to change parameters for
+ * @param inFile to get the additional file information from
+ * @throws Exception if an exception occurs on filename lookup
+ *
+ * @since Ant 1.7
+ */
+ private void setLiaisonDynamicFileParameters(
+ final XSLTLiaison liaison, final File inFile) throws Exception {
+ if (fileNameParameter != null) {
+ liaison.addParam(fileNameParameter, inFile.getName());
+ }
+ if (fileDirParameter != null) {
+ final String fileName = FileUtils.getRelativePath(baseDir, inFile);
+ final File file = new File(fileName);
+ // Give always a slash as file separator, so the stylesheet could be sure about that
+ // Use '.' so a dir+"/"+name would not result in an absolute path
+ liaison.addParam(fileDirParameter, file.getParent() != null ? file.getParent().replace(
+ '\\', '/') : ".");
+ }
+ }
+
+ /**
+ * Create the factory element to configure a trax liaison.
+ * @return the newly created factory element.
+ * @throws BuildException if the element is created more than one time.
+ */
+ public Factory createFactory() throws BuildException {
+ if (factory != null) {
+ handleError("'factory' element must be unique");
+ } else {
+ factory = new Factory();
+ }
+ return factory;
+ }
+
+ /**
+ * Throws an exception with the given message if failOnError is
+ * true, otherwise logs the message using the WARN level.
+ *
+ * @since Ant 1.8.0
+ */
+ protected void handleError(final String msg) {
+ if (failOnError) {
+ throw new BuildException(msg, getLocation());
+ }
+ log(msg, Project.MSG_WARN);
+ }
+
+
+ /**
+ * Throws an exception with the given nested exception if
+ * failOnError is true, otherwise logs the message using the WARN
+ * level.
+ *
+ * @since Ant 1.8.0
+ */
+ protected void handleError(final Throwable ex) {
+ if (failOnError) {
+ throw new BuildException(ex);
+ } else {
+ log("Caught an exception: " + ex, Project.MSG_WARN);
+ }
+ }
+
+ /**
+ * Throws an exception with the given nested exception if
+ * failOnError and failOnTransformationError are true, otherwise
+ * logs the message using the WARN level.
+ *
+ * @since Ant 1.8.0
+ */
+ protected void handleTransformationError(final Exception ex) {
+ if (failOnError && failOnTransformationError) {
+ throw new BuildException(ex);
+ } else {
+ log("Caught an error during transformation: " + ex,
+ Project.MSG_WARN);
+ }
+ }
+
+ /**
+ * The factory element to configure a transformer factory
+ * @since Ant 1.6
+ */
+ public static class Factory {
+
+ /** the factory class name to use for TraXLiaison */
+ private String name;
+
+ /**
+ * the list of factory attributes to use for TraXLiaison
+ */
+ private final Vector attributes = new Vector();
+
+ /**
+ * @return the name of the factory.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name of the factory
+ * @param name the name of the factory.
+ */
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Create an instance of a factory attribute.
+ * @param attr the newly created factory attribute
+ */
+ public void addAttribute(final Attribute attr) {
+ attributes.addElement(attr);
+ }
+
+ /**
+ * return the attribute elements.
+ * @return the enumeration of attributes
+ */
+ public Enumeration getAttributes() {
+ return attributes.elements();
+ }
+
+ /**
+ * A JAXP factory attribute. This is mostly processor specific, for
+ * example for Xalan 2.3+, the following attributes could be set:
+ * <ul>
+ * <li>http://xml.apache.org/xalan/features/optimize (true|false) </li>
+ * <li>http://xml.apache.org/xalan/features/incremental (true|false) </li>
+ * </ul>
+ */
+ public static class Attribute implements DynamicConfigurator {
+
+ /** attribute name, mostly processor specific */
+ private String name;
+
+ /** attribute value, often a boolean string */
+ private Object value;
+
+ /**
+ * @return the attribute name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return the output property value.
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * Not used.
+ * @param name not used
+ * @return null
+ * @throws BuildException never
+ */
+ public Object createDynamicElement(final String name) throws BuildException {
+ return null;
+ }
+
+ /**
+ * Set an attribute.
+ * Only "name" and "value" are supported as names.
+ * @param name the name of the attribute
+ * @param value the value of the attribute
+ * @throws BuildException on error
+ */
+ public void setDynamicAttribute(final String name, final String value) throws BuildException {
+ // only 'name' and 'value' exist.
+ if ("name".equalsIgnoreCase(name)) {
+ this.name = value;
+ } else if ("value".equalsIgnoreCase(name)) {
+ // a value must be of a given type
+ // say boolean|integer|string that are mostly used.
+ if ("true".equalsIgnoreCase(value)) {
+ this.value = Boolean.TRUE;
+ } else if ("false".equalsIgnoreCase(value)) {
+ this.value = Boolean.FALSE;
+ } else {
+ try {
+ this.value = new Integer(value);
+ } catch (final NumberFormatException e) {
+ this.value = value;
+ }
+ }
+ } else {
+ throw new BuildException("Unsupported attribute: " + name);
+ }
+ }
+ } // -- class Attribute
+ } // -- class Factory
+
+ /**
+ * Mapper implementation of the "traditional" way &lt;xslt&gt;
+ * mapped filenames.
+ *
+ * <p>If the file has an extension, chop it off. Append whatever
+ * the user has specified as extension or ".html".</p>
+ *
+ * @since Ant 1.6.2
+ */
+ private class StyleMapper implements FileNameMapper {
+ public void setFrom(final String from) {
+ }
+ public void setTo(final String to) {
+ }
+ public String[] mapFileName(String xmlFile) {
+ final int dotPos = xmlFile.lastIndexOf('.');
+ if (dotPos > 0) {
+ xmlFile = xmlFile.substring(0, dotPos);
+ }
+ return new String[] {xmlFile + targetExtension};
+ }
+ }
+
+ /**
+ * Configuration for Xalan2 traces.
+ *
+ * @since Ant 1.8.0
+ */
+ public final class TraceConfiguration {
+ private boolean elements, extension, generation, selection, templates;
+
+ /**
+ * Set to true if the listener is to print events that occur
+ * as each node is 'executed' in the stylesheet.
+ */
+ public void setElements(final boolean b) {
+ elements = b;
+ }
+
+ /**
+ * True if the listener is to print events that occur as each
+ * node is 'executed' in the stylesheet.
+ */
+ public boolean getElements() {
+ return elements;
+ }
+
+ /**
+ * Set to true if the listener is to print information after
+ * each extension event.
+ */
+ public void setExtension(final boolean b) {
+ extension = b;
+ }
+
+ /**
+ * True if the listener is to print information after each
+ * extension event.
+ */
+ public boolean getExtension() {
+ return extension;
+ }
+
+ /**
+ * Set to true if the listener is to print information after
+ * each result-tree generation event.
+ */
+ public void setGeneration(final boolean b) {
+ generation = b;
+ }
+
+ /**
+ * True if the listener is to print information after each
+ * result-tree generation event.
+ */
+ public boolean getGeneration() {
+ return generation;
+ }
+
+ /**
+ * Set to true if the listener is to print information after
+ * each selection event.
+ */
+ public void setSelection(final boolean b) {
+ selection = b;
+ }
+
+ /**
+ * True if the listener is to print information after each
+ * selection event.
+ */
+ public boolean getSelection() {
+ return selection;
+ }
+
+ /**
+ * Set to true if the listener is to print an event whenever a
+ * template is invoked.
+ */
+ public void setTemplates(final boolean b) {
+ templates = b;
+ }
+
+ /**
+ * True if the listener is to print an event whenever a
+ * template is invoked.
+ */
+ public boolean getTemplates() {
+ return templates;
+ }
+
+ /**
+ * The stream to write traces to.
+ */
+ public java.io.OutputStream getOutputStream() {
+ return new LogOutputStream(XSLTProcess.this);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XmlProperty.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XmlProperty.java
new file mode 100644
index 00000000..2830bdf9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/XmlProperty.java
@@ -0,0 +1,780 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.XMLCatalog;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.FileUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.SAXException;
+
+/**
+ * Loads property values from a valid XML file, generating the
+ * property names from the file's element and attribute names.
+ *
+ * <p>Example:</p>
+ * <pre>
+ * &lt;root-tag myattr="true"&gt;
+ * &lt;inner-tag someattr="val"&gt;Text&lt;/inner-tag&gt;
+ * &lt;a2&gt;&lt;a3&gt;&lt;a4&gt;false&lt;/a4&gt;&lt;/a3&gt;&lt;/a2&gt;
+ * &lt;x&gt;x1&lt;/x&gt;
+ * &lt;x&gt;x2&lt;/x&gt;
+ * &lt;/root-tag&gt;
+ *</pre>
+ *
+ * <p>this generates the following properties:</p>
+ *
+ * <pre>
+ * root-tag(myattr)=true
+ * root-tag.inner-tag=Text
+ * root-tag.inner-tag(someattr)=val
+ * root-tag.a2.a3.a4=false
+ * root-tag.x=x1,x2
+ * </pre>
+ *
+ * <p>The <i>collapseAttributes</i> property of this task can be set
+ * to true (the default is false) which will instead result in the
+ * following properties (note the difference in names of properties
+ * corresponding to XML attributes):</p>
+ *
+ * <pre>
+ * root-tag.myattr=true
+ * root-tag.inner-tag=Text
+ * root-tag.inner-tag.someattr=val
+ * root-tag.a2.a3.a4=false
+ * root-tag.x=x1,x2
+ * </pre>
+ *
+ * <p>Optionally, to more closely mirror the abilities of the Property
+ * task, a selected set of attributes can be treated specially. To
+ * enable this behavior, the "semanticAttributes" property of this task
+ * must be set to true (it defaults to false). If this attribute is
+ * specified, the following attributes take on special meaning
+ * (setting this to true implicitly sets collapseAttributes to true as
+ * well):</p>
+ *
+ * <ul>
+ * <li><b>value</b>: Identifies a text value for a property.</li>
+ * <li><b>location</b>: Identifies a file location for a property.</li>
+ * <li><b>id</b>: Sets an id for a property</li>
+ * <li><b>refid</b>: Sets a property to the value of another property
+ * based upon the provided id</li>
+ * <li><b>pathid</b>: Defines a path rather than a property with
+ * the given id.</li>
+ * </ul>
+ *
+ * <p>For example, with keepRoot = false, the following properties file:</p>
+ *
+ * <pre>
+ * &lt;root-tag&gt;
+ * &lt;build&gt;
+ * &lt;build folder="build"&gt;
+ * &lt;classes id="build.classes" location="${build.folder}/classes"/&gt;
+ * &lt;reference refid="build.classes"/&gt;
+ * &lt;/build&gt;
+ * &lt;compile&gt;
+ * &lt;classpath pathid="compile.classpath"&gt;
+ * &lt;pathelement location="${build.classes}"/&gt;
+ * &lt;/classpath&gt;
+ * &lt;/compile&gt;
+ * &lt;run-time&gt;
+ * &lt;jars&gt;*.jar&lt;/jars&gt;
+ * &lt;classpath pathid="run-time.classpath"&gt;
+ * &lt;path refid="compile.classpath"/&gt;
+ * &lt;pathelement path="${run-time.jars}"/&gt;
+ * &lt;/classpath&gt;
+ * &lt;/run-time&gt;
+ * &lt;/root-tag&gt;
+ * </pre>
+ *
+ * <p>is equivalent to the following entries in a build file:</p>
+ *
+ * <pre>
+ * &lt;property name="build" location="build"/&gt;
+ * &lt;property name="build.classes" location="${build.location}/classes"/&gt;
+ * &lt;property name="build.reference" refid="build.classes"/&gt;
+ *
+ * &lt;property name="run-time.jars" value="*.jar/&gt;
+ *
+ * &lt;classpath id="compile.classpath"&gt;
+ * &lt;pathelement location="${build.classes}"/&gt;
+ * &lt;/classpath&gt;
+ *
+ * &lt;classpath id="run-time.classpath"&gt;
+ * &lt;path refid="compile.classpath"/&gt;
+ * &lt;pathelement path="${run-time.jars}"/&gt;
+ * &lt;/classpath&gt;
+ * </pre>
+ *
+ * <p> This task <i>requires</i> the following attributes:</p>
+ *
+ * <ul>
+ * <li><b>file</b>: The name of the file to load.</li>
+ * </ul>
+ *
+ * <p>This task supports the following attributes:</p>
+ *
+ * <ul>
+ * <li><b>prefix</b>: Optionally specify a prefix applied to
+ * all properties loaded. Defaults to an empty string.</li>
+ * <li><b>keepRoot</b>: Indicate whether the root xml element
+ * is kept as part of property name. Defaults to true.</li>
+ * <li><b>validate</b>: Indicate whether the xml file is validated.
+ * Defaults to false.</li>
+ * <li><b>collapseAttributes</b>: Indicate whether attributes are
+ * stored in property names with parens or with period
+ * delimiters. Defaults to false, meaning properties
+ * are stored with parens (i.e., foo(attr)).</li>
+ * <li><b>semanticAttributes</b>: Indicate whether attributes
+ * named "location", "value", "refid" and "path"
+ * are interpreted as ant properties. Defaults
+ * to false.</li>
+ * <li><b>rootDirectory</b>: Indicate the directory to use
+ * as the root directory for resolving location
+ * properties. Defaults to the directory
+ * of the project using the task.</li>
+ * <li><b>includeSemanticAttribute</b>: Indicate whether to include
+ * the semantic attribute ("location" or "value") as
+ * part of the property name. Defaults to false.</li>
+ * </ul>
+ *
+ * @ant.task name="xmlproperty" category="xml"
+ */
+public class XmlProperty extends org.apache.tools.ant.Task {
+
+ private Resource src;
+ private String prefix = "";
+ private boolean keepRoot = true;
+ private boolean validate = false;
+ private boolean collapseAttributes = false;
+ private boolean semanticAttributes = false;
+ private boolean includeSemanticAttribute = false;
+ private File rootDirectory = null;
+ private Hashtable addedAttributes = new Hashtable();
+ private XMLCatalog xmlCatalog = new XMLCatalog();
+ private String delimiter = ",";
+
+ private static final String ID = "id";
+ private static final String REF_ID = "refid";
+ private static final String LOCATION = "location";
+ private static final String VALUE = "value";
+ private static final String PATH = "path";
+ private static final String PATHID = "pathid";
+ private static final String[] ATTRIBUTES = new String[] {
+ ID, REF_ID, LOCATION, VALUE, PATH, PATHID
+ };
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Constructor.
+ */
+ public XmlProperty() {
+ super();
+ }
+
+ /**
+ * Initializes the task.
+ */
+
+ public void init() {
+ super.init();
+ xmlCatalog.setProject(getProject());
+ }
+
+ /**
+ * @return the xmlCatalog as the entityresolver.
+ */
+ protected EntityResolver getEntityResolver() {
+ return xmlCatalog;
+ }
+
+ /**
+ * Run the task.
+ * @throws BuildException The exception raised during task execution.
+ * @todo validate the source file is valid before opening, print a better error message
+ * @todo add a verbose level log message listing the name of the file being loaded
+ */
+ public void execute() throws BuildException {
+ Resource r = getResource();
+
+ if (r == null) {
+ throw new BuildException("XmlProperty task requires a source resource");
+ }
+ try {
+ log("Loading " + src, Project.MSG_VERBOSE);
+
+ if (r.isExists()) {
+
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setValidating(validate);
+ factory.setNamespaceAware(false);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ builder.setEntityResolver(getEntityResolver());
+ Document document = null;
+ FileProvider fp = src.as(FileProvider.class);
+ if (fp != null) {
+ document = builder.parse(fp.getFile());
+ } else {
+ document = builder.parse(src.getInputStream());
+ }
+ Element topElement = document.getDocumentElement();
+
+ // Keep a hashtable of attributes added by this task.
+ // This task is allow to override its own properties
+ // but not other properties. So we need to keep track
+ // of which properties we've added.
+ addedAttributes = new Hashtable();
+
+ if (keepRoot) {
+ addNodeRecursively(topElement, prefix, null);
+ } else {
+ NodeList topChildren = topElement.getChildNodes();
+ int numChildren = topChildren.getLength();
+ for (int i = 0; i < numChildren; i++) {
+ addNodeRecursively(topChildren.item(i), prefix, null);
+ }
+ }
+ } else {
+ log("Unable to find property resource: " + r, Project.MSG_VERBOSE);
+ }
+
+ } catch (SAXException sxe) {
+ // Error generated during parsing
+ Exception x = sxe;
+ if (sxe.getException() != null) {
+ x = sxe.getException();
+ }
+ throw new BuildException("Failed to load " + src, x);
+ } catch (ParserConfigurationException pce) {
+ // Parser with specified options can't be built
+ throw new BuildException(pce);
+ } catch (IOException ioe) {
+ // I/O error
+ throw new BuildException("Failed to load " + src, ioe);
+ }
+ }
+
+ /** Iterate through all nodes in the tree. */
+ private void addNodeRecursively(Node node, String prefix, Object container) {
+ // Set the prefix for this node to include its tag name.
+ String nodePrefix = prefix;
+ if (node.getNodeType() != Node.TEXT_NODE) {
+ if (prefix.trim().length() > 0) {
+ nodePrefix += ".";
+ }
+ nodePrefix += node.getNodeName();
+ }
+ // Pass the container to the processing of this node,
+ Object nodeObject = processNode(node, nodePrefix, container);
+
+ // now, iterate through children.
+ if (node.hasChildNodes()) {
+ NodeList nodeChildren = node.getChildNodes();
+ int numChildren = nodeChildren.getLength();
+
+ for (int i = 0; i < numChildren; i++) {
+ // For each child, pass the object added by
+ // processNode to its children -- in other word, each
+ // object can pass information along to its children.
+ addNodeRecursively(nodeChildren.item(i), nodePrefix, nodeObject);
+ }
+ }
+ }
+
+ void addNodeRecursively(org.w3c.dom.Node node, String prefix) {
+ addNodeRecursively(node, prefix, null);
+ }
+
+ /**
+ * Process the given node, adding any required attributes from
+ * this child node alone -- but <em>not</em> processing any
+ * children.
+ *
+ * @param node the XML Node to parse
+ * @param prefix A string to prepend to any properties that get
+ * added by this node.
+ * @param container Optionally, an object that a parent node
+ * generated that this node might belong to. For example, this
+ * node could be within a node that generated a Path.
+ * @return the Object created by this node. Generally, this is
+ * either a String if this node resulted in setting an attribute,
+ * or a Path.
+ */
+ public Object processNode (Node node, String prefix, Object container) {
+
+ // Parse the attribute(s) and text of this node, adding
+ // properties for each.
+ // if the "path" attribute is specified, then return the created path
+ // which will be passed to the children of this node.
+ Object addedPath = null;
+
+ // The value of an id attribute of this node.
+ String id = null;
+
+ if (node.hasAttributes()) {
+
+ NamedNodeMap nodeAttributes = node.getAttributes();
+
+ // Is there an id attribute?
+ Node idNode = nodeAttributes.getNamedItem(ID);
+ id = semanticAttributes && idNode != null ? idNode.getNodeValue() : null;
+
+ // Now, iterate through the attributes adding them.
+ for (int i = 0; i < nodeAttributes.getLength(); i++) {
+
+ Node attributeNode = nodeAttributes.item(i);
+
+ if (!semanticAttributes) {
+ String attributeName = getAttributeName(attributeNode);
+ String attributeValue = getAttributeValue(attributeNode);
+ addProperty(prefix + attributeName, attributeValue, null);
+ } else {
+ String nodeName = attributeNode.getNodeName();
+ String attributeValue = getAttributeValue(attributeNode);
+
+ Path containingPath =
+ ((container != null) && (container instanceof Path))
+ ? (Path) container
+ : null;
+ /*
+ * The main conditional logic -- if the attribute
+ * is somehow "special" (i.e., it has known
+ * semantic meaning) then deal with it
+ * appropriately.
+ */
+ if (nodeName.equals(ID)) {
+ // ID has already been found above.
+ continue;
+ }
+ if (containingPath != null && nodeName.equals(PATH)) {
+ // A "path" attribute for a node within a Path object.
+ containingPath.setPath(attributeValue);
+ } else if (container instanceof Path && nodeName.equals(REF_ID)) {
+ // A "refid" attribute for a node within a Path object.
+ containingPath.setPath(attributeValue);
+ } else if (container instanceof Path && nodeName.equals(LOCATION)) {
+ // A "location" attribute for a node within a
+ // Path object.
+ containingPath.setLocation(resolveFile(attributeValue));
+ } else if (nodeName.equals(PATHID)) {
+ // A node identifying a new path
+ if (container != null) {
+ throw new BuildException("XmlProperty does not support nested paths");
+ }
+ addedPath = new Path(getProject());
+ getProject().addReference(attributeValue, addedPath);
+ } else {
+ // An arbitrary attribute.
+ String attributeName = getAttributeName(attributeNode);
+ addProperty(prefix + attributeName, attributeValue, id);
+ }
+ }
+ }
+ }
+ String nodeText = null;
+ boolean emptyNode = false;
+ boolean semanticEmptyOverride = false;
+ if (node.getNodeType() == Node.ELEMENT_NODE
+ && semanticAttributes
+ && node.hasAttributes()
+ && (node.getAttributes().getNamedItem(VALUE) != null
+ || node.getAttributes().getNamedItem(LOCATION) != null
+ || node.getAttributes().getNamedItem(REF_ID) != null
+ || node.getAttributes().getNamedItem(PATH) != null || node.getAttributes()
+ .getNamedItem(PATHID) != null)) {
+ semanticEmptyOverride = true;
+ }
+ if (node.getNodeType() == Node.TEXT_NODE) {
+ // For the text node, add a property.
+ nodeText = getAttributeValue(node);
+ } else if (node.getNodeType() == Node.ELEMENT_NODE
+ && node.getChildNodes().getLength() == 1
+ && node.getFirstChild().getNodeType() == Node.CDATA_SECTION_NODE) {
+
+ nodeText = node.getFirstChild().getNodeValue();
+ if ("".equals(nodeText) && !semanticEmptyOverride) {
+ emptyNode = true;
+ }
+ } else if (node.getNodeType() == Node.ELEMENT_NODE
+ && node.getChildNodes().getLength() == 0
+ && !semanticEmptyOverride) {
+ nodeText = "";
+ emptyNode = true;
+ } else if (node.getNodeType() == Node.ELEMENT_NODE
+ && node.getChildNodes().getLength() == 1
+ && node.getFirstChild().getNodeType() == Node.TEXT_NODE
+ && "".equals(node.getFirstChild().getNodeValue())
+ && !semanticEmptyOverride) {
+ nodeText = "";
+ emptyNode = true;
+ }
+ if (nodeText != null) {
+ // If the containing object was a String, then use it as the ID.
+ if (semanticAttributes && id == null && container instanceof String) {
+ id = (String) container;
+ }
+ if (nodeText.trim().length() != 0 || emptyNode) {
+ addProperty(prefix, nodeText, id);
+ }
+ }
+ // Return the Path we added or the ID of this node for
+ // children to reference if needed. Path objects are
+ // definitely used by child path elements, and ID may be used
+ // for a child text node.
+ return (addedPath != null ? addedPath : id);
+ }
+
+ /**
+ * Actually add the given property/value to the project
+ * after writing a log message.
+ */
+ private void addProperty (String name, String value, String id) {
+ String msg = name + ":" + value;
+ if (id != null) {
+ msg += ("(id=" + id + ")");
+ }
+ log(msg, Project.MSG_DEBUG);
+
+ if (addedAttributes.containsKey(name)) {
+ // If this attribute was added by this task, then
+ // we append this value to the existing value.
+ // We use the setProperty method which will
+ // forcibly override the property if it already exists.
+ // We need to put these properties into the project
+ // when we read them, though (instead of keeping them
+ // outside of the project and batch adding them at the end)
+ // to allow other properties to reference them.
+ value = (String) addedAttributes.get(name) + getDelimiter() + value;
+ getProject().setProperty(name, value);
+ addedAttributes.put(name, value);
+ } else if (getProject().getProperty(name) == null) {
+ getProject().setNewProperty(name, value);
+ addedAttributes.put(name, value);
+ } else {
+ log("Override ignored for property " + name, Project.MSG_VERBOSE);
+ }
+ if (id != null) {
+ getProject().addReference(id, value);
+ }
+ }
+
+ /**
+ * Return a reasonable attribute name for the given node.
+ * If we are using semantic attributes or collapsing
+ * attributes, the returned name is ".nodename".
+ * Otherwise, we return "(nodename)". This is long-standing
+ * (and default) &lt;xmlproperty&gt; behavior.
+ */
+ private String getAttributeName (Node attributeNode) {
+ String attributeName = attributeNode.getNodeName();
+
+ if (semanticAttributes) {
+ // Never include the "refid" attribute as part of the
+ // attribute name.
+ if (attributeName.equals(REF_ID)) {
+ return "";
+ }
+ // Otherwise, return it appended unless property to hide it is set.
+ if (!isSemanticAttribute(attributeName) || includeSemanticAttribute) {
+ return "." + attributeName;
+ }
+ return "";
+ }
+ return collapseAttributes ? "." + attributeName : "(" + attributeName + ")";
+ }
+
+ /**
+ * Return whether the provided attribute name is recognized or not.
+ */
+ private static boolean isSemanticAttribute (String attributeName) {
+ for (int i = 0; i < ATTRIBUTES.length; i++) {
+ if (attributeName.equals(ATTRIBUTES[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return the value for the given attribute.
+ * If we are not using semantic attributes, its just the
+ * literal string value of the attribute.
+ *
+ * <p>If we <em>are</em> using semantic attributes, then first
+ * dependent properties are resolved (i.e., ${foo} is resolved
+ * based on the foo property value), and then an appropriate data
+ * type is used. In particular, location-based properties are
+ * resolved to absolute file names. Also for refid values, look
+ * up the referenced object from the project.</p>
+ */
+ private String getAttributeValue (Node attributeNode) {
+ String nodeValue = attributeNode.getNodeValue().trim();
+ if (semanticAttributes) {
+ String attributeName = attributeNode.getNodeName();
+ nodeValue = getProject().replaceProperties(nodeValue);
+ if (attributeName.equals(LOCATION)) {
+ File f = resolveFile(nodeValue);
+ return f.getPath();
+ }
+ if (attributeName.equals(REF_ID)) {
+ Object ref = getProject().getReference(nodeValue);
+ if (ref != null) {
+ return ref.toString();
+ }
+ }
+ }
+ return nodeValue;
+ }
+
+ /**
+ * The XML file to parse; required.
+ * @param src the file to parse
+ */
+ public void setFile(File src) {
+ setSrcResource(new FileResource(src));
+ }
+
+ /**
+ * The resource to pack; required.
+ * @param src resource to expand
+ */
+ public void setSrcResource(Resource src) {
+ if (src.isDirectory()) {
+ throw new BuildException("the source can't be a directory");
+ }
+ if (src.as(FileProvider.class) != null || supportsNonFileResources()) {
+ this.src = src;
+ } else {
+ throw new BuildException("Only FileSystem resources are supported.");
+ }
+ }
+
+ /**
+ * Set the source resource.
+ * @param a the resource to pack as a single element Resource collection.
+ */
+ public void addConfigured(ResourceCollection a) {
+ if (a.size() != 1) {
+ throw new BuildException(
+ "only single argument resource collections are supported as archives");
+ }
+ setSrcResource(a.iterator().next());
+ }
+
+ /**
+ * the prefix to prepend to each property
+ * @param prefix the prefix to prepend to each property
+ */
+ public void setPrefix(String prefix) {
+ this.prefix = prefix.trim();
+ }
+
+ /**
+ * flag to include the xml root tag as a
+ * first value in the property name; optional,
+ * default is true
+ * @param keepRoot if true (default), include the xml root tag
+ */
+ public void setKeeproot(boolean keepRoot) {
+ this.keepRoot = keepRoot;
+ }
+
+ /**
+ * flag to validate the XML file; optional, default false
+ * @param validate if true validate the XML file, default false
+ */
+ public void setValidate(boolean validate) {
+ this.validate = validate;
+ }
+
+ /**
+ * flag to treat attributes as nested elements;
+ * optional, default false
+ * @param collapseAttributes if true treat attributes as nested elements
+ */
+ public void setCollapseAttributes(boolean collapseAttributes) {
+ this.collapseAttributes = collapseAttributes;
+ }
+
+ /**
+ * Attribute to enable special handling of attributes - see ant manual.
+ * @param semanticAttributes if true enable the special handling.
+ */
+ public void setSemanticAttributes(boolean semanticAttributes) {
+ this.semanticAttributes = semanticAttributes;
+ }
+
+ /**
+ * The directory to use for resolving file references.
+ * Ignored if semanticAttributes is not set to true.
+ * @param rootDirectory the directory.
+ */
+ public void setRootDirectory(File rootDirectory) {
+ this.rootDirectory = rootDirectory;
+ }
+
+ /**
+ * Include the semantic attribute name as part of the property name.
+ * Ignored if semanticAttributes is not set to true.
+ * @param includeSemanticAttribute if true include the semantic attribute
+ * name.
+ */
+ public void setIncludeSemanticAttribute(boolean includeSemanticAttribute) {
+ this.includeSemanticAttribute = includeSemanticAttribute;
+ }
+
+ /**
+ * add an XMLCatalog as a nested element; optional.
+ * @param catalog the XMLCatalog to use
+ */
+ public void addConfiguredXMLCatalog(XMLCatalog catalog) {
+ xmlCatalog.addConfiguredXMLCatalog(catalog);
+ }
+
+ /* Expose members for extensibility */
+
+ /**
+ * @return the file attribute.
+ */
+ protected File getFile () {
+ FileProvider fp = src.as(FileProvider.class);
+ return fp != null ? fp.getFile() : null;
+ }
+
+ /**
+ * @return the resource.
+ */
+ protected Resource getResource() {
+ // delegate this way around to support subclasses that
+ // overwrite getFile
+ File f = getFile();
+ FileProvider fp = src.as(FileProvider.class);
+ return f == null ? src : fp != null
+ && fp.getFile().equals(f) ? src : new FileResource(f);
+ }
+
+ /**
+ * @return the prefix attribute.
+ */
+ protected String getPrefix () {
+ return this.prefix;
+ }
+
+ /**
+ * @return the keeproot attribute.
+ */
+ protected boolean getKeeproot () {
+ return this.keepRoot;
+ }
+
+ /**
+ * @return the validate attribute.
+ */
+ protected boolean getValidate () {
+ return this.validate;
+ }
+
+ /**
+ * @return the collapse attributes attribute.
+ */
+ protected boolean getCollapseAttributes () {
+ return this.collapseAttributes;
+ }
+
+ /**
+ * @return the semantic attributes attribute.
+ */
+ protected boolean getSemanticAttributes () {
+ return this.semanticAttributes;
+ }
+
+ /**
+ * @return the root directory attribute.
+ */
+ protected File getRootDirectory () {
+ return this.rootDirectory;
+ }
+
+ /**
+ * @return the include semantic attribute.
+ */
+ protected boolean getIncludeSementicAttribute () {
+ return this.includeSemanticAttribute;
+ }
+
+ /**
+ * Let project resolve the file - or do it ourselves if
+ * rootDirectory has been set.
+ */
+ private File resolveFile(String fileName) {
+ return FILE_UTILS.resolveFile(rootDirectory == null ? getProject().getBaseDir()
+ : rootDirectory, fileName);
+ }
+
+ /**
+ * Whether this task can deal with non-file resources.
+ *
+ * <p>This implementation returns true only if this task is
+ * &lt;xmlproperty&gt;. Any subclass of this class that also wants to
+ * support non-file resources needs to override this method. We
+ * need to do so for backwards compatibility reasons since we
+ * can't expect subclasses to support resources.</p>
+ * @return true for this task.
+ * @since Ant 1.7
+ */
+ protected boolean supportsNonFileResources() {
+ return getClass().equals(XmlProperty.class);
+ }
+
+ /**
+ * Get the current delimiter.
+ * @return delimiter
+ */
+ public String getDelimiter() {
+ return delimiter;
+ }
+
+ /**
+ * Sets a new delimiter.
+ * @param delimiter new value
+ * @since Ant 1.7.1
+ */
+ public void setDelimiter(String delimiter) {
+ this.delimiter = delimiter;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Zip.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Zip.java
new file mode 100644
index 00000000..ddc9bd49
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Zip.java
@@ -0,0 +1,2274 @@
+/*
+ * 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.tools.ant.taskdefs;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Stack;
+import java.util.Vector;
+import java.util.zip.CRC32;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.FileScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.ArchiveFileSet;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.ZipFileSet;
+import org.apache.tools.ant.types.ZipScanner;
+import org.apache.tools.ant.types.resources.ArchiveResource;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.types.resources.ZipResource;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.GlobPatternMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.ant.util.MergingMapper;
+import org.apache.tools.ant.util.ResourceUtils;
+import org.apache.tools.zip.UnixStat;
+import org.apache.tools.zip.Zip64Mode;
+import org.apache.tools.zip.ZipEntry;
+import org.apache.tools.zip.ZipExtraField;
+import org.apache.tools.zip.ZipFile;
+import org.apache.tools.zip.ZipOutputStream;
+import org.apache.tools.zip.ZipOutputStream.UnicodeExtraFieldPolicy;
+
+/**
+ * Create a Zip file.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ */
+public class Zip extends MatchingTask {
+ private static final int BUFFER_SIZE = 8 * 1024;
+ private static final int ROUNDUP_MILLIS = 1999; // 2 seconds - 1
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ protected File zipFile;
+ // use to scan own archive
+ private ZipScanner zs;
+ private File baseDir;
+ protected Hashtable<String, String> entries = new Hashtable<String, String>();
+ private final Vector<FileSet> groupfilesets = new Vector<FileSet>();
+ private final Vector<ZipFileSet> filesetsFromGroupfilesets = new Vector<ZipFileSet>();
+ protected String duplicate = "add";
+ private boolean doCompress = true;
+ private boolean doUpdate = false;
+ // shadow of the above if the value is altered in execute
+ private boolean savedDoUpdate = false;
+ private boolean doFilesonly = false;
+ protected String archiveType = "zip";
+
+ // For directories:
+ private static final long EMPTY_CRC = new CRC32 ().getValue ();
+ protected String emptyBehavior = "skip";
+ private final Vector<ResourceCollection> resources = new Vector<ResourceCollection>();
+ protected Hashtable<String, String> addedDirs = new Hashtable<String, String>();
+ private final Vector<String> addedFiles = new Vector<String>();
+
+ private static final ResourceSelector MISSING_SELECTOR =
+ new ResourceSelector() {
+ public boolean isSelected(final Resource target) {
+ return !target.isExists();
+ }
+ };
+
+ private static final ResourceUtils.ResourceSelectorProvider
+ MISSING_DIR_PROVIDER = new ResourceUtils.ResourceSelectorProvider() {
+ public ResourceSelector
+ getTargetSelectorForSource(final Resource sr) {
+ return MISSING_SELECTOR;
+ }
+ };
+
+ /**
+ * If this flag is true, execute() will run most operations twice,
+ * the first time with {@link #skipWriting skipWriting} set to
+ * true and the second time with setting it to false.
+ *
+ * <p>The only situation in Ant's current code base where this is
+ * ever going to be true is if the jar task has been configured
+ * with a filesetmanifest other than "skip".</p>
+ */
+ protected boolean doubleFilePass = false;
+ /**
+ * whether the methods should just perform some sort of dry-run.
+ *
+ * <p>Will only ever be true in the first pass if the task
+ * performs two passes because {@link #doubleFilePass
+ * doubleFilePass} is true.</p>
+ */
+ protected boolean skipWriting = false;
+
+ /**
+ * Whether this is the first time the archive building methods are invoked.
+ *
+ * @return true if either {@link #doubleFilePass doubleFilePass}
+ * is false or {@link #skipWriting skipWriting} is true.
+ *
+ * @since Ant 1.8.0
+ */
+ protected final boolean isFirstPass() {
+ return !doubleFilePass || skipWriting;
+ }
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ // CheckStyle:VisibilityModifier ON
+
+ // This boolean is set if the task detects that the
+ // target is outofdate and has written to the target file.
+ private boolean updatedFile = false;
+
+ /**
+ * true when we are adding new files into the Zip file, as opposed
+ * to adding back the unchanged files
+ */
+ private boolean addingNewFiles = false;
+
+ /**
+ * Encoding to use for filenames, defaults to the platform's
+ * default encoding.
+ */
+ private String encoding;
+
+ /**
+ * Whether the original compression of entries coming from a ZIP
+ * archive should be kept (for example when updating an archive).
+ *
+ * @since Ant 1.6
+ */
+ private boolean keepCompression = false;
+
+ /**
+ * Whether the file modification times will be rounded up to the
+ * next even number of seconds.
+ *
+ * @since Ant 1.6.2
+ */
+ private boolean roundUp = true;
+
+ /**
+ * Comment for the archive.
+ * @since Ant 1.6.3
+ */
+ private String comment = "";
+
+ private int level = ZipOutputStream.DEFAULT_COMPRESSION;
+
+ /**
+ * Assume 0 Unix mode is intentional.
+ * @since Ant 1.8.0
+ */
+ private boolean preserve0Permissions = false;
+
+ /**
+ * Whether to set the language encoding flag when creating the archive.
+ *
+ * @since Ant 1.8.0
+ */
+ private boolean useLanguageEncodingFlag = true;
+
+ /**
+ * Whether to add unicode extra fields.
+ *
+ * @since Ant 1.8.0
+ */
+ private UnicodeExtraField createUnicodeExtraFields =
+ UnicodeExtraField.NEVER;
+
+ /**
+ * Whether to fall back to UTF-8 if a name cannot be encoded using
+ * the specified encoding.
+ *
+ * @since Ant 1.8.0
+ */
+ private boolean fallBackToUTF8 = false;
+
+ /**
+ * Whether to enable Zip64 extensions.
+ *
+ * @since Ant 1.9.1
+ */
+ private Zip64ModeAttribute zip64Mode = Zip64ModeAttribute.AS_NEEDED;
+
+ /**
+ * This is the name/location of where to
+ * create the .zip file.
+ * @param zipFile the path of the zipFile
+ * @deprecated since 1.5.x.
+ * Use setDestFile(File) instead.
+ * @ant.attribute ignore="true"
+ */
+ @Deprecated
+ public void setZipfile(final File zipFile) {
+ setDestFile(zipFile);
+ }
+
+ /**
+ * This is the name/location of where to
+ * create the file.
+ * @param file the path of the zipFile
+ * @since Ant 1.5
+ * @deprecated since 1.5.x.
+ * Use setDestFile(File) instead.
+ * @ant.attribute ignore="true"
+ */
+ @Deprecated
+ public void setFile(final File file) {
+ setDestFile(file);
+ }
+
+
+ /**
+ * The file to create; required.
+ * @since Ant 1.5
+ * @param destFile The new destination File
+ */
+ public void setDestFile(final File destFile) {
+ this.zipFile = destFile;
+ }
+
+ /**
+ * The file to create.
+ * @return the destination file
+ * @since Ant 1.5.2
+ */
+ public File getDestFile() {
+ return zipFile;
+ }
+
+
+ /**
+ * Directory from which to archive files; optional.
+ * @param baseDir the base directory
+ */
+ public void setBasedir(final File baseDir) {
+ this.baseDir = baseDir;
+ }
+
+ /**
+ * Whether we want to compress the files or only store them;
+ * optional, default=true;
+ * @param c if true, compress the files
+ */
+ public void setCompress(final boolean c) {
+ doCompress = c;
+ }
+
+ /**
+ * Whether we want to compress the files or only store them;
+ * @return true if the files are to be compressed
+ * @since Ant 1.5.2
+ */
+ public boolean isCompress() {
+ return doCompress;
+ }
+
+ /**
+ * If true, emulate Sun's jar utility by not adding parent directories;
+ * optional, defaults to false.
+ * @param f if true, emulate sun's jar by not adding parent directories
+ */
+ public void setFilesonly(final boolean f) {
+ doFilesonly = f;
+ }
+
+ /**
+ * If true, updates an existing file, otherwise overwrite
+ * any existing one; optional defaults to false.
+ * @param c if true, updates an existing zip file
+ */
+ public void setUpdate(final boolean c) {
+ doUpdate = c;
+ savedDoUpdate = c;
+ }
+
+ /**
+ * Are we updating an existing archive?
+ * @return true if updating an existing archive
+ */
+ public boolean isInUpdateMode() {
+ return doUpdate;
+ }
+
+ /**
+ * Adds a set of files.
+ * @param set the fileset to add
+ */
+ public void addFileset(final FileSet set) {
+ add(set);
+ }
+
+ /**
+ * Adds a set of files that can be
+ * read from an archive and be given a prefix/fullpath.
+ * @param set the zipfileset to add
+ */
+ public void addZipfileset(final ZipFileSet set) {
+ add(set);
+ }
+
+ /**
+ * Add a collection of resources to be archived.
+ * @param a the resources to archive
+ * @since Ant 1.7
+ */
+ public void add(final ResourceCollection a) {
+ resources.add(a);
+ }
+
+ /**
+ * Adds a group of zip files.
+ * @param set the group (a fileset) to add
+ */
+ public void addZipGroupFileset(final FileSet set) {
+ groupfilesets.addElement(set);
+ }
+
+ /**
+ * Sets behavior for when a duplicate file is about to be added -
+ * one of <code>add</code>, <code>preserve</code> or <code>fail</code>.
+ * Possible values are: <code>add</code> (keep both
+ * of the files); <code>preserve</code> (keep the first version
+ * of the file found); <code>fail</code> halt a problem
+ * Default for zip tasks is <code>add</code>
+ * @param df a <code>Duplicate</code> enumerated value
+ */
+ public void setDuplicate(final Duplicate df) {
+ duplicate = df.getValue();
+ }
+
+ /**
+ * Possible behaviors when there are no matching files for the task:
+ * "fail", "skip", or "create".
+ */
+ public static class WhenEmpty extends EnumeratedAttribute {
+ /**
+ * The string values for the enumerated value
+ * @return the values
+ */
+ @Override
+ public String[] getValues() {
+ return new String[] {"fail", "skip", "create"};
+ }
+ }
+
+ /**
+ * Sets behavior of the task when no files match.
+ * Possible values are: <code>fail</code> (throw an exception
+ * and halt the build); <code>skip</code> (do not create
+ * any archive, but issue a warning); <code>create</code>
+ * (make an archive with no entries).
+ * Default for zip tasks is <code>skip</code>;
+ * for jar tasks, <code>create</code>.
+ * @param we a <code>WhenEmpty</code> enumerated value
+ */
+ public void setWhenempty(final WhenEmpty we) {
+ emptyBehavior = we.getValue();
+ }
+
+ /**
+ * Encoding to use for filenames, defaults to the platform's
+ * default encoding.
+ *
+ * <p>For a list of possible values see <a
+ * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>.</p>
+ * @param encoding the encoding name
+ */
+ public void setEncoding(final String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Encoding to use for filenames.
+ * @return the name of the encoding to use
+ * @since Ant 1.5.2
+ */
+ public String getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * Whether the original compression of entries coming from a ZIP
+ * archive should be kept (for example when updating an archive).
+ * Default is false.
+ * @param keep if true, keep the original compression
+ * @since Ant 1.6
+ */
+ public void setKeepCompression(final boolean keep) {
+ keepCompression = keep;
+ }
+
+ /**
+ * Comment to use for archive.
+ *
+ * @param comment The content of the comment.
+ * @since Ant 1.6.3
+ */
+ public void setComment(final String comment) {
+ this.comment = comment;
+ }
+
+ /**
+ * Comment of the archive
+ *
+ * @return Comment of the archive.
+ * @since Ant 1.6.3
+ */
+ public String getComment() {
+ return comment;
+ }
+
+ /**
+ * Set the compression level to use. Default is
+ * ZipOutputStream.DEFAULT_COMPRESSION.
+ * @param level compression level.
+ * @since Ant 1.7
+ */
+ public void setLevel(final int level) {
+ this.level = level;
+ }
+
+ /**
+ * Get the compression level.
+ * @return compression level.
+ * @since Ant 1.7
+ */
+ public int getLevel() {
+ return level;
+ }
+
+ /**
+ * Whether the file modification times will be rounded up to the
+ * next even number of seconds.
+ *
+ * <p>Zip archives store file modification times with a
+ * granularity of two seconds, so the times will either be rounded
+ * up or down. If you round down, the archive will always seem
+ * out-of-date when you rerun the task, so the default is to round
+ * up. Rounding up may lead to a different type of problems like
+ * JSPs inside a web archive that seem to be slightly more recent
+ * than precompiled pages, rendering precompilation useless.</p>
+ * @param r a <code>boolean</code> value
+ * @since Ant 1.6.2
+ */
+ public void setRoundUp(final boolean r) {
+ roundUp = r;
+ }
+
+ /**
+ * Assume 0 Unix mode is intentional.
+ * @since Ant 1.8.0
+ */
+ public void setPreserve0Permissions(final boolean b) {
+ preserve0Permissions = b;
+ }
+
+ /**
+ * Assume 0 Unix mode is intentional.
+ * @since Ant 1.8.0
+ */
+ public boolean getPreserve0Permissions() {
+ return preserve0Permissions;
+ }
+
+ /**
+ * Whether to set the language encoding flag.
+ * @since Ant 1.8.0
+ */
+ public void setUseLanguageEncodingFlag(final boolean b) {
+ useLanguageEncodingFlag = b;
+ }
+
+ /**
+ * Whether the language encoding flag will be used.
+ * @since Ant 1.8.0
+ */
+ public boolean getUseLanguageEnodingFlag() {
+ return useLanguageEncodingFlag;
+ }
+
+ /**
+ * Whether Unicode extra fields will be created.
+ * @since Ant 1.8.0
+ */
+ public void setCreateUnicodeExtraFields(final UnicodeExtraField b) {
+ createUnicodeExtraFields = b;
+ }
+
+ /**
+ * Whether Unicode extra fields will be created.
+ * @since Ant 1.8.0
+ */
+ public UnicodeExtraField getCreateUnicodeExtraFields() {
+ return createUnicodeExtraFields;
+ }
+
+ /**
+ * Whether to fall back to UTF-8 if a name cannot be encoded using
+ * the specified encoding.
+ *
+ * <p>Defaults to false.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public void setFallBackToUTF8(final boolean b) {
+ fallBackToUTF8 = b;
+ }
+
+ /**
+ * Whether to fall back to UTF-8 if a name cannot be encoded using
+ * the specified encoding.
+ *
+ * @since Ant 1.8.0
+ */
+ public boolean getFallBackToUTF8() {
+ return fallBackToUTF8;
+ }
+
+ /**
+ * Whether Zip64 extensions should be used.
+ * @since Ant 1.9.1
+ */
+ public void setZip64Mode(final Zip64ModeAttribute b) {
+ zip64Mode = b;
+ }
+
+ /**
+ * Whether Zip64 extensions will be used.
+ * @since Ant 1.9.1
+ */
+ public Zip64ModeAttribute getZip64Mode() {
+ return zip64Mode;
+ }
+
+ /**
+ * validate and build
+ * @throws BuildException on error
+ */
+ @Override
+ public void execute() throws BuildException {
+
+ if (doubleFilePass) {
+ skipWriting = true;
+ executeMain();
+ skipWriting = false;
+ executeMain();
+ } else {
+ executeMain();
+ }
+ }
+
+ /**
+ * Get the value of the updatedFile attribute.
+ * This should only be called after executeMain has been
+ * called.
+ * @return true if executeMain has written to the zip file.
+ */
+ protected boolean hasUpdatedFile() {
+ return updatedFile;
+ }
+
+ /**
+ * Build the zip file.
+ * This is called twice if doubleFilePass is true.
+ * @throws BuildException on error
+ */
+ public void executeMain() throws BuildException {
+
+ checkAttributesAndElements();
+
+ // Renamed version of original file, if it exists
+ File renamedFile = null;
+ addingNewFiles = true;
+
+ processDoUpdate();
+ processGroupFilesets();
+
+ // collect filesets to pass them to getResourcesToAdd
+ final Vector<ResourceCollection> vfss = new Vector<ResourceCollection>();
+ if (baseDir != null) {
+ final FileSet fs = (FileSet) getImplicitFileSet().clone();
+ fs.setDir(baseDir);
+ vfss.addElement(fs);
+ }
+ final int size = resources.size();
+ for (int i = 0; i < size; i++) {
+ final ResourceCollection rc = resources.elementAt(i);
+ vfss.addElement(rc);
+ }
+
+ final ResourceCollection[] fss = new ResourceCollection[vfss.size()];
+ vfss.copyInto(fss);
+ boolean success = false;
+ try {
+ // can also handle empty archives
+ final ArchiveState state = getResourcesToAdd(fss, zipFile, false);
+
+ // quick exit if the target is up to date
+ if (!state.isOutOfDate()) {
+ return;
+ }
+
+ final File parent = zipFile.getParentFile();
+ if (parent != null && !parent.isDirectory()
+ && !(parent.mkdirs() || parent.isDirectory())) {
+ throw new BuildException("Failed to create missing parent"
+ + " directory for " + zipFile);
+ }
+
+ updatedFile = true;
+ if (!zipFile.exists() && state.isWithoutAnyResources()) {
+ createEmptyZip(zipFile);
+ return;
+ }
+ final Resource[][] addThem = state.getResourcesToAdd();
+
+ if (doUpdate) {
+ renamedFile = renameFile();
+ }
+
+ final String action = doUpdate ? "Updating " : "Building ";
+
+ if (!skipWriting) {
+ log(action + archiveType + ": " + zipFile.getAbsolutePath());
+ }
+
+ ZipOutputStream zOut = null;
+ try {
+ if (!skipWriting) {
+ zOut = new ZipOutputStream(zipFile);
+
+ zOut.setEncoding(encoding);
+ zOut.setUseLanguageEncodingFlag(useLanguageEncodingFlag);
+ zOut.setCreateUnicodeExtraFields(createUnicodeExtraFields.
+ getPolicy());
+ zOut.setFallbackToUTF8(fallBackToUTF8);
+ zOut.setMethod(doCompress
+ ? ZipOutputStream.DEFLATED : ZipOutputStream.STORED);
+ zOut.setLevel(level);
+ zOut.setUseZip64(zip64Mode.getMode());
+ }
+ initZipOutputStream(zOut);
+
+ // Add the explicit resource collections to the archive.
+ for (int i = 0; i < fss.length; i++) {
+ if (addThem[i].length != 0) {
+ addResources(fss[i], addThem[i], zOut);
+ }
+ }
+
+ if (doUpdate) {
+ addingNewFiles = false;
+ final ZipFileSet oldFiles = new ZipFileSet();
+ oldFiles.setProject(getProject());
+ oldFiles.setSrc(renamedFile);
+ oldFiles.setDefaultexcludes(false);
+
+ final int addSize = addedFiles.size();
+ for (int i = 0; i < addSize; i++) {
+ final PatternSet.NameEntry ne = oldFiles.createExclude();
+ ne.setName(addedFiles.elementAt(i));
+ }
+ final DirectoryScanner ds =
+ oldFiles.getDirectoryScanner(getProject());
+ ((ZipScanner) ds).setEncoding(encoding);
+
+ final String[] f = ds.getIncludedFiles();
+ Resource[] r = new Resource[f.length];
+ for (int i = 0; i < f.length; i++) {
+ r[i] = ds.getResource(f[i]);
+ }
+
+ if (!doFilesonly) {
+ final String[] d = ds.getIncludedDirectories();
+ final Resource[] dr = new Resource[d.length];
+ for (int i = 0; i < d.length; i++) {
+ dr[i] = ds.getResource(d[i]);
+ }
+ final Resource[] tmp = r;
+ r = new Resource[tmp.length + dr.length];
+ System.arraycopy(dr, 0, r, 0, dr.length);
+ System.arraycopy(tmp, 0, r, dr.length, tmp.length);
+ }
+ addResources(oldFiles, r, zOut);
+ }
+ if (zOut != null) {
+ zOut.setComment(comment);
+ }
+ finalizeZipOutputStream(zOut);
+
+ // If we've been successful on an update, delete the
+ // temporary file
+ if (doUpdate) {
+ if (!renamedFile.delete()) {
+ log ("Warning: unable to delete temporary file "
+ + renamedFile.getName(), Project.MSG_WARN);
+ }
+ }
+ success = true;
+ } finally {
+ // Close the output stream.
+ closeZout(zOut, success);
+ }
+ } catch (final IOException ioe) {
+ String msg = "Problem creating " + archiveType + ": "
+ + ioe.getMessage();
+
+ // delete a bogus ZIP file (but only if it's not the original one)
+ if ((!doUpdate || renamedFile != null) && !zipFile.delete()) {
+ msg += " (and the archive is probably corrupt but I could not "
+ + "delete it)";
+ }
+
+ if (doUpdate && renamedFile != null) {
+ try {
+ FILE_UTILS.rename(renamedFile, zipFile);
+ } catch (final IOException e) {
+ msg += " (and I couldn't rename the temporary file "
+ + renamedFile.getName() + " back)";
+ }
+ }
+
+ throw new BuildException(msg, ioe, getLocation());
+ } finally {
+ cleanUp();
+ }
+ }
+
+ /** rename the zip file. */
+ private File renameFile() {
+ final File renamedFile = FILE_UTILS.createTempFile(
+ "zip", ".tmp", zipFile.getParentFile(), true, false);
+ try {
+ FILE_UTILS.rename(zipFile, renamedFile);
+ } catch (final SecurityException e) {
+ throw new BuildException(
+ "Not allowed to rename old file ("
+ + zipFile.getAbsolutePath()
+ + ") to temporary file");
+ } catch (final IOException e) {
+ throw new BuildException(
+ "Unable to rename old file ("
+ + zipFile.getAbsolutePath()
+ + ") to temporary file");
+ }
+ return renamedFile;
+ }
+
+ /** Close zout */
+ private void closeZout(final ZipOutputStream zOut, final boolean success)
+ throws IOException {
+ if (zOut == null) {
+ return;
+ }
+ try {
+ zOut.close();
+ } catch (final IOException ex) {
+ // If we're in this finally clause because of an
+ // exception, we don't really care if there's an
+ // exception when closing the stream. E.g. if it
+ // throws "ZIP file must have at least one entry",
+ // because an exception happened before we added
+ // any files, then we must swallow this
+ // exception. Otherwise, the error that's reported
+ // will be the close() error, which is not the
+ // real cause of the problem.
+ if (success) {
+ throw ex;
+ }
+ }
+ }
+
+ /** Check the attributes and elements */
+ private void checkAttributesAndElements() {
+ if (baseDir == null && resources.size() == 0
+ && groupfilesets.size() == 0 && "zip".equals(archiveType)) {
+ throw new BuildException("basedir attribute must be set, "
+ + "or at least one "
+ + "resource collection must be given!");
+ }
+
+ if (zipFile == null) {
+ throw new BuildException("You must specify the "
+ + archiveType + " file to create!");
+ }
+
+ if (zipFile.exists() && !zipFile.isFile()) {
+ throw new BuildException(zipFile + " is not a file.");
+ }
+
+ if (zipFile.exists() && !zipFile.canWrite()) {
+ throw new BuildException(zipFile + " is read-only.");
+ }
+ }
+
+ /** Process doupdate */
+ private void processDoUpdate() {
+ // Whether or not an actual update is required -
+ // we don't need to update if the original file doesn't exist
+ if (doUpdate && !zipFile.exists()) {
+ doUpdate = false;
+ logWhenWriting("ignoring update attribute as " + archiveType
+ + " doesn't exist.", Project.MSG_DEBUG);
+ }
+ }
+
+ /** Process groupfilesets */
+ private void processGroupFilesets() {
+ // Add the files found in groupfileset to fileset
+ final int size = groupfilesets.size();
+ for (int i = 0; i < size; i++) {
+
+ logWhenWriting("Processing groupfileset ", Project.MSG_VERBOSE);
+ final FileSet fs = groupfilesets.elementAt(i);
+ final FileScanner scanner = fs.getDirectoryScanner(getProject());
+ final String[] files = scanner.getIncludedFiles();
+ final File basedir = scanner.getBasedir();
+ for (int j = 0; j < files.length; j++) {
+
+ logWhenWriting("Adding file " + files[j] + " to fileset",
+ Project.MSG_VERBOSE);
+ final ZipFileSet zf = new ZipFileSet();
+ zf.setProject(getProject());
+ zf.setSrc(new File(basedir, files[j]));
+ add(zf);
+ filesetsFromGroupfilesets.addElement(zf);
+ }
+ }
+ }
+
+ /**
+ * Indicates if the task is adding new files into the archive as opposed to
+ * copying back unchanged files from the backup copy
+ * @return true if adding new files
+ */
+ protected final boolean isAddingNewFiles() {
+ return addingNewFiles;
+ }
+
+ /**
+ * Add the given resources.
+ *
+ * @param fileset may give additional information like fullpath or
+ * permissions.
+ * @param resources the resources to add
+ * @param zOut the stream to write to
+ * @throws IOException on error
+ *
+ * @since Ant 1.5.2
+ */
+ protected final void addResources(final FileSet fileset, final Resource[] resources,
+ final ZipOutputStream zOut)
+ throws IOException {
+
+ String prefix = "";
+ String fullpath = "";
+ int dirMode = ArchiveFileSet.DEFAULT_DIR_MODE;
+ int fileMode = ArchiveFileSet.DEFAULT_FILE_MODE;
+
+ ArchiveFileSet zfs = null;
+ if (fileset instanceof ArchiveFileSet) {
+ zfs = (ArchiveFileSet) fileset;
+ prefix = zfs.getPrefix(getProject());
+ fullpath = zfs.getFullpath(getProject());
+ dirMode = zfs.getDirMode(getProject());
+ fileMode = zfs.getFileMode(getProject());
+ }
+
+ if (prefix.length() > 0 && fullpath.length() > 0) {
+ throw new BuildException("Both prefix and fullpath attributes must"
+ + " not be set on the same fileset.");
+ }
+
+ if (resources.length != 1 && fullpath.length() > 0) {
+ throw new BuildException("fullpath attribute may only be specified"
+ + " for filesets that specify a single"
+ + " file.");
+ }
+
+ if (prefix.length() > 0) {
+ if (!prefix.endsWith("/") && !prefix.endsWith("\\")) {
+ prefix += "/";
+ }
+ addParentDirs(null, prefix, zOut, "", dirMode);
+ }
+
+ ZipFile zf = null;
+ try {
+ boolean dealingWithFiles = false;
+ File base = null;
+
+ if (zfs == null || zfs.getSrc(getProject()) == null) {
+ dealingWithFiles = true;
+ base = fileset.getDir(getProject());
+ } else if (zfs instanceof ZipFileSet) {
+ zf = new ZipFile(zfs.getSrc(getProject()), encoding);
+ }
+
+ for (int i = 0; i < resources.length; i++) {
+ String name = null;
+ if (fullpath.length() > 0) {
+ name = fullpath;
+ } else {
+ name = resources[i].getName();
+ }
+ name = name.replace(File.separatorChar, '/');
+
+ if ("".equals(name)) {
+ continue;
+ }
+
+ if (resources[i].isDirectory()) {
+ if (doFilesonly) {
+ continue;
+ }
+ final int thisDirMode = zfs != null && zfs.hasDirModeBeenSet()
+ ? dirMode : getUnixMode(resources[i], zf, dirMode);
+ addDirectoryResource(resources[i], name, prefix,
+ base, zOut,
+ dirMode, thisDirMode);
+
+ } else { // !isDirectory
+
+ addParentDirs(base, name, zOut, prefix, dirMode);
+
+ if (dealingWithFiles) {
+ final File f = FILE_UTILS.resolveFile(base,
+ resources[i].getName());
+ zipFile(f, zOut, prefix + name, fileMode);
+ } else {
+ final int thisFileMode =
+ zfs != null && zfs.hasFileModeBeenSet()
+ ? fileMode : getUnixMode(resources[i], zf,
+ fileMode);
+ addResource(resources[i], name, prefix,
+ zOut, thisFileMode, zf,
+ zfs == null
+ ? null : zfs.getSrc(getProject()));
+ }
+ }
+ }
+ } finally {
+ if (zf != null) {
+ zf.close();
+ }
+ }
+ }
+
+ /**
+ * Add a directory entry to the archive using a specified
+ * Unix-mode and the default mode for its parent directories (if
+ * necessary).
+ */
+ private void addDirectoryResource(final Resource r, String name, final String prefix,
+ final File base, final ZipOutputStream zOut,
+ final int defaultDirMode, final int thisDirMode)
+ throws IOException {
+
+ if (!name.endsWith("/")) {
+ name = name + "/";
+ }
+
+ final int nextToLastSlash = name.lastIndexOf("/", name.length() - 2);
+ if (nextToLastSlash != -1) {
+ addParentDirs(base, name.substring(0, nextToLastSlash + 1),
+ zOut, prefix, defaultDirMode);
+ }
+ zipDir(r, zOut, prefix + name, thisDirMode,
+ r instanceof ZipResource
+ ? ((ZipResource) r).getExtraFields() : null);
+ }
+
+ /**
+ * Determine a Resource's Unix mode or return the given default
+ * value if not available.
+ */
+ private int getUnixMode(final Resource r, final ZipFile zf, final int defaultMode)
+ throws IOException {
+
+ int unixMode = defaultMode;
+ if (zf != null) {
+ final ZipEntry ze = zf.getEntry(r.getName());
+ unixMode = ze.getUnixMode();
+ if ((unixMode == 0 || unixMode == UnixStat.DIR_FLAG)
+ && !preserve0Permissions) {
+ unixMode = defaultMode;
+ }
+ } else if (r instanceof ArchiveResource) {
+ unixMode = ((ArchiveResource) r).getMode();
+ }
+ return unixMode;
+ }
+
+ /**
+ * Add a file entry.
+ */
+ private void addResource(final Resource r, final String name, final String prefix,
+ final ZipOutputStream zOut, final int mode,
+ final ZipFile zf, final File fromArchive)
+ throws IOException {
+
+ if (zf != null) {
+ final ZipEntry ze = zf.getEntry(r.getName());
+
+ if (ze != null) {
+ final boolean oldCompress = doCompress;
+ if (keepCompression) {
+ doCompress = (ze.getMethod() == ZipEntry.DEFLATED);
+ }
+ InputStream is = null;
+ try {
+ is = zf.getInputStream(ze);
+ zipFile(is, zOut, prefix + name, ze.getTime(),
+ fromArchive, mode, ze.getExtraFields(true));
+ } finally {
+ doCompress = oldCompress;
+ FileUtils.close(is);
+ }
+ }
+ } else {
+ InputStream is = null;
+ try {
+ is = r.getInputStream();
+ zipFile(is, zOut, prefix + name, r.getLastModified(),
+ fromArchive, mode, r instanceof ZipResource
+ ? ((ZipResource) r).getExtraFields() : null);
+ } finally {
+ FileUtils.close(is);
+ }
+ }
+ }
+
+ /**
+ * Add the given resources.
+ *
+ * @param rc may give additional information like fullpath or
+ * permissions.
+ * @param resources the resources to add
+ * @param zOut the stream to write to
+ * @throws IOException on error
+ *
+ * @since Ant 1.7
+ */
+ protected final void addResources(final ResourceCollection rc,
+ final Resource[] resources,
+ final ZipOutputStream zOut)
+ throws IOException {
+ if (rc instanceof FileSet) {
+ addResources((FileSet) rc, resources, zOut);
+ return;
+ }
+ for (int i = 0; i < resources.length; i++) {
+ final Resource resource = resources[i];
+ String name = resource.getName();
+ if (name == null) {
+ continue;
+ }
+ name = name.replace(File.separatorChar, '/');
+
+ if ("".equals(name)) {
+ continue;
+ }
+ if (resource.isDirectory() && doFilesonly) {
+ continue;
+ }
+ File base = null;
+ final FileProvider fp = resource.as(FileProvider.class);
+ if (fp != null) {
+ base = ResourceUtils.asFileResource(fp).getBaseDir();
+ }
+
+ if (resource.isDirectory()) {
+ addDirectoryResource(resource, name, "", base, zOut,
+ ArchiveFileSet.DEFAULT_DIR_MODE,
+ ArchiveFileSet.DEFAULT_DIR_MODE);
+
+ } else {
+ addParentDirs(base, name, zOut, "",
+ ArchiveFileSet.DEFAULT_DIR_MODE);
+
+ if (fp != null) {
+ final File f = (fp).getFile();
+ zipFile(f, zOut, name, ArchiveFileSet.DEFAULT_FILE_MODE);
+ } else {
+ addResource(resource, name, "", zOut,
+ ArchiveFileSet.DEFAULT_FILE_MODE,
+ null, null);
+ }
+ }
+ }
+ }
+
+ /**
+ * method for subclasses to override
+ * @param zOut the zip output stream
+ * @throws IOException on output error
+ * @throws BuildException on other errors
+ */
+ protected void initZipOutputStream(final ZipOutputStream zOut)
+ throws IOException, BuildException {
+ }
+
+ /**
+ * method for subclasses to override
+ * @param zOut the zip output stream
+ * @throws IOException on output error
+ * @throws BuildException on other errors
+ */
+ protected void finalizeZipOutputStream(final ZipOutputStream zOut)
+ throws IOException, BuildException {
+ }
+
+ /**
+ * Create an empty zip file
+ * @param zipFile the zip file
+ * @return true for historic reasons
+ * @throws BuildException on error
+ */
+ protected boolean createEmptyZip(final File zipFile) throws BuildException {
+ // In this case using java.util.zip will not work
+ // because it does not permit a zero-entry archive.
+ // Must create it manually.
+ if (!skipWriting) {
+ log("Note: creating empty " + archiveType + " archive " + zipFile,
+ Project.MSG_INFO);
+ }
+ OutputStream os = null;
+ try {
+ os = new FileOutputStream(zipFile);
+ // CheckStyle:MagicNumber OFF
+ // Cf. PKZIP specification.
+ final byte[] empty = new byte[22];
+ empty[0] = 80; // P
+ empty[1] = 75; // K
+ empty[2] = 5;
+ empty[3] = 6;
+ // remainder zeros
+ // CheckStyle:MagicNumber ON
+ os.write(empty);
+ } catch (final IOException ioe) {
+ throw new BuildException("Could not create empty ZIP archive "
+ + "(" + ioe.getMessage() + ")", ioe,
+ getLocation());
+ } finally {
+ FileUtils.close(os);
+ }
+ return true;
+ }
+
+ /**
+ * @since Ant 1.5.2
+ */
+ private synchronized ZipScanner getZipScanner() {
+ if (zs == null) {
+ zs = new ZipScanner();
+ zs.setEncoding(encoding);
+ zs.setSrc(zipFile);
+ }
+ return zs;
+ }
+
+ /**
+ * Collect the resources that are newer than the corresponding
+ * entries (or missing) in the original archive.
+ *
+ * <p>If we are going to recreate the archive instead of updating
+ * it, all resources should be considered as new, if a single one
+ * is. Because of this, subclasses overriding this method must
+ * call <code>super.getResourcesToAdd</code> and indicate with the
+ * third arg if they already know that the archive is
+ * out-of-date.</p>
+ *
+ * <p>This method first delegates to getNonFileSetResourcesToAdd
+ * and then invokes the FileSet-arg version. All this to keep
+ * backwards compatibility for subclasses that don't know how to
+ * deal with non-FileSet ResourceCollections.</p>
+ *
+ * @param rcs The resource collections to grab resources from
+ * @param zipFile intended archive file (may or may not exist)
+ * @param needsUpdate whether we already know that the archive is
+ * out-of-date. Subclasses overriding this method are supposed to
+ * set this value correctly in their call to
+ * <code>super.getResourcesToAdd</code>.
+ * @return an array of resources to add for each fileset passed in as well
+ * as a flag that indicates whether the archive is uptodate.
+ *
+ * @exception BuildException if it likes
+ * @since Ant 1.7
+ */
+ protected ArchiveState getResourcesToAdd(final ResourceCollection[] rcs,
+ final File zipFile,
+ final boolean needsUpdate)
+ throws BuildException {
+ final ArrayList<ResourceCollection> filesets = new ArrayList<ResourceCollection>();
+ final ArrayList<ResourceCollection> rest = new ArrayList<ResourceCollection>();
+ for (int i = 0; i < rcs.length; i++) {
+ if (rcs[i] instanceof FileSet) {
+ filesets.add(rcs[i]);
+ } else {
+ rest.add(rcs[i]);
+ }
+ }
+ final ResourceCollection[] rc =
+ rest.toArray(new ResourceCollection[rest.size()]);
+ ArchiveState as = getNonFileSetResourcesToAdd(rc, zipFile,
+ needsUpdate);
+
+ final FileSet[] fs = filesets.toArray(new FileSet[filesets
+ .size()]);
+ final ArchiveState as2 = getResourcesToAdd(fs, zipFile, as.isOutOfDate());
+ if (!as.isOutOfDate() && as2.isOutOfDate()) {
+ /*
+ * Bad luck.
+ *
+ * There are resources in the filesets that make the
+ * archive out of date, but not in the non-fileset
+ * resources. We need to rescan the non-FileSets to grab
+ * all of them now.
+ */
+ as = getNonFileSetResourcesToAdd(rc, zipFile, true);
+ }
+
+ final Resource[][] toAdd = new Resource[rcs.length][];
+ int fsIndex = 0;
+ int restIndex = 0;
+ for (int i = 0; i < rcs.length; i++) {
+ if (rcs[i] instanceof FileSet) {
+ toAdd[i] = as2.getResourcesToAdd()[fsIndex++];
+ } else {
+ toAdd[i] = as.getResourcesToAdd()[restIndex++];
+ }
+ }
+ return new ArchiveState(as2.isOutOfDate(), toAdd);
+ }
+
+ /*
+ * This is yet another hacky construct to extend the FileSet[]
+ * getResourcesToAdd method so we can pass the information whether
+ * non-fileset resources have been available to it without having
+ * to move the withEmpty behavior checks (since either would break
+ * subclasses in several ways).
+ */
+ private static final ThreadLocal<Boolean> HAVE_NON_FILE_SET_RESOURCES_TO_ADD = new ThreadLocal<Boolean>() {
+ @Override
+ protected Boolean initialValue() {
+ return Boolean.FALSE;
+ }
+ };
+
+ /**
+ * Collect the resources that are newer than the corresponding
+ * entries (or missing) in the original archive.
+ *
+ * <p>If we are going to recreate the archive instead of updating
+ * it, all resources should be considered as new, if a single one
+ * is. Because of this, subclasses overriding this method must
+ * call <code>super.getResourcesToAdd</code> and indicate with the
+ * third arg if they already know that the archive is
+ * out-of-date.</p>
+ *
+ * @param filesets The filesets to grab resources from
+ * @param zipFile intended archive file (may or may not exist)
+ * @param needsUpdate whether we already know that the archive is
+ * out-of-date. Subclasses overriding this method are supposed to
+ * set this value correctly in their call to
+ * <code>super.getResourcesToAdd</code>.
+ * @return an array of resources to add for each fileset passed in as well
+ * as a flag that indicates whether the archive is uptodate.
+ *
+ * @exception BuildException if it likes
+ */
+ protected ArchiveState getResourcesToAdd(final FileSet[] filesets,
+ final File zipFile,
+ boolean needsUpdate)
+ throws BuildException {
+
+ final Resource[][] initialResources = grabResources(filesets);
+ if (isEmpty(initialResources)) {
+ if (Boolean.FALSE.equals(HAVE_NON_FILE_SET_RESOURCES_TO_ADD.get())) {
+ if (needsUpdate && doUpdate) {
+ /*
+ * This is a rather hairy case.
+ *
+ * One of our subclasses knows that we need to
+ * update the archive, but at the same time, there
+ * are no resources known to us that would need to
+ * be added. Only the subclass seems to know
+ * what's going on.
+ *
+ * This happens if <jar> detects that the manifest
+ * has changed, for example. The manifest is not
+ * part of any resources because of our support
+ * for inline <manifest>s.
+ *
+ * If we invoke createEmptyZip like Ant 1.5.2 did,
+ * we'll loose all stuff that has been in the
+ * original archive (bugzilla report 17780).
+ */
+ return new ArchiveState(true, initialResources);
+ }
+
+ if (emptyBehavior.equals("skip")) {
+ if (doUpdate) {
+ logWhenWriting(archiveType + " archive " + zipFile
+ + " not updated because no new files were"
+ + " included.", Project.MSG_VERBOSE);
+ } else {
+ logWhenWriting("Warning: skipping " + archiveType
+ + " archive " + zipFile
+ + " because no files were included.",
+ Project.MSG_WARN);
+ }
+ } else if (emptyBehavior.equals("fail")) {
+ throw new BuildException("Cannot create " + archiveType
+ + " archive " + zipFile
+ + ": no files were included.",
+ getLocation());
+ } else {
+ // Create.
+ if (!zipFile.exists()) {
+ needsUpdate = true;
+ }
+ }
+ }
+
+ // either there are non-fileset resources or we
+ // (re-)create the archive anyway
+ return new ArchiveState(needsUpdate, initialResources);
+ }
+
+ // initialResources is not empty
+
+ if (!zipFile.exists()) {
+ return new ArchiveState(true, initialResources);
+ }
+
+ if (needsUpdate && !doUpdate) {
+ // we are recreating the archive, need all resources
+ return new ArchiveState(true, initialResources);
+ }
+
+ final Resource[][] newerResources = new Resource[filesets.length][];
+
+ for (int i = 0; i < filesets.length; i++) {
+ if (!(fileset instanceof ZipFileSet)
+ || ((ZipFileSet) fileset).getSrc(getProject()) == null) {
+ final File base = filesets[i].getDir(getProject());
+
+ for (int j = 0; j < initialResources[i].length; j++) {
+ final File resourceAsFile =
+ FILE_UTILS.resolveFile(base,
+ initialResources[i][j].getName());
+ if (resourceAsFile.equals(zipFile)) {
+ throw new BuildException("A zip file cannot include "
+ + "itself", getLocation());
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < filesets.length; i++) {
+ if (initialResources[i].length == 0) {
+ newerResources[i] = new Resource[] {};
+ continue;
+ }
+
+ FileNameMapper myMapper = new IdentityMapper();
+ if (filesets[i] instanceof ZipFileSet) {
+ final ZipFileSet zfs = (ZipFileSet) filesets[i];
+ if (zfs.getFullpath(getProject()) != null
+ && !zfs.getFullpath(getProject()).equals("")) {
+ // in this case all files from origin map to
+ // the fullPath attribute of the zipfileset at
+ // destination
+ final MergingMapper fm = new MergingMapper();
+ fm.setTo(zfs.getFullpath(getProject()));
+ myMapper = fm;
+
+ } else if (zfs.getPrefix(getProject()) != null
+ && !zfs.getPrefix(getProject()).equals("")) {
+ final GlobPatternMapper gm = new GlobPatternMapper();
+ gm.setFrom("*");
+ String prefix = zfs.getPrefix(getProject());
+ if (!prefix.endsWith("/") && !prefix.endsWith("\\")) {
+ prefix += "/";
+ }
+ gm.setTo(prefix + "*");
+ myMapper = gm;
+ }
+ }
+
+ newerResources[i] = selectOutOfDateResources(initialResources[i],
+ myMapper);
+ needsUpdate = needsUpdate || (newerResources[i].length > 0);
+
+ if (needsUpdate && !doUpdate) {
+ // we will return initialResources anyway, no reason
+ // to scan further.
+ break;
+ }
+ }
+
+ if (needsUpdate && !doUpdate) {
+ // we are recreating the archive, need all resources
+ return new ArchiveState(true, initialResources);
+ }
+
+ return new ArchiveState(needsUpdate, newerResources);
+ }
+
+ /**
+ * Collect the resources that are newer than the corresponding
+ * entries (or missing) in the original archive.
+ *
+ * <p>If we are going to recreate the archive instead of updating
+ * it, all resources should be considered as new, if a single one
+ * is. Because of this, subclasses overriding this method must
+ * call <code>super.getResourcesToAdd</code> and indicate with the
+ * third arg if they already know that the archive is
+ * out-of-date.</p>
+ *
+ * @param rcs The filesets to grab resources from
+ * @param zipFile intended archive file (may or may not exist)
+ * @param needsUpdate whether we already know that the archive is
+ * out-of-date. Subclasses overriding this method are supposed to
+ * set this value correctly in their call to
+ * <code>super.getResourcesToAdd</code>.
+ * @return an array of resources to add for each fileset passed in as well
+ * as a flag that indicates whether the archive is uptodate.
+ *
+ * @exception BuildException if it likes
+ */
+ protected ArchiveState getNonFileSetResourcesToAdd(final ResourceCollection[] rcs,
+ final File zipFile,
+ boolean needsUpdate)
+ throws BuildException {
+ /*
+ * Backwards compatibility forces us to repeat the logic of
+ * getResourcesToAdd(FileSet[], ...) here once again.
+ */
+
+ final Resource[][] initialResources = grabNonFileSetResources(rcs);
+ final boolean empty = isEmpty(initialResources);
+ HAVE_NON_FILE_SET_RESOURCES_TO_ADD.set(Boolean.valueOf(!empty));
+ if (empty) {
+ // no emptyBehavior handling since the FileSet version
+ // will take care of it.
+ return new ArchiveState(needsUpdate, initialResources);
+ }
+
+ // initialResources is not empty
+
+ if (!zipFile.exists()) {
+ return new ArchiveState(true, initialResources);
+ }
+
+ if (needsUpdate && !doUpdate) {
+ // we are recreating the archive, need all resources
+ return new ArchiveState(true, initialResources);
+ }
+
+ final Resource[][] newerResources = new Resource[rcs.length][];
+
+ for (int i = 0; i < rcs.length; i++) {
+ if (initialResources[i].length == 0) {
+ newerResources[i] = new Resource[] {};
+ continue;
+ }
+
+ for (int j = 0; j < initialResources[i].length; j++) {
+ final FileProvider fp =
+ initialResources[i][j].as(FileProvider.class);
+ if (fp != null && zipFile.equals(fp.getFile())) {
+ throw new BuildException("A zip file cannot include "
+ + "itself", getLocation());
+ }
+ }
+
+ newerResources[i] = selectOutOfDateResources(initialResources[i],
+ new IdentityMapper());
+ needsUpdate = needsUpdate || (newerResources[i].length > 0);
+
+ if (needsUpdate && !doUpdate) {
+ // we will return initialResources anyway, no reason
+ // to scan further.
+ break;
+ }
+ }
+
+ if (needsUpdate && !doUpdate) {
+ // we are recreating the archive, need all resources
+ return new ArchiveState(true, initialResources);
+ }
+
+ return new ArchiveState(needsUpdate, newerResources);
+ }
+
+ private Resource[] selectOutOfDateResources(final Resource[] initial,
+ final FileNameMapper mapper) {
+ final Resource[] rs = selectFileResources(initial);
+ Resource[] result =
+ ResourceUtils.selectOutOfDateSources(this, rs, mapper,
+ getZipScanner());
+ if (!doFilesonly) {
+ final Union u = new Union();
+ u.addAll(Arrays.asList(selectDirectoryResources(initial)));
+ final ResourceCollection rc =
+ ResourceUtils.selectSources(this, u, mapper,
+ getZipScanner(),
+ MISSING_DIR_PROVIDER);
+ if (rc.size() > 0) {
+ final ArrayList<Resource> newer = new ArrayList<Resource>();
+ newer.addAll(Arrays.asList(((Union) rc).listResources()));
+ newer.addAll(Arrays.asList(result));
+ result = newer.toArray(result);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Fetch all included and not excluded resources from the sets.
+ *
+ * <p>Included directories will precede included files.</p>
+ * @param filesets an array of filesets
+ * @return the resources included
+ * @since Ant 1.5.2
+ */
+ protected Resource[][] grabResources(final FileSet[] filesets) {
+ final Resource[][] result = new Resource[filesets.length][];
+ for (int i = 0; i < filesets.length; i++) {
+ boolean skipEmptyNames = true;
+ if (filesets[i] instanceof ZipFileSet) {
+ final ZipFileSet zfs = (ZipFileSet) filesets[i];
+ skipEmptyNames = zfs.getPrefix(getProject()).equals("")
+ && zfs.getFullpath(getProject()).equals("");
+ }
+ final DirectoryScanner rs =
+ filesets[i].getDirectoryScanner(getProject());
+ if (rs instanceof ZipScanner) {
+ ((ZipScanner) rs).setEncoding(encoding);
+ }
+ final Vector<Resource> resources = new Vector<Resource>();
+ if (!doFilesonly) {
+ final String[] directories = rs.getIncludedDirectories();
+ for (int j = 0; j < directories.length; j++) {
+ if (!"".equals(directories[j]) || !skipEmptyNames) {
+ resources.addElement(rs.getResource(directories[j]));
+ }
+ }
+ }
+ final String[] files = rs.getIncludedFiles();
+ for (int j = 0; j < files.length; j++) {
+ if (!"".equals(files[j]) || !skipEmptyNames) {
+ resources.addElement(rs.getResource(files[j]));
+ }
+ }
+
+ result[i] = new Resource[resources.size()];
+ resources.copyInto(result[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Fetch all included and not excluded resources from the collections.
+ *
+ * <p>Included directories will precede included files.</p>
+ * @param rcs an array of resource collections
+ * @return the resources included
+ * @since Ant 1.7
+ */
+ protected Resource[][] grabNonFileSetResources(final ResourceCollection[] rcs) {
+ final Resource[][] result = new Resource[rcs.length][];
+ for (int i = 0; i < rcs.length; i++) {
+ final ArrayList<Resource> dirs = new ArrayList<Resource>();
+ final ArrayList<Resource> files = new ArrayList<Resource>();
+ for (final Resource r : rcs[i]) {
+ if (r.isExists()) {
+ if (r.isDirectory()) {
+ dirs.add(r);
+ } else {
+ files.add(r);
+ }
+ }
+ }
+ // make sure directories are in alpha-order - this also
+ // ensures parents come before their children
+ Collections.sort(dirs, new Comparator<Resource>() {
+ public int compare(final Resource r1, final Resource r2) {
+ return r1.getName().compareTo(r2.getName());
+ }
+ });
+ final ArrayList<Resource> rs = new ArrayList<Resource>(dirs);
+ rs.addAll(files);
+ result[i] = rs.toArray(new Resource[rs.size()]);
+ }
+ return result;
+ }
+
+ /**
+ * Add a directory to the zip stream.
+ * @param dir the directort to add to the archive
+ * @param zOut the stream to write to
+ * @param vPath the name this entry shall have in the archive
+ * @param mode the Unix permissions to set.
+ * @throws IOException on error
+ * @since Ant 1.5.2
+ */
+ protected void zipDir(final File dir, final ZipOutputStream zOut, final String vPath,
+ final int mode)
+ throws IOException {
+ zipDir(dir, zOut, vPath, mode, null);
+ }
+
+ /**
+ * Add a directory to the zip stream.
+ * @param dir the directory to add to the archive
+ * @param zOut the stream to write to
+ * @param vPath the name this entry shall have in the archive
+ * @param mode the Unix permissions to set.
+ * @param extra ZipExtraFields to add
+ * @throws IOException on error
+ * @since Ant 1.6.3
+ */
+ protected void zipDir(final File dir, final ZipOutputStream zOut, final String vPath,
+ final int mode, final ZipExtraField[] extra)
+ throws IOException {
+ zipDir(dir == null ? (Resource) null : new FileResource(dir),
+ zOut, vPath, mode, extra);
+ }
+
+ /**
+ * Add a directory to the zip stream.
+ * @param dir the directory to add to the archive
+ * @param zOut the stream to write to
+ * @param vPath the name this entry shall have in the archive
+ * @param mode the Unix permissions to set.
+ * @param extra ZipExtraFields to add
+ * @throws IOException on error
+ * @since Ant 1.8.0
+ */
+ protected void zipDir(final Resource dir, final ZipOutputStream zOut, final String vPath,
+ final int mode, final ZipExtraField[] extra)
+ throws IOException {
+ if (doFilesonly) {
+ logWhenWriting("skipping directory " + vPath
+ + " for file-only archive",
+ Project.MSG_VERBOSE);
+ return;
+ }
+ if (addedDirs.get(vPath) != null) {
+ // don't add directories we've already added.
+ // no warning if we try, it is harmless in and of itself
+ return;
+ }
+
+ logWhenWriting("adding directory " + vPath, Project.MSG_VERBOSE);
+ addedDirs.put(vPath, vPath);
+
+ if (!skipWriting) {
+ final ZipEntry ze = new ZipEntry (vPath);
+
+ // ZIPs store time with a granularity of 2 seconds, round up
+ final int millisToAdd = roundUp ? ROUNDUP_MILLIS : 0;
+
+ if (dir != null && dir.isExists()) {
+ ze.setTime(dir.getLastModified() + millisToAdd);
+ } else {
+ ze.setTime(System.currentTimeMillis() + millisToAdd);
+ }
+ ze.setSize (0);
+ ze.setMethod (ZipEntry.STORED);
+ // This is faintly ridiculous:
+ ze.setCrc (EMPTY_CRC);
+ ze.setUnixMode(mode);
+
+ if (extra != null) {
+ ze.setExtraFields(extra);
+ }
+
+ zOut.putNextEntry(ze);
+ }
+ }
+
+ /*
+ * This is a hacky construct to extend the zipFile method to
+ * support a new parameter (extra fields to preserve) without
+ * breaking subclasses that override the old method signature.
+ */
+ private static final ThreadLocal<ZipExtraField[]> CURRENT_ZIP_EXTRA = new ThreadLocal<ZipExtraField[]>();
+
+ /**
+ * Provides the extra fields for the zip entry currently being
+ * added to the archive - if any.
+ * @since Ant 1.8.0
+ */
+ protected final ZipExtraField[] getCurrentExtraFields() {
+ return CURRENT_ZIP_EXTRA.get();
+ }
+
+ /**
+ * Sets the extra fields for the zip entry currently being
+ * added to the archive - if any.
+ * @since Ant 1.8.0
+ */
+ protected final void setCurrentExtraFields(final ZipExtraField[] extra) {
+ CURRENT_ZIP_EXTRA.set(extra);
+ }
+
+ /**
+ * Adds a new entry to the archive, takes care of duplicates as well.
+ *
+ * @param in the stream to read data for the entry from. The
+ * caller of the method is responsible for closing the stream.
+ * @param zOut the stream to write to.
+ * @param vPath the name this entry shall have in the archive.
+ * @param lastModified last modification time for the entry.
+ * @param fromArchive the original archive we are copying this
+ * entry from, will be null if we are not copying from an archive.
+ * @param mode the Unix permissions to set.
+ *
+ * @since Ant 1.5.2
+ * @throws IOException on error
+ */
+ protected void zipFile(InputStream in, final ZipOutputStream zOut, final String vPath,
+ final long lastModified, final File fromArchive, final int mode)
+ throws IOException {
+ // fromArchive is used in subclasses overriding this method
+
+ if (entries.containsKey(vPath)) {
+
+ if (duplicate.equals("preserve")) {
+ logWhenWriting(vPath + " already added, skipping",
+ Project.MSG_INFO);
+ return;
+ } else if (duplicate.equals("fail")) {
+ throw new BuildException("Duplicate file " + vPath
+ + " was found and the duplicate "
+ + "attribute is 'fail'.");
+ } else {
+ // duplicate equal to add, so we continue
+ logWhenWriting("duplicate file " + vPath
+ + " found, adding.", Project.MSG_VERBOSE);
+ }
+ } else {
+ logWhenWriting("adding entry " + vPath, Project.MSG_VERBOSE);
+ }
+
+ entries.put(vPath, vPath);
+
+ if (!skipWriting) {
+ final ZipEntry ze = new ZipEntry(vPath);
+ ze.setTime(lastModified);
+ ze.setMethod(doCompress ? ZipEntry.DEFLATED : ZipEntry.STORED);
+
+ /*
+ * ZipOutputStream.putNextEntry expects the ZipEntry to
+ * know its size and the CRC sum before you start writing
+ * the data when using STORED mode - unless it is seekable.
+ *
+ * This forces us to process the data twice.
+ */
+ if (!zOut.isSeekable() && !doCompress) {
+ long size = 0;
+ final CRC32 cal = new CRC32();
+ if (!in.markSupported()) {
+ // Store data into a byte[]
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+ final byte[] buffer = new byte[BUFFER_SIZE];
+ int count = 0;
+ do {
+ size += count;
+ cal.update(buffer, 0, count);
+ bos.write(buffer, 0, count);
+ count = in.read(buffer, 0, buffer.length);
+ } while (count != -1);
+ in = new ByteArrayInputStream(bos.toByteArray());
+
+ } else {
+ in.mark(Integer.MAX_VALUE);
+ final byte[] buffer = new byte[BUFFER_SIZE];
+ int count = 0;
+ do {
+ size += count;
+ cal.update(buffer, 0, count);
+ count = in.read(buffer, 0, buffer.length);
+ } while (count != -1);
+ in.reset();
+ }
+ ze.setSize(size);
+ ze.setCrc(cal.getValue());
+ }
+
+ ze.setUnixMode(mode);
+ final ZipExtraField[] extra = getCurrentExtraFields();
+ if (extra != null) {
+ ze.setExtraFields(extra);
+ }
+
+ zOut.putNextEntry(ze);
+
+ final byte[] buffer = new byte[BUFFER_SIZE];
+ int count = 0;
+ do {
+ if (count != 0) {
+ zOut.write(buffer, 0, count);
+ }
+ count = in.read(buffer, 0, buffer.length);
+ } while (count != -1);
+ }
+ addedFiles.addElement(vPath);
+ }
+
+ /**
+ * Adds a new entry to the archive, takes care of duplicates as well.
+ *
+ * @param in the stream to read data for the entry from. The
+ * caller of the method is responsible for closing the stream.
+ * @param zOut the stream to write to.
+ * @param vPath the name this entry shall have in the archive.
+ * @param lastModified last modification time for the entry.
+ * @param fromArchive the original archive we are copying this
+ * entry from, will be null if we are not copying from an archive.
+ * @param mode the Unix permissions to set.
+ * @param extra ZipExtraFields to add
+ *
+ * @since Ant 1.8.0
+ * @throws IOException on error
+ */
+ protected final void zipFile(final InputStream in, final ZipOutputStream zOut,
+ final String vPath, final long lastModified,
+ final File fromArchive, final int mode,
+ final ZipExtraField[] extra)
+ throws IOException {
+ try {
+ setCurrentExtraFields(extra);
+ zipFile(in, zOut, vPath, lastModified, fromArchive, mode);
+ } finally {
+ setCurrentExtraFields(null);
+ }
+ }
+
+ /**
+ * Method that gets called when adding from <code>java.io.File</code> instances.
+ *
+ * <p>This implementation delegates to the six-arg version.</p>
+ *
+ * @param file the file to add to the archive
+ * @param zOut the stream to write to
+ * @param vPath the name this entry shall have in the archive
+ * @param mode the Unix permissions to set.
+ * @throws IOException on error
+ *
+ * @since Ant 1.5.2
+ */
+ protected void zipFile(final File file, final ZipOutputStream zOut, final String vPath,
+ final int mode)
+ throws IOException {
+ if (file.equals(zipFile)) {
+ throw new BuildException("A zip file cannot include itself",
+ getLocation());
+ }
+
+ final FileInputStream fIn = new FileInputStream(file);
+ try {
+ // ZIPs store time with a granularity of 2 seconds, round up
+ zipFile(fIn, zOut, vPath,
+ file.lastModified() + (roundUp ? ROUNDUP_MILLIS : 0),
+ null, mode);
+ } finally {
+ fIn.close();
+ }
+ }
+
+ /**
+ * Ensure all parent dirs of a given entry have been added.
+ * @param baseDir the base directory to use (may be null)
+ * @param entry the entry name to create directories from
+ * @param zOut the stream to write to
+ * @param prefix a prefix to place on the created entries
+ * @param dirMode the directory mode
+ * @throws IOException on error
+ * @since Ant 1.5.2
+ */
+ protected final void addParentDirs(final File baseDir, final String entry,
+ final ZipOutputStream zOut, final String prefix,
+ final int dirMode)
+ throws IOException {
+ if (!doFilesonly) {
+ final Stack<String> directories = new Stack<String>();
+ int slashPos = entry.length();
+
+ while ((slashPos = entry.lastIndexOf('/', slashPos - 1)) != -1) {
+ final String dir = entry.substring(0, slashPos + 1);
+ if (addedDirs.get(prefix + dir) != null) {
+ break;
+ }
+ directories.push(dir);
+ }
+
+ while (!directories.isEmpty()) {
+ final String dir = directories.pop();
+ File f = null;
+ if (baseDir != null) {
+ f = new File(baseDir, dir);
+ } else {
+ f = new File(dir);
+ }
+ zipDir(f, zOut, prefix + dir, dirMode);
+ }
+ }
+ }
+
+ /**
+ * Do any clean up necessary to allow this instance to be used again.
+ *
+ * <p>When we get here, the Zip file has been closed and all we
+ * need to do is to reset some globals.</p>
+ *
+ * <p>This method will only reset globals that have been changed
+ * during execute(), it will not alter the attributes or nested
+ * child elements. If you want to reset the instance so that you
+ * can later zip a completely different set of files, you must use
+ * the reset method.</p>
+ *
+ * @see #reset
+ */
+ protected void cleanUp() {
+ addedDirs.clear();
+ addedFiles.removeAllElements();
+ entries.clear();
+ addingNewFiles = false;
+ doUpdate = savedDoUpdate;
+ final Enumeration<ZipFileSet> e = filesetsFromGroupfilesets.elements();
+ while (e.hasMoreElements()) {
+ final ZipFileSet zf = e.nextElement();
+ resources.removeElement(zf);
+ }
+ filesetsFromGroupfilesets.removeAllElements();
+ HAVE_NON_FILE_SET_RESOURCES_TO_ADD.set(Boolean.FALSE);
+ }
+
+ /**
+ * Makes this instance reset all attributes to their default
+ * values and forget all children.
+ *
+ * @since Ant 1.5
+ *
+ * @see #cleanUp
+ */
+ public void reset() {
+ resources.removeAllElements();
+ zipFile = null;
+ baseDir = null;
+ groupfilesets.removeAllElements();
+ duplicate = "add";
+ archiveType = "zip";
+ doCompress = true;
+ emptyBehavior = "skip";
+ doUpdate = false;
+ doFilesonly = false;
+ encoding = null;
+ }
+
+ /**
+ * Check is the resource arrays are empty.
+ * @param r the arrays to check
+ * @return true if all individual arrays are empty
+ *
+ * @since Ant 1.5.2
+ */
+ protected static final boolean isEmpty(final Resource[][] r) {
+ for (int i = 0; i < r.length; i++) {
+ if (r[i].length > 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Drops all non-file resources from the given array.
+ * @param orig the resources to filter
+ * @return the filters resources
+ * @since Ant 1.6
+ */
+ protected Resource[] selectFileResources(final Resource[] orig) {
+ return selectResources(orig,
+ new ResourceSelector() {
+ public boolean isSelected(final Resource r) {
+ if (!r.isDirectory()) {
+ return true;
+ } else if (doFilesonly) {
+ logWhenWriting("Ignoring directory "
+ + r.getName()
+ + " as only files will"
+ + " be added.",
+ Project.MSG_VERBOSE);
+ }
+ return false;
+ }
+ });
+ }
+
+ /**
+ * Drops all non-directory resources from the given array.
+ * @param orig the resources to filter
+ * @return the filters resources
+ * @since Ant 1.8.0
+ */
+ protected Resource[] selectDirectoryResources(final Resource[] orig) {
+ return selectResources(orig,
+ new ResourceSelector() {
+ public boolean isSelected(final Resource r) {
+ return r.isDirectory();
+ }
+ });
+ }
+
+ /**
+ * Drops all resources from the given array that are not selected
+ * @param orig the resources to filter
+ * @return the filters resources
+ * @since Ant 1.8.0
+ */
+ protected Resource[] selectResources(final Resource[] orig,
+ final ResourceSelector selector) {
+ if (orig.length == 0) {
+ return orig;
+ }
+
+ final ArrayList<Resource> v = new ArrayList<Resource>(orig.length);
+ for (int i = 0; i < orig.length; i++) {
+ if (selector.isSelected(orig[i])) {
+ v.add(orig[i]);
+ }
+ }
+
+ if (v.size() != orig.length) {
+ return v.toArray(new Resource[v.size()]);
+ }
+ return orig;
+ }
+
+ /**
+ * Logs a message at the given output level, but only if this is
+ * the pass that will actually create the archive.
+ *
+ * @since Ant 1.8.0
+ */
+ protected void logWhenWriting(final String msg, final int level) {
+ if (!skipWriting) {
+ log(msg, level);
+ }
+ }
+
+ /**
+ * Possible behaviors when a duplicate file is added:
+ * "add", "preserve" or "fail"
+ */
+ public static class Duplicate extends EnumeratedAttribute {
+ /**
+ * @see EnumeratedAttribute#getValues()
+ */
+ /** {@inheritDoc} */
+ @Override
+ public String[] getValues() {
+ return new String[] {"add", "preserve", "fail"};
+ }
+ }
+
+ /**
+ * Holds the up-to-date status and the out-of-date resources of
+ * the original archive.
+ *
+ * @since Ant 1.5.3
+ */
+ public static class ArchiveState {
+ private final boolean outOfDate;
+ private final Resource[][] resourcesToAdd;
+
+ ArchiveState(final boolean state, final Resource[][] r) {
+ outOfDate = state;
+ resourcesToAdd = r;
+ }
+
+ /**
+ * Return the outofdate status.
+ * @return the outofdate status
+ */
+ public boolean isOutOfDate() {
+ return outOfDate;
+ }
+
+ /**
+ * Get the resources to add.
+ * @return the resources to add
+ */
+ public Resource[][] getResourcesToAdd() {
+ return resourcesToAdd;
+ }
+ /**
+ * find out if there are absolutely no resources to add
+ * @since Ant 1.6.3
+ * @return true if there are no resources to add
+ */
+ public boolean isWithoutAnyResources() {
+ if (resourcesToAdd == null) {
+ return true;
+ }
+ for (int counter = 0; counter < resourcesToAdd.length; counter++) {
+ if (resourcesToAdd[counter] != null) {
+ if (resourcesToAdd[counter].length > 0) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Policiy for creation of Unicode extra fields: never, always or
+ * not-encodeable.
+ *
+ * @since Ant 1.8.0
+ */
+ public static final class UnicodeExtraField extends EnumeratedAttribute {
+ private static final Map<String, UnicodeExtraFieldPolicy> POLICIES = new HashMap<String, UnicodeExtraFieldPolicy>();
+ private static final String NEVER_KEY = "never";
+ private static final String ALWAYS_KEY = "always";
+ private static final String N_E_KEY = "not-encodeable";
+ static {
+ POLICIES.put(NEVER_KEY,
+ ZipOutputStream.UnicodeExtraFieldPolicy.NEVER);
+ POLICIES.put(ALWAYS_KEY,
+ ZipOutputStream.UnicodeExtraFieldPolicy.ALWAYS);
+ POLICIES.put(N_E_KEY,
+ ZipOutputStream.UnicodeExtraFieldPolicy
+ .NOT_ENCODEABLE);
+ }
+
+ @Override
+ public String[] getValues() {
+ return new String[] {NEVER_KEY, ALWAYS_KEY, N_E_KEY};
+ }
+
+ public static final UnicodeExtraField NEVER =
+ new UnicodeExtraField(NEVER_KEY);
+
+ private UnicodeExtraField(final String name) {
+ setValue(name);
+ }
+
+ public UnicodeExtraField() {
+ }
+
+ public ZipOutputStream.UnicodeExtraFieldPolicy getPolicy() {
+ return POLICIES.get(getValue());
+ }
+ }
+
+
+ /**
+ * The choices for Zip64 extensions.
+ *
+ * <p><b>never</b>: never add any Zip64 extensions. This will
+ * cause the task to fail if you try to add entries bigger than
+ * 4GB or create an archive bigger than 4GB or holding more that
+ * 65535 entries.</p>
+ *
+ * <p><b>as-needed</b>: create Zip64 extensions only when the
+ * entry's size is bigger than 4GB or one of the archive limits is
+ * hit. This mode also adds partial Zip64 extensions for all
+ * deflated entries written by Ant.</p>
+ *
+ * <p><b>always</b>: create Zip64 extensions for all entries.</p>
+ *
+ * <p><b>Note</b> some ZIP implementations don't handle Zip64
+ * extensions well and others may fail if the Zip64 extra field
+ * data is only present inside the local file header but not the
+ * central directory - which is what <em>as-needed</em> may result
+ * in. Java5 and Microsoft Visual Studio's Extension loader are
+ * known to fconsider the archive broken in such cases. If you
+ * are targeting such an archiver uset the value <em>never</em>
+ * unless you know you need Zip64 extensions.</p>
+ *
+ * @since Ant 1.9.1
+ */
+ public static final class Zip64ModeAttribute extends EnumeratedAttribute {
+ private static final Map<String, Zip64Mode> MODES = new HashMap<String, Zip64Mode>();
+
+ private static final String NEVER_KEY = "never";
+ private static final String ALWAYS_KEY = "always";
+ private static final String A_N_KEY = "as-needed";
+ static {
+ MODES.put(NEVER_KEY, Zip64Mode.Never);
+ MODES.put(ALWAYS_KEY, Zip64Mode.Always);
+ MODES.put(A_N_KEY, Zip64Mode.AsNeeded);
+ }
+
+ @Override
+ public String[] getValues() {
+ return new String[] {NEVER_KEY, ALWAYS_KEY, A_N_KEY};
+ }
+
+ public static final Zip64ModeAttribute NEVER =
+ new Zip64ModeAttribute(NEVER_KEY);
+ public static final Zip64ModeAttribute AS_NEEDED =
+ new Zip64ModeAttribute(A_N_KEY);
+
+ private Zip64ModeAttribute(final String name) {
+ setValue(name);
+ }
+
+ public Zip64ModeAttribute() {
+ }
+
+ public Zip64Mode getMode() {
+ return MODES.get(getValue());
+ }
+
+ }
+ }
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/AptCompilerAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/AptCompilerAdapter.java
new file mode 100644
index 00000000..9503ac02
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/AptCompilerAdapter.java
@@ -0,0 +1,187 @@
+/*
+ * 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.tools.ant.taskdefs.compilers;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Apt;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+
+/**
+ * <p>The implementation of the apt compiler for JDK 1.5.</p>
+ *
+ * <p>As usual, the low level entry points for Java tools are neither documented or
+ * stable; this entry point may change from that of 1.5.0_01-b08 without any
+ * warning at all. The IDE decompile of the tool entry points is as follows:</p>
+ * <pre>
+ * public class Main {
+ * public Main() ;
+ *
+ * public static transient void main(String... strings);
+ *
+ * public static transient int process(String... strings);
+ *
+ * public static transient int process(PrintWriter printWriter,
+ * String... strings);
+ * public static transient int process(
+ * AnnotationProcessorFactory annotationProcessorFactory,
+ * String... strings);
+ *
+ * public static transient int process(
+ * AnnotationProcessorFactory annotationProcessorFactory,
+ * PrintWriter printWriter,
+ * String... strings);
+ * private static transient int processing(
+ * AnnotationProcessorFactory annotationProcessorFactory,
+ * PrintWriter printWriter,
+ * String... strings) ;
+ * }
+ * </pre>
+ *
+ * This Adapter is designed to run Apt in-JVM, an option that is not actually
+ * exposed to end-users, because it was too brittle during beta testing; classpath
+ * problems being the core issue.
+ *
+ * @since Ant 1.7
+ */
+public class AptCompilerAdapter extends DefaultCompilerAdapter {
+
+ /**
+ * Integer returned by the Apt compiler to indicate success.
+ */
+ private static final int APT_COMPILER_SUCCESS = 0;
+ /**
+ * class in tools.jar that implements APT
+ */
+ public static final String APT_ENTRY_POINT = "com.sun.tools.apt.Main";
+
+ /**
+ * method used to compile.
+ */
+ public static final String APT_METHOD_NAME = "process";
+
+ /**
+ * Get the facade task that fronts this adapter
+ *
+ * @return task instance
+ * @see DefaultCompilerAdapter#getJavac()
+ */
+ protected Apt getApt() {
+ return (Apt) getJavac();
+ }
+
+ /**
+ * Using the front end arguments, set up the command line to run Apt
+ *
+ * @param apt task
+ * @param cmd command that is set up with the various switches from the task
+ * options
+ */
+ static void setAptCommandlineSwitches(final Apt apt, final Commandline cmd) {
+
+ if (!apt.isCompile()) {
+ cmd.createArgument().setValue("-nocompile");
+ }
+
+ // Process the factory class
+ final String factory = apt.getFactory();
+ if (factory != null) {
+ cmd.createArgument().setValue("-factory");
+ cmd.createArgument().setValue(factory);
+ }
+
+ // Process the factory path
+ final Path factoryPath = apt.getFactoryPath();
+ if (factoryPath != null) {
+ cmd.createArgument().setValue("-factorypath");
+ cmd.createArgument().setPath(factoryPath);
+ }
+
+ final File preprocessDir = apt.getPreprocessDir();
+ if (preprocessDir != null) {
+ cmd.createArgument().setValue("-s");
+ cmd.createArgument().setFile(preprocessDir);
+ }
+
+ // Process the processor options
+ final Vector options = apt.getOptions();
+ final Enumeration elements = options.elements();
+ Apt.Option opt;
+ StringBuffer arg = null;
+ while (elements.hasMoreElements()) {
+ opt = (Apt.Option) elements.nextElement();
+ arg = new StringBuffer();
+ arg.append("-A").append(opt.getName());
+ if (opt.getValue() != null) {
+ arg.append("=").append(opt.getValue());
+ }
+ cmd.createArgument().setValue(arg.toString());
+ }
+ }
+
+ /**
+ * using our front end task, set up the command line switches
+ *
+ * @param cmd command line to set up
+ */
+ protected void setAptCommandlineSwitches(final Commandline cmd) {
+ final Apt apt = getApt();
+ setAptCommandlineSwitches(apt, cmd);
+ }
+
+ /**
+ * Run the compilation.
+ * @return true on success.
+ * @throws BuildException if the compilation has problems.
+ */
+ public boolean execute() throws BuildException {
+ attributes.log("Using apt compiler", Project.MSG_VERBOSE);
+ //set up the javac options
+ final Commandline cmd = setupModernJavacCommand();
+ //then add the Apt options
+ setAptCommandlineSwitches(cmd);
+
+ //finally invoke APT
+ // Use reflection to be able to build on all JDKs:
+ try {
+ final Class c = Class.forName(APT_ENTRY_POINT);
+ final Object compiler = c.newInstance();
+ final Method compile = c.getMethod(APT_METHOD_NAME,
+ new Class[]{(new String[]{}).getClass()});
+ final int result = ((Integer) compile.invoke
+ (compiler, new Object[]{cmd.getArguments()}))
+ .intValue();
+ return (result == APT_COMPILER_SUCCESS);
+ } catch (final BuildException be) {
+ //rethrow build exceptions
+ throw be;
+ } catch (final Exception ex) {
+ //cast everything else to a build exception
+ throw new BuildException("Error starting apt compiler",
+ ex, location);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/AptExternalCompilerAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/AptExternalCompilerAdapter.java
new file mode 100644
index 00000000..dadb55ba
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/AptExternalCompilerAdapter.java
@@ -0,0 +1,71 @@
+/*
+ * 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.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Apt;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * The implementation of the apt compiler for JDK 1.5 using an external process
+ *
+ * @since Ant 1.7
+ */
+public class AptExternalCompilerAdapter extends DefaultCompilerAdapter {
+
+
+ /**
+ * Get the facade task that fronts this adapter
+ *
+ * @return task instance
+ * @see DefaultCompilerAdapter#getJavac()
+ */
+ protected Apt getApt() {
+ return (Apt) getJavac();
+ }
+
+ /**
+ * Performs a compile using the Javac externally.
+ * @return true the compilation was successful.
+ * @throws BuildException if there is a problem.
+ */
+ public boolean execute() throws BuildException {
+ attributes.log("Using external apt compiler", Project.MSG_VERBOSE);
+
+
+ // Setup the apt executable
+ Apt apt = getApt();
+ Commandline cmd = new Commandline();
+ cmd.setExecutable(apt.getAptExecutable());
+ setupModernJavacCommandlineSwitches(cmd);
+ AptCompilerAdapter.setAptCommandlineSwitches(apt, cmd);
+ int firstFileName = cmd.size();
+ //add the files
+ logAndAddFilesToCompile(cmd);
+
+ //run
+ return 0 == executeExternalCompile(cmd.getCommandline(),
+ firstFileName,
+ true);
+
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapter.java
new file mode 100644
index 00000000..5a275b8c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapter.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.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Javac;
+
+/**
+ * The interface that all compiler adapters must adhere to.
+ *
+ * <p>A compiler adapter is an adapter that interprets the javac's
+ * parameters in preparation to be passed off to the compiler this
+ * adapter represents. As all the necessary values are stored in the
+ * Javac task itself, the only thing all adapters need is the javac
+ * task, the execute command and a parameterless constructor (for
+ * reflection).</p>
+ *
+ * @since Ant 1.3
+ */
+
+public interface CompilerAdapter {
+
+ /**
+ * Sets the compiler attributes, which are stored in the Javac task.
+ * @param attributes the compiler attributes
+ */
+ void setJavac(Javac attributes);
+
+ /**
+ * Executes the task.
+ *
+ * @return has the compilation been successful
+ * @throws BuildException on error
+ */
+ boolean execute() throws BuildException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterExtension.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterExtension.java
new file mode 100644
index 00000000..038b9cbe
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterExtension.java
@@ -0,0 +1,40 @@
+/*
+ * 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.tools.ant.taskdefs.compilers;
+
+/**
+ * Extension interface for compilers that support source extensions
+ * other than .java.
+ *
+ * @since Ant 1.8.2
+ */
+public interface CompilerAdapterExtension {
+
+ /**
+ * Returns a list of source file extensions that are recognized by
+ * this compiler adapter.
+ *
+ * <p>For example, most compiler adapters will return [ "java" ],
+ * but a compiler adapter that can compile both Java and Groovy
+ * source code would return [ "java", "groovy" ].</p>
+ *
+ * @return list of source file extensions recognized by this
+ * compiler adapter.
+ */
+ String[] getSupportedFileExtensions();
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterFactory.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterFactory.java
new file mode 100644
index 00000000..aeecb4a5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterFactory.java
@@ -0,0 +1,202 @@
+/*
+ * 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.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.ClasspathUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Creates the necessary compiler adapter, given basic criteria.
+ *
+ * @since Ant 1.3
+ */
+public final class CompilerAdapterFactory {
+ private static final String MODERN_COMPILER = "com.sun.tools.javac.Main";
+
+ /** This is a singleton -- can't create instances!! */
+ private CompilerAdapterFactory() {
+ }
+
+ /**
+ * Based on the parameter passed in, this method creates the necessary
+ * factory desired.
+ *
+ * The current mapping for compiler names are as follows:
+ * <ul><li>jikes = jikes compiler
+ * <li>classic, javac1.1, javac1.2 = the standard compiler from JDK
+ * 1.1/1.2
+ * <li>modern, javac1.3, javac1.4, javac1.5 = the compiler of JDK 1.3+
+ * <li>jvc, microsoft = the command line compiler from Microsoft's SDK
+ * for Java / Visual J++
+ * <li>kjc = the kopi compiler</li>
+ * <li>gcj = the gcj compiler from gcc</li>
+ * <li>sj, symantec = the Symantec Java compiler</li>
+ * <li><i>a fully qualified classname</i> = the name of a compiler
+ * adapter
+ * </ul>
+ *
+ * @param compilerType either the name of the desired compiler, or the
+ * full classname of the compiler's adapter.
+ * @param task a task to log through.
+ * @return the compiler adapter
+ * @throws BuildException if the compiler type could not be resolved into
+ * a compiler adapter.
+ */
+ public static CompilerAdapter getCompiler(String compilerType, Task task)
+ throws BuildException {
+ return getCompiler(compilerType, task, null);
+ }
+
+ /**
+ * Based on the parameter passed in, this method creates the necessary
+ * factory desired.
+ *
+ * The current mapping for compiler names are as follows:
+ * <ul><li>jikes = jikes compiler
+ * <li>classic, javac1.1, javac1.2 = the standard compiler from JDK
+ * 1.1/1.2
+ * <li>modern, javac1.3, javac1.4, javac1.5 = the compiler of JDK 1.3+
+ * <li>jvc, microsoft = the command line compiler from Microsoft's SDK
+ * for Java / Visual J++
+ * <li>kjc = the kopi compiler</li>
+ * <li>gcj = the gcj compiler from gcc</li>
+ * <li>sj, symantec = the Symantec Java compiler</li>
+ * <li><i>a fully qualified classname</i> = the name of a compiler
+ * adapter
+ * </ul>
+ *
+ * @param compilerType either the name of the desired compiler, or the
+ * full classname of the compiler's adapter.
+ * @param task a task to log through.
+ * @param classpath the classpath to use when looking up an
+ * adapter class
+ * @return the compiler adapter
+ * @throws BuildException if the compiler type could not be resolved into
+ * a compiler adapter.
+ * @since Ant 1.8.0
+ */
+ public static CompilerAdapter getCompiler(String compilerType, Task task,
+ Path classpath)
+ throws BuildException {
+ if (compilerType.equalsIgnoreCase("jikes")) {
+ return new Jikes();
+ }
+ if (compilerType.equalsIgnoreCase("extjavac")) {
+ return new JavacExternal();
+ }
+ if (compilerType.equalsIgnoreCase("classic")
+ || compilerType.equalsIgnoreCase("javac1.1")
+ || compilerType.equalsIgnoreCase("javac1.2")) {
+ task.log("This version of java does "
+ + "not support the classic "
+ + "compiler; upgrading to modern",
+ Project.MSG_WARN);
+ compilerType = "modern";
+ }
+ //on java<=1.3 the modern falls back to classic if it is not found
+ //but on java>=1.4 we just bail out early
+ if (compilerType.equalsIgnoreCase("modern")
+ || compilerType.equalsIgnoreCase("javac1.3")
+ || compilerType.equalsIgnoreCase("javac1.4")
+ || compilerType.equalsIgnoreCase("javac1.5")
+ || compilerType.equalsIgnoreCase("javac1.6")
+ || compilerType.equalsIgnoreCase("javac1.7")
+ || compilerType.equalsIgnoreCase("javac1.8")
+ || compilerType.equalsIgnoreCase("javac1.9")) {
+ // does the modern compiler exist?
+ if (doesModernCompilerExist()) {
+ return new Javac13();
+ } else {
+ throw new BuildException("Unable to find a javac "
+ + "compiler;\n"
+ + MODERN_COMPILER
+ + " is not on the "
+ + "classpath.\n"
+ + "Perhaps JAVA_HOME does not"
+ + " point to the JDK.\n"
+ + "It is currently set to \""
+ + JavaEnvUtils.getJavaHome()
+ + "\"");
+ }
+ }
+
+ if (compilerType.equalsIgnoreCase("jvc")
+ || compilerType.equalsIgnoreCase("microsoft")) {
+ return new Jvc();
+ }
+ if (compilerType.equalsIgnoreCase("kjc")) {
+ return new Kjc();
+ }
+ if (compilerType.equalsIgnoreCase("gcj")) {
+ return new Gcj();
+ }
+ if (compilerType.equalsIgnoreCase("sj")
+ || compilerType.equalsIgnoreCase("symantec")) {
+ return new Sj();
+ }
+ return resolveClassName(compilerType,
+ // Memory-Leak in line below
+ task.getProject().createClassLoader(classpath));
+ }
+
+ /**
+ * query for the Modern compiler existing
+ * @return true if classic os on the classpath
+ */
+ private static boolean doesModernCompilerExist() {
+ try {
+ Class.forName(MODERN_COMPILER);
+ return true;
+ } catch (ClassNotFoundException cnfe) {
+ try {
+ ClassLoader cl = CompilerAdapterFactory.class.getClassLoader();
+ if (cl != null) {
+ cl.loadClass(MODERN_COMPILER);
+ return true;
+ }
+ } catch (ClassNotFoundException cnfe2) {
+ // Ignore Exception
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tries to resolve the given classname into a compiler adapter.
+ * Throws a fit if it can't.
+ *
+ * @param className The fully qualified classname to be created.
+ * @param loader the classloader to use
+ * @throws BuildException This is the fit that is thrown if className
+ * isn't an instance of CompilerAdapter.
+ */
+ private static CompilerAdapter resolveClassName(String className,
+ ClassLoader loader)
+ throws BuildException {
+ return (CompilerAdapter) ClasspathUtils.newInstance(className,
+ loader != null ? loader :
+ CompilerAdapterFactory.class.getClassLoader(),
+ CompilerAdapter.class);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapter.java
new file mode 100644
index 00000000..519163cc
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapter.java
@@ -0,0 +1,736 @@
+/*
+ * 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.tools.ant.taskdefs.compilers;
+
+//Java5 style
+//import static org.apache.tools.ant.util.StringUtils.LINE_SEP;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.Javac;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * This is the default implementation for the CompilerAdapter interface.
+ * Currently, this is a cut-and-paste of the original javac task.
+ *
+ * @since Ant 1.3
+ */
+public abstract class DefaultCompilerAdapter
+ implements CompilerAdapter, CompilerAdapterExtension {
+
+ private static final int COMMAND_LINE_LIMIT;
+ static {
+ if (Os.isFamily("os/2")) {
+ // OS/2 CMD.EXE has a much smaller limit around 1K
+ COMMAND_LINE_LIMIT = 1000;
+ } else {
+ COMMAND_LINE_LIMIT = 4096; // 4K
+ }
+ }
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ protected Path src;
+ protected File destDir;
+ protected String encoding;
+ protected boolean debug = false;
+ protected boolean optimize = false;
+ protected boolean deprecation = false;
+ protected boolean depend = false;
+ protected boolean verbose = false;
+ protected String target;
+ protected Path bootclasspath;
+ protected Path extdirs;
+ protected Path compileClasspath;
+ protected Path compileSourcepath;
+ protected Project project;
+ protected Location location;
+ protected boolean includeAntRuntime;
+ protected boolean includeJavaRuntime;
+ protected String memoryInitialSize;
+ protected String memoryMaximumSize;
+
+ protected File[] compileList;
+ protected Javac attributes;
+
+ //must keep for subclass BC, though unused:
+ // CheckStyle:ConstantNameCheck OFF - bc
+ protected static final String lSep = StringUtils.LINE_SEP;
+
+ // CheckStyle:ConstantNameCheck ON
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Set the Javac instance which contains the configured compilation
+ * attributes.
+ *
+ * @param attributes a configured Javac task.
+ */
+ public void setJavac(final Javac attributes) {
+ this.attributes = attributes;
+ src = attributes.getSrcdir();
+ destDir = attributes.getDestdir();
+ encoding = attributes.getEncoding();
+ debug = attributes.getDebug();
+ optimize = attributes.getOptimize();
+ deprecation = attributes.getDeprecation();
+ depend = attributes.getDepend();
+ verbose = attributes.getVerbose();
+ target = attributes.getTarget();
+ bootclasspath = attributes.getBootclasspath();
+ extdirs = attributes.getExtdirs();
+ compileList = attributes.getFileList();
+ compileClasspath = attributes.getClasspath();
+ compileSourcepath = attributes.getSourcepath();
+ project = attributes.getProject();
+ location = attributes.getLocation();
+ includeAntRuntime = attributes.getIncludeantruntime();
+ includeJavaRuntime = attributes.getIncludejavaruntime();
+ memoryInitialSize = attributes.getMemoryInitialSize();
+ memoryMaximumSize = attributes.getMemoryMaximumSize();
+ }
+
+ /**
+ * Get the Javac task instance associated with this compiler adapter
+ *
+ * @return the configured Javac task instance used by this adapter.
+ */
+ public Javac getJavac() {
+ return attributes;
+ }
+
+ /**
+ * By default, only recognize files with a Java extension,
+ * but specialized compilers can recognize multiple kinds
+ * of files.
+ */
+ public String[] getSupportedFileExtensions() {
+ return new String[] {"java"};
+ }
+
+ /**
+ * Get the project this compiler adapter was created in.
+ * @return the owner project
+ * @since Ant 1.6
+ */
+ protected Project getProject() {
+ return project;
+ }
+
+ /**
+ * Builds the compilation classpath.
+ * @return the compilation class path
+ */
+ protected Path getCompileClasspath() {
+ final Path classpath = new Path(project);
+
+ // add dest dir to classpath so that previously compiled and
+ // untouched classes are on classpath
+
+ if (destDir != null && getJavac().isIncludeDestClasses()) {
+ classpath.setLocation(destDir);
+ }
+
+ // Combine the build classpath with the system classpath, in an
+ // order determined by the value of build.sysclasspath
+
+ Path cp = compileClasspath;
+ if (cp == null) {
+ cp = new Path(project);
+ }
+ if (includeAntRuntime) {
+ classpath.addExisting(cp.concatSystemClasspath("last"));
+ } else {
+ classpath.addExisting(cp.concatSystemClasspath("ignore"));
+ }
+
+ if (includeJavaRuntime) {
+ classpath.addJavaRuntime();
+ }
+
+ return classpath;
+ }
+
+ /**
+ * Get the command line arguments for the switches.
+ * @param cmd the command line
+ * @return the command line
+ */
+ protected Commandline setupJavacCommandlineSwitches(final Commandline cmd) {
+ return setupJavacCommandlineSwitches(cmd, false);
+ }
+
+ /**
+ * Does the command line argument processing common to classic and
+ * modern. Doesn't add the files to compile.
+ * @param cmd the command line
+ * @param useDebugLevel if true set set the debug level with the -g switch
+ * @return the command line
+ */
+ protected Commandline setupJavacCommandlineSwitches(final Commandline cmd,
+ final boolean useDebugLevel) {
+ final Path classpath = getCompileClasspath();
+ // For -sourcepath, use the "sourcepath" value if present.
+ // Otherwise default to the "srcdir" value.
+ Path sourcepath = null;
+ if (compileSourcepath != null) {
+ sourcepath = compileSourcepath;
+ } else {
+ sourcepath = src;
+ }
+
+ final String memoryParameterPrefix = assumeJava11() ? "-J-" : "-J-X";
+ if (memoryInitialSize != null) {
+ if (!attributes.isForkedJavac()) {
+ attributes.log("Since fork is false, ignoring "
+ + "memoryInitialSize setting.",
+ Project.MSG_WARN);
+ } else {
+ cmd.createArgument().setValue(memoryParameterPrefix
+ + "ms" + memoryInitialSize);
+ }
+ }
+
+ if (memoryMaximumSize != null) {
+ if (!attributes.isForkedJavac()) {
+ attributes.log("Since fork is false, ignoring "
+ + "memoryMaximumSize setting.",
+ Project.MSG_WARN);
+ } else {
+ cmd.createArgument().setValue(memoryParameterPrefix
+ + "mx" + memoryMaximumSize);
+ }
+ }
+
+ if (attributes.getNowarn()) {
+ cmd.createArgument().setValue("-nowarn");
+ }
+
+ if (deprecation) {
+ cmd.createArgument().setValue("-deprecation");
+ }
+
+ if (destDir != null) {
+ cmd.createArgument().setValue("-d");
+ cmd.createArgument().setFile(destDir);
+ }
+
+ cmd.createArgument().setValue("-classpath");
+
+ // Just add "sourcepath" to classpath ( for JDK1.1 )
+ // as well as "bootclasspath" and "extdirs"
+ if (assumeJava11()) {
+ final Path cp = new Path(project);
+
+ final Path bp = getBootClassPath();
+ if (bp.size() > 0) {
+ cp.append(bp);
+ }
+
+ if (extdirs != null) {
+ cp.addExtdirs(extdirs);
+ }
+ cp.append(classpath);
+ cp.append(sourcepath);
+ cmd.createArgument().setPath(cp);
+ } else {
+ cmd.createArgument().setPath(classpath);
+ // If the buildfile specifies sourcepath="", then don't
+ // output any sourcepath.
+ if (sourcepath.size() > 0) {
+ cmd.createArgument().setValue("-sourcepath");
+ cmd.createArgument().setPath(sourcepath);
+ }
+ if (target != null) {
+ cmd.createArgument().setValue("-target");
+ cmd.createArgument().setValue(target);
+ }
+
+ final Path bp = getBootClassPath();
+ if (bp.size() > 0) {
+ cmd.createArgument().setValue("-bootclasspath");
+ cmd.createArgument().setPath(bp);
+ }
+
+ if (extdirs != null && extdirs.size() > 0) {
+ cmd.createArgument().setValue("-extdirs");
+ cmd.createArgument().setPath(extdirs);
+ }
+ }
+
+ if (encoding != null) {
+ cmd.createArgument().setValue("-encoding");
+ cmd.createArgument().setValue(encoding);
+ }
+ if (debug) {
+ if (useDebugLevel && !assumeJava11()) {
+ final String debugLevel = attributes.getDebugLevel();
+ if (debugLevel != null) {
+ cmd.createArgument().setValue("-g:" + debugLevel);
+ } else {
+ cmd.createArgument().setValue("-g");
+ }
+ } else {
+ cmd.createArgument().setValue("-g");
+ }
+ } else if (getNoDebugArgument() != null) {
+ cmd.createArgument().setValue(getNoDebugArgument());
+ }
+ if (optimize) {
+ cmd.createArgument().setValue("-O");
+ }
+
+ if (depend) {
+ if (assumeJava11()) {
+ cmd.createArgument().setValue("-depend");
+ } else if (assumeJava12()) {
+ cmd.createArgument().setValue("-Xdepend");
+ } else {
+ attributes.log("depend attribute is not supported by the "
+ + "modern compiler", Project.MSG_WARN);
+ }
+ }
+
+ if (verbose) {
+ cmd.createArgument().setValue("-verbose");
+ }
+
+ addCurrentCompilerArgs(cmd);
+
+ return cmd;
+ }
+
+ /**
+ * Does the command line argument processing for modern. Doesn't
+ * add the files to compile.
+ * @param cmd the command line
+ * @return the command line
+ */
+ protected Commandline setupModernJavacCommandlineSwitches(final Commandline cmd) {
+ setupJavacCommandlineSwitches(cmd, true);
+ if (!assumeJava13()) { // -source added with JDK 1.4
+ final String t = attributes.getTarget();
+ if (attributes.getSource() != null) {
+ cmd.createArgument().setValue("-source");
+ cmd.createArgument()
+ .setValue(adjustSourceValue(attributes.getSource()));
+
+ } else if (t != null && mustSetSourceForTarget(t)) {
+ setImplicitSourceSwitch(cmd, t, adjustSourceValue(t));
+ }
+ }
+ return cmd;
+ }
+
+ /**
+ * Does the command line argument processing for modern and adds
+ * the files to compile as well.
+ * @return the command line
+ */
+ protected Commandline setupModernJavacCommand() {
+ final Commandline cmd = new Commandline();
+ setupModernJavacCommandlineSwitches(cmd);
+
+ logAndAddFilesToCompile(cmd);
+ return cmd;
+ }
+
+ /**
+ * Set up the command line.
+ * @return the command line
+ */
+ protected Commandline setupJavacCommand() {
+ return setupJavacCommand(false);
+ }
+
+ /**
+ * Does the command line argument processing for classic and adds
+ * the files to compile as well.
+ * @param debugLevelCheck if true set the debug level with the -g switch
+ * @return the command line
+ */
+ protected Commandline setupJavacCommand(final boolean debugLevelCheck) {
+ final Commandline cmd = new Commandline();
+ setupJavacCommandlineSwitches(cmd, debugLevelCheck);
+ logAndAddFilesToCompile(cmd);
+ return cmd;
+ }
+
+ /**
+ * Logs the compilation parameters, adds the files to compile and logs the
+ * &quot;niceSourceList&quot;
+ * @param cmd the command line
+ */
+ protected void logAndAddFilesToCompile(final Commandline cmd) {
+ attributes.log("Compilation " + cmd.describeArguments(),
+ Project.MSG_VERBOSE);
+
+ final StringBuffer niceSourceList = new StringBuffer("File");
+ if (compileList.length != 1) {
+ niceSourceList.append("s");
+ }
+ niceSourceList.append(" to be compiled:");
+
+ niceSourceList.append(StringUtils.LINE_SEP);
+
+ for (int i = 0; i < compileList.length; i++) {
+ final String arg = compileList[i].getAbsolutePath();
+ cmd.createArgument().setValue(arg);
+ niceSourceList.append(" ");
+ niceSourceList.append(arg);
+ niceSourceList.append(StringUtils.LINE_SEP);
+ }
+
+ attributes.log(niceSourceList.toString(), Project.MSG_VERBOSE);
+ }
+
+ /**
+ * Do the compile with the specified arguments.
+ * @param args - arguments to pass to process on command line
+ * @param firstFileName - index of the first source file in args,
+ * if the index is negative, no temporary file will ever be
+ * created, but this may hit the command line length limit on your
+ * system.
+ * @return the exit code of the compilation
+ */
+ protected int executeExternalCompile(final String[] args, final int firstFileName) {
+ return executeExternalCompile(args, firstFileName, true);
+ }
+
+ /**
+ * Do the compile with the specified arguments.
+ *
+ * <p>The working directory if the executed process will be the
+ * project's base directory.</p>
+ *
+ * @param args - arguments to pass to process on command line
+ * @param firstFileName - index of the first source file in args,
+ * if the index is negative, no temporary file will ever be
+ * created, but this may hit the command line length limit on your
+ * system.
+ * @param quoteFiles - if set to true, filenames containing
+ * spaces will be quoted when they appear in the external file.
+ * This is necessary when running JDK 1.4's javac and probably
+ * others.
+ * @return the exit code of the compilation
+ *
+ * @since Ant 1.6
+ */
+ protected int executeExternalCompile(final String[] args, final int firstFileName,
+ final boolean quoteFiles) {
+ String[] commandArray = null;
+ File tmpFile = null;
+
+ try {
+ /*
+ * Many system have been reported to get into trouble with
+ * long command lines - no, not only Windows ;-).
+ *
+ * POSIX seems to define a lower limit of 4k, so use a temporary
+ * file if the total length of the command line exceeds this limit.
+ */
+ if (Commandline.toString(args).length() > COMMAND_LINE_LIMIT
+ && firstFileName >= 0) {
+ BufferedWriter out = null;
+ try {
+ tmpFile = FILE_UTILS.createTempFile(
+ "files", "", getJavac().getTempdir(), true, true);
+ out = new BufferedWriter(new FileWriter(tmpFile));
+ for (int i = firstFileName; i < args.length; i++) {
+ if (quoteFiles && args[i].indexOf(" ") > -1) {
+ args[i] = args[i].replace(File.separatorChar, '/');
+ out.write("\"" + args[i] + "\"");
+ } else {
+ out.write(args[i]);
+ }
+ out.newLine();
+ }
+ out.flush();
+ commandArray = new String[firstFileName + 1];
+ System.arraycopy(args, 0, commandArray, 0, firstFileName);
+ commandArray[firstFileName] = "@" + tmpFile;
+ } catch (final IOException e) {
+ throw new BuildException("Error creating temporary file",
+ e, location);
+ } finally {
+ FileUtils.close(out);
+ }
+ } else {
+ commandArray = args;
+ }
+
+ try {
+ final Execute exe = new Execute(
+ new LogStreamHandler(attributes,
+ Project.MSG_INFO,
+ Project.MSG_WARN));
+ if (Os.isFamily("openvms")) {
+ //Use the VM launcher instead of shell launcher on VMS
+ //for java
+ exe.setVMLauncher(true);
+ }
+ exe.setAntRun(project);
+ exe.setWorkingDirectory(project.getBaseDir());
+ exe.setCommandline(commandArray);
+ exe.execute();
+ return exe.getExitValue();
+ } catch (final IOException e) {
+ throw new BuildException("Error running " + args[0]
+ + " compiler", e, location);
+ }
+ } finally {
+ if (tmpFile != null) {
+ tmpFile.delete();
+ }
+ }
+ }
+
+ /**
+ * Add extdirs to classpath
+ * @param classpath the classpath to use
+ * @deprecated since 1.5.x.
+ * Use org.apache.tools.ant.types.Path#addExtdirs instead.
+ */
+ @Deprecated
+ protected void addExtdirsToClasspath(final Path classpath) {
+ classpath.addExtdirs(extdirs);
+ }
+
+ /**
+ * Adds the command line arguments specific to the current implementation.
+ * @param cmd the command line to use
+ */
+ protected void addCurrentCompilerArgs(final Commandline cmd) {
+ cmd.addArguments(getJavac().getCurrentCompilerArgs());
+ }
+
+ /**
+ * Shall we assume JDK 1.1 command line switches?
+ * @return true if jdk 1.1
+ * @since Ant 1.5
+ */
+ protected boolean assumeJava11() {
+ return "javac1.1".equals(attributes.getCompilerVersion());
+ }
+
+ /**
+ * Shall we assume JDK 1.2 command line switches?
+ * @return true if jdk 1.2
+ * @since Ant 1.5
+ */
+ protected boolean assumeJava12() {
+ return "javac1.2".equals(attributes.getCompilerVersion());
+ }
+
+ /**
+ * Shall we assume JDK 1.3 command line switches?
+ * @return true if jdk 1.3
+ * @since Ant 1.5
+ */
+ protected boolean assumeJava13() {
+ return "javac1.3".equals(attributes.getCompilerVersion());
+ }
+
+ /**
+ * Shall we assume JDK 1.4 command line switches?
+ * @return true if jdk 1.4
+ * @since Ant 1.6.3
+ */
+ protected boolean assumeJava14() {
+ return assumeJavaXY("javac1.4", JavaEnvUtils.JAVA_1_4);
+ }
+
+ /**
+ * Shall we assume JDK 1.5 command line switches?
+ * @return true if JDK 1.5
+ * @since Ant 1.6.3
+ */
+ protected boolean assumeJava15() {
+ return assumeJavaXY("javac1.5", JavaEnvUtils.JAVA_1_5);
+ }
+
+ /**
+ * Shall we assume JDK 1.6 command line switches?
+ * @return true if JDK 1.6
+ * @since Ant 1.7
+ */
+ protected boolean assumeJava16() {
+ return assumeJavaXY("javac1.6", JavaEnvUtils.JAVA_1_6);
+ }
+
+ /**
+ * Shall we assume JDK 1.7 command line switches?
+ * @return true if JDK 1.7
+ * @since Ant 1.8.2
+ */
+ protected boolean assumeJava17() {
+ return assumeJavaXY("javac1.7", JavaEnvUtils.JAVA_1_7);
+ }
+
+ /**
+ * Shall we assume JDK 1.8 command line switches?
+ * @return true if JDK 1.8
+ * @since Ant 1.8.3
+ */
+ protected boolean assumeJava18() {
+ return assumeJavaXY("javac1.8", JavaEnvUtils.JAVA_1_8);
+ }
+
+ /**
+ * Shall we assume JDK 1.9 command line switches?
+ * @return true if JDK 1.9
+ * @since Ant 1.9.4
+ */
+ protected boolean assumeJava19() {
+ return assumeJavaXY("javac1.9", JavaEnvUtils.JAVA_1_9);
+ }
+
+ /**
+ * Shall we assume command line switches for the given version of Java?
+ * @since Ant 1.8.3
+ */
+ private boolean assumeJavaXY(final String javacXY, final String javaEnvVersionXY) {
+ return javacXY.equals(attributes.getCompilerVersion())
+ || ("classic".equals(attributes.getCompilerVersion())
+ && JavaEnvUtils.isJavaVersion(javaEnvVersionXY))
+ || ("modern".equals(attributes.getCompilerVersion())
+ && JavaEnvUtils.isJavaVersion(javaEnvVersionXY))
+ || ("extJavac".equals(attributes.getCompilerVersion())
+ && JavaEnvUtils.isJavaVersion(javaEnvVersionXY));
+ }
+
+ /**
+ * Combines a user specified bootclasspath with the system
+ * bootclasspath taking build.sysclasspath into account.
+ *
+ * @return a non-null Path instance that combines the user
+ * specified and the system bootclasspath.
+ */
+ protected Path getBootClassPath() {
+ final Path bp = new Path(project);
+ if (bootclasspath != null) {
+ bp.append(bootclasspath);
+ }
+ return bp.concatSystemBootClasspath("ignore");
+ }
+
+ /**
+ * The argument the compiler wants to see if the debug attribute
+ * has been set to false.
+ *
+ * <p>A return value of <code>null</code> means no argument at all.</p>
+ *
+ * @return "-g:none" unless we expect to invoke a JDK 1.1 compiler.
+ *
+ * @since Ant 1.6.3
+ */
+ protected String getNoDebugArgument() {
+ return assumeJava11() ? null : "-g:none";
+ }
+
+ private void setImplicitSourceSwitch(final Commandline cmd,
+ final String target, final String source) {
+ attributes.log("", Project.MSG_WARN);
+ attributes.log(" WARNING", Project.MSG_WARN);
+ attributes.log("", Project.MSG_WARN);
+ attributes.log("The -source switch defaults to " + getDefaultSource()
+ + ".",
+ Project.MSG_WARN);
+ attributes.log("If you specify -target " + target
+ + " you now must also specify -source " + source
+ + ".", Project.MSG_WARN);
+ attributes.log("Ant will implicitly add -source " + source
+ + " for you. Please change your build file.",
+ Project.MSG_WARN);
+ cmd.createArgument().setValue("-source");
+ cmd.createArgument().setValue(source);
+ }
+
+ /**
+ * A string that describes the default value for -source of the
+ * selected JDK's javac.
+ */
+ private String getDefaultSource() {
+ if (assumeJava15() || assumeJava16()) {
+ return "1.5 in JDK 1.5 and 1.6";
+ }
+ if (assumeJava17()) {
+ return "1.7 in JDK 1.7";
+ }
+ if (assumeJava18()) {
+ return "1.8 in JDK 1.8";
+ }
+ if (assumeJava19()) {
+ return "1.9 in JDK 1.9";
+ }
+ return "";
+ }
+
+ /**
+ * Whether the selected -target is known to be incompatible with
+ * the default -source value of the selected JDK's javac.
+ *
+ * <p>Assumes it will never be called unless the selected JDK is
+ * at least Java 1.5.</p>
+ *
+ * @param t the -target value, must not be null
+ */
+ private boolean mustSetSourceForTarget(String t) {
+ if (assumeJava14()) {
+ return false;
+ }
+ if (t.startsWith("1.")) {
+ t = t.substring(2);
+ }
+ return t.equals("1") || t.equals("2") || t.equals("3") || t.equals("4")
+ || ((t.equals("5") || t.equals("6"))
+ && !assumeJava15() && !assumeJava16())
+ || (t.equals("7") && !assumeJava17())
+ || (t.equals("8") && !assumeJava18())
+ || (t.equals("9") && !assumeJava19());
+ }
+
+
+ /**
+ * Turn the task's attribute for -source into soemthing that is
+ * understood by all javac's after 1.4.
+ *
+ * <p>support for -source 1.1 and -source 1.2 has been added with
+ * JDK 1.4.2 but isn't present in 1.5.0+</p>
+ */
+ private String adjustSourceValue(final String source) {
+ return (source.equals("1.1") || source.equals("1.2")) ? "1.3" : source;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Gcj.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Gcj.java
new file mode 100644
index 00000000..3167cc24
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Gcj.java
@@ -0,0 +1,160 @@
+/*
+ * 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.tools.ant.taskdefs.compilers;
+
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * The implementation of the gcj compiler.
+ * This is primarily a cut-and-paste from the jikes.
+ *
+ * @since Ant 1.4
+ */
+public class Gcj extends DefaultCompilerAdapter {
+
+ /**
+ * Performs a compile using the gcj compiler.
+ * @return true if the compilation succeeded
+ * @throws BuildException on error
+ */
+ public boolean execute() throws BuildException {
+ Commandline cmd;
+ attributes.log("Using gcj compiler", Project.MSG_VERBOSE);
+ cmd = setupGCJCommand();
+
+ int firstFileName = cmd.size();
+ logAndAddFilesToCompile(cmd);
+
+ return
+ executeExternalCompile(cmd.getCommandline(), firstFileName) == 0;
+ }
+
+ /**
+ * Set up the gcj commandline.
+ * @return the command line
+ */
+ protected Commandline setupGCJCommand() {
+ Commandline cmd = new Commandline();
+ Path classpath = new Path(project);
+
+ // gcj doesn't support bootclasspath dir (-bootclasspath)
+ // so we'll emulate it for compatibility and convenience.
+ Path p = getBootClassPath();
+ if (p.size() > 0) {
+ classpath.append(p);
+ }
+
+ // gcj doesn't support an extension dir (-extdir)
+ // so we'll emulate it for compatibility and convenience.
+ if (extdirs != null || includeJavaRuntime) {
+ classpath.addExtdirs(extdirs);
+ }
+
+ classpath.append(getCompileClasspath());
+
+ // Gcj has no option for source-path so we
+ // will add it to classpath.
+ if (compileSourcepath != null) {
+ classpath.append(compileSourcepath);
+ } else {
+ classpath.append(src);
+ }
+
+ String exec = getJavac().getExecutable();
+ cmd.setExecutable(exec == null ? "gcj" : exec);
+
+ if (destDir != null) {
+ cmd.createArgument().setValue("-d");
+ cmd.createArgument().setFile(destDir);
+
+ if (!destDir.exists()
+ && !(destDir.mkdirs() || destDir.isDirectory())) {
+ throw new BuildException("Can't make output directories. "
+ + "Maybe permission is wrong. ");
+ }
+ }
+
+ cmd.createArgument().setValue("-classpath");
+ cmd.createArgument().setPath(classpath);
+
+ if (encoding != null) {
+ cmd.createArgument().setValue("--encoding=" + encoding);
+ }
+ if (debug) {
+ cmd.createArgument().setValue("-g1");
+ }
+ if (optimize) {
+ cmd.createArgument().setValue("-O");
+ }
+
+ /**
+ * gcj should be set for generate class.
+ * ... if no 'compile to native' argument is passed
+ */
+ if (!isNativeBuild()) {
+ cmd.createArgument().setValue("-C");
+ }
+
+ if (attributes.getSource() != null) {
+ String source = attributes.getSource();
+ cmd.createArgument().setValue("-fsource=" + source);
+ }
+
+ if (attributes.getTarget() != null) {
+ String target = attributes.getTarget();
+ cmd.createArgument().setValue("-ftarget=" + target);
+ }
+
+ addCurrentCompilerArgs(cmd);
+
+ return cmd;
+ }
+
+ /**
+ * Whether any of the arguments given via &lt;compilerarg&gt;
+ * implies that compilation to native code is requested.
+ * @return true if compilation to native code is requested
+ * @since Ant 1.6.2
+ */
+ public boolean isNativeBuild() {
+ boolean nativeBuild = false;
+ String[] additionalArguments = getJavac().getCurrentCompilerArgs();
+ int argsLength = 0;
+ while (!nativeBuild && argsLength < additionalArguments.length) {
+ int conflictLength = 0;
+ while (!nativeBuild
+ && conflictLength < CONFLICT_WITH_DASH_C.length) {
+ nativeBuild = (additionalArguments[argsLength].startsWith
+ (CONFLICT_WITH_DASH_C[conflictLength]));
+ conflictLength++;
+ }
+ argsLength++;
+ }
+ return nativeBuild;
+ }
+
+ private static final String [] CONFLICT_WITH_DASH_C = {
+ "-o" , "--main=", "-D", "-fjni", "-L"
+ };
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Javac12.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Javac12.java
new file mode 100644
index 00000000..3fd8ccdd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Javac12.java
@@ -0,0 +1,91 @@
+/*
+ * 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.tools.ant.taskdefs.compilers;
+
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * The implementation of the javac compiler for JDK 1.2
+ * This is primarily a cut-and-paste from the original javac task before it
+ * was refactored.
+ *
+ * @since Ant 1.3
+ * @deprecated Use {@link Javac13} instead.
+ */
+public class Javac12 extends DefaultCompilerAdapter {
+ protected static final String CLASSIC_COMPILER_CLASSNAME = "sun.tools.javac.Main";
+
+ /**
+ * Run the compilation.
+ * @return true if the compiler ran with a zero exit result (ok)
+ * @exception BuildException if the compilation has problems.
+ */
+ public boolean execute() throws BuildException {
+ attributes.log("Using classic compiler", Project.MSG_VERBOSE);
+ Commandline cmd = setupJavacCommand(true);
+
+ OutputStream logstr = new LogOutputStream(attributes, Project.MSG_WARN);
+ try {
+ // Create an instance of the compiler, redirecting output to
+ // the project log
+ Class c = Class.forName(CLASSIC_COMPILER_CLASSNAME);
+ Constructor cons =
+ c.getConstructor(new Class[] {OutputStream.class,
+ String.class});
+ Object compiler
+ = cons.newInstance(new Object[] {logstr, "javac"});
+
+ // Call the compile() method
+ Method compile = c.getMethod("compile",
+ new Class [] {String[].class});
+ Boolean ok =
+ (Boolean) compile.invoke(compiler,
+ new Object[] {cmd.getArguments()});
+ return ok.booleanValue();
+ } catch (ClassNotFoundException ex) {
+ throw new BuildException("Cannot use classic compiler , as it is "
+ + "not available. \n"
+ + " A common solution is "
+ + "to set the environment variable"
+ + " JAVA_HOME to your jdk directory.\n"
+ + "It is currently set to \""
+ + JavaEnvUtils.getJavaHome()
+ + "\"",
+ location);
+ } catch (Exception ex) {
+ if (ex instanceof BuildException) {
+ throw (BuildException) ex;
+ } else {
+ throw new BuildException("Error starting classic compiler: ",
+ ex, location);
+ }
+ } finally {
+ FileUtils.close(logstr);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Javac13.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Javac13.java
new file mode 100644
index 00000000..acb6a7f0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Javac13.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.tools.ant.taskdefs.compilers;
+
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+
+
+/**
+ * The implementation of the javac compiler for JDK 1.3
+ * This is primarily a cut-and-paste from the original javac task before it
+ * was refactored.
+ *
+ * @since Ant 1.3
+ */
+public class Javac13 extends DefaultCompilerAdapter {
+
+ /**
+ * Integer returned by the "Modern" jdk1.3 compiler to indicate success.
+ */
+ private static final int MODERN_COMPILER_SUCCESS = 0;
+
+ /**
+ * Run the compilation.
+ * @return true if the compiler ran with a zero exit result (ok)
+ * @exception BuildException if the compilation has problems.
+ */
+ public boolean execute() throws BuildException {
+ attributes.log("Using modern compiler", Project.MSG_VERBOSE);
+ Commandline cmd = setupModernJavacCommand();
+
+ // Use reflection to be able to build on all JDKs >= 1.1:
+ try {
+ Class c = Class.forName ("com.sun.tools.javac.Main");
+ Object compiler = c.newInstance ();
+ Method compile = c.getMethod ("compile",
+ new Class [] {(new String [] {}).getClass ()});
+ int result = ((Integer) compile.invoke
+ (compiler, new Object[] {cmd.getArguments()}))
+ .intValue ();
+ return (result == MODERN_COMPILER_SUCCESS);
+ } catch (Exception ex) {
+ if (ex instanceof BuildException) {
+ throw (BuildException) ex;
+ } else {
+ throw new BuildException("Error starting modern compiler",
+ ex, location);
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/JavacExternal.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/JavacExternal.java
new file mode 100644
index 00000000..ab284544
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/JavacExternal.java
@@ -0,0 +1,92 @@
+/*
+ * 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.tools.ant.taskdefs.compilers;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Performs a compile using javac externally.
+ *
+ * @since Ant 1.4
+ */
+public class JavacExternal extends DefaultCompilerAdapter {
+
+ /**
+ * Performs a compile using the Javac externally.
+ * @return true if the compilation succeeded
+ * @throws BuildException on error
+ */
+ public boolean execute() throws BuildException {
+ attributes.log("Using external javac compiler", Project.MSG_VERBOSE);
+
+ Commandline cmd = new Commandline();
+ cmd.setExecutable(getJavac().getJavacExecutable());
+ if (!assumeJava11() && !assumeJava12()) {
+ setupModernJavacCommandlineSwitches(cmd);
+ } else {
+ setupJavacCommandlineSwitches(cmd, true);
+ }
+ int firstFileName = assumeJava11() ? -1 : cmd.size();
+ logAndAddFilesToCompile(cmd);
+ //On VMS platform, we need to create a special java options file
+ //containing the arguments and classpath for the javac command.
+ //The special file is supported by the "-V" switch on the VMS JVM.
+ if (Os.isFamily("openvms")) {
+ return execOnVMS(cmd, firstFileName);
+ }
+ return
+ executeExternalCompile(cmd.getCommandline(), firstFileName,
+ true)
+ == 0;
+ }
+
+ /**
+ * helper method to execute our command on VMS.
+ * @param cmd
+ * @param firstFileName
+ * @return
+ */
+ private boolean execOnVMS(Commandline cmd, int firstFileName) {
+ File vmsFile = null;
+ try {
+ vmsFile = JavaEnvUtils.createVmsJavaOptionFile(cmd.getArguments());
+ String[] commandLine = {cmd.getExecutable(),
+ "-V",
+ vmsFile.getPath()};
+ return 0 == executeExternalCompile(commandLine,
+ firstFileName,
+ true);
+
+ } catch (IOException e) {
+ throw new BuildException("Failed to create a temporary file for \"-V\" switch");
+ } finally {
+ FileUtils.delete(vmsFile);
+ }
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Jikes.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Jikes.java
new file mode 100644
index 00000000..eac1bcfc
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Jikes.java
@@ -0,0 +1,221 @@
+/*
+ * 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.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * The implementation of the jikes compiler.
+ * This is primarily a cut-and-paste from the original javac task before it
+ * was refactored.
+ *
+ * @since Ant 1.3
+ */
+public class Jikes extends DefaultCompilerAdapter {
+
+ /**
+ * Performs a compile using the Jikes compiler from IBM.
+ * Mostly of this code is identical to doClassicCompile()
+ * However, it does not support all options like
+ * extdirs, deprecation and so on, because
+ * there is no option in jikes and I don't understand
+ * what they should do.
+ *
+ * It has been successfully tested with jikes &gt;1.10.
+ * @return true if the compilation succeeded
+ * @throws BuildException on error
+ */
+ public boolean execute() throws BuildException {
+ attributes.log("Using jikes compiler", Project.MSG_VERBOSE);
+
+ Commandline cmd = new Commandline();
+
+ // For -sourcepath, use the "sourcepath" value if present.
+ // Otherwise default to the "srcdir" value.
+ Path sourcepath = null;
+ if (compileSourcepath != null) {
+ sourcepath = compileSourcepath;
+ } else {
+ sourcepath = src;
+ }
+ // If the buildfile specifies sourcepath="", then don't
+ // output any sourcepath.
+ if (sourcepath.size() > 0) {
+ cmd.createArgument().setValue("-sourcepath");
+ cmd.createArgument().setPath(sourcepath);
+ }
+
+ Path classpath = new Path(project);
+
+ if (bootclasspath == null || bootclasspath.size() == 0) {
+ // no bootclasspath, therefore, get one from the java runtime
+ includeJavaRuntime = true;
+ } else {
+ // there is a bootclasspath stated. By default, the
+ // includeJavaRuntime is false. If the user has stated a
+ // bootclasspath and said to include the java runtime, it's on
+ // their head!
+ }
+ classpath.append(getCompileClasspath());
+
+ // if the user has set JIKESPATH we should add the contents as well
+ String jikesPath = System.getProperty("jikes.class.path");
+ if (jikesPath != null) {
+ classpath.append(new Path(project, jikesPath));
+ }
+
+ if (extdirs != null && extdirs.size() > 0) {
+ cmd.createArgument().setValue("-extdirs");
+ cmd.createArgument().setPath(extdirs);
+ }
+
+ String exec = getJavac().getExecutable();
+ cmd.setExecutable(exec == null ? "jikes" : exec);
+
+ if (deprecation) {
+ cmd.createArgument().setValue("-deprecation");
+ }
+
+ if (destDir != null) {
+ cmd.createArgument().setValue("-d");
+ cmd.createArgument().setFile(destDir);
+ }
+
+ cmd.createArgument().setValue("-classpath");
+ cmd.createArgument().setPath(classpath);
+
+ if (encoding != null) {
+ cmd.createArgument().setValue("-encoding");
+ cmd.createArgument().setValue(encoding);
+ }
+ if (debug) {
+ String debugLevel = attributes.getDebugLevel();
+ if (debugLevel != null) {
+ cmd.createArgument().setValue("-g:" + debugLevel);
+ } else {
+ cmd.createArgument().setValue("-g");
+ }
+ } else {
+ cmd.createArgument().setValue("-g:none");
+ }
+ if (optimize) {
+ cmd.createArgument().setValue("-O");
+ }
+ if (verbose) {
+ cmd.createArgument().setValue("-verbose");
+ }
+ if (depend) {
+ cmd.createArgument().setValue("-depend");
+ }
+
+ if (target != null) {
+ cmd.createArgument().setValue("-target");
+ cmd.createArgument().setValue(target);
+ }
+
+ addPropertyParams(cmd);
+
+ if (attributes.getSource() != null) {
+ cmd.createArgument().setValue("-source");
+ String source = attributes.getSource();
+ if (source.equals("1.1") || source.equals("1.2")) {
+ // support for -source 1.1 and -source 1.2 has been
+ // added with JDK 1.4.2, Jikes doesn't like it
+ attributes.log("Jikes doesn't support '-source " + source
+ + "', will use '-source 1.3' instead");
+ cmd.createArgument().setValue("1.3");
+ } else {
+ cmd.createArgument().setValue(source);
+ }
+ }
+ addCurrentCompilerArgs(cmd);
+
+ int firstFileName = cmd.size();
+
+ Path boot = getBootClassPath();
+ if (boot.size() > 0) {
+ cmd.createArgument().setValue("-bootclasspath");
+ cmd.createArgument().setPath(boot);
+ }
+ logAndAddFilesToCompile(cmd);
+
+ return executeExternalCompile(cmd.getCommandline(), firstFileName) == 0;
+ }
+
+ private void addPropertyParams(Commandline cmd) {
+ /**
+ * TODO
+ * Perhaps we shouldn't use properties for these
+ * three options (emacs mode, warnings and pedantic),
+ * but include it in the javac directive?
+ */
+
+ /**
+ * Jikes has the nice feature to print error
+ * messages in a form readable by emacs, so
+ * that emacs can directly set the cursor
+ * to the place, where the error occurred.
+ */
+ String emacsProperty = project.getProperty("build.compiler.emacs");
+ if (emacsProperty != null && Project.toBoolean(emacsProperty)) {
+ cmd.createArgument().setValue("+E");
+ }
+
+ /**
+ * Jikes issues more warnings that javac, for
+ * example, when you have files in your classpath
+ * that don't exist. As this is often the case, these
+ * warning can be pretty annoying.
+ */
+ String warningsProperty = project.getProperty("build.compiler.warnings");
+ if (warningsProperty != null) {
+ attributes.log("!! the build.compiler.warnings property is " + "deprecated. !!",
+ Project.MSG_WARN);
+ attributes.log("!! Use the nowarn attribute instead. !!", Project.MSG_WARN);
+ if (!Project.toBoolean(warningsProperty)) {
+ cmd.createArgument().setValue("-nowarn");
+ }
+ }
+ if (attributes.getNowarn()) {
+ cmd.createArgument().setValue("-nowarn");
+ }
+
+ /**
+ * Jikes can issue pedantic warnings.
+ */
+ String pedanticProperty = project.getProperty("build.compiler.pedantic");
+ if (pedanticProperty != null && Project.toBoolean(pedanticProperty)) {
+ cmd.createArgument().setValue("+P");
+ }
+
+ /**
+ * Jikes supports something it calls "full dependency
+ * checking", see the jikes documentation for differences
+ * between -depend and +F.
+ */
+ String fullDependProperty = project.getProperty("build.compiler.fulldepend");
+ if (fullDependProperty != null
+ && Project.toBoolean(fullDependProperty)) {
+ cmd.createArgument().setValue("+F");
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Jvc.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Jvc.java
new file mode 100644
index 00000000..85ec4793
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Jvc.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.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * The implementation of the jvc compiler from microsoft.
+ * This is primarily a cut-and-paste from the original javac task before it
+ * was refactored.
+ *
+ * @since Ant 1.3
+ */
+public class Jvc extends DefaultCompilerAdapter {
+
+ /**
+ * Run the compilation.
+ * @return true if the compiler ran with a zero exit result (ok)
+ * @exception BuildException if the compilation has problems.
+ */
+ public boolean execute() throws BuildException {
+ attributes.log("Using jvc compiler", Project.MSG_VERBOSE);
+
+ Path classpath = new Path(project);
+
+ // jvc doesn't support bootclasspath dir (-bootclasspath)
+ // so we'll emulate it for compatibility and convenience.
+ Path p = getBootClassPath();
+ if (p.size() > 0) {
+ classpath.append(p);
+ }
+
+ if (includeJavaRuntime) {
+ // jvc doesn't support an extension dir (-extdir)
+ // so we'll emulate it for compatibility and convenience.
+ classpath.addExtdirs(extdirs);
+ }
+
+ classpath.append(getCompileClasspath());
+
+ // jvc has no option for source-path so we
+ // will add it to classpath.
+ if (compileSourcepath != null) {
+ classpath.append(compileSourcepath);
+ } else {
+ classpath.append(src);
+ }
+
+ Commandline cmd = new Commandline();
+ String exec = getJavac().getExecutable();
+ cmd.setExecutable(exec == null ? "jvc" : exec);
+
+ if (destDir != null) {
+ cmd.createArgument().setValue("/d");
+ cmd.createArgument().setFile(destDir);
+ }
+
+ // Add the Classpath before the "internal" one.
+ cmd.createArgument().setValue("/cp:p");
+ cmd.createArgument().setPath(classpath);
+
+ boolean msExtensions = true;
+ String mse = getProject().getProperty("build.compiler.jvc.extensions");
+ if (mse != null) {
+ msExtensions = Project.toBoolean(mse);
+ }
+
+ if (msExtensions) {
+ // Enable MS-Extensions and ...
+ cmd.createArgument().setValue("/x-");
+ // ... do not display a Message about this.
+ cmd.createArgument().setValue("/nomessage");
+ }
+
+ // Do not display Logo
+ cmd.createArgument().setValue("/nologo");
+
+ if (debug) {
+ cmd.createArgument().setValue("/g");
+ }
+ if (optimize) {
+ cmd.createArgument().setValue("/O");
+ }
+ if (verbose) {
+ cmd.createArgument().setValue("/verbose");
+ }
+
+ addCurrentCompilerArgs(cmd);
+
+ int firstFileName = cmd.size();
+ logAndAddFilesToCompile(cmd);
+
+ return
+ executeExternalCompile(cmd.getCommandline(), firstFileName,
+ false) == 0;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Kjc.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Kjc.java
new file mode 100644
index 00000000..68b5ba18
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Kjc.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.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ExecuteJava;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * The implementation of the Java compiler for KJC.
+ * This is primarily a cut-and-paste from Jikes.java and
+ * DefaultCompilerAdapter.
+ *
+ * @since Ant 1.4
+ */
+public class Kjc extends DefaultCompilerAdapter {
+
+ /**
+ * Run the compilation.
+ * @return true if the compilation succeeded
+ * @exception BuildException if the compilation has problems.
+ */
+ public boolean execute() throws BuildException {
+ attributes.log("Using kjc compiler", Project.MSG_VERBOSE);
+ Commandline cmd = setupKjcCommand();
+ cmd.setExecutable("at.dms.kjc.Main");
+ ExecuteJava ej = new ExecuteJava();
+ ej.setJavaCommand(cmd);
+ return ej.fork(getJavac()) == 0;
+ }
+
+ /**
+ * setup kjc command arguments.
+ * @return the command line
+ */
+ protected Commandline setupKjcCommand() {
+ Commandline cmd = new Commandline();
+
+ // generate classpath, because kjc doesn't support sourcepath.
+ Path classpath = getCompileClasspath();
+
+ if (deprecation) {
+ cmd.createArgument().setValue("-deprecation");
+ }
+
+ if (destDir != null) {
+ cmd.createArgument().setValue("-d");
+ cmd.createArgument().setFile(destDir);
+ }
+
+ // generate the clsspath
+ cmd.createArgument().setValue("-classpath");
+
+ Path cp = new Path(project);
+
+ // kjc don't have bootclasspath option.
+ Path p = getBootClassPath();
+ if (p.size() > 0) {
+ cp.append(p);
+ }
+
+ if (extdirs != null) {
+ cp.addExtdirs(extdirs);
+ }
+
+ cp.append(classpath);
+ if (compileSourcepath != null) {
+ cp.append(compileSourcepath);
+ } else {
+ cp.append(src);
+ }
+
+ cmd.createArgument().setPath(cp);
+
+ // kjc-1.5A doesn't support -encoding option now.
+ // but it will be supported near the feature.
+ if (encoding != null) {
+ cmd.createArgument().setValue("-encoding");
+ cmd.createArgument().setValue(encoding);
+ }
+
+ if (debug) {
+ cmd.createArgument().setValue("-g");
+ }
+
+ if (optimize) {
+ cmd.createArgument().setValue("-O2");
+ }
+
+ if (verbose) {
+ cmd.createArgument().setValue("-verbose");
+ }
+
+ addCurrentCompilerArgs(cmd);
+
+ logAndAddFilesToCompile(cmd);
+ return cmd;
+ }
+}
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Sj.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Sj.java
new file mode 100644
index 00000000..0dcc0e4c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/compilers/Sj.java
@@ -0,0 +1,61 @@
+/*
+ * 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.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * The implementation of the sj compiler.
+ * Uses the defaults for DefaultCompilerAdapter
+ *
+ * @since Ant 1.4
+ */
+public class Sj extends DefaultCompilerAdapter {
+
+ /**
+ * Performs a compile using the sj compiler from Symantec.
+ * @return true if the compilation succeeded
+ * @throws BuildException on error
+ */
+ public boolean execute() throws BuildException {
+ attributes.log("Using symantec java compiler", Project.MSG_VERBOSE);
+
+ Commandline cmd = setupJavacCommand();
+ String exec = getJavac().getExecutable();
+ cmd.setExecutable(exec == null ? "sj" : exec);
+
+ int firstFileName = cmd.size() - compileList.length;
+
+ return
+ executeExternalCompile(cmd.getCommandline(), firstFileName) == 0;
+ }
+
+ /**
+ * Returns null since sj either has -g for debug=true or no
+ * argument at all.
+ * @return null.
+ * @since Ant 1.6.3
+ */
+ protected String getNoDebugArgument() {
+ return null;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/And.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/And.java
new file mode 100644
index 00000000..91b34c82
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/And.java
@@ -0,0 +1,50 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import java.util.Enumeration;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * &lt;and&gt; condition container.
+ *
+ * <p>Iterates over all conditions and returns false as soon as one
+ * evaluates to false.</p>
+ *
+ * @since Ant 1.4
+ */
+public class And extends ConditionBase implements Condition {
+
+ /**
+ * @return true if all the contained conditions evaluates to true
+ * @exception BuildException if an error occurs
+ */
+ public boolean eval() throws BuildException {
+ Enumeration e = getConditions();
+ while (e.hasMoreElements()) {
+ Condition c = (Condition) e.nextElement();
+ if (!c.eval()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/AntVersion.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/AntVersion.java
new file mode 100644
index 00000000..aadf5a7e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/AntVersion.java
@@ -0,0 +1,171 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.DeweyDecimal;
+
+/**
+ * An Ant version condition.
+ * @since Ant 1.7
+ */
+public class AntVersion extends Task implements Condition {
+
+ private String atLeast = null;
+ private String exactly = null;
+ private String propertyname = null;
+
+ /**
+ * Run as a task.
+ * @throws BuildException if an error occurs.
+ */
+ public void execute() throws BuildException {
+ if (propertyname == null) {
+ throw new BuildException("'property' must be set.");
+ }
+ if (atLeast != null || exactly != null) {
+ // If condition values are set, evaluate the condition
+ if (eval()) {
+ getProject().setNewProperty(propertyname, getVersion().toString());
+ }
+ } else {
+ // Raw task
+ getProject().setNewProperty(propertyname, getVersion().toString());
+ }
+ }
+
+ /**
+ * Evaluate the condition.
+ * @return true if the condition is true.
+ * @throws BuildException if an error occurs.
+ */
+ public boolean eval() throws BuildException {
+ validate();
+ DeweyDecimal actual = getVersion();
+ if (null != atLeast) {
+ return actual.isGreaterThanOrEqual(new DeweyDecimal(atLeast));
+ }
+ if (null != exactly) {
+ return actual.isEqual(new DeweyDecimal(exactly));
+ }
+ //default
+ return false;
+ }
+
+ private void validate() throws BuildException {
+ if (atLeast != null && exactly != null) {
+ throw new BuildException("Only one of atleast or exactly may be set.");
+ }
+ if (null == atLeast && null == exactly) {
+ throw new BuildException("One of atleast or exactly must be set.");
+ }
+ if (atLeast != null) {
+ try {
+ new DeweyDecimal(atLeast);
+ } catch (NumberFormatException e) {
+ throw new BuildException(
+ "The 'atleast' attribute is not a Dewey Decimal eg 1.1.0 : "
+ + atLeast);
+ }
+ } else {
+ try {
+ new DeweyDecimal(exactly);
+ } catch (NumberFormatException e) {
+ throw new BuildException(
+ "The 'exactly' attribute is not a Dewey Decimal eg 1.1.0 : "
+ + exactly);
+ }
+ }
+ }
+
+ private DeweyDecimal getVersion() {
+ Project p = new Project();
+ p.init();
+ char[] versionString = p.getProperty("ant.version").toCharArray();
+ StringBuffer sb = new StringBuffer();
+ boolean foundFirstDigit = false;
+ for (int i = 0; i < versionString.length; i++) {
+ if (Character.isDigit(versionString[i])) {
+ sb.append(versionString[i]);
+ foundFirstDigit = true;
+ }
+ if (versionString[i] == '.' && foundFirstDigit) {
+ sb.append(versionString[i]);
+ }
+ if (Character.isLetter(versionString[i]) && foundFirstDigit) {
+ break;
+ }
+ }
+ return new DeweyDecimal(sb.toString());
+ }
+
+ /**
+ * Get the atleast attribute.
+ * @return the atleast attribute.
+ */
+ public String getAtLeast() {
+ return atLeast;
+ }
+
+ /**
+ * Set the atleast attribute.
+ * This is of the form major.minor.point.
+ * For example 1.7.0.
+ * @param atLeast the version to check against.
+ */
+ public void setAtLeast(String atLeast) {
+ this.atLeast = atLeast;
+ }
+
+ /**
+ * Get the exactly attribute.
+ * @return the exactly attribute.
+ */
+ public String getExactly() {
+ return exactly;
+ }
+
+ /**
+ * Set the exactly attribute.
+ * This is of the form major.minor.point.
+ * For example 1.7.0.
+ * @param exactly the version to check against.
+ */
+ public void setExactly(String exactly) {
+ this.exactly = exactly;
+ }
+
+ /**
+ * Get the name of the property to hold the ant version.
+ * @return the name of the property.
+ */
+ public String getProperty() {
+ return propertyname;
+ }
+
+ /**
+ * Set the name of the property to hold the ant version.
+ * @param propertyname the name of the property.
+ */
+ public void setProperty(String propertyname) {
+ this.propertyname = propertyname;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Condition.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Condition.java
new file mode 100644
index 00000000..62adbf36
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Condition.java
@@ -0,0 +1,35 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Interface for conditions to use inside the &lt;condition&gt; task.
+ *
+ */
+public interface Condition {
+ /**
+ * Is this condition true?
+ * @return true if the condition is true
+ * @exception BuildException if an error occurs
+ */
+ boolean eval() throws BuildException;
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ConditionBase.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ConditionBase.java
new file mode 100644
index 00000000..d057b46d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ConditionBase.java
@@ -0,0 +1,281 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.taskdefs.Available;
+import org.apache.tools.ant.taskdefs.Checksum;
+import org.apache.tools.ant.taskdefs.UpToDate;
+
+/**
+ * Baseclass for the &lt;condition&gt; task as well as several
+ * conditions - ensures that the types of conditions inside the task
+ * and the "container" conditions are in sync.
+ *
+ * @since Ant 1.4
+ */
+public abstract class ConditionBase extends ProjectComponent {
+
+ /**
+ * name of the component
+ */
+ private String taskName = "condition";
+
+ /**
+ *
+ */
+ private Vector conditions = new Vector();
+
+ /**
+ * Simple constructor.
+ */
+ protected ConditionBase() {
+ taskName = "component";
+ }
+
+ /**
+ * Constructor that takes the name of the task in the task name.
+ * @param taskName the name of the task.
+ * @since Ant 1.7
+ */
+ protected ConditionBase(String taskName) {
+ this.taskName = taskName;
+ }
+
+ /**
+ * Count the conditions.
+ *
+ * @return the number of conditions in the container
+ * @since 1.1
+ */
+ protected int countConditions() {
+ return conditions.size();
+ }
+
+ /**
+ * Iterate through all conditions.
+ *
+ * @return an enumeration to use for iteration
+ * @since 1.1
+ */
+ protected final Enumeration getConditions() {
+ return conditions.elements();
+ }
+
+ /**
+ * Sets the name to use in logging messages.
+ *
+ * @param name The name to use in logging messages.
+ * Should not be <code>null</code>.
+ * @since Ant 1.7
+ */
+ public void setTaskName(String name) {
+ this.taskName = name;
+ }
+
+ /**
+ * Returns the name to use in logging messages.
+ *
+ * @return the name to use in logging messages.
+ * @since Ant 1.7
+ */
+ public String getTaskName() {
+ return taskName;
+ }
+
+ /**
+ * Add an &lt;available&gt; condition.
+ * @param a an available condition
+ * @since 1.1
+ */
+ public void addAvailable(Available a) {
+ conditions.addElement(a);
+ }
+
+ /**
+ * Add an &lt;checksum&gt; condition.
+ *
+ * @param c a Checksum condition
+ * @since 1.4, Ant 1.5
+ */
+ public void addChecksum(Checksum c) {
+ conditions.addElement(c);
+ }
+
+ /**
+ * Add an &lt;uptodate&gt; condition.
+ *
+ * @param u an UpToDate condition
+ * @since 1.1
+ */
+ public void addUptodate(UpToDate u) {
+ conditions.addElement(u);
+ }
+
+ /**
+ * Add an &lt;not&gt; condition "container".
+ *
+ * @param n a Not condition
+ * @since 1.1
+ */
+ public void addNot(Not n) {
+ conditions.addElement(n);
+ }
+
+ /**
+ * Add an &lt;and&gt; condition "container".
+ *
+ * @param a an And condition
+ * @since 1.1
+ */
+ public void addAnd(And a) {
+ conditions.addElement(a);
+ }
+
+ /**
+ * Add an &lt;or&gt; condition "container".
+ *
+ * @param o an Or condition
+ * @since 1.1
+ */
+ public void addOr(Or o) {
+ conditions.addElement(o);
+ }
+
+ /**
+ * Add an &lt;equals&gt; condition.
+ *
+ * @param e an Equals condition
+ * @since 1.1
+ */
+ public void addEquals(Equals e) {
+ conditions.addElement(e);
+ }
+
+ /**
+ * Add an &lt;os&gt; condition.
+ *
+ * @param o an Os condition
+ * @since 1.1
+ */
+ public void addOs(Os o) {
+ conditions.addElement(o);
+ }
+
+ /**
+ * Add an &lt;isset&gt; condition.
+ *
+ * @param i an IsSet condition
+ * @since Ant 1.5
+ */
+ public void addIsSet(IsSet i) {
+ conditions.addElement(i);
+ }
+
+ /**
+ * Add an &lt;http&gt; condition.
+ *
+ * @param h an Http condition
+ * @since Ant 1.5
+ */
+ public void addHttp(Http h) {
+ conditions.addElement(h);
+ }
+
+ /**
+ * Add a &lt;socket&gt; condition.
+ *
+ * @param s a Socket condition
+ * @since Ant 1.5
+ */
+ public void addSocket(Socket s) {
+ conditions.addElement(s);
+ }
+
+ /**
+ * Add a &lt;filesmatch&gt; condition.
+ *
+ * @param test a FilesMatch condition
+ * @since Ant 1.5
+ */
+ public void addFilesMatch(FilesMatch test) {
+ conditions.addElement(test);
+ }
+
+ /**
+ * Add a &lt;contains&gt; condition.
+ *
+ * @param test a Contains condition
+ * @since Ant 1.5
+ */
+ public void addContains(Contains test) {
+ conditions.addElement(test);
+ }
+
+ /**
+ * Add a &lt;istrue&gt; condition.
+ *
+ * @param test an IsTrue condition
+ * @since Ant 1.5
+ */
+ public void addIsTrue(IsTrue test) {
+ conditions.addElement(test);
+ }
+
+ /**
+ * Add a &lt;isfalse&gt; condition.
+ *
+ * @param test an IsFalse condition
+ * @since Ant 1.5
+ */
+ public void addIsFalse(IsFalse test) {
+ conditions.addElement(test);
+ }
+
+ /**
+ * Add an &lt;isreference&gt; condition.
+ *
+ * @param i an IsReference condition
+ * @since Ant 1.6
+ */
+ public void addIsReference(IsReference i) {
+ conditions.addElement(i);
+ }
+
+ /**
+ * Add an &lt;isfileselected&gt; condition.
+ * @param test the condition
+ */
+ public void addIsFileSelected(IsFileSelected test) {
+ conditions.addElement(test);
+ }
+
+ /**
+ * Add an arbitrary condition
+ * @param c a condition
+ * @since Ant 1.6
+ */
+ public void add(Condition c) {
+ conditions.addElement(c);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Contains.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Contains.java
new file mode 100644
index 00000000..8830a39d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Contains.java
@@ -0,0 +1,76 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Is one string part of another string?
+ *
+ *
+ * @since Ant 1.5
+ */
+public class Contains implements Condition {
+
+ private String string, subString;
+ private boolean caseSensitive = true;
+
+ /**
+ * The string to search in.
+ * @param string the string to search in
+ * @since Ant 1.5
+ */
+ public void setString(String string) {
+ this.string = string;
+ }
+
+ /**
+ * The string to search for.
+ * @param subString the string to search for
+ * @since Ant 1.5
+ */
+ public void setSubstring(String subString) {
+ this.subString = subString;
+ }
+
+ /**
+ * Whether to search ignoring case or not.
+ * @param b if false, ignore case
+ * @since Ant 1.5
+ */
+ public void setCasesensitive(boolean b) {
+ caseSensitive = b;
+ }
+
+ /**
+ * @since Ant 1.5
+ * @return true if the substring is within the string
+ * @exception BuildException if the attributes are not set correctly
+ */
+ public boolean eval() throws BuildException {
+ if (string == null || subString == null) {
+ throw new BuildException("both string and substring are required "
+ + "in contains");
+ }
+
+ return caseSensitive
+ ? string.indexOf(subString) > -1
+ : string.toLowerCase().indexOf(subString.toLowerCase()) > -1;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Equals.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Equals.java
new file mode 100644
index 00000000..2d930f98
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Equals.java
@@ -0,0 +1,148 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Simple comparison condition.
+ *
+ * @since Ant 1.4
+ */
+public class Equals implements Condition {
+ private static final int REQUIRED = 1 | 2;
+
+ private Object arg1, arg2;
+ private boolean trim = false;
+ private boolean caseSensitive = true;
+ private int args;
+ private boolean forcestring = false;
+
+ /**
+ * Set the first argument
+ * @param arg1 the first argument.
+ * @since Ant 1.8
+ */
+ public void setArg1(Object arg1) {
+ if (arg1 instanceof String) {
+ setArg1((String) arg1);
+ } else {
+ setArg1Internal(arg1);
+ }
+ }
+
+ /**
+ * Set the first string
+ *
+ * @param a1 the first string
+ */
+ public void setArg1(String a1) {
+ setArg1Internal(a1);
+ }
+
+ private void setArg1Internal(Object arg1) {
+ this.arg1 = arg1;
+ args |= 1;
+ }
+
+ /**
+ * Set the second argument
+ * @param arg2 the second argument.
+ * @since Ant 1.8
+ */
+ public void setArg2(Object arg2) {
+ if (arg2 instanceof String) {
+ setArg2((String) arg2);
+ } else {
+ setArg2Internal(arg2);
+ }
+ }
+
+ /**
+ * Set the second string
+ *
+ * @param a2 the second string
+ */
+ public void setArg2(String a2) {
+ setArg2Internal(a2);
+ }
+
+ private void setArg2Internal(Object arg2) {
+ this.arg2 = arg2;
+ args |= 2;
+ }
+
+ /**
+ * Should we want to trim the arguments before comparing them?
+ * @param b if true trim the arguments
+ * @since Ant 1.5
+ */
+ public void setTrim(boolean b) {
+ trim = b;
+ }
+
+ /**
+ * Should the comparison be case sensitive?
+ * @param b if true use a case sensitive comparison (this is the
+ * default)
+ * @since Ant 1.5
+ */
+ public void setCasesensitive(boolean b) {
+ caseSensitive = b;
+ }
+
+ /**
+ * Set whether to force string comparisons for non-equal, non-string objects.
+ * This allows object properties (legal in Ant 1.8.x+) to be compared as strings.
+ * @param forcestring value to set
+ * @since Ant 1.8.1
+ */
+ public void setForcestring(boolean forcestring) {
+ this.forcestring = forcestring;
+ }
+
+ /**
+ * @return true if the two strings are equal
+ * @exception BuildException if the attributes are not set correctly
+ */
+ public boolean eval() throws BuildException {
+ if ((args & REQUIRED) != REQUIRED) {
+ throw new BuildException("both arg1 and arg2 are required in equals");
+ }
+ if (arg1 == arg2 || arg1 != null && arg1.equals(arg2)) {
+ return true;
+ }
+ if (forcestring) {
+ arg1 = arg1 == null || arg1 instanceof String ? arg1 : arg1.toString();
+ arg2 = arg2 == null || arg2 instanceof String ? arg2 : arg2.toString();
+ }
+ if (arg1 instanceof String && trim) {
+ arg1 = ((String) arg1).trim();
+ }
+ if (arg2 instanceof String && trim) {
+ arg2 = ((String) arg2).trim();
+ }
+ if (arg1 instanceof String && arg2 instanceof String) {
+ String s1 = (String) arg1;
+ String s2 = (String) arg2;
+ return caseSensitive ? s1.equals(s2) : s1.equalsIgnoreCase(s2);
+ }
+ return false;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/FilesMatch.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/FilesMatch.java
new file mode 100644
index 00000000..5e99398f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/FilesMatch.java
@@ -0,0 +1,101 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Compares two files for equality based on size and
+ * content. Timestamps are not at all looked at.
+ *
+ * @since Ant 1.5
+ */
+
+public class FilesMatch implements Condition {
+
+ /**
+ * Helper that provides the file comparison method.
+ */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * files to compare
+ */
+ private File file1, file2;
+
+ private boolean textfile = false;
+
+
+ /**
+ * Sets the File1 attribute
+ *
+ * @param file1 The new File1 value
+ */
+ public void setFile1(File file1) {
+ this.file1 = file1;
+ }
+
+
+ /**
+ * Sets the File2 attribute
+ *
+ * @param file2 The new File2 value
+ */
+ public void setFile2(File file2) {
+ this.file2 = file2;
+ }
+
+ /**
+ * Set whether to ignore line endings when comparing files.
+ * @param textfile whether to ignore line endings.
+ */
+ public void setTextfile(boolean textfile) {
+ this.textfile = textfile;
+ }
+
+ /**
+ * comparison method of the interface
+ *
+ * @return true if the files are equal
+ * @exception BuildException if it all went pear-shaped
+ */
+ public boolean eval()
+ throws BuildException {
+
+ //validate
+ if (file1 == null || file2 == null) {
+ throw new BuildException("both file1 and file2 are required in "
+ + "filesmatch");
+ }
+
+ //#now match the files
+ boolean matches = false;
+ try {
+ matches = FILE_UTILS.contentEquals(file1, file2, textfile);
+ } catch (IOException ioe) {
+ throw new BuildException("when comparing files: "
+ + ioe.getMessage(), ioe);
+ }
+ return matches;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/HasFreeSpace.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/HasFreeSpace.java
new file mode 100644
index 00000000..420c1894
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/HasFreeSpace.java
@@ -0,0 +1,101 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.ReflectWrapper;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * &lt;hasfreespace&gt;
+ * <p>Condition returns true if selected partition
+ * has the requested space, false otherwise.</p>
+ * @since Ant 1.7
+ */
+public class HasFreeSpace implements Condition {
+
+ private String partition;
+ private String needed;
+
+ /**
+ * Evaluate the condition.
+ * @return true if there enough free space.
+ * @throws BuildException if there is a problem.
+ */
+ public boolean eval() throws BuildException {
+ validate();
+ try {
+ if (JavaEnvUtils.isAtLeastJavaVersion("1.6")) {
+ //reflection to avoid bootstrap/build problems
+ File fs = new File(partition);
+ ReflectWrapper w = new ReflectWrapper(fs);
+ long free = ((Long) w.invoke("getFreeSpace")).longValue();
+ return free >= StringUtils.parseHumanSizes(needed);
+ } else {
+ throw new BuildException("HasFreeSpace condition not supported on Java5 or less.");
+ }
+ } catch (Exception e) {
+ throw new BuildException(e);
+ }
+ }
+
+ private void validate() throws BuildException {
+ if (null == partition) {
+ throw new BuildException("Please set the partition attribute.");
+ }
+ if (null == needed) {
+ throw new BuildException("Please set the needed attribute.");
+ }
+ }
+
+ /**
+ * The partition/device to check
+ * @return the partition.
+ */
+ public String getPartition() {
+ return partition;
+ }
+
+ /**
+ * Set the partition name.
+ * @param partition the name to use.
+ */
+ public void setPartition(String partition) {
+ this.partition = partition;
+ }
+
+ /**
+ * The amount of free space required
+ * @return the amount required
+ */
+ public String getNeeded() {
+ return needed;
+ }
+
+ /**
+ * Set the amount of space required.
+ * @param needed the amount required.
+ */
+ public void setNeeded(String needed) {
+ this.needed = needed;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/HasMethod.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/HasMethod.java
new file mode 100644
index 00000000..002af7a7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/HasMethod.java
@@ -0,0 +1,192 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * test for a method
+ */
+public class HasMethod extends ProjectComponent implements Condition {
+ private String classname;
+ private String method;
+ private String field;
+ private Path classpath;
+ private AntClassLoader loader;
+ private boolean ignoreSystemClasses = false;
+
+
+ /**
+ * Set the classpath to be used when searching for classes and resources.
+ *
+ * @param classpath an Ant Path object containing the search path.
+ */
+ public void setClasspath(Path classpath) {
+ createClasspath().append(classpath);
+ }
+
+ /**
+ * Classpath to be used when searching for classes and resources.
+ *
+ * @return an empty Path instance to be configured by Ant.
+ */
+ public Path createClasspath() {
+ if (this.classpath == null) {
+ this.classpath = new Path(getProject());
+ }
+ return this.classpath.createPath();
+ }
+
+ /**
+ * Set the classpath by reference.
+ *
+ * @param r a Reference to a Path instance to be used as the classpath
+ * value.
+ */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * Set the classname attribute.
+ * @param classname the name of the class to check.
+ */
+ public void setClassname(String classname) {
+ this.classname = classname;
+ }
+
+ /**
+ * Set the name of the method.
+ * @param method the name of the method to check.
+ */
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ /**
+ * Set the name of the field.
+ * @param field the name of the field to check.
+ */
+ public void setField(String field) {
+ this.field = field;
+ }
+
+ /**
+ * Set whether to ignore system classes when looking for the class.
+ * @param ignoreSystemClasses a <code>boolean</code> value.
+ */
+ public void setIgnoreSystemClasses(boolean ignoreSystemClasses) {
+ this.ignoreSystemClasses = ignoreSystemClasses;
+ }
+
+ /**
+ * Check if a given class can be loaded.
+ */
+ private Class loadClass(String classname) {
+ try {
+ if (ignoreSystemClasses) {
+ loader = getProject().createClassLoader(classpath);
+ loader.setParentFirst(false);
+ loader.addJavaLibraries();
+ try {
+ return loader.findClass(classname);
+ } catch (SecurityException se) {
+ // class found but restricted name
+ throw new BuildException("class \"" + classname
+ + "\" was found but a"
+ + " SecurityException has been"
+ + " raised while loading it",
+ se);
+ }
+ } else if (loader != null) {
+ // How do we ever get here?
+ return loader.loadClass(classname);
+ } else {
+ ClassLoader l = this.getClass().getClassLoader();
+ // Can return null to represent the bootstrap class loader.
+ // see API docs of Class.getClassLoader.
+ if (l != null) {
+ return Class.forName(classname, true, l);
+ } else {
+ return Class.forName(classname);
+ }
+ }
+ } catch (ClassNotFoundException e) {
+ throw new BuildException("class \"" + classname
+ + "\" was not found");
+ } catch (NoClassDefFoundError e) {
+ throw new BuildException("Could not load dependent class \""
+ + e.getMessage()
+ + "\" for class \"" + classname + "\"");
+ }
+ }
+
+
+ /** {@inheritDoc}. */
+ public boolean eval() throws BuildException {
+ if (classname == null) {
+ throw new BuildException("No classname defined");
+ }
+ ClassLoader preLoadClass = loader;
+ try {
+ Class clazz = loadClass(classname);
+ if (method != null) {
+ return isMethodFound(clazz);
+ }
+ if (field != null) {
+ return isFieldFound(clazz);
+ }
+ throw new BuildException("Neither method nor field defined");
+ } finally {
+ if (preLoadClass != loader && loader != null) {
+ loader.cleanup();
+ loader = null;
+ }
+ }
+ }
+
+ private boolean isFieldFound(Class clazz) {
+ Field[] fields = clazz.getDeclaredFields();
+ for (int i = 0; i < fields.length; i++) {
+ Field fieldEntry = fields[i];
+ if (fieldEntry.getName().equals(field)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isMethodFound(Class clazz) {
+ Method[] methods = clazz.getDeclaredMethods();
+ for (int i = 0; i < methods.length; i++) {
+ Method methodEntry = methods[i];
+ if (methodEntry.getName().equals(method)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Http.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Http.java
new file mode 100644
index 00000000..1dc94204
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Http.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.tools.ant.taskdefs.condition;
+
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Locale;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Condition to wait for a HTTP request to succeed. Its attribute(s) are:
+ * url - the URL of the request.
+ * errorsBeginAt - number at which errors begin at; default=400.
+ * requestMethod - HTTP request method to use; GET, HEAD, etc. default=GET
+ * @since Ant 1.5
+ */
+public class Http extends ProjectComponent implements Condition {
+ private static final int ERROR_BEGINS = 400;
+ private static final String DEFAULT_REQUEST_METHOD = "GET";
+
+ private String spec = null;
+ private String requestMethod = DEFAULT_REQUEST_METHOD;
+
+
+ /**
+ * Set the url attribute
+ * @param url the url of the request
+ */
+ public void setUrl(String url) {
+ spec = url;
+ }
+
+ private int errorsBeginAt = ERROR_BEGINS;
+
+ /**
+ * Set the errorsBeginAt attribute
+ * @param errorsBeginAt number at which errors begin at, default is
+ * 400
+ */
+ public void setErrorsBeginAt(int errorsBeginAt) {
+ this.errorsBeginAt = errorsBeginAt;
+ }
+
+ /**
+ * Sets the method to be used when issuing the HTTP request.
+ *
+ * @param method The HTTP request method to use. Valid values are
+ * the same as those accepted by the
+ * HttpURLConnection.setRequestMetho d() method,
+ * such as "GET", "HEAD", "TRACE", etc. The default
+ * if not specified is "GET".
+ *
+ * @see java.net.HttpURLConnection#setRequestMethod
+ * @since Ant 1.8.0
+ */
+ public void setRequestMethod(String method) {
+ this.requestMethod = method == null ? DEFAULT_REQUEST_METHOD
+ : method.toUpperCase(Locale.ENGLISH);
+ }
+
+ /**
+ * @return true if the HTTP request succeeds
+ * @exception BuildException if an error occurs
+ */
+ public boolean eval() throws BuildException {
+ if (spec == null) {
+ throw new BuildException("No url specified in http condition");
+ }
+ log("Checking for " + spec, Project.MSG_VERBOSE);
+ try {
+ URL url = new URL(spec);
+ try {
+ URLConnection conn = url.openConnection();
+ if (conn instanceof HttpURLConnection) {
+ HttpURLConnection http = (HttpURLConnection) conn;
+ http.setRequestMethod(requestMethod);
+ int code = http.getResponseCode();
+ log("Result code for " + spec + " was " + code,
+ Project.MSG_VERBOSE);
+ if (code > 0 && code < errorsBeginAt) {
+ return true;
+ }
+ return false;
+ }
+ } catch (java.net.ProtocolException pe) {
+ throw new BuildException("Invalid HTTP protocol: "
+ + requestMethod, pe);
+ } catch (java.io.IOException e) {
+ return false;
+ }
+ } catch (MalformedURLException e) {
+ throw new BuildException("Badly formed URL: " + spec, e);
+ }
+ return true;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsFailure.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsFailure.java
new file mode 100644
index 00000000..b0ffb9ec
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsFailure.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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.taskdefs.Execute;
+
+/**
+ * Condition to test a return-code for failure.
+ * @since Ant 1.7
+ */
+public class IsFailure implements Condition {
+ private int code;
+
+ /**
+ * Set the return code to check.
+ * @param c the return code.
+ */
+ public void setCode(int c) {
+ code = c;
+ }
+
+ /**
+ * Get the return code that will be checked by this IsFailure condition.
+ * @return return code as int.
+ */
+ public int getCode() {
+ return code;
+ }
+
+ /**
+ * Fulfill the condition interface.
+ * @return the result of evaluating the specified return code.
+ */
+ public boolean eval() {
+ return Execute.isFailure(code);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsFalse.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsFalse.java
new file mode 100644
index 00000000..0b3e69b4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsFalse.java
@@ -0,0 +1,55 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Condition that tests whether a given string evals to false
+ *
+ * @since Ant 1.5
+ */
+public class IsFalse extends ProjectComponent implements Condition {
+ /**
+ * what we eval
+ */
+ private Boolean value = null;
+
+ /**
+ * set the value to be tested; let ant eval it to true/false
+ * @param value the value to test
+ */
+ public void setValue(boolean value) {
+ this.value = value ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ /**
+ * @return the inverted value;
+ * @throws BuildException if someone forgot to spec a value
+ */
+ public boolean eval() throws BuildException {
+ if (value == null) {
+ throw new BuildException("Nothing to test for falsehood");
+ }
+ return !value.booleanValue();
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsFileSelected.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsFileSelected.java
new file mode 100644
index 00000000..efdb6648
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsFileSelected.java
@@ -0,0 +1,81 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.selectors.AbstractSelectorContainer;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * This is a condition that checks to see if a file passes an embedded selector.
+ */
+public class IsFileSelected extends AbstractSelectorContainer implements Condition {
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private File file;
+ private File baseDir;
+
+ /**
+ * The file to check.
+ * @param file the file to check if if passes the embedded selector.
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * The base directory to use.
+ * @param baseDir the base directory to use, if null use the project's
+ * basedir.
+ */
+ public void setBaseDir(File baseDir) {
+ this.baseDir = baseDir;
+ }
+
+ /**
+ * validate the parameters.
+ */
+ public void validate() {
+ if (selectorCount() != 1) {
+ throw new BuildException("Only one selector allowed");
+ }
+ super.validate();
+ }
+
+ /**
+ * Evaluate the selector with the file.
+ * @return true if the file is selected by the embedded selector.
+ */
+ public boolean eval() {
+ if (file == null) {
+ throw new BuildException("file attribute not set");
+ }
+ validate();
+ File myBaseDir = baseDir;
+ if (myBaseDir == null) {
+ myBaseDir = getProject().getBaseDir();
+ }
+
+ FileSelector f = getSelectors(getProject())[0];
+ return f.isSelected(
+ myBaseDir, FILE_UTILS.removeLeadingPath(myBaseDir, file), file);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsLastModified.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsLastModified.java
new file mode 100644
index 00000000..ddacd998
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsLastModified.java
@@ -0,0 +1,219 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.taskdefs.Touch;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Condition that makes assertions about the last modified date of a
+ * resource.
+ *
+ * @since Ant 1.8.0
+ */
+public class IsLastModified extends ProjectComponent implements Condition {
+ private long millis = -1;
+ private String dateTime = null;
+ private Touch.DateFormatFactory dfFactory = Touch.DEFAULT_DF_FACTORY;
+ private Resource resource;
+ private CompareMode mode = CompareMode.EQUALS;
+
+ /**
+ * Set the new modification time of file(s) touched
+ * in milliseconds since midnight Jan 1 1970.
+ * @param millis the <code>long</code> timestamp to use.
+ */
+ public void setMillis(long millis) {
+ this.millis = millis;
+ }
+
+ /**
+ * Set the new modification time of file(s) touched
+ * in the format &quot;MM/DD/YYYY HH:MM AM <i>or</i> PM&quot;
+ * or &quot;MM/DD/YYYY HH:MM:SS AM <i>or</i> PM&quot;.
+ * @param dateTime the <code>String</code> date in the specified format.
+ */
+ public void setDatetime(String dateTime) {
+ this.dateTime = dateTime;
+ }
+
+ /**
+ * Set the format of the datetime attribute.
+ * @param pattern the <code>SimpleDateFormat</code>-compatible
+ * format pattern.
+ */
+ public void setPattern(final String pattern) {
+ dfFactory = new Touch.DateFormatFactory() {
+ public DateFormat getPrimaryFormat() {
+ return new SimpleDateFormat(pattern);
+ }
+ public DateFormat getFallbackFormat() {
+ return null;
+ }
+ };
+ }
+
+ /**
+ * The resource to test.
+ * @param r the resource to test
+ */
+ public void add(Resource r) {
+ if (resource != null) {
+ throw new BuildException("only one resource can be tested");
+ }
+ resource = r;
+ }
+
+ /**
+ * The type of comparison to test.
+ * @param mode the mode of comparison.
+ */
+ public void setMode(CompareMode mode) {
+ this.mode = mode;
+ }
+
+ /**
+ * Argument validation.
+ * @throws BuildException if the required attributes are not supplied or
+ * if there is an inconsistency in the attributes.
+ */
+ protected void validate() throws BuildException {
+ if (millis >= 0 && dateTime != null) {
+ throw new BuildException("Only one of dateTime and millis can be"
+ + " set");
+ }
+ if (millis < 0 && dateTime == null) {
+ throw new BuildException("millis or dateTime is required");
+ }
+ if (resource == null) {
+ throw new BuildException("resource is required");
+ }
+ }
+
+ /**
+ * Calculate timestamp as millis either based on millis or
+ * dateTime (and pattern) attribute.
+ * @return time in milliseconds
+ * @throws BuildException if the date cannot be parsed.
+ */
+ protected long getMillis() throws BuildException {
+ if (millis >= 0) {
+ return millis;
+ }
+ if ("now".equalsIgnoreCase(dateTime)) {
+ return System.currentTimeMillis();
+ }
+ DateFormat df = dfFactory.getPrimaryFormat();
+ ParseException pe = null;
+ try {
+ return df.parse(dateTime).getTime();
+ } catch (ParseException peOne) {
+ df = dfFactory.getFallbackFormat();
+ if (df == null) {
+ pe = peOne;
+ } else {
+ try {
+ return df.parse(dateTime).getTime();
+ } catch (ParseException peTwo) {
+ pe = peTwo;
+ }
+ }
+ }
+ if (pe != null) {
+ throw new BuildException(pe.getMessage(), pe, getLocation());
+ }
+ /* NOTREACHED */
+ return 0;
+ }
+
+ /**
+ * evaluate the condition
+ * @return true or false depending on the compoarison mode and the time of the resource
+ * @throws BuildException
+ */
+ public boolean eval() throws BuildException {
+ validate();
+ long expected = getMillis();
+ long actual = resource.getLastModified();
+ log("expected timestamp: " + expected + " (" + new Date(expected) + ")"
+ + ", actual timestamp: " + actual + " (" + new Date(actual) + ")" ,
+ Project.MSG_VERBOSE);
+ if (CompareMode.EQUALS_TEXT.equals(mode.getValue())) {
+ return expected == actual;
+ }
+ if (CompareMode.BEFORE_TEXT.equals(mode.getValue())) {
+ return expected > actual;
+ }
+ if (CompareMode.NOT_BEFORE_TEXT.equals(mode.getValue())) {
+ return expected <= actual;
+ }
+ if (CompareMode.AFTER_TEXT.equals(mode.getValue())) {
+ return expected < actual;
+ }
+ if (CompareMode.NOT_AFTER_TEXT.equals(mode.getValue())) {
+ return expected >= actual;
+ }
+ throw new BuildException("Unknown mode " + mode.getValue());
+ }
+
+ /**
+ * describes comparison modes.
+ */
+ public static class CompareMode extends EnumeratedAttribute {
+ private static final String EQUALS_TEXT = "equals";
+ private static final String BEFORE_TEXT = "before";
+ private static final String AFTER_TEXT = "after";
+ private static final String NOT_BEFORE_TEXT = "not-before";
+ private static final String NOT_AFTER_TEXT = "not-after";
+
+ private static final CompareMode EQUALS = new CompareMode(EQUALS_TEXT);
+
+ /**
+ * creates a CompareMode instance of type equals
+ */
+ public CompareMode() {
+ this(EQUALS_TEXT);
+ }
+
+ /**
+ * creates a comparemode instance
+ * @param s one of the authorized values for comparemode
+ */
+ public CompareMode(String s) {
+ super();
+ setValue(s);
+ }
+
+ public String[] getValues() {
+ return new String[] {
+ EQUALS_TEXT, BEFORE_TEXT, AFTER_TEXT, NOT_BEFORE_TEXT,
+ NOT_AFTER_TEXT,
+ };
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsReachable.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsReachable.java
new file mode 100644
index 00000000..e699da11
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsReachable.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.tools.ant.taskdefs.condition;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * <p>Test for a host being reachable using ICMP "ping" packets &amp; echo operations.
+ * Ping packets are very reliable for assessing reachability in a LAN or WAN,
+ * but they do not get through any well-configured firewall. Echo (port 7) may.</p>
+ *
+ * <p>This condition turns unknown host exceptions into false conditions. This is
+ * because on a laptop, DNS is one of the first services lost when the network
+ * goes; you are implicitly offline.</p>
+ *
+ * <p>If a URL is supplied instead of a host, the hostname is extracted and used in
+ * the test--all other parts of the URL are discarded.</p>
+ *
+ * <p>The test may not work through firewalls; that is, something may be reachable
+ * using a protocol such as HTTP, while the lower level ICMP packets get dropped
+ * on the floor. Similarly, a host may be detected as reachable with ICMP, but not
+ * reachable on other ports (i.e. port 80), because of firewalls.</p>
+ *
+ * <p>Requires Java 5+ to work properly. On Java 1.4, if a hostname
+ * can be resolved, the destination is assumed to be reachable.</p>
+ *
+ * @since Ant 1.7
+ */
+public class IsReachable extends ProjectComponent implements Condition {
+
+ private static final int SECOND = 1000; // millis per second
+ private String host;
+ private String url;
+
+ /**
+ * The default timeout.
+ */
+ public static final int DEFAULT_TIMEOUT = 30;
+ private int timeout = DEFAULT_TIMEOUT;
+ /**
+ * Error when no hostname is defined
+ */
+ public static final String ERROR_NO_HOSTNAME = "No hostname defined";
+ /**
+ * Error when invalid timeout value is defined
+ */
+ public static final String ERROR_BAD_TIMEOUT = "Invalid timeout value";
+ /**
+ * Unknown host message is seen.
+ */
+ private static final String WARN_UNKNOWN_HOST = "Unknown host: ";
+ /**
+ * Network error message is seen.
+ */
+ public static final String ERROR_ON_NETWORK = "network error to ";
+ /** Error message when url and host are specified. */
+ public static final String ERROR_BOTH_TARGETS
+ = "Both url and host have been specified";
+ /** Error message when no reachably test avail. */
+ public static final String MSG_NO_REACHABLE_TEST
+ = "cannot do a proper reachability test on this Java version";
+ /** Error message when an invalid url is used. */
+ public static final String ERROR_BAD_URL = "Bad URL ";
+ /** Error message when no hostname in url. */
+ public static final String ERROR_NO_HOST_IN_URL = "No hostname in URL ";
+ /** The method name to look for in InetAddress */
+ public static final String METHOD_NAME = "isReachable";
+
+ /**
+ * Set the host to ping.
+ *
+ * @param host the host to ping.
+ */
+ public void setHost(final String host) {
+ this.host = host;
+ }
+
+ /**
+ * Set the URL from which to extract the hostname.
+ *
+ * @param url a URL object.
+ */
+ public void setUrl(final String url) {
+ this.url = url;
+ }
+
+ /**
+ * Set the timeout for the reachability test in seconds.
+ *
+ * @param timeout the timeout in seconds.
+ */
+ public void setTimeout(final int timeout) {
+ this.timeout = timeout;
+ }
+
+ /**
+ * emptyness test
+ *
+ * @param string param to check
+ *
+ * @return true if it is empty
+ */
+ private boolean empty(final String string) {
+ return string == null || string.length() == 0;
+ }
+
+ private static Class[] parameterTypes = {Integer.TYPE};
+
+ /**
+ * Evaluate the condition.
+ *
+ * @return true if the condition is true.
+ *
+ * @throws org.apache.tools.ant.BuildException
+ * if an error occurs
+ */
+ public boolean eval() throws BuildException {
+ if (empty(host) && empty(url)) {
+ throw new BuildException(ERROR_NO_HOSTNAME);
+ }
+ if (timeout < 0) {
+ throw new BuildException(ERROR_BAD_TIMEOUT);
+ }
+ String target = host;
+ if (!empty(url)) {
+ if (!empty(host)) {
+ throw new BuildException(ERROR_BOTH_TARGETS);
+ }
+ try {
+ //get the host of a url
+ final URL realURL = new URL(url);
+ target = realURL.getHost();
+ if (empty(target)) {
+ throw new BuildException(ERROR_NO_HOST_IN_URL + url);
+ }
+ } catch (final MalformedURLException e) {
+ throw new BuildException(ERROR_BAD_URL + url, e);
+ }
+ }
+ log("Probing host " + target, Project.MSG_VERBOSE);
+ InetAddress address;
+ try {
+ address = InetAddress.getByName(target);
+ } catch (final UnknownHostException e1) {
+ log(WARN_UNKNOWN_HOST + target);
+ return false;
+ }
+ log("Host address = " + address.getHostAddress(),
+ Project.MSG_VERBOSE);
+ boolean reachable;
+ //Java1.5: reachable = address.isReachable(timeout * 1000);
+ Method reachableMethod = null;
+ try {
+ reachableMethod = InetAddress.class.getMethod(METHOD_NAME,
+ parameterTypes);
+ final Object[] params = new Object[1];
+ params[0] = new Integer(timeout * SECOND);
+ try {
+ reachable = ((Boolean) reachableMethod.invoke(address, params))
+ .booleanValue();
+ } catch (final IllegalAccessException e) {
+ //utterly implausible, but catered for anyway
+ throw new BuildException("When calling " + reachableMethod);
+ } catch (final InvocationTargetException e) {
+ //assume this is an IOexception about un readability
+ final Throwable nested = e.getTargetException();
+ log(ERROR_ON_NETWORK + target + ": " + nested.toString());
+ //any kind of fault: not reachable.
+ reachable = false;
+ }
+ } catch (final NoSuchMethodException e) {
+ //java1.4
+ log("Not found: InetAddress." + METHOD_NAME, Project.MSG_VERBOSE);
+ log(MSG_NO_REACHABLE_TEST);
+ reachable = true;
+
+ }
+
+ log("host is" + (reachable ? "" : " not") + " reachable", Project.MSG_VERBOSE);
+ return reachable;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsReference.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsReference.java
new file mode 100644
index 00000000..f172849b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsReference.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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Condition that tests whether a given reference has been defined.
+ *
+ * <p>Optionally tests whether it is of a given type/class.</p>
+ *
+ * @since Ant 1.6
+ */
+public class IsReference extends ProjectComponent implements Condition {
+ private Reference ref;
+ private String type;
+
+ /**
+ * Set the refid attribute.
+ *
+ * @param r a Reference value
+ */
+ public void setRefid(Reference r) {
+ ref = r;
+ }
+
+ /**
+ * Set the type attribute. This is optional attribute.
+ *
+ * @param type an ant component type name
+ */
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ /**
+ * @return true if the reference exists and if type is set, if
+ * the reference is the same type
+ * @exception BuildException if an error occurs
+ */
+ public boolean eval() throws BuildException {
+ if (ref == null) {
+ throw new BuildException("No reference specified for isreference "
+ + "condition");
+ }
+
+ String key = ref.getRefId();
+ if (!getProject().hasReference(key)) {
+ return false;
+ } else if (type == null) {
+ return true;
+ } else {
+ Object o = getProject().getReference(key);
+ Class typeClass =
+ (Class) getProject().getDataTypeDefinitions().get(type);
+
+ if (typeClass == null) {
+ typeClass =
+ (Class) getProject().getTaskDefinitions().get(type);
+ }
+
+ if (typeClass == null) {
+ // don't know the type, should throw exception instead?
+ return false;
+ }
+
+ return typeClass.isAssignableFrom(o.getClass());
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsSet.java
new file mode 100644
index 00000000..d4a59145
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsSet.java
@@ -0,0 +1,51 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Condition that tests whether a given property has been set.
+ *
+ * @since Ant 1.5
+ */
+public class IsSet extends ProjectComponent implements Condition {
+ private String property;
+
+ /**
+ * Set the property attribute
+ *
+ * @param p the property name
+ */
+ public void setProperty(String p) {
+ property = p;
+ }
+
+ /**
+ * @return true if the property exists
+ * @exception BuildException if the property attribute is not set
+ */
+ public boolean eval() throws BuildException {
+ if (property == null) {
+ throw new BuildException("No property specified for isset " + "condition");
+ }
+ return getProject().getProperty(property) != null;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsSigned.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsSigned.java
new file mode 100644
index 00000000..585fb3a7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsSigned.java
@@ -0,0 +1,152 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ManifestTask;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.zip.ZipEntry;
+import org.apache.tools.zip.ZipFile;
+
+/**
+ * Checks whether a jarfile is signed: if the name of the
+ * signature is passed, the file is checked for presence of that
+ * particular signature; otherwise the file is checked for the
+ * existence of any signature.
+ */
+public class IsSigned extends DataType implements Condition {
+
+ private static final String SIG_START = "META-INF/";
+ private static final String SIG_END = ".SF";
+ private static final int SHORT_SIG_LIMIT = 8;
+
+ private String name;
+ private File file;
+
+ /**
+ * The jarfile that is to be tested for the presence
+ * of a signature.
+ * @param file jarfile to be tested.
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * The signature name to check jarfile for.
+ * @param name signature to look for.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns <code>true</code> if the file exists and is signed with
+ * the signature specified, or, if <code>name</code> wasn't
+ * specified, if the file contains a signature.
+ * @param zipFile the zipfile to check
+ * @param name the signature to check (may be killed)
+ * @return true if the file is signed.
+ * @throws IOException on error
+ */
+ public static boolean isSigned(File zipFile, String name)
+ throws IOException {
+ ZipFile jarFile = null;
+ try {
+ jarFile = new ZipFile(zipFile);
+ if (null == name) {
+ Enumeration entries = jarFile.getEntries();
+ while (entries.hasMoreElements()) {
+ String eName = ((ZipEntry) entries.nextElement()).getName();
+ if (eName.startsWith(SIG_START)
+ && eName.endsWith(SIG_END)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ name = replaceInvalidChars(name);
+ boolean shortSig = jarFile.getEntry(SIG_START
+ + name.toUpperCase()
+ + SIG_END) != null;
+ boolean longSig = false;
+ if (name.length() > SHORT_SIG_LIMIT) {
+ longSig = jarFile.getEntry(
+ SIG_START
+ + name.substring(0, SHORT_SIG_LIMIT).toUpperCase()
+ + SIG_END) != null;
+ }
+
+ return shortSig || longSig;
+ } finally {
+ ZipFile.closeQuietly(jarFile);
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if the file exists and is signed with
+ * the signature specified, or, if <code>name</code> wasn't
+ * specified, if the file contains a signature.
+ * @return true if the file is signed.
+ */
+ public boolean eval() {
+ if (file == null) {
+ throw new BuildException("The file attribute must be set.");
+ }
+ if (!file.exists()) {
+ log("The file \"" + file.getAbsolutePath()
+ + "\" does not exist.", Project.MSG_VERBOSE);
+ return false;
+ }
+
+ boolean r = false;
+ try {
+ r = isSigned(file, name);
+ } catch (IOException e) {
+ log("Got IOException reading file \"" + file.getAbsolutePath()
+ + "\"" + e, Project.MSG_WARN);
+ }
+
+ if (r) {
+ log("File \"" + file.getAbsolutePath() + "\" is signed.",
+ Project.MSG_VERBOSE);
+ }
+ return r;
+ }
+
+ private static String replaceInvalidChars(final String name) {
+ StringBuffer sb = new StringBuffer();
+ final int len = name.length();
+ boolean changes = false;
+ for (int i = 0; i < len; i++) {
+ final char ch = name.charAt(i);
+ if (ManifestTask.VALID_ATTRIBUTE_CHARS.indexOf(ch) < 0) {
+ sb.append("_");
+ changes = true;
+ } else {
+ sb.append(ch);
+ }
+ }
+ return changes ? sb.toString() : name;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsTrue.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsTrue.java
new file mode 100644
index 00000000..753b441d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/IsTrue.java
@@ -0,0 +1,55 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Condition that tests whether a given string evals to true
+ *
+ * @since Ant 1.5
+ */
+public class IsTrue extends ProjectComponent implements Condition {
+ /**
+ * what we eval
+ */
+ private Boolean value = null;
+
+ /**
+ * set the value to be tested; let ant eval it to true/false
+ * @param value the value to test
+ */
+ public void setValue(boolean value) {
+ this.value = value ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ /**
+ * @return the value
+ * @throws BuildException if someone forgot to spec a value
+ */
+ public boolean eval() throws BuildException {
+ if (value == null) {
+ throw new BuildException("Nothing to test for truth");
+ }
+ return value.booleanValue();
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Matches.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Matches.java
new file mode 100644
index 00000000..ac37f8fe
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Matches.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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.RegularExpression;
+import org.apache.tools.ant.util.regexp.Regexp;
+import org.apache.tools.ant.util.regexp.RegexpUtil;
+
+/**
+ * Simple regular expression condition.
+ *
+ * @since Ant 1.7
+ */
+public class Matches extends ProjectComponent implements Condition {
+
+ private String string;
+ private boolean caseSensitive = true;
+ private boolean multiLine = false;
+ private boolean singleLine = false;
+ private RegularExpression regularExpression;
+
+ /**
+ * Set the string
+ *
+ * @param string the string to match
+ */
+ public void setString(String string) {
+ this.string = string;
+ }
+
+ /**
+ * Set the regular expression to match against
+ *
+ * @param pattern the regular expression pattern
+ */
+ public void setPattern(String pattern) {
+ if (regularExpression != null) {
+ throw new BuildException(
+ "Only one regular expression is allowed.");
+ }
+ regularExpression = new RegularExpression();
+ regularExpression.setPattern(pattern);
+ }
+
+ /**
+ * A regular expression.
+ * You can use this element to refer to a previously
+ * defined regular expression datatype instance
+ * @param regularExpression the regular expression object
+ * to be configured as an element
+ */
+ public void addRegexp(RegularExpression regularExpression) {
+ if (this.regularExpression != null) {
+ throw new BuildException(
+ "Only one regular expression is allowed.");
+ }
+ this.regularExpression = regularExpression;
+ }
+
+ /**
+ * Whether to ignore case or not.
+ * @param b if false, ignore case.
+ * @since Ant 1.7
+ */
+ public void setCasesensitive(boolean b) {
+ caseSensitive = b;
+ }
+
+ /**
+ * Whether to match should be multiline.
+ * @param b the value to set.
+ */
+ public void setMultiline(boolean b) {
+ multiLine = b;
+ }
+
+ /**
+ * Whether to treat input as singleline ('.' matches newline).
+ * Corresponds to java.util.regex.Pattern.DOTALL.
+ * @param b the value to set.
+ */
+ public void setSingleLine(boolean b) {
+ singleLine = b;
+ }
+
+ /**
+ * @return true if the string matches the regular expression pattern
+ * @exception BuildException if the attributes are not set correctly
+ */
+ public boolean eval() throws BuildException {
+ if (string == null) {
+ throw new BuildException(
+ "Parameter string is required in matches.");
+ }
+ if (regularExpression == null) {
+ throw new BuildException("Missing pattern in matches.");
+ }
+ int options = RegexpUtil.asOptions(caseSensitive, multiLine, singleLine);
+ Regexp regexp = regularExpression.getRegexp(getProject());
+ return regexp.matches(string, options);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Not.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Not.java
new file mode 100644
index 00000000..a39dcbb0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Not.java
@@ -0,0 +1,50 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * &lt;not&gt; condition.
+ *
+ * Evaluates to true if the single condition nested into it is false
+ * and vice versa.
+ *
+ * @since Ant 1.4
+ */
+public class Not extends ConditionBase implements Condition {
+
+ /**
+ * Evaluate condition
+ *
+ * @return true if the condition is true.
+ * @throws BuildException if the condition is not configured correctly.
+ */
+ public boolean eval() throws BuildException {
+ if (countConditions() > 1) {
+ throw new BuildException("You must not nest more than one "
+ + "condition into <not>");
+ }
+ if (countConditions() < 1) {
+ throw new BuildException("You must nest a condition into <not>");
+ }
+ return !((Condition) getConditions().nextElement()).eval();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Or.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Or.java
new file mode 100644
index 00000000..aedfe74f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Or.java
@@ -0,0 +1,50 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import java.util.Enumeration;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * &lt;or&gt; condition container.
+ *
+ * <p>Iterates over all conditions and returns true as soon as one
+ * evaluates to true.</p>
+ *
+ * @since Ant 1.4
+ */
+public class Or extends ConditionBase implements Condition {
+
+ /**
+ * @return true if any of the contained conditions evaluate to true
+ * @exception BuildException if an error occurs
+ */
+ public boolean eval() throws BuildException {
+ Enumeration e = getConditions();
+ while (e.hasMoreElements()) {
+ Condition c = (Condition) e.nextElement();
+ if (c.eval()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Os.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Os.java
new file mode 100644
index 00000000..974c396f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Os.java
@@ -0,0 +1,321 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import java.util.Locale;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Condition that tests the OS type.
+ *
+ * @since Ant 1.4
+ */
+public class Os implements Condition {
+ private static final String OS_NAME =
+ System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
+ private static final String OS_ARCH =
+ System.getProperty("os.arch").toLowerCase(Locale.ENGLISH);
+ private static final String OS_VERSION =
+ System.getProperty("os.version").toLowerCase(Locale.ENGLISH);
+ private static final String PATH_SEP =
+ System.getProperty("path.separator");
+
+ /**
+ * OS family to look for
+ */
+ private String family;
+ /**
+ * Name of OS
+ */
+ private String name;
+ /**
+ * version of OS
+ */
+ private String version;
+ /**
+ * OS architecture
+ */
+ private String arch;
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_WINDOWS = "windows";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_9X = "win9x";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_NT = "winnt";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_OS2 = "os/2";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_NETWARE = "netware";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_DOS = "dos";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_MAC = "mac";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_TANDEM = "tandem";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_UNIX = "unix";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_VMS = "openvms";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_ZOS = "z/os";
+ /** OS family that can be tested for. {@value} */
+ public static final String FAMILY_OS400 = "os/400";
+
+ /**
+ * OpenJDK is reported to call MacOS X "Darwin"
+ * @see https://issues.apache.org/bugzilla/show_bug.cgi?id=44889
+ * @see https://issues.apache.org/jira/browse/HADOOP-3318
+ */
+ private static final String DARWIN = "darwin";
+
+ /**
+ * Default constructor
+ *
+ */
+ public Os() {
+ //default
+ }
+
+ /**
+ * Constructor that sets the family attribute
+ * @param family a String value
+ */
+ public Os(String family) {
+ setFamily(family);
+ }
+
+ /**
+ * Sets the desired OS family type
+ *
+ * @param f The OS family type desired<br>
+ * Possible values:<br>
+ * <ul>
+ * <li>dos</li>
+ * <li>mac</li>
+ * <li>netware</li>
+ * <li>os/2</li>
+ * <li>tandem</li>
+ * <li>unix</li>
+ * <li>windows</li>
+ * <li>win9x</li>
+ * <li>z/os</li>
+ * <li>os/400</li>
+ * </ul>
+ */
+ public void setFamily(String f) {
+ family = f.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Sets the desired OS name
+ *
+ * @param name The OS name
+ */
+ public void setName(String name) {
+ this.name = name.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Sets the desired OS architecture
+ *
+ * @param arch The OS architecture
+ */
+ public void setArch(String arch) {
+ this.arch = arch.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Sets the desired OS version
+ *
+ * @param version The OS version
+ */
+ public void setVersion(String version) {
+ this.version = version.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the type of
+ * that set in setFamily.
+ * @return true if the os matches.
+ * @throws BuildException if there is an error.
+ * @see Os#setFamily(String)
+ */
+ public boolean eval() throws BuildException {
+ return isOs(family, name, arch, version);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the
+ * given OS family.
+ * @param family the family to check for
+ * @return true if the OS matches
+ * @since 1.5
+ */
+ public static boolean isFamily(String family) {
+ return isOs(family, null, null, null);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the
+ * given OS name.
+ *
+ * @param name the OS name to check for
+ * @return true if the OS matches
+ * @since 1.7
+ */
+ public static boolean isName(String name) {
+ return isOs(null, name, null, null);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the
+ * given OS architecture.
+ *
+ * @param arch the OS architecture to check for
+ * @return true if the OS matches
+ * @since 1.7
+ */
+ public static boolean isArch(String arch) {
+ return isOs(null, null, arch, null);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the
+ * given OS version.
+ *
+ * @param version the OS version to check for
+ * @return true if the OS matches
+ * @since 1.7
+ */
+ public static boolean isVersion(String version) {
+ return isOs(null, null, null, version);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the
+ * given OS family, name, architecture and version
+ *
+ * @param family The OS family
+ * @param name The OS name
+ * @param arch The OS architecture
+ * @param version The OS version
+ * @return true if the OS matches
+ * @since 1.7
+ */
+ public static boolean isOs(String family, String name, String arch,
+ String version) {
+ boolean retValue = false;
+
+ if (family != null || name != null || arch != null
+ || version != null) {
+
+ boolean isFamily = true;
+ boolean isName = true;
+ boolean isArch = true;
+ boolean isVersion = true;
+
+ if (family != null) {
+
+ //windows probing logic relies on the word 'windows' in
+ //the OS
+ boolean isWindows = OS_NAME.indexOf(FAMILY_WINDOWS) > -1;
+ boolean is9x = false;
+ boolean isNT = false;
+ if (isWindows) {
+ //there are only four 9x platforms that we look for
+ is9x = (OS_NAME.indexOf("95") >= 0
+ || OS_NAME.indexOf("98") >= 0
+ || OS_NAME.indexOf("me") >= 0
+ //wince isn't really 9x, but crippled enough to
+ //be a muchness. Ant doesn't run on CE, anyway.
+ || OS_NAME.indexOf("ce") >= 0);
+ isNT = !is9x;
+ }
+ if (family.equals(FAMILY_WINDOWS)) {
+ isFamily = isWindows;
+ } else if (family.equals(FAMILY_9X)) {
+ isFamily = isWindows && is9x;
+ } else if (family.equals(FAMILY_NT)) {
+ isFamily = isWindows && isNT;
+ } else if (family.equals(FAMILY_OS2)) {
+ isFamily = OS_NAME.indexOf(FAMILY_OS2) > -1;
+ } else if (family.equals(FAMILY_NETWARE)) {
+ isFamily = OS_NAME.indexOf(FAMILY_NETWARE) > -1;
+ } else if (family.equals(FAMILY_DOS)) {
+ isFamily = PATH_SEP.equals(";") && !isFamily(FAMILY_NETWARE);
+ } else if (family.equals(FAMILY_MAC)) {
+ isFamily = OS_NAME.indexOf(FAMILY_MAC) > -1
+ || OS_NAME.indexOf(DARWIN) > -1;
+ } else if (family.equals(FAMILY_TANDEM)) {
+ isFamily = OS_NAME.indexOf("nonstop_kernel") > -1;
+ } else if (family.equals(FAMILY_UNIX)) {
+ isFamily = PATH_SEP.equals(":")
+ && !isFamily(FAMILY_VMS)
+ && (!isFamily(FAMILY_MAC) || OS_NAME.endsWith("x")
+ || OS_NAME.indexOf(DARWIN) > -1);
+ } else if (family.equals(FAMILY_ZOS)) {
+ isFamily = OS_NAME.indexOf(FAMILY_ZOS) > -1
+ || OS_NAME.indexOf("os/390") > -1;
+ } else if (family.equals(FAMILY_OS400)) {
+ isFamily = OS_NAME.indexOf(FAMILY_OS400) > -1;
+ } else if (family.equals(FAMILY_VMS)) {
+ isFamily = OS_NAME.indexOf(FAMILY_VMS) > -1;
+ } else {
+ throw new BuildException(
+ "Don\'t know how to detect os family \""
+ + family + "\"");
+ }
+ }
+ if (name != null) {
+ isName = name.equals(OS_NAME);
+ }
+ if (arch != null) {
+ isArch = arch.equals(OS_ARCH);
+ }
+ if (version != null) {
+ isVersion = version.equals(OS_VERSION);
+ }
+ retValue = isFamily && isName && isArch && isVersion;
+ }
+ return retValue;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ParserSupports.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ParserSupports.java
new file mode 100644
index 00000000..d2834019
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ParserSupports.java
@@ -0,0 +1,150 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.XMLReader;
+
+/**
+ * Test for the XML parser supporting a particular feature
+ * @since Ant 1.7
+ */
+public class ParserSupports extends ProjectComponent implements Condition {
+
+ private String feature;
+ private String property;
+ private String value;
+ // Error messages
+ /** error - combined attributes not allowed */
+ public static final String ERROR_BOTH_ATTRIBUTES =
+ "Property and feature attributes are exclusive";
+ /** feature */
+ public static final String FEATURE = "feature";
+ /** property */
+ public static final String PROPERTY = "property";
+
+ /** error - not recognized */
+ public static final String NOT_RECOGNIZED =
+ " not recognized: ";
+ /** error - not supported */
+ public static final String NOT_SUPPORTED =
+ " not supported: ";
+ /** error - missing attribute */
+ public static final String ERROR_NO_ATTRIBUTES =
+ "Neither feature or property are set";
+ /** error - no value */
+ public static final String ERROR_NO_VALUE =
+ "A value is needed when testing for property support";
+
+ /**
+ * Feature to probe for.
+ * @param feature the feature to probe for.
+ */
+ public void setFeature(String feature) {
+ this.feature = feature;
+ }
+
+ /**
+ * Property to probe for
+ * @param property the property to probe for.
+ */
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ /**
+ * Optional value to set.
+ * Converted to a boolean value when setting a property
+ * @param value the value to set.
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc}. */
+ public boolean eval() throws BuildException {
+ if (feature != null && property != null) {
+ throw new BuildException(ERROR_BOTH_ATTRIBUTES);
+ }
+ if (feature == null && property == null) {
+ throw new BuildException(ERROR_NO_ATTRIBUTES);
+ }
+ //pick a value that is good for everything
+ if (feature != null) {
+ return evalFeature();
+ }
+ if (value == null) {
+ throw new BuildException(ERROR_NO_VALUE);
+ }
+ return evalProperty();
+ }
+
+ /**
+ * Get our reader
+ * @return a reader
+ */
+ private XMLReader getReader() {
+ JAXPUtils.getParser();
+ return JAXPUtils.getXMLReader();
+ }
+
+ /**
+ * Set a feature
+ * @return true if the feature could be set
+ */
+ public boolean evalFeature() {
+ XMLReader reader = getReader();
+ if (value == null) {
+ value = "true";
+ }
+ boolean v = Project.toBoolean(value);
+ try {
+ reader.setFeature(feature, v);
+ } catch (SAXNotRecognizedException e) {
+ log(FEATURE + NOT_RECOGNIZED + feature, Project.MSG_VERBOSE);
+ return false;
+ } catch (SAXNotSupportedException e) {
+ log(FEATURE + NOT_SUPPORTED + feature, Project.MSG_VERBOSE);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Set a property
+ * @return true if the feature could be set
+ */
+ public boolean evalProperty() {
+ XMLReader reader = getReader();
+ try {
+ reader.setProperty(property, value);
+ } catch (SAXNotRecognizedException e) {
+ log(PROPERTY + NOT_RECOGNIZED + property, Project.MSG_VERBOSE);
+ return false;
+ } catch (SAXNotSupportedException e) {
+ log(PROPERTY + NOT_SUPPORTED + property, Project.MSG_VERBOSE);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ResourceContains.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ResourceContains.java
new file mode 100644
index 00000000..76a9ad35
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ResourceContains.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.tools.ant.taskdefs.condition;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * &lt;resourcecontains&gt;
+ * Is a string contained in a resource (file currently)?
+ * @since Ant 1.7.1
+ */
+public class ResourceContains implements Condition {
+
+ private Project project;
+ private String substring;
+ private Resource resource;
+ private String refid;
+ private boolean casesensitive = true;
+
+ /**
+ * Set this condition's Project.
+ * @param project Project
+ */
+ public void setProject(Project project) {
+ this.project = project;
+ }
+
+ /**
+ * Get this condition's Project.
+ * @return Project
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * Sets the resource to search
+ * @param r the value to use.
+ */
+ public void setResource(String r) {
+ this.resource = new FileResource(new File(r));
+ }
+
+ /**
+ * Sets the refid to search; should indicate a resource directly
+ * or by way of a single-element ResourceCollection.
+ * @param refid the value to use.
+ */
+ public void setRefid(String refid) {
+ this.refid = refid;
+ }
+
+ private void resolveRefid() {
+ try {
+ if (getProject() == null) {
+ throw new BuildException("Cannot retrieve refid; project unset");
+ }
+ Object o = getProject().getReference(refid);
+ if (!(o instanceof Resource)) {
+ if (o instanceof ResourceCollection) {
+ ResourceCollection rc = (ResourceCollection) o;
+ if (rc.size() == 1) {
+ o = rc.iterator().next();
+ }
+ } else {
+ throw new BuildException(
+ "Illegal value at '" + refid + "': " + String.valueOf(o));
+ }
+ }
+ this.resource = (Resource) o;
+ } finally {
+ refid = null;
+ }
+ }
+
+ /**
+ * Sets the substring to look for
+ * @param substring the value to use.
+ */
+ public void setSubstring(String substring) {
+ this.substring = substring;
+ }
+
+ /**
+ * Sets case sensitivity attribute.
+ * @param casesensitive the value to use.
+ */
+ public void setCasesensitive(boolean casesensitive) {
+ this.casesensitive = casesensitive;
+ }
+
+ private void validate() {
+ if (resource != null && refid != null) {
+ throw new BuildException("Cannot set both resource and refid");
+ }
+ if (resource == null && refid != null) {
+ resolveRefid();
+ }
+ if (resource == null || substring == null) {
+ throw new BuildException("both resource and substring are required "
+ + "in <resourcecontains>");
+ }
+ }
+
+ /**
+ * Evaluates the condition.
+ * @return true if the substring is contained in the resource
+ * @throws BuildException if there is a problem.
+ */
+ public synchronized boolean eval() throws BuildException {
+ validate();
+
+ if (substring.length() == 0) {
+ if (getProject() != null) {
+ getProject().log("Substring is empty; returning true",
+ Project.MSG_VERBOSE);
+ }
+ return true;
+ }
+ if (resource.getSize() == 0) {
+ return false;
+ }
+
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
+ String contents = FileUtils.safeReadFully(reader);
+ String sub = substring;
+ if (!casesensitive) {
+ contents = contents.toLowerCase();
+ sub = sub.toLowerCase();
+ }
+ return contents.indexOf(sub) >= 0;
+ } catch (IOException e) {
+ throw new BuildException("There was a problem accessing resource : " + resource);
+ } finally {
+ FileUtils.close(reader);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ResourceExists.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ResourceExists.java
new file mode 100644
index 00000000..feda65b2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ResourceExists.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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Condition that checks whether a given resource exists.
+ *
+ * @since Ant 1.8.0
+ */
+public class ResourceExists extends ProjectComponent implements Condition {
+ private Resource resource;
+
+ /**
+ * The resource to test.
+ */
+ public void add(Resource r) {
+ if (resource != null) {
+ throw new BuildException("only one resource can be tested");
+ }
+ resource = r;
+ }
+
+ /**
+ * Argument validation.
+ */
+ protected void validate() throws BuildException {
+ if (resource == null) {
+ throw new BuildException("resource is required");
+ }
+ }
+
+ public boolean eval() throws BuildException {
+ validate();
+ return resource.isExists();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ResourcesMatch.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ResourcesMatch.java
new file mode 100644
index 00000000..29e3e800
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/ResourcesMatch.java
@@ -0,0 +1,93 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.util.ResourceUtils;
+
+/**
+ * Compares resources for equality based on size and content.
+ * All resources specified must match; no resource collections
+ * specified is an error condition; if resource collections are
+ * specified, but yield fewer than two elements, the condition
+ * evaluates to <code>true</code>.
+ * @since Ant 1.7
+ */
+public class ResourcesMatch implements Condition {
+
+ private Union resources = null;
+ private boolean asText = false;
+
+ /**
+ * Set whether to treat resources as if they were text files,
+ * ignoring line endings.
+ * @param asText whether to ignore line endings.
+ */
+ public void setAsText(boolean asText) {
+ this.asText = asText;
+ }
+
+ /**
+ * Add a resource collection.
+ * @param rc the resource collection to add.
+ */
+ public void add(ResourceCollection rc) {
+ if (rc == null) {
+ return;
+ }
+ resources = resources == null ? new Union() : resources;
+ resources.add(rc);
+ }
+
+ /**
+ * Verify that all resources match.
+ * @return true if all resources are equal.
+ * @exception BuildException if there is an error.
+ */
+ public boolean eval() throws BuildException {
+ if (resources == null) {
+ throw new BuildException(
+ "You must specify one or more nested resource collections");
+ }
+ if (resources.size() > 1) {
+ Iterator<Resource> i = resources.iterator();
+ Resource r1 = (Resource) i.next();
+ Resource r2 = null;
+
+ while (i.hasNext()) {
+ r2 = (Resource) i.next();
+ try {
+ if (!ResourceUtils.contentEquals(r1, r2, asText)) {
+ return false;
+ }
+ } catch (IOException ioe) {
+ throw new BuildException("when comparing resources "
+ + r1.toString() + " and " + r2.toString(), ioe);
+ }
+ r1 = r2;
+ }
+ }
+ return true;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Socket.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Socket.java
new file mode 100644
index 00000000..d6a69ec8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Socket.java
@@ -0,0 +1,87 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Condition to wait for a TCP/IP socket to have a listener. Its attributes are:
+ * server - the name of the server.
+ * port - the port number of the socket.
+ *
+ * @since Ant 1.5
+ */
+public class Socket extends ProjectComponent implements Condition {
+ private String server = null;
+ private int port = 0;
+
+ /**
+ * Set the server attribute
+ *
+ * @param server the server name
+ */
+ public void setServer(String server) {
+ this.server = server;
+ }
+
+ /**
+ * Set the port attribute
+ *
+ * @param port the port number of the socket
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ * @return true if a socket can be created
+ * @exception BuildException if the attributes are not set
+ */
+ public boolean eval() throws BuildException {
+ if (server == null) {
+ throw new BuildException("No server specified in socket "
+ + "condition");
+ }
+ if (port == 0) {
+ throw new BuildException("No port specified in socket condition");
+ }
+ log("Checking for listener at " + server + ":" + port,
+ Project.MSG_VERBOSE);
+ java.net.Socket s = null;
+ try {
+ s = new java.net.Socket(server, port);
+ } catch (IOException e) {
+ return false;
+ } finally {
+ if (s != null) {
+ try {
+ s.close();
+ } catch (IOException ioe) {
+ // Intentionally left blank
+ }
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/TypeFound.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/TypeFound.java
new file mode 100644
index 00000000..f97c7f44
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/TypeFound.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.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.AntTypeDefinition;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.ProjectHelper;
+
+/**
+ * looks for a task or other Ant type that exists. Existence is defined as
+ * the type is defined, and its implementation class is present. This
+ * will work for datatypes and preset, script and macro definitions.
+ */
+public class TypeFound extends ProjectComponent implements Condition {
+
+ private String name;
+ private String uri;
+
+ /**
+ * the task or other type to look for
+ * @param name the name of the type
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * The URI for this definition.
+ * @param uri the namespace URI. If this is not set, use the
+ * default ant namespace.
+ */
+ public void setURI(String uri) {
+ this.uri = uri;
+ }
+
+ /**
+ * test for a task or other ant type existing in the current project
+ * @param typename the name of the type
+ * @return true if the typename exists
+ */
+ protected boolean doesTypeExist(String typename) {
+
+ ComponentHelper helper =
+ ComponentHelper.getComponentHelper(getProject());
+ String componentName = ProjectHelper.genComponentName(uri, typename);
+ AntTypeDefinition def = helper.getDefinition(componentName);
+ if (def == null) {
+ return false;
+ }
+ //now verify that the class has an implementation
+ boolean found = def.getExposedClass(getProject()) != null;
+ if (!found) {
+ String text = helper.diagnoseCreationFailure(componentName, "type");
+ log(text, Project.MSG_VERBOSE);
+ }
+ return found;
+ }
+
+
+ /**
+ * Is this condition true?
+ * @return true if the condition is true
+ * @exception BuildException if an error occurs
+ */
+ public boolean eval() throws BuildException {
+ if (name == null) {
+ throw new BuildException("No type specified");
+ }
+ return doesTypeExist(name);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Xor.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Xor.java
new file mode 100644
index 00000000..a2e675cb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/condition/Xor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.tools.ant.taskdefs.condition;
+
+import java.util.Enumeration;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * The <tt>Xor</tt> condition type to exclusive or operations.
+ * This does not shortcut stuff.
+ * @since Ant 1.7
+ */
+public class Xor extends ConditionBase implements Condition {
+
+ /**
+ * Evaluate the contained conditions.
+ * @return the result of xoring the conditions together.
+ * @throws org.apache.tools.ant.BuildException
+ * if an error occurs.
+ */
+ public boolean eval() throws BuildException {
+ Enumeration e = getConditions();
+ //initial state is false.
+ boolean state = false;
+ while (e.hasMoreElements()) {
+ Condition c = (Condition) e.nextElement();
+ //every condition is xored against the previous one
+ state ^= c.eval();
+ }
+ return state;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CVSEntry.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CVSEntry.java
new file mode 100644
index 00000000..b1a9a123
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CVSEntry.java
@@ -0,0 +1,112 @@
+/*
+ * 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.tools.ant.taskdefs.cvslib;
+
+import java.util.Date;
+import java.util.Vector;
+
+/**
+ * CVS Entry.
+ *
+ */
+public class CVSEntry {
+ private Date date;
+ private String author;
+ private final String comment;
+ private final Vector files = new Vector();
+
+ /**
+ * Creates a new instance of a CVSEntry
+ * @param date the date
+ * @param author the author
+ * @param comment a comment to be added to the revision
+ */
+ public CVSEntry(final Date date, final String author, final String comment) {
+ this.date = date;
+ this.author = author;
+ this.comment = comment;
+ }
+
+ /**
+ * Adds a file to the CVSEntry
+ * @param file the file to add
+ * @param revision the revision
+ */
+ public void addFile(final String file, final String revision) {
+ files.addElement(new RCSFile(file, revision));
+ }
+
+ /**
+ * Adds a file to the CVSEntry
+ * @param file the file to add
+ * @param revision the revision
+ * @param previousRevision the previous revision
+ */
+ public void addFile(final String file, final String revision, final String previousRevision) {
+ files.addElement(new RCSFile(file, revision, previousRevision));
+ }
+
+ /**
+ * Gets the date of the CVSEntry
+ * @return the date
+ */
+ public Date getDate() {
+ return date;
+ }
+
+ /**
+ * Sets the author of the CVSEntry
+ * @param author the author
+ */
+ public void setAuthor(final String author) {
+ this.author = author;
+ }
+
+ /**
+ * Gets the author of the CVSEntry
+ * @return the author
+ */
+ public String getAuthor() {
+ return author;
+ }
+
+ /**
+ * Gets the comment for the CVSEntry
+ * @return the comment
+ */
+ public String getComment() {
+ return comment;
+ }
+
+ /**
+ * Gets the files in this CVSEntry
+ * @return the files
+ */
+ public Vector getFiles() {
+ return files;
+ }
+
+ /**
+ * Gets a String containing author, date, files and comment
+ * @return a string representation of this CVSEntry
+ */
+ public String toString() {
+ return getAuthor() + "\n" + getDate() + "\n" + getFiles() + "\n"
+ + getComment();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogParser.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogParser.java
new file mode 100644
index 00000000..0096aadf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogParser.java
@@ -0,0 +1,321 @@
+/*
+ * 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.tools.ant.taskdefs.cvslib;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+
+import org.apache.tools.ant.taskdefs.AbstractCvsTask;
+import org.apache.tools.ant.util.CollectionUtils;
+
+/**
+ * A class used to parse the output of the CVS log command.
+ *
+ */
+class ChangeLogParser {
+ //private static final int GET_ENTRY = 0;
+ private static final int GET_FILE = 1;
+ private static final int GET_DATE = 2;
+ private static final int GET_COMMENT = 3;
+ private static final int GET_REVISION = 4;
+ private static final int GET_PREVIOUS_REV = 5;
+
+// FIXME formatters are not thread-safe
+
+ /** input format for dates read in from cvs log */
+ private static final SimpleDateFormat INPUT_DATE
+ = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US);
+ /**
+ * New formatter used to parse CVS date/timestamp.
+ */
+ private static final SimpleDateFormat CVS1129_INPUT_DATE =
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z", Locale.US);
+
+ static {
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+ INPUT_DATE.setTimeZone(utc);
+ CVS1129_INPUT_DATE.setTimeZone(utc);
+ }
+
+ //The following is data used while processing stdout of CVS command
+ private String file;
+ private String date;
+ private String author;
+ private String comment;
+ private String revision;
+ private String previousRevision;
+
+ private int status = GET_FILE;
+
+ /** rcs entries */
+ private final Hashtable entries = new Hashtable();
+
+ private final boolean remote;
+ private final String[] moduleNames;
+ private final int[] moduleNameLengths;
+
+ public ChangeLogParser() {
+ this(false, "", CollectionUtils.EMPTY_LIST);
+ }
+
+ public ChangeLogParser(boolean remote, String packageName, List modules) {
+ this.remote = remote;
+
+ ArrayList names = new ArrayList();
+ if (packageName != null) {
+ for (StringTokenizer tok = new StringTokenizer(packageName);
+ tok.hasMoreTokens();) {
+ names.add(tok.nextToken());
+ }
+ }
+ for (Iterator iter = modules.iterator(); iter.hasNext();) {
+ AbstractCvsTask.Module m = (AbstractCvsTask.Module) iter.next();
+ names.add(m.getName());
+ }
+
+ moduleNames = (String[]) names.toArray(new String[names.size()]);
+ moduleNameLengths = new int[moduleNames.length];
+ for (int i = 0; i < moduleNames.length; i++) {
+ moduleNameLengths[i] = moduleNames[i].length();
+ }
+ }
+
+ /**
+ * Get a list of rcs entries as an array.
+ *
+ * @return a list of rcs entries as an array
+ */
+ public CVSEntry[] getEntrySetAsArray() {
+ final CVSEntry[] array = new CVSEntry[ entries.size() ];
+ int i = 0;
+ for (Enumeration e = entries.elements(); e.hasMoreElements();) {
+ array[i++] = (CVSEntry) e.nextElement();
+ }
+ return array;
+ }
+
+ /**
+ * Receive notification about the process writing
+ * to standard output.
+ * @param line the line to process
+ */
+ public void stdout(final String line) {
+ switch(status) {
+ case GET_FILE:
+ // make sure attributes are reset when
+ // working on a 'new' file.
+ reset();
+ processFile(line);
+ break;
+ case GET_REVISION:
+ processRevision(line);
+ break;
+
+ case GET_DATE:
+ processDate(line);
+ break;
+
+ case GET_COMMENT:
+ processComment(line);
+ break;
+
+ case GET_PREVIOUS_REV:
+ processGetPreviousRevision(line);
+ break;
+
+ default:
+ // Do nothing
+ break;
+ }
+ }
+
+ /**
+ * Process a line while in "GET_COMMENT" state.
+ *
+ * @param line the line
+ */
+ private void processComment(final String line) {
+ final String lineSeparator = System.getProperty("line.separator");
+ if (line.equals(
+ "=============================================================================")) {
+ //We have ended changelog for that particular file
+ //so we can save it
+ final int end
+ = comment.length() - lineSeparator.length(); //was -1
+ comment = comment.substring(0, end);
+ saveEntry();
+ status = GET_FILE;
+ } else if (line.equals("----------------------------")) {
+ final int end
+ = comment.length() - lineSeparator.length(); //was -1
+ comment = comment.substring(0, end);
+ status = GET_PREVIOUS_REV;
+ } else {
+ comment += line + lineSeparator;
+ }
+ }
+
+ /**
+ * Process a line while in "GET_FILE" state.
+ *
+ * @param line the line to process
+ */
+ private void processFile(final String line) {
+ if (!remote && line.startsWith("Working file:")) {
+ // CheckStyle:MagicNumber OFF
+ file = line.substring(14, line.length());
+ // CheckStyle:MagicNumber ON
+ status = GET_REVISION;
+ } else if (remote && line.startsWith("RCS file:")) {
+ // exclude the part of the RCS filename up to and
+ // including the module name (and the path separator)
+ int startOfFileName = 0;
+ for (int i = 0; i < moduleNames.length; i++) {
+ int index = line.indexOf(moduleNames[i]);
+ if (index >= 0) {
+ startOfFileName = index + moduleNameLengths[i] + 1;
+ break;
+ }
+ }
+ int endOfFileName = line.indexOf(",v");
+ if (endOfFileName == -1) {
+ file = line.substring(startOfFileName);
+ } else {
+ file = line.substring(startOfFileName, endOfFileName);
+ }
+ status = GET_REVISION;
+ }
+ }
+
+ /**
+ * Process a line while in "REVISION" state.
+ *
+ * @param line the line to process
+ */
+ private void processRevision(final String line) {
+ if (line.startsWith("revision")) {
+ // CheckStyle:MagicNumber OFF
+ revision = line.substring(9);
+ // CheckStyle:MagicNumber ON
+ status = GET_DATE;
+ } else if (line.startsWith("======")) {
+ //There were no revisions in this changelog
+ //entry so lets move onto next file
+ status = GET_FILE;
+ }
+ }
+
+ /**
+ * Process a line while in "DATE" state.
+ *
+ * @param line the line to process
+ */
+ private void processDate(final String line) {
+ if (line.startsWith("date:")) {
+ // The date format is using a - format since 1.12.9 so we have:
+ // 1.12.9-: 'date: YYYY/mm/dd HH:mm:ss; author: name;'
+ // 1.12.9+: 'date: YYYY-mm-dd HH:mm:ss Z; author: name'
+ int endOfDateIndex = line.indexOf(';');
+ date = line.substring("date: ".length(), endOfDateIndex);
+
+ int startOfAuthorIndex = line.indexOf("author: ", endOfDateIndex + 1);
+ int endOfAuthorIndex = line.indexOf(';', startOfAuthorIndex + 1);
+ author = line.substring("author: ".length() + startOfAuthorIndex, endOfAuthorIndex);
+
+ status = GET_COMMENT;
+
+ //Reset comment to empty here as we can accumulate multiple lines
+ //in the processComment method
+ comment = "";
+ }
+ }
+
+ /**
+ * Process a line while in "GET_PREVIOUS_REVISION" state.
+ *
+ * @param line the line to process
+ */
+ private void processGetPreviousRevision(final String line) {
+ if (!line.startsWith("revision ")) {
+ throw new IllegalStateException("Unexpected line from CVS: "
+ + line);
+ }
+ previousRevision = line.substring("revision ".length());
+
+ saveEntry();
+
+ revision = previousRevision;
+ status = GET_DATE;
+ }
+
+ /**
+ * Utility method that saves the current entry.
+ */
+ private void saveEntry() {
+ final String entryKey = date + author + comment;
+ CVSEntry entry;
+ if (!entries.containsKey(entryKey)) {
+ Date dateObject = parseDate(date);
+ entry = new CVSEntry(dateObject, author, comment);
+ entries.put(entryKey, entry);
+ } else {
+ entry = (CVSEntry) entries.get(entryKey);
+ }
+
+ entry.addFile(file, revision, previousRevision);
+ }
+
+ /**
+ * Parse date out from expected format.
+ *
+ * @param date the string holding date
+ * @return the date object or null if unknown date format
+ */
+ private Date parseDate(final String date) {
+ try {
+ return INPUT_DATE.parse(date);
+ } catch (ParseException e) {
+ try {
+ return CVS1129_INPUT_DATE.parse(date);
+ } catch (ParseException e2) {
+ throw new IllegalStateException("Invalid date format: " + date);
+ }
+ }
+ }
+
+ /**
+ * Reset all internal attributes except status.
+ */
+ public void reset() {
+ this.file = null;
+ this.date = null;
+ this.author = null;
+ this.comment = null;
+ this.revision = null;
+ this.previousRevision = null;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogTask.java
new file mode 100644
index 00000000..b4b51ced
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogTask.java
@@ -0,0 +1,489 @@
+/*
+ * 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.tools.ant.taskdefs.cvslib;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.AbstractCvsTask;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Examines the output of cvs log and group related changes together.
+ *
+ * It produces an XML output representing the list of changes.
+ * <pre>
+ * <font color=#0000ff>&lt;!-- Root element --&gt;</font>
+ * <font color=#6a5acd>&lt;!ELEMENT</font> changelog <font color=#ff00ff>
+ * (entry</font><font color=#ff00ff>+</font><font color=#ff00ff>)
+ * </font><font color=#6a5acd>&gt;</font>
+ * <font color=#0000ff>&lt;!-- CVS Entry --&gt;</font>
+ * <font color=#6a5acd>&lt;!ELEMENT</font> entry <font color=#ff00ff>
+ * (date,author,file</font><font color=#ff00ff>+</font><font color=#ff00ff>,msg)
+ * </font><font color=#6a5acd>&gt;</font>
+ * <font color=#0000ff>&lt;!-- Date of cvs entry --&gt;</font>
+ * <font color=#6a5acd>&lt;!ELEMENT</font> date <font color=#ff00ff>(#PCDATA)
+ * </font><font color=#6a5acd>&gt;</font>
+ * <font color=#0000ff>&lt;!-- Author of change --&gt;</font>
+ * <font color=#6a5acd>&lt;!ELEMENT</font> author <font color=#ff00ff>(#PCDATA)
+ * </font><font color=#6a5acd>&gt;</font>
+ * <font color=#0000ff>&lt;!-- List of files affected --&gt;</font>
+ * <font color=#6a5acd>&lt;!ELEMENT</font> msg <font color=#ff00ff>(#PCDATA)
+ * </font><font color=#6a5acd>&gt;</font>
+ * <font color=#0000ff>&lt;!-- File changed --&gt;</font>
+ * <font color=#6a5acd>&lt;!ELEMENT</font> file <font color=#ff00ff>
+ * (name,revision,prevrevision</font><font color=#ff00ff>?</font>
+ * <font color=#ff00ff>)</font><font color=#6a5acd>&gt;</font>
+ * <font color=#0000ff>&lt;!-- Name of the file --&gt;</font>
+ * <font color=#6a5acd>&lt;!ELEMENT</font> name <font color=#ff00ff>(#PCDATA)
+ * </font><font color=#6a5acd>&gt;</font>
+ * <font color=#0000ff>&lt;!-- Revision number --&gt;</font>
+ * <font color=#6a5acd>&lt;!ELEMENT</font> revision <font color=#ff00ff>
+ * (#PCDATA)</font><font color=#6a5acd>&gt;</font>
+ * <font color=#0000ff>&lt;!-- Previous revision number --&gt;</font>
+ * <font color=#6a5acd>&lt;!ELEMENT</font> prevrevision <font color=#ff00ff>
+ * (#PCDATA)</font><font color=#6a5acd>&gt;</font>
+ * </pre>
+ *
+ * @since Ant 1.5
+ * @ant.task name="cvschangelog" category="scm"
+ */
+public class ChangeLogTask extends AbstractCvsTask {
+ /** User list */
+ private File usersFile;
+
+ /** User list */
+ private Vector cvsUsers = new Vector();
+
+ /** Input dir */
+ private File inputDir;
+
+ /** Output file */
+ private File destFile;
+
+ /** The earliest date at which to start processing entries. */
+ private Date startDate;
+
+ /** The latest date at which to stop processing entries. */
+ private Date endDate;
+
+ /** Determines whether log (false) or rlog (true) is used */
+ private boolean remote = false;
+
+ /** Start tag when doing tag ranges. */
+ private String startTag;
+
+ /** End tag when doing tag ranges. */
+ private String endTag;
+
+ /**
+ * Filesets containing list of files against which the cvs log will be
+ * performed. If empty then all files in the working directory will
+ * be checked.
+ */
+ private final Vector filesets = new Vector();
+
+
+ /**
+ * Set the base dir for cvs.
+ *
+ * @param inputDir The new dir value
+ */
+ public void setDir(final File inputDir) {
+ this.inputDir = inputDir;
+ }
+
+
+ /**
+ * Set the output file for the log.
+ *
+ * @param destFile The new destfile value
+ */
+ public void setDestfile(final File destFile) {
+ this.destFile = destFile;
+ }
+
+
+ /**
+ * Set a lookup list of user names &amp; addresses
+ *
+ * @param usersFile The file containing the users info.
+ */
+ public void setUsersfile(final File usersFile) {
+ this.usersFile = usersFile;
+ }
+
+
+ /**
+ * Add a user to list changelog knows about.
+ *
+ * @param user the user
+ */
+ public void addUser(final CvsUser user) {
+ cvsUsers.addElement(user);
+ }
+
+
+ /**
+ * Set the date at which the changelog should start.
+ *
+ * @param start The date at which the changelog should start.
+ */
+ public void setStart(final Date start) {
+ this.startDate = start;
+ }
+
+
+ /**
+ * Set the date at which the changelog should stop.
+ *
+ * @param endDate The date at which the changelog should stop.
+ */
+ public void setEnd(final Date endDate) {
+ this.endDate = endDate;
+ }
+
+
+ /**
+ * Set the number of days worth of log entries to process.
+ *
+ * @param days the number of days of log to process.
+ */
+ public void setDaysinpast(final int days) {
+ // CheckStyle:MagicNumber OFF
+ final long time = System.currentTimeMillis()
+ - (long) days * 24 * 60 * 60 * 1000;
+ // CheckStyle:MagicNumber ON
+
+ setStart(new Date(time));
+ }
+
+ /**
+ * Whether to use rlog against a remote repository instead of log
+ * in a working copy's directory.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setRemote(final boolean remote) {
+ this.remote = remote;
+ }
+
+ /**
+ * Set the tag at which the changelog should start.
+ *
+ * @param start The date at which the changelog should start.
+ */
+ public void setStartTag(final String start) {
+ this.startTag = start;
+ }
+
+
+ /**
+ * Set the tag at which the changelog should stop.
+ *
+ * @param end The date at which the changelog should stop.
+ */
+ public void setEndTag(final String end) {
+ this.endTag = end;
+ }
+
+ /**
+ * Adds a set of files about which cvs logs will be generated.
+ *
+ * @param fileSet a set of files about which cvs logs will be generated.
+ */
+ public void addFileset(final FileSet fileSet) {
+ filesets.addElement(fileSet);
+ }
+
+
+ /**
+ * Execute task
+ *
+ * @exception BuildException if something goes wrong executing the
+ * cvs command
+ */
+ public void execute() throws BuildException {
+ File savedDir = inputDir; // may be altered in validate
+
+ try {
+
+ validate();
+ final Properties userList = new Properties();
+
+ loadUserlist(userList);
+
+ final int size = cvsUsers.size();
+ for (int i = 0; i < size; i++) {
+ final CvsUser user = (CvsUser) cvsUsers.get(i);
+ user.validate();
+ userList.put(user.getUserID(), user.getDisplayname());
+ }
+
+ if (!remote) {
+ setCommand("log");
+
+ if (getTag() != null) {
+ CvsVersion myCvsVersion = new CvsVersion();
+ myCvsVersion.setProject(getProject());
+ myCvsVersion.setTaskName("cvsversion");
+ myCvsVersion.setCvsRoot(getCvsRoot());
+ myCvsVersion.setCvsRsh(getCvsRsh());
+ myCvsVersion.setPassfile(getPassFile());
+ myCvsVersion.setDest(inputDir);
+ myCvsVersion.execute();
+ if (myCvsVersion.supportsCvsLogWithSOption()) {
+ addCommandArgument("-S");
+ }
+ }
+ } else {
+ // supply 'rlog' as argument instead of command
+ setCommand("");
+ addCommandArgument("rlog");
+ // Do not print name/header if no revisions
+ // selected. This is quicker: less output to parse.
+ addCommandArgument("-S");
+ // Do not list tags. This is quicker: less output to
+ // parse.
+ addCommandArgument("-N");
+ }
+ if (null != startTag || null != endTag) {
+ // man, do I get spoiled by C#'s ?? operator
+ String startValue = startTag == null ? "" : startTag;
+ String endValue = endTag == null ? "" : endTag;
+ addCommandArgument("-r" + startValue + "::" + endValue);
+ } else if (null != startDate) {
+ final SimpleDateFormat outputDate =
+ new SimpleDateFormat("yyyy-MM-dd");
+
+ // We want something of the form: -d ">=YYYY-MM-dd"
+ final String dateRange = ">=" + outputDate.format(startDate);
+
+ // Supply '-d' as a separate argument - Bug# 14397
+ addCommandArgument("-d");
+ addCommandArgument(dateRange);
+ }
+
+ // Check if list of files to check has been specified
+ if (!filesets.isEmpty()) {
+ final Enumeration e = filesets.elements();
+
+ while (e.hasMoreElements()) {
+ final FileSet fileSet = (FileSet) e.nextElement();
+ final DirectoryScanner scanner =
+ fileSet.getDirectoryScanner(getProject());
+ final String[] files = scanner.getIncludedFiles();
+
+ for (int i = 0; i < files.length; i++) {
+ addCommandArgument(files[i]);
+ }
+ }
+ }
+
+ final ChangeLogParser parser = new ChangeLogParser(remote,
+ getPackage(),
+ getModules());
+ final RedirectingStreamHandler handler =
+ new RedirectingStreamHandler(parser);
+
+ log(getCommand(), Project.MSG_VERBOSE);
+
+ setDest(inputDir);
+ setExecuteStreamHandler(handler);
+ try {
+ super.execute();
+ } finally {
+ final String errors = handler.getErrors();
+
+ if (null != errors) {
+ log(errors, Project.MSG_ERR);
+ }
+ }
+ final CVSEntry[] entrySet = parser.getEntrySetAsArray();
+ final CVSEntry[] filteredEntrySet = filterEntrySet(entrySet);
+
+ replaceAuthorIdWithName(userList, filteredEntrySet);
+
+ writeChangeLog(filteredEntrySet);
+
+ } finally {
+ inputDir = savedDir;
+ }
+ }
+
+ /**
+ * Validate the parameters specified for task.
+ *
+ * @throws BuildException if fails validation checks
+ */
+ private void validate()
+ throws BuildException {
+ if (null == inputDir) {
+ inputDir = getProject().getBaseDir();
+ }
+ if (null == destFile) {
+ final String message = "Destfile must be set.";
+
+ throw new BuildException(message);
+ }
+ if (!inputDir.exists()) {
+ final String message = "Cannot find base dir "
+ + inputDir.getAbsolutePath();
+
+ throw new BuildException(message);
+ }
+ if (null != usersFile && !usersFile.exists()) {
+ final String message = "Cannot find user lookup list "
+ + usersFile.getAbsolutePath();
+
+ throw new BuildException(message);
+ }
+ if ((null != startTag || null != endTag)
+ && (null != startDate || null != endDate)) {
+ final String message = "Specify either a tag or date range,"
+ + " not both";
+ throw new BuildException(message);
+ }
+ }
+
+ /**
+ * Load the userlist from the userList file (if specified) and add to
+ * list of users.
+ *
+ * @param userList the file of users
+ * @throws BuildException if file can not be loaded for some reason
+ */
+ private void loadUserlist(final Properties userList)
+ throws BuildException {
+ if (null != usersFile) {
+ try {
+ userList.load(new FileInputStream(usersFile));
+ } catch (final IOException ioe) {
+ throw new BuildException(ioe.toString(), ioe);
+ }
+ }
+ }
+
+ /**
+ * Filter the specified entries according to an appropriate rule.
+ *
+ * @param entrySet the entry set to filter
+ * @return the filtered entry set
+ */
+ private CVSEntry[] filterEntrySet(final CVSEntry[] entrySet) {
+ final Vector results = new Vector();
+
+ for (int i = 0; i < entrySet.length; i++) {
+ final CVSEntry cvsEntry = entrySet[i];
+ final Date date = cvsEntry.getDate();
+
+ //bug#30471
+ //this is caused by Date.after throwing a NullPointerException
+ //for some reason there's no date set in the CVSEntry
+ //Java 1.3.1 API
+ //http://java.sun.com/j2se/1.3/docs/api/java/util/Date.html#after(java.util.Date)
+ //doesn't throw NullPointerException
+ //Java 1.4.2 + 1.5 API
+ //http://java.sun.com/j2se/1.4.2/docs/api/java/util/Date.html#after(java.util.Date)
+ //according to the docs it doesn't throw, according to the bug report it does
+ //http://java.sun.com/j2se/1.5.0/docs/api/java/util/Date.html#after(java.util.Date)
+ //according to the docs it does throw
+
+ //for now skip entries which are missing a date
+ if (null == date) {
+ continue;
+ }
+
+ if (null != startDate && startDate.after(date)) {
+ //Skip dates that are too early
+ continue;
+ }
+ if (null != endDate && endDate.before(date)) {
+ //Skip dates that are too late
+ continue;
+ }
+ results.addElement(cvsEntry);
+ }
+
+ final CVSEntry[] resultArray = new CVSEntry[results.size()];
+
+ results.copyInto(resultArray);
+ return resultArray;
+ }
+
+ /**
+ * replace all known author's id's with their maven specified names
+ */
+ private void replaceAuthorIdWithName(final Properties userList,
+ final CVSEntry[] entrySet) {
+ for (int i = 0; i < entrySet.length; i++) {
+
+ final CVSEntry entry = entrySet[ i ];
+ if (userList.containsKey(entry.getAuthor())) {
+ entry.setAuthor(userList.getProperty(entry.getAuthor()));
+ }
+ }
+ }
+
+ /**
+ * Print changelog to file specified in task.
+ *
+ * @param entrySet the entry set to write.
+ * @throws BuildException if there is an error writing changelog.
+ */
+ private void writeChangeLog(final CVSEntry[] entrySet)
+ throws BuildException {
+ FileOutputStream output = null;
+
+ try {
+ output = new FileOutputStream(destFile);
+
+ final PrintWriter writer =
+ new PrintWriter(new OutputStreamWriter(output, "UTF-8"));
+
+ final ChangeLogWriter serializer = new ChangeLogWriter();
+
+ serializer.printChangeLog(writer, entrySet);
+
+ if (writer.checkError()) {
+ throw new IOException("Encountered an error writing changelog");
+ }
+ } catch (final UnsupportedEncodingException uee) {
+ getProject().log(uee.toString(), Project.MSG_ERR);
+ } catch (final IOException ioe) {
+ throw new BuildException(ioe.toString(), ioe);
+ } finally {
+ FileUtils.close(output);
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogWriter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogWriter.java
new file mode 100644
index 00000000..2385e51f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogWriter.java
@@ -0,0 +1,114 @@
+/*
+ * 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.tools.ant.taskdefs.cvslib;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Enumeration;
+import java.util.TimeZone;
+
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.DOMUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Class used to generate an XML changelog.
+ *
+ */
+public class ChangeLogWriter {
+ /** output format for dates written to xml file */
+ private static final SimpleDateFormat OUTPUT_DATE
+ = new SimpleDateFormat("yyyy-MM-dd");
+ /** output format for times written to xml file */
+ private static final SimpleDateFormat OUTPUT_TIME
+ = new SimpleDateFormat("HH:mm");
+ /** stateless helper for writing the XML document */
+ private static final DOMElementWriter DOM_WRITER = new DOMElementWriter();
+
+ static {
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+ OUTPUT_DATE.setTimeZone(utc);
+ OUTPUT_TIME.setTimeZone(utc);
+ }
+
+ /**
+ * Print out the specified entries.
+ *
+ * @param output writer to which to send output.
+ * @param entries the entries to be written.
+ */
+ public void printChangeLog(final PrintWriter output,
+ final CVSEntry[] entries) {
+ try {
+ output.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ Document doc = DOMUtils.newDocument();
+ Element root = doc.createElement("changelog");
+ DOM_WRITER.openElement(root, output, 0, "\t");
+ output.println();
+ for (int i = 0; i < entries.length; i++) {
+ final CVSEntry entry = entries[i];
+
+ printEntry(doc, output, entry);
+ }
+ DOM_WRITER.closeElement(root, output, 0, "\t", true);
+ output.flush();
+ output.close();
+ } catch (IOException e) {
+ throw new org.apache.tools.ant.BuildException(e);
+ }
+ }
+
+
+ /**
+ * Print out an individual entry in changelog.
+ *
+ * @param doc Document used to create elements.
+ * @param entry the entry to print
+ * @param output writer to which to send output.
+ */
+ private void printEntry(Document doc, final PrintWriter output,
+ final CVSEntry entry) throws IOException {
+ Element ent = doc.createElement("entry");
+ DOMUtils.appendTextElement(ent, "date",
+ OUTPUT_DATE.format(entry.getDate()));
+ DOMUtils.appendTextElement(ent, "time",
+ OUTPUT_TIME.format(entry.getDate()));
+ DOMUtils.appendCDATAElement(ent, "author", entry.getAuthor());
+
+ final Enumeration enumeration = entry.getFiles().elements();
+
+ while (enumeration.hasMoreElements()) {
+ final RCSFile file = (RCSFile) enumeration.nextElement();
+
+ Element f = DOMUtils.createChildElement(ent, "file");
+ DOMUtils.appendCDATAElement(f, "name", file.getName());
+ DOMUtils.appendTextElement(f, "revision", file.getRevision());
+
+ final String previousRevision = file.getPreviousRevision();
+ if (previousRevision != null) {
+ DOMUtils.appendTextElement(f, "prevrevision",
+ previousRevision);
+ }
+ }
+ DOMUtils.appendCDATAElement(ent, "msg", entry.getComment());
+ DOM_WRITER.write(ent, output, 1, "\t");
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagDiff.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagDiff.java
new file mode 100644
index 00000000..a90bcc01
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagDiff.java
@@ -0,0 +1,576 @@
+/*
+ * 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.tools.ant.taskdefs.cvslib;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.AbstractCvsTask;
+import org.apache.tools.ant.util.CollectionUtils;
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.DOMUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Examines the output of cvs rdiff between two tags.
+ *
+ * It produces an XML output representing the list of changes.
+ * <PRE>
+ * &lt;!-- Root element --&gt;
+ * &lt;!ELEMENT tagdiff ( entry+ ) &gt;
+ * &lt;!-- Start tag of the report --&gt;
+ * &lt;!ATTLIST tagdiff startTag NMTOKEN #IMPLIED &gt;
+ * &lt;!-- End tag of the report --&gt;
+ * &lt;!ATTLIST tagdiff endTag NMTOKEN #IMPLIED &gt;
+ * &lt;!-- Start date of the report --&gt;
+ * &lt;!ATTLIST tagdiff startDate NMTOKEN #IMPLIED &gt;
+ * &lt;!-- End date of the report --&gt;
+ * &lt;!ATTLIST tagdiff endDate NMTOKEN #IMPLIED &gt;
+ *
+ * &lt;!-- CVS tag entry --&gt;
+ * &lt;!ELEMENT entry ( file ) &gt;
+ * &lt;!-- File added, changed or removed --&gt;
+ * &lt;!ELEMENT file ( name, revision?, prevrevision? ) &gt;
+ * &lt;!-- Name of the file --&gt;
+ * &lt;!ELEMENT name ( #PCDATA ) &gt;
+ * &lt;!-- Revision number --&gt;
+ * &lt;!ELEMENT revision ( #PCDATA ) &gt;
+ * &lt;!-- Previous revision number --&gt;
+ * &lt;!ELEMENT prevrevision ( #PCDATA ) &gt;
+ * </PRE>
+ *
+ * @since Ant 1.5
+ * @ant.task name="cvstagdiff"
+ */
+public class CvsTagDiff extends AbstractCvsTask {
+
+ /**
+ * Used to create the temp file for cvs log
+ */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** stateless helper for writing the XML document */
+ private static final DOMElementWriter DOM_WRITER = new DOMElementWriter();
+
+ /**
+ * Token to identify the word file in the rdiff log
+ */
+ static final String FILE_STRING = "File ";
+ /**
+ * Length of token to identify the word file in the rdiff log
+ */
+ static final int FILE_STRING_LENGTH = FILE_STRING.length();
+ /**
+ * Token to identify the word file in the rdiff log
+ */
+ static final String TO_STRING = " to ";
+ /**
+ * Token to identify a new file in the rdiff log
+ */
+ static final String FILE_IS_NEW = " is new;";
+ /**
+ * Token to identify the revision
+ */
+ static final String REVISION = "revision ";
+
+ /**
+ * Token to identify a modified file in the rdiff log
+ */
+ static final String FILE_HAS_CHANGED = " changed from revision ";
+
+ /**
+ * Token to identify a removed file in the rdiff log
+ */
+ static final String FILE_WAS_REMOVED = " is removed";
+
+ /**
+ * The cvs package/module to analyse
+ */
+ private String mypackage;
+
+ /**
+ * The earliest tag from which diffs are to be included in the report.
+ */
+ private String mystartTag;
+
+ /**
+ * The latest tag from which diffs are to be included in the report.
+ */
+ private String myendTag;
+
+ /**
+ * The earliest date from which diffs are to be included in the report.
+ */
+ private String mystartDate;
+
+ /**
+ * The latest date from which diffs are to be included in the report.
+ */
+ private String myendDate;
+
+ /**
+ * The file in which to write the diff report.
+ */
+ private File mydestfile;
+
+ /**
+ * Used to skip over removed files
+ */
+ private boolean ignoreRemoved = false;
+
+ /**
+ * temporary list of package names.
+ */
+ private List packageNames = new ArrayList();
+
+ /**
+ * temporary list of "File:" + package name + "/" for all packages.
+ */
+ private String[] packageNamePrefixes = null;
+
+ /**
+ * temporary list of length values for prefixes.
+ */
+ private int[] packageNamePrefixLengths = null;
+
+ /**
+ * The package/module to analyze.
+ * @param p the name of the package to analyse
+ */
+ @Override
+ public void setPackage(String p) {
+ mypackage = p;
+ }
+
+ /**
+ * Set the start tag.
+ *
+ * @param s the start tag.
+ */
+ public void setStartTag(String s) {
+ mystartTag = s;
+ }
+
+ /**
+ * Set the start date.
+ *
+ * @param s the start date.
+ */
+ public void setStartDate(String s) {
+ mystartDate = s;
+ }
+
+ /**
+ * Set the end tag.
+ *
+ * @param s the end tag.
+ */
+ public void setEndTag(String s) {
+ myendTag = s;
+ }
+
+ /**
+ * Set the end date.
+ *
+ * @param s the end date.
+ */
+ public void setEndDate(String s) {
+ myendDate = s;
+ }
+
+ /**
+ * Set the output file for the diff.
+ *
+ * @param f the output file for the diff.
+ */
+ public void setDestFile(File f) {
+ mydestfile = f;
+ }
+
+ /**
+ * Set the ignore removed indicator.
+ *
+ * @param b the ignore removed indicator.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setIgnoreRemoved(boolean b) {
+ ignoreRemoved = b;
+ }
+
+
+ /**
+ * Execute task.
+ *
+ * @exception BuildException if an error occurs
+ */
+ @Override
+ public void execute() throws BuildException {
+ // validate the input parameters
+ validate();
+
+ // build the rdiff command
+ addCommandArgument("rdiff");
+ addCommandArgument("-s");
+ if (mystartTag != null) {
+ addCommandArgument("-r");
+ addCommandArgument(mystartTag);
+ } else {
+ addCommandArgument("-D");
+ addCommandArgument(mystartDate);
+ }
+ if (myendTag != null) {
+ addCommandArgument("-r");
+ addCommandArgument(myendTag);
+ } else {
+ addCommandArgument("-D");
+ addCommandArgument(myendDate);
+ }
+
+ // force command not to be null
+ setCommand("");
+ File tmpFile = null;
+ try {
+ handlePackageNames();
+
+ tmpFile = FILE_UTILS.createTempFile("cvstagdiff", ".log", null,
+ true, true);
+ setOutput(tmpFile);
+
+ // run the cvs command
+ super.execute();
+
+ // parse the rdiff
+ CvsTagEntry[] entries = parseRDiff(tmpFile);
+
+ // write the tag diff
+ writeTagDiff(entries);
+
+ } finally {
+ packageNamePrefixes = null;
+ packageNamePrefixLengths = null;
+ packageNames.clear();
+ if (tmpFile != null) {
+ tmpFile.delete();
+ }
+ }
+ }
+
+ /**
+ * Parse the tmpFile and return and array of CvsTagEntry to be
+ * written in the output.
+ *
+ * @param tmpFile the File containing the output of the cvs rdiff command
+ * @return the entries in the output
+ * @exception BuildException if an error occurs
+ */
+ private CvsTagEntry[] parseRDiff(File tmpFile) throws BuildException {
+ // parse the output of the command
+ BufferedReader reader = null;
+
+ try {
+ reader = new BufferedReader(new FileReader(tmpFile));
+
+ // entries are of the form:
+ //CVS 1.11
+ // File module/filename is new; current revision 1.1
+ //CVS 1.11.9
+ // File module/filename is new; cvstag_2003_11_03_2 revision 1.1
+ // or
+ // File module/filename changed from revision 1.4 to 1.6
+ // or
+ // File module/filename is removed; not included in
+ // release tag SKINLF_12
+ //CVS 1.11.9
+ // File testantoine/antoine.bat is removed; TESTANTOINE_1 revision 1.1.1.1
+ //
+ // get rid of 'File module/"
+ Vector entries = new Vector();
+
+ String line = reader.readLine();
+
+ while (null != line) {
+ line = removePackageName(line, packageNamePrefixes,
+ packageNamePrefixLengths);
+ if (line != null) {
+ // use || in a perl like fashion
+ boolean processed
+ = doFileIsNew(entries, line)
+ || doFileHasChanged(entries, line)
+ || doFileWasRemoved(entries, line);
+ }
+ line = reader.readLine();
+ }
+
+ CvsTagEntry[] array = new CvsTagEntry[entries.size()];
+ entries.copyInto(array);
+
+ return array;
+ } catch (IOException e) {
+ throw new BuildException("Error in parsing", e);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ log(e.toString(), Project.MSG_ERR);
+ }
+ }
+ }
+ }
+
+ private boolean doFileIsNew(Vector entries, String line) {
+ int index = line.indexOf(FILE_IS_NEW);
+ if (index == -1) {
+ return false;
+ }
+ // it is a new file
+ // set the revision but not the prevrevision
+ String filename = line.substring(0, index);
+ String rev = null;
+ int indexrev = line.indexOf(REVISION, index);
+ if (indexrev != -1) {
+ rev = line.substring(indexrev + REVISION.length());
+ }
+ CvsTagEntry entry = new CvsTagEntry(filename, rev);
+ entries.addElement(entry);
+ log(entry.toString(), Project.MSG_VERBOSE);
+ return true;
+ }
+
+ private boolean doFileHasChanged(Vector entries, String line) {
+ int index = line.indexOf(FILE_HAS_CHANGED);
+ if (index == -1) {
+ return false;
+ }
+ // it is a modified file
+ // set the revision and the prevrevision
+ String filename = line.substring(0, index);
+ int revSeparator = line.indexOf(" to ", index);
+ String prevRevision =
+ line.substring(index + FILE_HAS_CHANGED.length(),
+ revSeparator);
+ String revision = line.substring(revSeparator + TO_STRING.length());
+ CvsTagEntry entry = new CvsTagEntry(filename,
+ revision,
+ prevRevision);
+ entries.addElement(entry);
+ log(entry.toString(), Project.MSG_VERBOSE);
+ return true;
+ }
+
+ private boolean doFileWasRemoved(Vector entries, String line) {
+ if (ignoreRemoved) {
+ return false;
+ }
+ int index = line.indexOf(FILE_WAS_REMOVED);
+ if (index == -1) {
+ return false;
+ }
+ // it is a removed file
+ String filename = line.substring(0, index);
+ String rev = null;
+ int indexrev = line.indexOf(REVISION, index);
+ if (indexrev != -1) {
+ rev = line.substring(indexrev + REVISION.length());
+ }
+ CvsTagEntry entry = new CvsTagEntry(filename, null, rev);
+ entries.addElement(entry);
+ log(entry.toString(), Project.MSG_VERBOSE);
+ return true;
+ }
+
+ /**
+ * Write the rdiff log.
+ *
+ * @param entries a <code>CvsTagEntry[]</code> value
+ * @exception BuildException if an error occurs
+ */
+ private void writeTagDiff(CvsTagEntry[] entries) throws BuildException {
+ FileOutputStream output = null;
+ try {
+ output = new FileOutputStream(mydestfile);
+ PrintWriter writer = new PrintWriter(
+ new OutputStreamWriter(output, "UTF-8"));
+ writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ Document doc = DOMUtils.newDocument();
+ Element root = doc.createElement("tagdiff");
+ if (mystartTag != null) {
+ root.setAttribute("startTag", mystartTag);
+ } else {
+ root.setAttribute("startDate", mystartDate);
+ }
+ if (myendTag != null) {
+ root.setAttribute("endTag", myendTag);
+ } else {
+ root.setAttribute("endDate", myendDate);
+ }
+
+ root.setAttribute("cvsroot", getCvsRoot());
+ root.setAttribute("package",
+ CollectionUtils.flattenToString(packageNames));
+ DOM_WRITER.openElement(root, writer, 0, "\t");
+ writer.println();
+ for (int i = 0, c = entries.length; i < c; i++) {
+ writeTagEntry(doc, writer, entries[i]);
+ }
+ DOM_WRITER.closeElement(root, writer, 0, "\t", true);
+ writer.flush();
+ if (writer.checkError()) {
+ throw new IOException("Encountered an error writing tagdiff");
+ }
+ writer.close();
+ } catch (UnsupportedEncodingException uee) {
+ log(uee.toString(), Project.MSG_ERR);
+ } catch (IOException ioe) {
+ throw new BuildException(ioe.toString(), ioe);
+ } finally {
+ if (null != output) {
+ try {
+ output.close();
+ } catch (IOException ioe) {
+ log(ioe.toString(), Project.MSG_ERR);
+ }
+ }
+ }
+ }
+
+ /**
+ * Write a single entry to the given writer.
+ *
+ * @param doc Document used to create elements.
+ * @param writer a <code>PrintWriter</code> value
+ * @param entry a <code>CvsTagEntry</code> value
+ */
+ private void writeTagEntry(Document doc, PrintWriter writer,
+ CvsTagEntry entry)
+ throws IOException {
+ Element ent = doc.createElement("entry");
+ Element f = DOMUtils.createChildElement(ent, "file");
+ DOMUtils.appendCDATAElement(f, "name", entry.getFile());
+ if (entry.getRevision() != null) {
+ DOMUtils.appendTextElement(f, "revision", entry.getRevision());
+ }
+ if (entry.getPreviousRevision() != null) {
+ DOMUtils.appendTextElement(f, "prevrevision",
+ entry.getPreviousRevision());
+ }
+ DOM_WRITER.write(ent, writer, 1, "\t");
+ }
+
+ /**
+ * Validate the parameters specified for task.
+ *
+ * @exception BuildException if a parameter is not correctly set
+ */
+ private void validate() throws BuildException {
+ if (null == mypackage && getModules().size() == 0) {
+ throw new BuildException("Package/module must be set.");
+ }
+
+ if (null == mydestfile) {
+ throw new BuildException("Destfile must be set.");
+ }
+
+ if (null == mystartTag && null == mystartDate) {
+ throw new BuildException("Start tag or start date must be set.");
+ }
+
+ if (null != mystartTag && null != mystartDate) {
+ throw new BuildException("Only one of start tag and start date "
+ + "must be set.");
+ }
+
+ if (null == myendTag && null == myendDate) {
+ throw new BuildException("End tag or end date must be set.");
+ }
+
+ if (null != myendTag && null != myendDate) {
+ throw new BuildException("Only one of end tag and end date must "
+ + "be set.");
+ }
+ }
+
+ /**
+ * collects package names from the package attribute and nested
+ * module elements.
+ */
+ private void handlePackageNames() {
+ if (mypackage != null) {
+ // support multiple packages
+ StringTokenizer myTokenizer = new StringTokenizer(mypackage);
+ while (myTokenizer.hasMoreTokens()) {
+ String pack = myTokenizer.nextToken();
+ packageNames.add(pack);
+ addCommandArgument(pack);
+ }
+ }
+ for (Iterator iter = getModules().iterator(); iter.hasNext();) {
+ AbstractCvsTask.Module m = (AbstractCvsTask.Module) iter.next();
+ packageNames.add(m.getName());
+ // will be added to command line in super.execute()
+ }
+ packageNamePrefixes = new String[packageNames.size()];
+ packageNamePrefixLengths = new int[packageNames.size()];
+ for (int i = 0; i < packageNamePrefixes.length; i++) {
+ packageNamePrefixes[i] = FILE_STRING + packageNames.get(i) + "/";
+ packageNamePrefixLengths[i] = packageNamePrefixes[i].length();
+ }
+ }
+
+
+ /**
+ * removes a "File: module/" prefix if present.
+ *
+ * @return null if the line was shorter than expected.
+ */
+ private static String removePackageName(String line,
+ String[] packagePrefixes,
+ int[] prefixLengths) {
+ if (line.length() < FILE_STRING_LENGTH) {
+ return null;
+ }
+ boolean matched = false;
+ for (int i = 0; i < packagePrefixes.length; i++) {
+ if (line.startsWith(packagePrefixes[i])) {
+ matched = true;
+ line = line.substring(prefixLengths[i]);
+ break;
+ }
+ }
+ if (!matched) {
+ line = line.substring(FILE_STRING_LENGTH);
+ }
+ return line;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagEntry.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagEntry.java
new file mode 100644
index 00000000..6e349c7d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagEntry.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.tools.ant.taskdefs.cvslib;
+
+/**
+ * Holds the information of a line of rdiff
+ */
+public class CvsTagEntry {
+
+ /** the filename */
+ private String filename;
+
+ /** the previous revision */
+ private String prevRevision;
+
+ /** the revision */
+ private String revision;
+
+ /**
+ * Creates a new CvsTagEntry
+ * @param filename the filename to add
+ */
+ public CvsTagEntry(final String filename) {
+ this(filename, null, null);
+ }
+
+ /**
+ * Creates a new CvsTagEntry
+ * @param filename the filename to add
+ * @param revision the revision
+ */
+ public CvsTagEntry(final String filename, final String revision) {
+ this(filename, revision, null);
+ }
+
+ /**
+ * Creates a new CvsTagEntry
+ * @param filename the filename to add
+ * @param revision the revision
+ * @param prevRevision the previous revision
+ */
+ public CvsTagEntry(final String filename, final String revision,
+ final String prevRevision) {
+ this.filename = filename;
+ this.revision = revision;
+ this.prevRevision = prevRevision;
+ }
+
+ /**
+ * Gets the filename for this CvsTagEntry
+ * @return the filename
+ */
+ public String getFile() {
+ return filename;
+ }
+
+ /**
+ * Gets the revision for this CvsTagEntry
+ * @return the revision
+ */
+ public String getRevision() {
+ return revision;
+ }
+
+ /**
+ * Gets the previous revision for this CvsTagEntry
+ * @return the previous revision
+ */
+ public String getPreviousRevision() {
+ return prevRevision;
+ }
+
+ /**
+ * Gets a String containing filename and difference from previous version
+ * @return a string representation of this CVSTagEntry
+ */
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(filename);
+ if (revision == null) {
+ buffer.append(" was removed");
+ if (prevRevision != null) {
+ buffer.append("; previous revision was ").append(prevRevision);
+ }
+ } else if (prevRevision == null) {
+ buffer.append(" is new; current revision is ")
+ .append(revision);
+ } else {
+ buffer.append(" has changed from ")
+ .append(prevRevision).append(" to ").append(revision);
+ }
+ return buffer.toString();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsUser.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsUser.java
new file mode 100644
index 00000000..85a2fc6b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsUser.java
@@ -0,0 +1,93 @@
+/*
+ * 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.tools.ant.taskdefs.cvslib;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Represents a CVS user with a userID and a full name.
+ *
+ */
+public class CvsUser {
+ /** The user's Id */
+ private String userID;
+ /** The user's full name */
+ private String displayName;
+
+
+ /**
+ * Set the user's fullname
+ *
+ * @param displayName the user's full name
+ */
+ public void setDisplayname(final String displayName) {
+ this.displayName = displayName;
+ }
+
+
+ /**
+ * Set the user's id
+ *
+ * @param userID the user's new id value.
+ */
+ public void setUserid(final String userID) {
+ this.userID = userID;
+ }
+
+
+ /**
+ * Get the user's id.
+ *
+ * @return The userID value
+ */
+ public String getUserID() {
+ return userID;
+ }
+
+
+ /**
+ * Get the user's full name
+ *
+ * @return the user's full name
+ */
+ public String getDisplayname() {
+ return displayName;
+ }
+
+
+ /**
+ * Validate that this object is configured.
+ *
+ * @exception BuildException if the instance has not be correctly
+ * configured.
+ */
+ public void validate() throws BuildException {
+ if (null == userID) {
+ final String message = "Username attribute must be set.";
+
+ throw new BuildException(message);
+ }
+ if (null == displayName) {
+ final String message =
+ "Displayname attribute must be set for userID " + userID;
+
+ throw new BuildException(message);
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsVersion.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsVersion.java
new file mode 100644
index 00000000..618da4ec
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsVersion.java
@@ -0,0 +1,169 @@
+/*
+ * 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.tools.ant.taskdefs.cvslib;
+
+import java.io.ByteArrayOutputStream;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.AbstractCvsTask;
+
+/**
+ * this task allows to find out the client and the server version of a
+ * CVS installation
+ *
+ * example usage :
+ * &lt;cvsversion
+ * cvsRoot=&quot;:pserver:anoncvs@cvs.apache.org:/home/cvspublic&quot;
+ * passfile=&quot;c:/programme/cygwin/home/antoine/.cvspass&quot;
+ * clientversionproperty=&quot;apacheclient&quot;
+ * serverversionproperty=&quot;apacheserver&quot; /&gt;
+ *
+ * the task can be used also in the API by calling its execute method,
+ * then calling getServerVersion and/or getClientVersion
+ *
+ * @ant.task category="scm"
+ * @since ant 1.6.1
+ */
+public class CvsVersion extends AbstractCvsTask {
+ static final long VERSION_1_11_2 = 11102;
+ static final long MULTIPLY = 100;
+ private String clientVersion;
+ private String serverVersion;
+ private String clientVersionProperty;
+ private String serverVersionProperty;
+
+ /**
+ * Get the CVS client version
+ * @return CVS client version
+ */
+ public String getClientVersion() {
+ return clientVersion;
+ }
+ /**
+ * Get the CVS server version
+ * @return CVS server version
+ */
+ public String getServerVersion() {
+ return serverVersion;
+ }
+ /**
+ * Set a property where to store the CVS client version
+ * @param clientVersionProperty property for CVS client version
+ */
+ public void setClientVersionProperty(String clientVersionProperty) {
+ this.clientVersionProperty = clientVersionProperty;
+ }
+
+ /**
+ * Set a property where to store the CVS server version
+ * @param serverVersionProperty property for CVS server version
+ */
+ public void setServerVersionProperty(String serverVersionProperty) {
+ this.serverVersionProperty = serverVersionProperty;
+ }
+ /**
+ * Find out if the server version supports log with S option
+ * @return boolean indicating if the server version supports log with S option
+ */
+ public boolean supportsCvsLogWithSOption() {
+ if (serverVersion == null) {
+ return false;
+ }
+ StringTokenizer tokenizer = new StringTokenizer(serverVersion, ".");
+ long counter = MULTIPLY * MULTIPLY;
+ long version = 0;
+ while (tokenizer.hasMoreTokens()) {
+ String s = tokenizer.nextToken();
+ int i = 0;
+ for (i = 0; i < s.length(); i++) {
+ if (!Character.isDigit(s.charAt(i))) {
+ break;
+ }
+ }
+ String s2 = s.substring(0, i);
+ version = version + counter * Long.parseLong(s2);
+ if (counter == 1) {
+ break;
+ }
+ counter = counter / MULTIPLY;
+ }
+ return (version >= VERSION_1_11_2);
+ }
+ /**
+ * the execute method running CvsVersion
+ */
+ public void execute() {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ this.setOutputStream(bos);
+ ByteArrayOutputStream berr = new ByteArrayOutputStream();
+ this.setErrorStream(berr);
+ setCommand("version");
+ super.execute();
+ String output = bos.toString();
+ log("Received version response \"" + output + "\"",
+ Project.MSG_DEBUG);
+ StringTokenizer st = new StringTokenizer(output);
+ boolean client = false;
+ boolean server = false;
+ String cvs = null;
+ String cachedVersion = null;
+ boolean haveReadAhead = false;
+ while (haveReadAhead || st.hasMoreTokens()) {
+ String currentToken = haveReadAhead ? cachedVersion : st.nextToken();
+ haveReadAhead = false;
+ if (currentToken.equals("Client:")) {
+ client = true;
+ } else if (currentToken.equals("Server:")) {
+ server = true;
+ } else if (currentToken.startsWith("(CVS")
+ && currentToken.endsWith(")")) {
+ cvs = currentToken.length() == 5 ? "" : " " + currentToken;
+ }
+ if (!client && !server && cvs != null
+ && cachedVersion == null && st.hasMoreTokens()) {
+ cachedVersion = st.nextToken();
+ haveReadAhead = true;
+ } else if (client && cvs != null) {
+ if (st.hasMoreTokens()) {
+ clientVersion = st.nextToken() + cvs;
+ }
+ client = false;
+ cvs = null;
+ } else if (server && cvs != null) {
+ if (st.hasMoreTokens()) {
+ serverVersion = st.nextToken() + cvs;
+ }
+ server = false;
+ cvs = null;
+ } else if (currentToken.equals("(client/server)")
+ && cvs != null && cachedVersion != null
+ && !client && !server) {
+ client = server = true;
+ clientVersion = serverVersion = cachedVersion + cvs;
+ cachedVersion = cvs = null;
+ }
+ }
+ if (clientVersionProperty != null) {
+ getProject().setNewProperty(clientVersionProperty, clientVersion);
+ }
+ if (serverVersionProperty != null) {
+ getProject().setNewProperty(serverVersionProperty, serverVersion);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/RCSFile.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/RCSFile.java
new file mode 100644
index 00000000..70a8cf36
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/RCSFile.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.tools.ant.taskdefs.cvslib;
+
+/**
+ * Represents a RCS File change.
+ *
+ */
+class RCSFile {
+ private String name;
+ private String revision;
+ private String previousRevision;
+
+
+ RCSFile(final String name, final String rev) {
+ this(name, rev, null);
+ }
+
+
+ RCSFile(final String name,
+ final String revision,
+ final String previousRevision) {
+ this.name = name;
+ this.revision = revision;
+ if (!revision.equals(previousRevision)) {
+ this.previousRevision = previousRevision;
+ }
+ }
+
+ /**
+ * Gets the name of the RCSFile
+ * @return name of the file
+ */
+ String getName() {
+ return name;
+ }
+
+ /**
+ * Gets the revision number of the RCSFile
+ * @return the revision number (as String)
+ */
+ String getRevision() {
+ return revision;
+ }
+
+ /**
+ * Gets the previous revision of the RCSFile
+ * @return the previous revision number (as String)
+ */
+ String getPreviousRevision() {
+ return previousRevision;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingOutputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingOutputStream.java
new file mode 100644
index 00000000..f2b61cf9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingOutputStream.java
@@ -0,0 +1,46 @@
+/*
+ * 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.tools.ant.taskdefs.cvslib;
+
+import org.apache.tools.ant.util.LineOrientedOutputStream;
+
+/**
+ * A dummy stream that just passes stuff to the parser.
+ */
+class RedirectingOutputStream extends LineOrientedOutputStream {
+ private final ChangeLogParser parser;
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param parser the parser to which output is sent.
+ */
+ public RedirectingOutputStream(final ChangeLogParser parser) {
+ this.parser = parser;
+ }
+
+ /**
+ * Logs a line to the log system of ant.
+ *
+ * @param line the line to log.
+ */
+ protected void processLine(final String line) {
+ parser.stdout(line);
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingStreamHandler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingStreamHandler.java
new file mode 100644
index 00000000..713de0c4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingStreamHandler.java
@@ -0,0 +1,61 @@
+/*
+ * 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.tools.ant.taskdefs.cvslib;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.PumpStreamHandler;
+
+/**
+ * A dummy stream handler that just passes stuff to the parser.
+ *
+ */
+class RedirectingStreamHandler
+ extends PumpStreamHandler {
+ RedirectingStreamHandler(final ChangeLogParser parser) {
+ super(new RedirectingOutputStream(parser),
+ new ByteArrayOutputStream());
+ }
+
+
+ String getErrors() {
+ try {
+ final ByteArrayOutputStream error
+ = (ByteArrayOutputStream) getErr();
+
+ return error.toString("ASCII");
+ } catch (final Exception e) {
+ return null;
+ }
+ }
+
+
+ public void stop() {
+ super.stop();
+ try {
+ getErr().close();
+ getOut().close();
+ } catch (final IOException e) {
+ // plain impossible
+ throw new BuildException(e);
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/defaults.properties b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/defaults.properties
new file mode 100644
index 00000000..9cf1499f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/defaults.properties
@@ -0,0 +1,210 @@
+# 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.
+#
+# standard ant tasks
+ant=org.apache.tools.ant.taskdefs.Ant
+antcall=org.apache.tools.ant.taskdefs.CallTarget
+antstructure=org.apache.tools.ant.taskdefs.AntStructure
+antversion=org.apache.tools.ant.taskdefs.condition.AntVersion
+apply=org.apache.tools.ant.taskdefs.Transform
+apt=org.apache.tools.ant.taskdefs.Apt
+attributenamespacedef=org.apache.tools.ant.taskdefs.AttributeNamespaceDef
+augment=org.apache.tools.ant.taskdefs.AugmentReference
+available=org.apache.tools.ant.taskdefs.Available
+basename=org.apache.tools.ant.taskdefs.Basename
+bindtargets=org.apache.tools.ant.taskdefs.BindTargets
+buildnumber=org.apache.tools.ant.taskdefs.BuildNumber
+bunzip2=org.apache.tools.ant.taskdefs.BUnzip2
+bzip2=org.apache.tools.ant.taskdefs.BZip2
+checksum=org.apache.tools.ant.taskdefs.Checksum
+chmod=org.apache.tools.ant.taskdefs.Chmod
+classloader=org.apache.tools.ant.taskdefs.Classloader
+commandlauncher=org.apache.tools.ant.taskdefs.CommandLauncherTask
+componentdef=org.apache.tools.ant.taskdefs.Componentdef
+concat=org.apache.tools.ant.taskdefs.Concat
+condition=org.apache.tools.ant.taskdefs.ConditionTask
+copy=org.apache.tools.ant.taskdefs.Copy
+cvs=org.apache.tools.ant.taskdefs.Cvs
+cvschangelog=org.apache.tools.ant.taskdefs.cvslib.ChangeLogTask
+cvspass=org.apache.tools.ant.taskdefs.CVSPass
+cvstagdiff=org.apache.tools.ant.taskdefs.cvslib.CvsTagDiff
+cvsversion=org.apache.tools.ant.taskdefs.cvslib.CvsVersion
+defaultexcludes=org.apache.tools.ant.taskdefs.DefaultExcludes
+delete=org.apache.tools.ant.taskdefs.Delete
+dependset=org.apache.tools.ant.taskdefs.DependSet
+diagnostics=org.apache.tools.ant.taskdefs.DiagnosticsTask
+dirname=org.apache.tools.ant.taskdefs.Dirname
+ear=org.apache.tools.ant.taskdefs.Ear
+echo=org.apache.tools.ant.taskdefs.Echo
+echoproperties=org.apache.tools.ant.taskdefs.optional.EchoProperties
+echoxml=org.apache.tools.ant.taskdefs.EchoXML
+exec=org.apache.tools.ant.taskdefs.ExecTask
+fail=org.apache.tools.ant.taskdefs.Exit
+filter=org.apache.tools.ant.taskdefs.Filter
+fixcrlf=org.apache.tools.ant.taskdefs.FixCRLF
+#funtest=org.apache.tools.ant.taskdefs.optional.testing.Funtest
+genkey=org.apache.tools.ant.taskdefs.GenerateKey
+get=org.apache.tools.ant.taskdefs.Get
+gunzip=org.apache.tools.ant.taskdefs.GUnzip
+gzip=org.apache.tools.ant.taskdefs.GZip
+hostinfo=org.apache.tools.ant.taskdefs.HostInfo
+import=org.apache.tools.ant.taskdefs.ImportTask
+include=org.apache.tools.ant.taskdefs.ImportTask
+input=org.apache.tools.ant.taskdefs.Input
+jar=org.apache.tools.ant.taskdefs.Jar
+java=org.apache.tools.ant.taskdefs.Java
+javac=org.apache.tools.ant.taskdefs.Javac
+javadoc=org.apache.tools.ant.taskdefs.Javadoc
+length=org.apache.tools.ant.taskdefs.Length
+loadfile=org.apache.tools.ant.taskdefs.LoadFile
+loadproperties=org.apache.tools.ant.taskdefs.LoadProperties
+loadresource=org.apache.tools.ant.taskdefs.LoadResource
+local=org.apache.tools.ant.taskdefs.Local
+macrodef=org.apache.tools.ant.taskdefs.MacroDef
+mail=org.apache.tools.ant.taskdefs.email.EmailTask
+makeurl=org.apache.tools.ant.taskdefs.MakeUrl
+manifest=org.apache.tools.ant.taskdefs.ManifestTask
+manifestclasspath=org.apache.tools.ant.taskdefs.ManifestClassPath
+mkdir=org.apache.tools.ant.taskdefs.Mkdir
+move=org.apache.tools.ant.taskdefs.Move
+nice=org.apache.tools.ant.taskdefs.Nice
+parallel=org.apache.tools.ant.taskdefs.Parallel
+patch=org.apache.tools.ant.taskdefs.Patch
+pathconvert=org.apache.tools.ant.taskdefs.PathConvert
+presetdef=org.apache.tools.ant.taskdefs.PreSetDef
+projecthelper=org.apache.tools.ant.taskdefs.ProjectHelperTask
+property=org.apache.tools.ant.taskdefs.Property
+propertyhelper=org.apache.tools.ant.taskdefs.PropertyHelperTask
+record=org.apache.tools.ant.taskdefs.Recorder
+replace=org.apache.tools.ant.taskdefs.Replace
+resourcecount=org.apache.tools.ant.taskdefs.ResourceCount
+retry=org.apache.tools.ant.taskdefs.Retry
+rmic=org.apache.tools.ant.taskdefs.Rmic
+sequential=org.apache.tools.ant.taskdefs.Sequential
+signjar=org.apache.tools.ant.taskdefs.SignJar
+sleep=org.apache.tools.ant.taskdefs.Sleep
+sql=org.apache.tools.ant.taskdefs.SQLExec
+subant=org.apache.tools.ant.taskdefs.SubAnt
+sync=org.apache.tools.ant.taskdefs.Sync
+tar=org.apache.tools.ant.taskdefs.Tar
+taskdef=org.apache.tools.ant.taskdefs.Taskdef
+tempfile=org.apache.tools.ant.taskdefs.TempFile
+touch=org.apache.tools.ant.taskdefs.Touch
+tstamp=org.apache.tools.ant.taskdefs.Tstamp
+truncate=org.apache.tools.ant.taskdefs.Truncate
+typedef=org.apache.tools.ant.taskdefs.Typedef
+unjar=org.apache.tools.ant.taskdefs.Expand
+untar=org.apache.tools.ant.taskdefs.Untar
+unwar=org.apache.tools.ant.taskdefs.Expand
+unzip=org.apache.tools.ant.taskdefs.Expand
+uptodate=org.apache.tools.ant.taskdefs.UpToDate
+waitfor=org.apache.tools.ant.taskdefs.WaitFor
+war=org.apache.tools.ant.taskdefs.War
+whichresource=org.apache.tools.ant.taskdefs.WhichResource
+xmlproperty=org.apache.tools.ant.taskdefs.XmlProperty
+xslt=org.apache.tools.ant.taskdefs.XSLTProcess
+zip=org.apache.tools.ant.taskdefs.Zip
+
+# optional tasks
+antlr=org.apache.tools.ant.taskdefs.optional.ANTLR
+attrib=org.apache.tools.ant.taskdefs.optional.windows.Attrib
+blgenclient=org.apache.tools.ant.taskdefs.optional.ejb.BorlandGenerateClient
+cab=org.apache.tools.ant.taskdefs.optional.Cab
+cccheckin=org.apache.tools.ant.taskdefs.optional.clearcase.CCCheckin
+cccheckout=org.apache.tools.ant.taskdefs.optional.clearcase.CCCheckout
+cclock=org.apache.tools.ant.taskdefs.optional.clearcase.CCLock
+ccmcheckin=org.apache.tools.ant.taskdefs.optional.ccm.CCMCheckin
+ccmcheckintask=org.apache.tools.ant.taskdefs.optional.ccm.CCMCheckinDefault
+ccmcheckout=org.apache.tools.ant.taskdefs.optional.ccm.CCMCheckout
+ccmcreatetask=org.apache.tools.ant.taskdefs.optional.ccm.CCMCreateTask
+ccmkattr=org.apache.tools.ant.taskdefs.optional.clearcase.CCMkattr
+ccmkbl=org.apache.tools.ant.taskdefs.optional.clearcase.CCMkbl
+ccmkdir=org.apache.tools.ant.taskdefs.optional.clearcase.CCMkdir
+ccmkelem=org.apache.tools.ant.taskdefs.optional.clearcase.CCMkelem
+ccmklabel=org.apache.tools.ant.taskdefs.optional.clearcase.CCMklabel
+ccmklbtype=org.apache.tools.ant.taskdefs.optional.clearcase.CCMklbtype
+ccmreconfigure=org.apache.tools.ant.taskdefs.optional.ccm.CCMReconfigure
+ccrmtype=org.apache.tools.ant.taskdefs.optional.clearcase.CCRmtype
+ccuncheckout=org.apache.tools.ant.taskdefs.optional.clearcase.CCUnCheckout
+ccunlock=org.apache.tools.ant.taskdefs.optional.clearcase.CCUnlock
+ccupdate=org.apache.tools.ant.taskdefs.optional.clearcase.CCUpdate
+chgrp=org.apache.tools.ant.taskdefs.optional.unix.Chgrp
+chown=org.apache.tools.ant.taskdefs.optional.unix.Chown
+depend=org.apache.tools.ant.taskdefs.optional.depend.Depend
+ejbjar=org.apache.tools.ant.taskdefs.optional.ejb.EjbJar
+ftp=org.apache.tools.ant.taskdefs.optional.net.FTP
+image=org.apache.tools.ant.taskdefs.optional.image.Image
+iplanet-ejbc=org.apache.tools.ant.taskdefs.optional.ejb.IPlanetEjbcTask
+jarlib-available=org.apache.tools.ant.taskdefs.optional.extension.JarLibAvailableTask
+jarlib-display=org.apache.tools.ant.taskdefs.optional.extension.JarLibDisplayTask
+jarlib-manifest=org.apache.tools.ant.taskdefs.optional.extension.JarLibManifestTask
+jarlib-resolve=org.apache.tools.ant.taskdefs.optional.extension.JarLibResolveTask
+javacc=org.apache.tools.ant.taskdefs.optional.javacc.JavaCC
+javah=org.apache.tools.ant.taskdefs.optional.Javah
+jdepend=org.apache.tools.ant.taskdefs.optional.jdepend.JDependTask
+jjdoc=org.apache.tools.ant.taskdefs.optional.javacc.JJDoc
+jjtree=org.apache.tools.ant.taskdefs.optional.javacc.JJTree
+junit=org.apache.tools.ant.taskdefs.optional.junit.JUnitTask
+junitreport=org.apache.tools.ant.taskdefs.optional.junit.XMLResultAggregator
+native2ascii=org.apache.tools.ant.taskdefs.optional.Native2Ascii
+netrexxc=org.apache.tools.ant.taskdefs.optional.NetRexxC
+propertyfile=org.apache.tools.ant.taskdefs.optional.PropertyFile
+pvcs=org.apache.tools.ant.taskdefs.optional.pvcs.Pvcs
+replaceregexp=org.apache.tools.ant.taskdefs.optional.ReplaceRegExp
+rexec=org.apache.tools.ant.taskdefs.optional.net.RExecTask
+rpm=org.apache.tools.ant.taskdefs.optional.Rpm
+schemavalidate=org.apache.tools.ant.taskdefs.optional.SchemaValidate
+scp=org.apache.tools.ant.taskdefs.optional.ssh.Scp
+script=org.apache.tools.ant.taskdefs.optional.Script
+scriptdef=org.apache.tools.ant.taskdefs.optional.script.ScriptDef
+serverdeploy=org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy
+setproxy=org.apache.tools.ant.taskdefs.optional.net.SetProxy
+soscheckin=org.apache.tools.ant.taskdefs.optional.sos.SOSCheckin
+soscheckout=org.apache.tools.ant.taskdefs.optional.sos.SOSCheckout
+sosget=org.apache.tools.ant.taskdefs.optional.sos.SOSGet
+soslabel=org.apache.tools.ant.taskdefs.optional.sos.SOSLabel
+sound=org.apache.tools.ant.taskdefs.optional.sound.SoundTask
+splash=org.apache.tools.ant.taskdefs.optional.splash.SplashTask
+sshexec=org.apache.tools.ant.taskdefs.optional.ssh.SSHExec
+sshsession=org.apache.tools.ant.taskdefs.optional.ssh.SSHSession
+symlink=org.apache.tools.ant.taskdefs.optional.unix.Symlink
+telnet=org.apache.tools.ant.taskdefs.optional.net.TelnetTask
+translate=org.apache.tools.ant.taskdefs.optional.i18n.Translate
+verifyjar=org.apache.tools.ant.taskdefs.VerifyJar
+vssadd=org.apache.tools.ant.taskdefs.optional.vss.MSVSSADD
+vsscheckin=org.apache.tools.ant.taskdefs.optional.vss.MSVSSCHECKIN
+vsscheckout=org.apache.tools.ant.taskdefs.optional.vss.MSVSSCHECKOUT
+vsscp=org.apache.tools.ant.taskdefs.optional.vss.MSVSSCP
+vsscreate=org.apache.tools.ant.taskdefs.optional.vss.MSVSSCREATE
+vssget=org.apache.tools.ant.taskdefs.optional.vss.MSVSSGET
+vsshistory=org.apache.tools.ant.taskdefs.optional.vss.MSVSSHISTORY
+vsslabel=org.apache.tools.ant.taskdefs.optional.vss.MSVSSLABEL
+wljspc=org.apache.tools.ant.taskdefs.optional.jsp.WLJspc
+xmlvalidate=org.apache.tools.ant.taskdefs.optional.XMLValidateTask
+
+
+# deprecated ant tasks (kept for back compatibility)
+copydir=org.apache.tools.ant.taskdefs.Copydir
+copyfile=org.apache.tools.ant.taskdefs.Copyfile
+copypath=org.apache.tools.ant.taskdefs.CopyPath
+deltree=org.apache.tools.ant.taskdefs.Deltree
+execon=org.apache.tools.ant.taskdefs.ExecuteOn
+javadoc2=org.apache.tools.ant.taskdefs.Javadoc
+jlink=org.apache.tools.ant.taskdefs.optional.jlink.JlinkTask
+jspc=org.apache.tools.ant.taskdefs.optional.jsp.JspC
+mimemail=org.apache.tools.ant.taskdefs.optional.net.MimeMail
+rename=org.apache.tools.ant.taskdefs.Rename
+renameext=org.apache.tools.ant.taskdefs.optional.RenameExtensions
+style=org.apache.tools.ant.taskdefs.XSLTProcess
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/EmailAddress.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/EmailAddress.java
new file mode 100644
index 00000000..edc9c9dc
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/EmailAddress.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.tools.ant.taskdefs.email;
+
+/**
+ * Holds an email address.
+ *
+ * @since Ant 1.5
+ */
+public class EmailAddress {
+ private String name;
+ private String address;
+
+
+ /** Creates an empty email address */
+ public EmailAddress() {
+ }
+
+
+ /**
+ * Creates a new email address based on the given string
+ *
+ * @param email the email address (with or without &lt;&gt;)
+ * Acceptable forms include:
+ * address
+ * &lt;address&gt;
+ * name &lt;address&gt;
+ * &lt;address&gt; name
+ * (name) address
+ * address (name)
+ */
+ // Make a limited attempt to extract a sanitized name and email address
+ // Algorithm based on the one found in Ant's MailMessage.java
+ public EmailAddress(String email) {
+ final int minLen = 9;
+ int len = email.length();
+
+ // shortcut for "<address>"
+ if (len > minLen) {
+ if ((email.charAt(0) == '<' || email.charAt(1) == '<')
+ && (email.charAt(len - 1) == '>' || email.charAt(len - 2) == '>')) {
+ this.address = trim(email, true);
+ return;
+ }
+ }
+
+ int paramDepth = 0;
+ int start = 0;
+ int end = 0;
+ int nStart = 0;
+ int nEnd = 0;
+
+ for (int i = 0; i < len; i++) {
+ char c = email.charAt(i);
+ if (c == '(') {
+ paramDepth++;
+ if (start == 0) {
+ end = i; // support "address (name)"
+ nStart = i + 1;
+ }
+ } else if (c == ')') {
+ paramDepth--;
+ if (end == 0) {
+ start = i + 1; // support "(name) address"
+ nEnd = i;
+ }
+ } else if (paramDepth == 0 && c == '<') {
+ if (start == 0) {
+ nEnd = i;
+ }
+ start = i + 1;
+ } else if (paramDepth == 0 && c == '>') {
+ end = i;
+ if (end != len - 1) {
+ nStart = i + 1;
+ }
+ }
+ }
+
+ // DEBUG: System.out.println( email );
+ if (end == 0) {
+ end = len;
+ }
+ // DEBUG: System.out.println( "address: " + start + " " + end );
+ if (nEnd == 0) {
+ nEnd = len;
+ }
+ // DEBUG: System.out.println( "name: " + nStart + " " + nEnd );
+
+ this.address = trim(email.substring(start, end), true);
+ this.name = trim(email.substring(nStart, nEnd), false);
+
+ // if the two substrings are longer than the original, then name
+ // contains address - so reset the name to null
+ if (this.name.length() + this.address.length() > len) {
+ this.name = null;
+ }
+ }
+
+ /**
+ * A specialised trim() that trims whitespace,
+ * '(', ')', '"', '<', '>' from the start and end of strings
+ */
+ private String trim(String t, boolean trimAngleBrackets) {
+ int start = 0;
+ int end = t.length();
+ boolean trim = false;
+ do {
+ trim = false;
+ if (t.charAt(end - 1) == ')'
+ || (t.charAt(end - 1) == '>' && trimAngleBrackets)
+ || (t.charAt(end - 1) == '"' && t.charAt(end - 2) != '\\')
+ || t.charAt(end - 1) <= '\u0020') {
+ trim = true;
+ end--;
+ }
+ if (t.charAt(start) == '('
+ || (t.charAt(start) == '<' && trimAngleBrackets)
+ || t.charAt(start) == '"'
+ || t.charAt(start) <= '\u0020') {
+ trim = true;
+ start++;
+ }
+ } while (trim);
+ return t.substring(start, end);
+ }
+
+
+ /**
+ * Sets the personal / display name of the address.
+ *
+ * @param name the display name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+
+ /**
+ * Sets the email address.
+ *
+ * @param address the actual email address (without &lt;&gt;)
+ */
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+
+ /**
+ * Constructs a string "name &lt;address&gt;" or "address"
+ *
+ * @return a string representation of the address
+ */
+ public String toString() {
+ if (name == null) {
+ return address;
+ } else {
+ return name + " <" + address + ">";
+ }
+ }
+
+
+ /**
+ * Returns the address
+ *
+ * @return the address part
+ */
+ public String getAddress() {
+ return address;
+ }
+
+
+ /**
+ * Returns the display name
+ *
+ * @return the display name part
+ */
+ public String getName() {
+ return name;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/EmailTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/EmailTask.java
new file mode 100644
index 00000000..0a5bc681
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/EmailTask.java
@@ -0,0 +1,634 @@
+/*
+ * 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.tools.ant.taskdefs.email;
+
+import java.io.File;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.ClasspathUtils;
+
+/**
+ * A task to send SMTP email. This is a refactoring of the SendMail and
+ * MimeMail tasks such that both are within a single task.
+ *
+ * @since Ant 1.5
+ * @ant.task name="mail" category="network"
+ */
+public class EmailTask extends Task {
+ private static final int SMTP_PORT = 25;
+
+ /** Constant to show that the best available mailer should be used. */
+ public static final String AUTO = "auto";
+ /** Constant to allow the Mime mailer to be requested */
+ public static final String MIME = "mime";
+ /** Constant to allow the UU mailer to be requested */
+ public static final String UU = "uu";
+ /** Constant to allow the plaintext mailer to be requested */
+ public static final String PLAIN = "plain";
+
+ /**
+ * Enumerates the encoding constants.
+ */
+ public static class Encoding extends EnumeratedAttribute {
+ /**
+ * finds the valid encoding values
+ *
+ * @return a list of valid entries
+ */
+ public String[] getValues() {
+ return new String[] {AUTO, MIME, UU, PLAIN};
+ }
+ }
+
+ private String encoding = AUTO;
+ /** host running SMTP */
+ private String host = "localhost";
+ private Integer port = null;
+ /** subject field */
+ private String subject = null;
+ /** any text */
+ private Message message = null;
+ /** failure flag */
+ private boolean failOnError = true;
+ private boolean includeFileNames = false;
+ private String messageMimeType = null;
+ private String messageFileInputEncoding;
+ /* special headers */
+ /** sender */
+ private EmailAddress from = null;
+ /** replyto */
+ private Vector replyToList = new Vector();
+ /** TO recipients */
+ private Vector toList = new Vector();
+ /** CC (Carbon Copy) recipients */
+ private Vector ccList = new Vector();
+ /** BCC (Blind Carbon Copy) recipients */
+ private Vector bccList = new Vector();
+
+ /** generic headers */
+ private Vector headers = new Vector();
+
+ /** file list */
+ private Path attachments = null;
+ /** Character set for MimeMailer*/
+ private String charset = null;
+ /** User for SMTP auth */
+ private String user = null;
+ /** Password for SMTP auth */
+ private String password = null;
+ /** indicate if the user wishes SSL-TLS */
+ private boolean ssl = false;
+ /** indicate if the user wishes support for STARTTLS */
+ private boolean starttls = false;
+
+ /** ignore invalid recipients? */
+ private boolean ignoreInvalidRecipients = false;
+
+ /**
+ * Set the user for SMTP auth; this requires JavaMail.
+ * @param user the String username.
+ * @since Ant 1.6
+ */
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ /**
+ * Set the password for SMTP auth; this requires JavaMail.
+ * @param password the String password.
+ * @since Ant 1.6
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ /**
+ * Set whether to send data over SSL.
+ * @param ssl boolean; if true SSL will be used.
+ * @since Ant 1.6
+ */
+ public void setSSL(boolean ssl) {
+ this.ssl = ssl;
+ }
+
+ /**
+ * Set whether to allow authentication to switch to a TLS
+ * connection via STARTTLS.
+ * @param b boolean; if true STARTTLS will be supported.
+ * @since Ant 1.8.0
+ */
+ public void setEnableStartTLS(boolean b) {
+ this.starttls = b;
+ }
+
+ /**
+ * Set the preferred encoding method.
+ *
+ * @param encoding The encoding (one of AUTO, MIME, UU, PLAIN).
+ */
+ public void setEncoding(Encoding encoding) {
+ this.encoding = encoding.getValue();
+ }
+
+ /**
+ * Set the mail server port.
+ *
+ * @param port The port to use.
+ */
+ public void setMailport(int port) {
+ this.port = new Integer(port);
+ }
+
+ /**
+ * Set the host.
+ *
+ * @param host The host to connect to.
+ */
+ public void setMailhost(String host) {
+ this.host = host;
+ }
+
+ /**
+ * Set the subject line of the email.
+ *
+ * @param subject Subject of this email.
+ */
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ /**
+ * Shorthand method to set the message.
+ *
+ * @param message Message body of this email.
+ */
+ public void setMessage(String message) {
+ if (this.message != null) {
+ throw new BuildException("Only one message can be sent in an "
+ + "email");
+ }
+ this.message = new Message(message);
+ this.message.setProject(getProject());
+ }
+
+ /**
+ * Shorthand method to set the message from a file.
+ *
+ * @param file The file from which to take the message.
+ */
+ public void setMessageFile(File file) {
+ if (this.message != null) {
+ throw new BuildException("Only one message can be sent in an "
+ + "email");
+ }
+ this.message = new Message(file);
+ this.message.setProject(getProject());
+ }
+
+ /**
+ * Shorthand method to set type of the text message, text/plain by default
+ * but text/html or text/xml is quite feasible.
+ *
+ * @param type The new MessageMimeType value.
+ */
+ public void setMessageMimeType(String type) {
+ this.messageMimeType = type;
+ }
+
+ /**
+ * Add a message element.
+ *
+ * @param message The message object.
+ * @throws BuildException if a message has already been added.
+ */
+ public void addMessage(Message message) throws BuildException {
+ if (this.message != null) {
+ throw new BuildException(
+ "Only one message can be sent in an email");
+ }
+ this.message = message;
+ }
+
+ /**
+ * Add a from address element.
+ *
+ * @param address The address to send from.
+ */
+ public void addFrom(EmailAddress address) {
+ if (this.from != null) {
+ throw new BuildException("Emails can only be from one address");
+ }
+ this.from = address;
+ }
+
+ /**
+ * Shorthand to set the from address element.
+ *
+ * @param address The address to send mail from.
+ */
+ public void setFrom(String address) {
+ if (this.from != null) {
+ throw new BuildException("Emails can only be from one address");
+ }
+ this.from = new EmailAddress(address);
+ }
+
+ /**
+ * Add a replyto address element.
+ *
+ * @param address The address to reply to.
+ * @since Ant 1.6
+ */
+ public void addReplyTo(EmailAddress address) {
+ this.replyToList.add(address);
+ }
+
+ /**
+ * Shorthand to set the replyto address element.
+ *
+ * @param address The address to which replies should be directed.
+ * @since Ant 1.6
+ */
+ public void setReplyTo(String address) {
+ this.replyToList.add(new EmailAddress(address));
+ }
+
+ /**
+ * Add a to address element.
+ *
+ * @param address An email address.
+ */
+ public void addTo(EmailAddress address) {
+ toList.addElement(address);
+ }
+
+ /**
+ * Shorthand to set the "to" address element.
+ *
+ * @param list Comma-separated list of addresses.
+ */
+ public void setToList(String list) {
+ StringTokenizer tokens = new StringTokenizer(list, ",");
+
+ while (tokens.hasMoreTokens()) {
+ toList.addElement(new EmailAddress(tokens.nextToken()));
+ }
+ }
+
+ /**
+ * Add a "cc" address element.
+ *
+ * @param address The email address.
+ */
+ public void addCc(EmailAddress address) {
+ ccList.addElement(address);
+ }
+
+ /**
+ * Shorthand to set the "cc" address element.
+ *
+ * @param list Comma separated list of addresses.
+ */
+ public void setCcList(String list) {
+ StringTokenizer tokens = new StringTokenizer(list, ",");
+
+ while (tokens.hasMoreTokens()) {
+ ccList.addElement(new EmailAddress(tokens.nextToken()));
+ }
+ }
+
+ /**
+ * Add a "bcc" address element.
+ *
+ * @param address The email address.
+ */
+ public void addBcc(EmailAddress address) {
+ bccList.addElement(address);
+ }
+
+ /**
+ * Shorthand to set the "bcc" address element.
+ *
+ * @param list comma separated list of addresses.
+ */
+ public void setBccList(String list) {
+ StringTokenizer tokens = new StringTokenizer(list, ",");
+
+ while (tokens.hasMoreTokens()) {
+ bccList.addElement(new EmailAddress(tokens.nextToken()));
+ }
+ }
+
+ /**
+ * Set whether BuildExceptions should be passed back to the core.
+ *
+ * @param failOnError The new FailOnError value.
+ */
+ public void setFailOnError(boolean failOnError) {
+ this.failOnError = failOnError;
+ }
+
+ /**
+ * Set the list of files to be attached.
+ *
+ * @param filenames Comma-separated list of files.
+ */
+ public void setFiles(String filenames) {
+ StringTokenizer t = new StringTokenizer(filenames, ", ");
+
+ while (t.hasMoreTokens()) {
+ createAttachments()
+ .add(new FileResource(getProject().resolveFile(t.nextToken())));
+ }
+ }
+
+ /**
+ * Add a set of files (nested fileset attribute).
+ *
+ * @param fs The fileset.
+ */
+ public void addFileset(FileSet fs) {
+ createAttachments().add(fs);
+ }
+
+ /**
+ * Creates a Path as container for attachments. Supports any
+ * filesystem resource-collections that way.
+ * @return the path to be configured.
+ * @since Ant 1.7
+ */
+ public Path createAttachments() {
+ if (attachments == null) {
+ attachments = new Path(getProject());
+ }
+ return attachments.createPath();
+ }
+
+ /**
+ * Create a nested header element.
+ * @return a Header instance.
+ */
+ public Header createHeader() {
+ Header h = new Header();
+ headers.add(h);
+ return h;
+ }
+
+ /**
+ * Set whether to include filenames.
+ *
+ * @param includeFileNames Whether to include filenames in the text of the
+ * message.
+ */
+ public void setIncludefilenames(boolean includeFileNames) {
+ this.includeFileNames = includeFileNames;
+ }
+
+ /**
+ * Get whether file names should be included.
+ *
+ * @return Identifies whether file names should be included.
+ */
+ public boolean getIncludeFileNames() {
+ return includeFileNames;
+ }
+
+ /**
+ * Whether invalid recipients should be ignored (but a warning
+ * will be logged) instead of making the task fail.
+ *
+ * <p>Even with this property set to true the task will still fail
+ * if the mail couldn't be sent to any recipient at all.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public void setIgnoreInvalidRecipients(boolean b) {
+ ignoreInvalidRecipients = b;
+ }
+
+ /**
+ * Send an email.
+ */
+ public void execute() {
+ Message savedMessage = message;
+
+ try {
+ Mailer mailer = null;
+
+ // prepare for the auto select mechanism
+ boolean autoFound = false;
+ // try MIME format
+ if (encoding.equals(MIME)
+ || (encoding.equals(AUTO) && !autoFound)) {
+ try {
+ //check to make sure that activation.jar
+ //and mail.jar are available - see bug 31969
+ Class.forName("javax.activation.DataHandler");
+ Class.forName("javax.mail.internet.MimeMessage");
+
+ mailer = (Mailer) ClasspathUtils.newInstance(
+ "org.apache.tools.ant.taskdefs.email.MimeMailer",
+ EmailTask.class.getClassLoader(), Mailer.class);
+ autoFound = true;
+
+ log("Using MIME mail", Project.MSG_VERBOSE);
+ } catch (BuildException e) {
+ logBuildException("Failed to initialise MIME mail: ", e);
+ }
+ }
+ // SMTP auth only allowed with MIME mail
+ if (!autoFound && ((user != null) || (password != null))
+ && (encoding.equals(UU) || encoding.equals(PLAIN))) {
+ throw new BuildException("SMTP auth only possible with MIME mail");
+ }
+ // SSL only allowed with MIME mail
+ if (!autoFound && (ssl || starttls)
+ && (encoding.equals(UU) || encoding.equals(PLAIN))) {
+ throw new BuildException("SSL and STARTTLS only possible with"
+ + " MIME mail");
+ }
+ // try UU format
+ if (encoding.equals(UU)
+ || (encoding.equals(AUTO) && !autoFound)) {
+ try {
+ mailer = (Mailer) ClasspathUtils.newInstance(
+ "org.apache.tools.ant.taskdefs.email.UUMailer",
+ EmailTask.class.getClassLoader(), Mailer.class);
+ autoFound = true;
+ log("Using UU mail", Project.MSG_VERBOSE);
+ } catch (BuildException e) {
+ logBuildException("Failed to initialise UU mail: ", e);
+ }
+ }
+ // try plain format
+ if (encoding.equals(PLAIN)
+ || (encoding.equals(AUTO) && !autoFound)) {
+ mailer = new PlainMailer();
+ autoFound = true;
+ log("Using plain mail", Project.MSG_VERBOSE);
+ }
+ // a valid mailer must be present by now
+ if (mailer == null) {
+ throw new BuildException("Failed to initialise encoding: "
+ + encoding);
+ }
+ // a valid message is required
+ if (message == null) {
+ message = new Message();
+ message.setProject(getProject());
+ }
+ // an address to send from is required
+ if (from == null || from.getAddress() == null) {
+ throw new BuildException("A from element is required");
+ }
+ // at least one address to send to/cc/bcc is required
+ if (toList.isEmpty() && ccList.isEmpty() && bccList.isEmpty()) {
+ throw new BuildException("At least one of to, cc or bcc must "
+ + "be supplied");
+ }
+ // set the mimetype if not done already (and required)
+ if (messageMimeType != null) {
+ if (message.isMimeTypeSpecified()) {
+ throw new BuildException("The mime type can only be "
+ + "specified in one location");
+ }
+ message.setMimeType(messageMimeType);
+ }
+ // set the character set if not done already (and required)
+ if (charset != null) {
+ if (message.getCharset() != null) {
+ throw new BuildException("The charset can only be "
+ + "specified in one location");
+ }
+ message.setCharset(charset);
+ }
+ message.setInputEncoding(messageFileInputEncoding);
+
+ // identify which files should be attached
+ Vector<File> files = new Vector<File>();
+ if (attachments != null) {
+ for (Resource r : attachments) {
+ files.addElement(r.as(FileProvider.class)
+ .getFile());
+ }
+ }
+ // let the user know what's going to happen
+ log("Sending email: " + subject, Project.MSG_INFO);
+ log("From " + from, Project.MSG_VERBOSE);
+ log("ReplyTo " + replyToList, Project.MSG_VERBOSE);
+ log("To " + toList, Project.MSG_VERBOSE);
+ log("Cc " + ccList, Project.MSG_VERBOSE);
+ log("Bcc " + bccList, Project.MSG_VERBOSE);
+
+ // pass the params to the mailer
+ mailer.setHost(host);
+ if (port != null) {
+ mailer.setPort(port.intValue());
+ mailer.setPortExplicitlySpecified(true);
+ } else {
+ mailer.setPort(SMTP_PORT);
+ mailer.setPortExplicitlySpecified(false);
+ }
+ mailer.setUser(user);
+ mailer.setPassword(password);
+ mailer.setSSL(ssl);
+ mailer.setEnableStartTLS(starttls);
+ mailer.setMessage(message);
+ mailer.setFrom(from);
+ mailer.setReplyToList(replyToList);
+ mailer.setToList(toList);
+ mailer.setCcList(ccList);
+ mailer.setBccList(bccList);
+ mailer.setFiles(files);
+ mailer.setSubject(subject);
+ mailer.setTask(this);
+ mailer.setIncludeFileNames(includeFileNames);
+ mailer.setHeaders(headers);
+ mailer.setIgnoreInvalidRecipients(ignoreInvalidRecipients);
+
+ // send the email
+ mailer.send();
+
+ // let the user know what happened
+ int count = files.size();
+
+ log("Sent email with " + count + " attachment"
+ + (count == 1 ? "" : "s"), Project.MSG_INFO);
+ } catch (BuildException e) {
+ logBuildException("Failed to send email: ", e);
+ if (failOnError) {
+ throw e;
+ }
+ } catch (Exception e) {
+ log("Failed to send email: " + e.getMessage(), Project.MSG_WARN);
+ if (failOnError) {
+ throw new BuildException(e);
+ }
+ } finally {
+ message = savedMessage;
+ }
+ }
+
+ private void logBuildException(String reason, BuildException e) {
+ Throwable t = e.getCause() == null ? e : e.getCause();
+ log(reason + t.getMessage(), Project.MSG_WARN);
+ }
+
+ /**
+ * Sets the character set of mail message.
+ * Will be ignored if mimeType contains ....; Charset=... substring or
+ * encoding is not <code>mime</code>.
+ * @param charset the character encoding to use.
+ * @since Ant 1.6
+ */
+ public void setCharset(String charset) {
+ this.charset = charset;
+ }
+
+ /**
+ * Returns the character set of mail message.
+ *
+ * @return Charset of mail message.
+ * @since Ant 1.6
+ */
+ public String getCharset() {
+ return charset;
+ }
+
+ /**
+ * Sets the encoding to expect when reading the message from a file.
+ * <p>Will be ignored if the message has been specified inline.</p>
+ * @param encoding the name of the charset used
+ * @since Ant 1.9.4
+ */
+ public void setMessageFileInputEncoding(String encoding) {
+ messageFileInputEncoding = encoding;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/Header.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/Header.java
new file mode 100644
index 00000000..6bcfb66f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/Header.java
@@ -0,0 +1,61 @@
+/*
+ * 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.tools.ant.taskdefs.email;
+
+/**
+ * Class representing a generic e-mail header.
+ * @since Ant 1.7
+ */
+public class Header {
+ private String name;
+ private String value;
+
+ /**
+ * Set the name of this Header.
+ * @param name the name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the name of this Header.
+ * @return name as String.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the value of this Header.
+ * @param value the value to set.
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Get the value of this Header.
+ * @return value as String.
+ */
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/Mailer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/Mailer.java
new file mode 100644
index 00000000..4aaa9823
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/Mailer.java
@@ -0,0 +1,277 @@
+/*
+ * 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.tools.ant.taskdefs.email;
+
+import java.io.File;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.DateUtils;
+
+/**
+ * Base class for the various emailing implementations.
+ *
+ * @since Ant 1.5
+ */
+public abstract class Mailer {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected String host = null;
+ protected int port = -1;
+ protected String user = null;
+ protected String password = null;
+ // CheckStyle:MemberNameCheck OFF - bc
+ protected boolean SSL = false;
+ // CheckStyle:MemberNameCheck ON
+ protected Message message;
+ protected EmailAddress from;
+ protected Vector<EmailAddress> replyToList = null;
+ protected Vector<EmailAddress> toList = null;
+ protected Vector<EmailAddress> ccList = null;
+ protected Vector<EmailAddress> bccList = null;
+ protected Vector<File> files = null;
+ protected String subject = null;
+ protected Task task;
+ protected boolean includeFileNames = false;
+ protected Vector<Header> headers = null;
+ // CheckStyle:VisibilityModifier ON
+ private boolean ignoreInvalidRecipients = false;
+ private boolean starttls = false;
+ private boolean portExplicitlySpecified = false;
+
+ /**
+ * Set the mail server.
+ *
+ * @param host the mail server name.
+ */
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ /**
+ * Set the smtp port.
+ *
+ * @param port the SMTP port.
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ * Whether the port has been explicitly specified by the user.
+ * @since Ant 1.8.2
+ */
+ public void setPortExplicitlySpecified(boolean explicit) {
+ portExplicitlySpecified = explicit;
+ }
+
+ /**
+ * Whether the port has been explicitly specified by the user.
+ * @since Ant 1.8.2
+ */
+ protected boolean isPortExplicitlySpecified() {
+ return portExplicitlySpecified;
+ }
+
+ /**
+ * Set the user for smtp auth.
+ *
+ * @param user the username.
+ * @since Ant 1.6
+ */
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ /**
+ * Set the password for smtp auth.
+ *
+ * @param password the authentication password.
+ * @since Ant 1.6
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ /**
+ * Set whether to send the mail through SSL.
+ *
+ * @param ssl if true use SSL transport.
+ * @since Ant 1.6
+ */
+ public void setSSL(boolean ssl) {
+ this.SSL = ssl;
+ }
+
+ /**
+ * Set whether to allow authentication to switch to a TLS
+ * connection via STARTTLS.
+ * @param b boolean; if true STARTTLS will be supported.
+ * @since Ant 1.8.0
+ */
+ public void setEnableStartTLS(boolean b) {
+ this.starttls = b;
+ }
+
+ protected boolean isStartTLSEnabled() {
+ return starttls;
+ }
+
+ /**
+ * Set the message.
+ *
+ * @param m the message content.
+ */
+ public void setMessage(Message m) {
+ this.message = m;
+ }
+
+ /**
+ * Set the address to send from.
+ *
+ * @param from the sender.
+ */
+ public void setFrom(EmailAddress from) {
+ this.from = from;
+ }
+
+ /**
+ * Set the replyto addresses.
+ *
+ * @param list a vector of reployTo addresses.
+ * @since Ant 1.6
+ */
+ public void setReplyToList(Vector<EmailAddress> list) {
+ this.replyToList = list;
+ }
+
+ /**
+ * Set the to addresses.
+ *
+ * @param list a vector of recipient addresses.
+ */
+ public void setToList(Vector<EmailAddress> list) {
+ this.toList = list;
+ }
+
+ /**
+ * Set the cc addresses.
+ *
+ * @param list a vector of cc addresses.
+ */
+ public void setCcList(Vector<EmailAddress> list) {
+ this.ccList = list;
+ }
+
+ /**
+ * Set the bcc addresses.
+ *
+ * @param list a vector of the bcc addresses.
+ */
+ public void setBccList(Vector<EmailAddress> list) {
+ this.bccList = list;
+ }
+
+ /**
+ * Set the files to attach.
+ *
+ * @param files list of files to attach to the email.
+ */
+ public void setFiles(Vector<File> files) {
+ this.files = files;
+ }
+
+ /**
+ * Set the subject.
+ *
+ * @param subject the subject line.
+ */
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ /**
+ * Set the owning task.
+ *
+ * @param task the owning task instance.
+ */
+ public void setTask(Task task) {
+ this.task = task;
+ }
+
+ /**
+ * Indicate whether filenames should be listed in the body.
+ *
+ * @param b if true list attached file names in the body content.
+ */
+ public void setIncludeFileNames(boolean b) {
+ this.includeFileNames = b;
+ }
+
+ /**
+ * Set the generic headers to add to the email.
+ * @param v a Vector presumed to contain Header objects.
+ * @since Ant 1.7
+ */
+ public void setHeaders(Vector<Header> v) {
+ this.headers = v;
+ }
+
+ /**
+ * Send the email.
+ *
+ * @throws BuildException if the email can't be sent.
+ */
+ public abstract void send()
+ throws BuildException;
+
+ /**
+ * Whether invalid recipients should be ignored (but a warning
+ * will be logged) instead of making the task fail.
+ *
+ * <p>Even with this property set to true the task will still fail
+ * if the mail couldn't be sent to any recipient at all.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public void setIgnoreInvalidRecipients(boolean b) {
+ ignoreInvalidRecipients = b;
+ }
+
+ /**
+ * Whether invalid recipients should be ignored.
+ *
+ * @since Ant 1.8.0
+ */
+ protected boolean shouldIgnoreInvalidRecipients() {
+ return ignoreInvalidRecipients;
+ }
+
+ /**
+ * Return the current Date in a format suitable for a SMTP date
+ * header.
+ *
+ * @return the current date in SMTP suitable format.
+ *
+ * @since Ant 1.5
+ */
+ protected final String getDate() {
+ return DateUtils.getDateForHeader();
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/Message.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/Message.java
new file mode 100644
index 00000000..c121f5d7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/Message.java
@@ -0,0 +1,203 @@
+/*
+ * 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.tools.ant.taskdefs.email;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.Reader;
+
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Class representing an email message.
+ *
+ * @since Ant 1.5
+ */
+public class Message extends ProjectComponent {
+ private File messageSource = null;
+ private StringBuffer buffer = new StringBuffer();
+ private String mimeType = "text/plain";
+ private boolean specified = false;
+ private String charset = null;
+ private String inputEncoding;
+
+ /** Creates a new empty message */
+ public Message() {
+ }
+
+
+ /**
+ * Creates a new message based on the given string
+ *
+ * @param text the message
+ */
+ public Message(String text) {
+ addText(text);
+ }
+
+
+ /**
+ * Creates a new message using the contents of the given file.
+ *
+ * @param file the source of the message
+ */
+ public Message(File file) {
+ messageSource = file;
+ }
+
+
+ /**
+ * Adds a textual part of the message
+ *
+ * @param text some text to add
+ */
+ public void addText(String text) {
+ buffer.append(text);
+ }
+
+
+ /**
+ * Sets the source file of the message
+ *
+ * @param src the source of the message
+ */
+ public void setSrc(File src) {
+ this.messageSource = src;
+ }
+
+
+ /**
+ * Sets the content type for the message
+ *
+ * @param mimeType a mime type e.g. "text/plain"
+ */
+ public void setMimeType(String mimeType) {
+ this.mimeType = mimeType;
+ specified = true;
+ }
+
+
+ /**
+ * Returns the content type
+ *
+ * @return the mime type
+ */
+ public String getMimeType() {
+ return mimeType;
+ }
+
+
+ /**
+ * Prints the message onto an output stream
+ *
+ * @param ps The print stream to write to
+ * @throws IOException if an error occurs
+ */
+ public void print(PrintStream ps)
+ throws IOException {
+ // We need character encoding aware printing here.
+ // So, using BufferedWriter over OutputStreamWriter instead of PrintStream
+ BufferedWriter out = null;
+ try {
+ out
+ = charset != null ? new BufferedWriter(new OutputStreamWriter(ps, charset))
+ : new BufferedWriter(new OutputStreamWriter(ps));
+ if (messageSource != null) {
+ // Read message from a file
+ Reader freader = getReader(messageSource);
+
+ try {
+ BufferedReader in = new BufferedReader(freader);
+ String line = null;
+ while ((line = in.readLine()) != null) {
+ out.write(getProject().replaceProperties(line));
+ out.newLine();
+ }
+ } finally {
+ freader.close();
+ }
+ } else {
+ out.write(getProject().replaceProperties(buffer.substring(0)));
+ out.newLine();
+ }
+ out.flush();
+ } finally {
+ //do not close the out writer as it is reused afterwards by the mail task
+ }
+ }
+
+
+ /**
+ * Returns true if the mimeType has been set.
+ *
+ * @return false if the default value is in use
+ */
+ public boolean isMimeTypeSpecified() {
+ return specified;
+ }
+
+ /**
+ * Sets the character set of mail message.
+ * Will be ignored if mimeType contains ....; Charset=... substring.
+ * @param charset the character set name.
+ * @since Ant 1.6
+ */
+ public void setCharset(String charset) {
+ this.charset = charset;
+ }
+ /**
+ * Returns the charset of mail message.
+ *
+ * @return Charset of mail message.
+ * @since Ant 1.6
+ */
+ public String getCharset() {
+ return charset;
+ }
+
+ /**
+ * Sets the encoding to expect when reading the message from a file.
+ * <p>Will be ignored if the message has been specified inline.</p>
+ * @param encoding the name of the charset used
+ * @since Ant 1.9.4
+ */
+ public void setInputEncoding(String encoding) {
+ this.inputEncoding = encoding;
+ }
+
+ private Reader getReader(File f) throws IOException {
+ if (inputEncoding != null) {
+ FileInputStream fis = new FileInputStream(f);
+ try {
+ return new InputStreamReader(fis, inputEncoding);
+ } catch (IOException ex) {
+ fis.close();
+ throw ex;
+ }
+ }
+ return new FileReader(f);
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/MimeMailer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/MimeMailer.java
new file mode 100644
index 00000000..186d71e0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/MimeMailer.java
@@ -0,0 +1,343 @@
+/*
+ * 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.tools.ant.taskdefs.email;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.activation.DataHandler;
+import javax.activation.FileDataSource;
+import javax.mail.Address;
+import javax.mail.Authenticator;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.PasswordAuthentication;
+import javax.mail.SendFailedException;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+
+/**
+ * Uses the JavaMail classes to send Mime format email.
+ *
+ * @since Ant 1.5
+ */
+public class MimeMailer extends Mailer {
+ private static final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
+
+ private static final String GENERIC_ERROR =
+ "Problem while sending mime mail:";
+
+ /** Default character set */
+ private static final String DEFAULT_CHARSET
+ = System.getProperty("file.encoding");
+
+ // To work properly with national charsets we have to use
+ // implementation of interface javax.activation.DataSource
+ /**
+ * String data source implementation.
+ * @since Ant 1.6
+ */
+ class StringDataSource implements javax.activation.DataSource {
+ private String data = null;
+ private String type = null;
+ private String charset = null;
+ private ByteArrayOutputStream out;
+
+ public InputStream getInputStream() throws IOException {
+ if (data == null && out == null) {
+ throw new IOException("No data");
+ }
+ if (out != null) {
+ final String encodedOut = out.toString(charset);
+ data = (data != null) ? data.concat(encodedOut) : encodedOut;
+ out = null;
+ }
+ return new ByteArrayInputStream(data.getBytes(charset));
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ out = (out == null) ? new ByteArrayOutputStream() : out;
+ return out;
+ }
+
+ public void setContentType(final String type) {
+ this.type = type.toLowerCase(Locale.ENGLISH);
+ }
+
+ public String getContentType() {
+ if (type != null && type.indexOf("charset") > 0
+ && type.startsWith("text/")) {
+ return type;
+ }
+ // Must be like "text/plain; charset=windows-1251"
+ return new StringBuffer(type != null ? type : "text/plain").append(
+ "; charset=").append(charset).toString();
+ }
+
+ public String getName() {
+ return "StringDataSource";
+ }
+
+ public void setCharset(final String charset) {
+ this.charset = charset;
+ }
+
+ public String getCharset() {
+ return charset;
+ }
+ }
+
+ /**
+ * Send the email.
+ *
+ * @throws BuildException if the email can't be sent.
+ */
+ public void send() {
+ try {
+ final Properties props = new Properties();
+
+ props.put("mail.smtp.host", host);
+ props.put("mail.smtp.port", String.valueOf(port));
+
+ // Aside, the JDK is clearly unaware of the Scottish
+ // 'session', which involves excessive quantities of
+ // alcohol :-)
+ Session sesh;
+ Authenticator auth = null;
+ if (SSL) {
+ try {
+ final Provider p = (Provider) Class.forName(
+ "com.sun.net.ssl.internal.ssl.Provider").newInstance();
+ Security.addProvider(p);
+ } catch (final Exception e) {
+ throw new BuildException("could not instantiate ssl "
+ + "security provider, check that you have JSSE in "
+ + "your classpath");
+ }
+ // SMTP provider
+ props.put("mail.smtp.socketFactory.class", SSL_FACTORY);
+ props.put("mail.smtp.socketFactory.fallback", "false");
+ props.put("mail.smtps.host", host);
+ if (isPortExplicitlySpecified()) {
+ props.put("mail.smtps.port", String.valueOf(port));
+ props.put("mail.smtp.socketFactory.port",
+ String.valueOf(port));
+ }
+ }
+ if (user != null || password != null) {
+ props.put("mail.smtp.auth", "true");
+ auth = new SimpleAuthenticator(user, password);
+ }
+ if (isStartTLSEnabled()) {
+ props.put("mail.smtp.starttls.enable", "true");
+ }
+ sesh = Session.getInstance(props, auth);
+
+ //create the message
+ final MimeMessage msg = new MimeMessage(sesh);
+ final MimeMultipart attachments = new MimeMultipart();
+
+ //set the sender
+ if (from.getName() == null) {
+ msg.setFrom(new InternetAddress(from.getAddress()));
+ } else {
+ msg.setFrom(new InternetAddress(from.getAddress(),
+ from.getName()));
+ }
+ // set the reply to addresses
+ msg.setReplyTo(internetAddresses(replyToList));
+ msg.setRecipients(Message.RecipientType.TO,
+ internetAddresses(toList));
+ msg.setRecipients(Message.RecipientType.CC,
+ internetAddresses(ccList));
+ msg.setRecipients(Message.RecipientType.BCC,
+ internetAddresses(bccList));
+
+ // Choosing character set of the mail message
+ // First: looking it from MimeType
+ String charset = parseCharSetFromMimeType(message.getMimeType());
+ if (charset != null) {
+ // Assign/reassign message charset from MimeType
+ message.setCharset(charset);
+ } else {
+ // Next: looking if charset having explicit definition
+ charset = message.getCharset();
+ if (charset == null) {
+ // Using default
+ charset = DEFAULT_CHARSET;
+ message.setCharset(charset);
+ }
+ }
+ // Using javax.activation.DataSource paradigm
+ final StringDataSource sds = new StringDataSource();
+ sds.setContentType(message.getMimeType());
+ sds.setCharset(charset);
+
+ if (subject != null) {
+ msg.setSubject(subject, charset);
+ }
+ msg.addHeader("Date", getDate());
+
+ if (headers != null) {
+ for (final Iterator iter = headers.iterator(); iter.hasNext();) {
+ final Header h = (Header) iter.next();
+ msg.addHeader(h.getName(), h.getValue());
+ }
+ }
+ final PrintStream out = new PrintStream(sds.getOutputStream());
+ message.print(out);
+ out.close();
+
+ final MimeBodyPart textbody = new MimeBodyPart();
+ textbody.setDataHandler(new DataHandler(sds));
+ attachments.addBodyPart(textbody);
+
+ final Enumeration e = files.elements();
+
+ while (e.hasMoreElements()) {
+ final File file = (File) e.nextElement();
+
+ MimeBodyPart body;
+
+ body = new MimeBodyPart();
+ if (!file.exists() || !file.canRead()) {
+ throw new BuildException("File \"" + file.getAbsolutePath()
+ + "\" does not exist or is not "
+ + "readable.");
+ }
+ final FileDataSource fileData = new FileDataSource(file);
+ final DataHandler fileDataHandler = new DataHandler(fileData);
+
+ body.setDataHandler(fileDataHandler);
+ body.setFileName(file.getName());
+ attachments.addBodyPart(body);
+ }
+ msg.setContent(attachments);
+ try {
+ // Send the message using SMTP, or SMTPS if the host uses SSL
+ final Transport transport = sesh.getTransport(SSL ? "smtps" : "smtp");
+ transport.connect(host, user, password);
+ transport.sendMessage(msg, msg.getAllRecipients());
+ } catch (final SendFailedException sfe) {
+ if (!shouldIgnoreInvalidRecipients()) {
+ throw new BuildException(GENERIC_ERROR, sfe);
+ } else if (sfe.getValidSentAddresses() == null
+ || sfe.getValidSentAddresses().length == 0) {
+ throw new BuildException("Couldn't reach any recipient",
+ sfe);
+ } else {
+ Address[] invalid = sfe.getInvalidAddresses();
+ if (invalid == null) {
+ invalid = new Address[0];
+ }
+ for (int i = 0; i < invalid.length; i++) {
+ didntReach(invalid[i], "invalid", sfe);
+ }
+ Address[] validUnsent = sfe.getValidUnsentAddresses();
+ if (validUnsent == null) {
+ validUnsent = new Address[0];
+ }
+ for (int i = 0; i < validUnsent.length; i++) {
+ didntReach(validUnsent[i], "valid", sfe);
+ }
+ }
+ }
+ } catch (final MessagingException e) {
+ throw new BuildException(GENERIC_ERROR, e);
+ } catch (final IOException e) {
+ throw new BuildException(GENERIC_ERROR, e);
+ }
+ }
+
+ private static InternetAddress[] internetAddresses(final Vector list)
+ throws AddressException, UnsupportedEncodingException {
+ final int size = list.size();
+ final InternetAddress[] addrs = new InternetAddress[size];
+
+ for (int i = 0; i < size; ++i) {
+ final EmailAddress addr = (EmailAddress) list.elementAt(i);
+
+ final String name = addr.getName();
+ addrs[i] = (name == null)
+ ? new InternetAddress(addr.getAddress())
+ : new InternetAddress(addr.getAddress(), name);
+ }
+ return addrs;
+ }
+
+ private String parseCharSetFromMimeType(final String type) {
+ if (type == null) {
+ return null;
+ }
+ final int pos = type.indexOf("charset");
+ if (pos < 0) {
+ return null;
+ }
+ // Assuming mime type in form "text/XXXX; charset=XXXXXX"
+ final StringTokenizer token = new StringTokenizer(type.substring(pos), "=; ");
+ token.nextToken(); // Skip 'charset='
+ return token.nextToken();
+ }
+
+ private void didntReach(final Address addr, final String category,
+ final MessagingException ex) {
+ final String msg = "Failed to send mail to " + category + " address "
+ + addr + " because of " + ex.getMessage();
+ if (task != null) {
+ task.log(msg, Project.MSG_WARN);
+ } else {
+ System.err.println(msg);
+ }
+ }
+
+ static class SimpleAuthenticator extends Authenticator {
+ private String user = null;
+ private String password = null;
+ public SimpleAuthenticator(final String user, final String password) {
+ this.user = user;
+ this.password = password;
+ }
+ public PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(user, password);
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/PlainMailer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/PlainMailer.java
new file mode 100644
index 00000000..20524ac3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/PlainMailer.java
@@ -0,0 +1,177 @@
+/*
+ * 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.tools.ant.taskdefs.email;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Enumeration;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.mail.MailMessage;
+
+/**
+ * Class responsible for sending email through raw protocol methods.
+ *
+ * @since Ant 1.5
+ */
+class PlainMailer extends Mailer {
+ /**
+ * Sends the email using the apache MailMessage class.
+ *
+ * @see org.apache.tools.mail.MailMessage
+ */
+ public void send() {
+ try {
+ MailMessage mailMessage = new MailMessage(host, port);
+
+ mailMessage.from(from.toString());
+
+ Enumeration e;
+ boolean atLeastOneRcptReached = false;
+
+ e = replyToList.elements();
+ while (e.hasMoreElements()) {
+ mailMessage.replyto(e.nextElement().toString());
+ }
+ e = toList.elements();
+ while (e.hasMoreElements()) {
+ String to = e.nextElement().toString();
+ try {
+ mailMessage.to(to);
+ atLeastOneRcptReached = true;
+ } catch (IOException ex) {
+ badRecipient(to, ex);
+ }
+ }
+ e = ccList.elements();
+ while (e.hasMoreElements()) {
+ String to = e.nextElement().toString();
+ try {
+ mailMessage.cc(to);
+ atLeastOneRcptReached = true;
+ } catch (IOException ex) {
+ badRecipient(to, ex);
+ }
+ }
+ e = bccList.elements();
+ while (e.hasMoreElements()) {
+ String to = e.nextElement().toString();
+ try {
+ mailMessage.bcc(to);
+ atLeastOneRcptReached = true;
+ } catch (IOException ex) {
+ badRecipient(to, ex);
+ }
+ }
+ if (!atLeastOneRcptReached) {
+ throw new BuildException("Couldn't reach any recipient");
+ }
+ if (subject != null) {
+ mailMessage.setSubject(subject);
+ }
+ mailMessage.setHeader("Date", getDate());
+ if (message.getCharset() != null) {
+ mailMessage.setHeader("Content-Type", message.getMimeType()
+ + "; charset=\"" + message.getCharset() + "\"");
+ } else {
+ mailMessage.setHeader("Content-Type", message.getMimeType());
+ }
+ if (headers != null) {
+ e = headers.elements();
+ while (e.hasMoreElements()) {
+ Header h = (Header) e.nextElement();
+ mailMessage.setHeader(h.getName(), h.getValue());
+ }
+ }
+ PrintStream out = mailMessage.getPrintStream();
+ message.print(out);
+
+ e = files.elements();
+ while (e.hasMoreElements()) {
+ attach((File) e.nextElement(), out);
+ }
+ mailMessage.sendAndClose();
+ } catch (IOException ioe) {
+ throw new BuildException("IO error sending mail", ioe);
+ }
+
+ }
+
+ /**
+ * Attaches a file to this email
+ *
+ * @param file The file to attache
+ * @param out The message stream to add to
+ * @throws IOException if errors occur
+ */
+ protected void attach(File file, PrintStream out)
+ throws IOException {
+ if (!file.exists() || !file.canRead()) {
+ throw new BuildException("File \"" + file.getName()
+ + "\" does not exist or is not "
+ + "readable.");
+ }
+
+ if (includeFileNames) {
+ out.println();
+
+ String filename = file.getName();
+ int filenamelength = filename.length();
+
+ out.println(filename);
+ for (int star = 0; star < filenamelength; star++) {
+ out.print('=');
+ }
+ out.println();
+ }
+
+ int length;
+ final int maxBuf = 1024;
+ byte[] buf = new byte[maxBuf];
+ FileInputStream finstr = new FileInputStream(file);
+
+ try {
+ BufferedInputStream in = new BufferedInputStream(finstr, buf.length);
+
+ while ((length = in.read(buf)) != -1) {
+ out.write(buf, 0, length);
+ }
+ } finally {
+ finstr.close();
+ }
+ }
+
+ private void badRecipient(String rcpt, IOException reason) {
+ String msg = "Failed to send mail to " + rcpt;
+ if (shouldIgnoreInvalidRecipients()) {
+ msg += " because of :" + reason.getMessage();
+ if (task != null) {
+ task.log(msg, Project.MSG_WARN);
+ } else {
+ System.err.println(msg);
+ }
+ } else {
+ throw new BuildException(msg, reason);
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/UUMailer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/UUMailer.java
new file mode 100644
index 00000000..d6542be4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/email/UUMailer.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.tools.ant.taskdefs.email;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.UUEncoder;
+
+/**
+ * An emailer that uuencodes attachments.
+ *
+ * @since Ant 1.5
+ */
+class UUMailer extends PlainMailer {
+ protected void attach(File file, PrintStream out)
+ throws IOException {
+ if (!file.exists() || !file.canRead()) {
+ throw new BuildException("File \"" + file.getName()
+ + "\" does not exist or is not "
+ + "readable.");
+ }
+
+ FileInputStream finstr = new FileInputStream(file);
+
+ try {
+ BufferedInputStream in = new BufferedInputStream(finstr);
+ UUEncoder encoder = new UUEncoder(file.getName());
+
+ encoder.encode(in, out);
+
+ } finally {
+ finstr.close();
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/CommandLauncher.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/CommandLauncher.java
new file mode 100644
index 00000000..8c126efe
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/CommandLauncher.java
@@ -0,0 +1,213 @@
+/*
+ * 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.tools.ant.taskdefs.launcher;
+
+import static org.apache.tools.ant.MagicNames.ANT_SHELL_LAUNCHER_REF_ID;
+import static org.apache.tools.ant.MagicNames.ANT_VM_LAUNCHER_REF_ID;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * A command launcher for a particular JVM/OS platform. This class is
+ * a general purpose command launcher which can only launch commands
+ * in the current working directory.
+ */
+public class CommandLauncher {
+
+ protected static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private static CommandLauncher vmLauncher = null;
+ private static CommandLauncher shellLauncher = null;
+
+ static {
+ if(!Os.isFamily("os/2")) {
+ vmLauncher = new Java13CommandLauncher();
+ }
+
+ if (Os.isFamily("mac") && !Os.isFamily("unix")) {
+ // Mac
+ shellLauncher = new MacCommandLauncher(new CommandLauncher());
+ } else if (Os.isFamily("os/2")) {
+ // OS/2
+ shellLauncher = new OS2CommandLauncher(new CommandLauncher());
+ } else if (Os.isFamily("windows")) {
+ CommandLauncher baseLauncher = new CommandLauncher();
+
+ if (!Os.isFamily("win9x")) {
+ // Windows XP/2000/NT
+ shellLauncher = new WinNTCommandLauncher(baseLauncher);
+ } else {
+ // Windows 98/95 - need to use an auxiliary script
+ shellLauncher =
+ new ScriptCommandLauncher("bin/antRun.bat", baseLauncher);
+ }
+ } else if (Os.isFamily("netware")) {
+
+ CommandLauncher baseLauncher = new CommandLauncher();
+
+ shellLauncher =
+ new PerlScriptCommandLauncher("bin/antRun.pl", baseLauncher);
+ } else if (Os.isFamily("openvms")) {
+ // OpenVMS
+ shellLauncher = new VmsCommandLauncher();
+ } else {
+ // Generic
+ shellLauncher = new ScriptCommandLauncher("bin/antRun",
+ new CommandLauncher());
+ }
+ }
+
+ /**
+ * Launches the given command in a new process.
+ *
+ * @param project
+ * The project that the command is part of.
+ * @param cmd
+ * The command to execute.
+ * @param env
+ * The environment for the new process. If null, the
+ * environment of the current process is used.
+ * @return the created Process.
+ * @throws IOException
+ * if attempting to run a command in a specific directory.
+ */
+ public Process exec(Project project, String[] cmd, String[] env)
+ throws IOException {
+ if(project != null) {
+ project.log("Execute:CommandLauncher: "
+ + Commandline.describeCommand(cmd), Project.MSG_DEBUG);
+ }
+ return Runtime.getRuntime().exec(cmd, env);
+ }
+
+ /**
+ * Launches the given command in a new process, in the given
+ * working directory.
+ *
+ * @param project
+ * The project that the command is part of.
+ * @param cmd
+ * The command to execute.
+ * @param env
+ * The environment for the new process. If null, the
+ * environment of the current process is used.
+ * @param workingDir
+ * The directory to start the command in. If null, the
+ * current directory is used.
+ * @return the created Process.
+ * @throws IOException
+ * if trying to change directory.
+ */
+ public Process exec(Project project, String[] cmd, String[] env,
+ File workingDir) throws IOException {
+ if (workingDir == null) {
+ return exec(project, cmd, env);
+ }
+ throw new IOException("Cannot execute a process in different "
+ + "directory under this JVM");
+ }
+
+ /**
+ * Obtains the shell launcher configured for the given project or
+ * the default shell launcher.
+ */
+ public static CommandLauncher getShellLauncher(Project project) {
+ CommandLauncher launcher = extractLauncher(ANT_SHELL_LAUNCHER_REF_ID,
+ project);
+ if (launcher == null) {
+ launcher = shellLauncher;
+ }
+
+ return launcher;
+ }
+
+ /**
+ * Obtains the VM launcher configured for the given project or
+ * the default VM launcher.
+ */
+ public static CommandLauncher getVMLauncher(Project project) {
+ CommandLauncher launcher = extractLauncher(ANT_VM_LAUNCHER_REF_ID,
+ project);
+ if (launcher == null) {
+ launcher = vmLauncher;
+ }
+ return launcher;
+ }
+
+ private static CommandLauncher extractLauncher(String referenceName,
+ Project project) {
+ CommandLauncher launcher = null;
+ if (project != null) {
+ launcher = (CommandLauncher) project.getReference(referenceName);
+ }
+
+ if (launcher == null) {
+ launcher = getSystemLauncher(referenceName);
+ }
+ return launcher;
+ }
+
+ private static CommandLauncher getSystemLauncher(String launcherRefId) {
+ CommandLauncher launcher = null;
+ String launcherClass = System.getProperty(launcherRefId);
+ if (launcherClass != null) {
+ try {
+ launcher = (CommandLauncher) Class.forName(launcherClass)
+ .newInstance();
+ } catch(InstantiationException e) {
+ System.err.println("Could not instantiate launcher class "
+ + launcherClass + ": " + e.getMessage());
+ } catch(IllegalAccessException e) {
+ System.err.println("Could not instantiate launcher class "
+ + launcherClass + ": " + e.getMessage());
+ } catch(ClassNotFoundException e) {
+ System.err.println("Could not instantiate launcher class "
+ + launcherClass + ": " + e.getMessage());
+ }
+ }
+
+ return launcher;
+ }
+
+ /**
+ * Sets the VM launcher to use for the given project.
+ */
+ public static void setVMLauncher(Project project,
+ CommandLauncher launcher) {
+ if (project != null) {
+ project.addReference(ANT_VM_LAUNCHER_REF_ID, launcher);
+ }
+ }
+
+ /**
+ * Sets the shell launcher to use for the given project.
+ */
+ public static void setShellLauncher(Project project,
+ CommandLauncher launcher) {
+ if (project != null) {
+ project.addReference(ANT_SHELL_LAUNCHER_REF_ID, launcher);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/CommandLauncherProxy.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/CommandLauncherProxy.java
new file mode 100644
index 00000000..32ddf2b2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/CommandLauncherProxy.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.tools.ant.taskdefs.launcher;
+
+import java.io.IOException;
+
+import org.apache.tools.ant.Project;
+
+/**
+ * A command launcher that proxies another command
+ * launcher. Sub-classes override exec(args, env, workdir).
+ */
+public class CommandLauncherProxy extends CommandLauncher {
+ private final CommandLauncher myLauncher;
+
+ protected CommandLauncherProxy(CommandLauncher launcher) {
+ myLauncher = launcher;
+ }
+
+ /**
+ * Launches the given command in a new process. Delegates this
+ * method to the proxied launcher.
+ *
+ * @param project
+ * the Ant project.
+ * @param cmd
+ * the command line to execute as an array of strings.
+ * @param env
+ * the environment to set as an array of strings.
+ * @return the created Process.
+ * @throws IOException
+ * forwarded from the exec method of the command launcher.
+ */
+ @Override
+ public Process exec(Project project, String[] cmd, String[] env)
+ throws IOException {
+ return myLauncher.exec(project, cmd, env);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/Java13CommandLauncher.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/Java13CommandLauncher.java
new file mode 100644
index 00000000..0a018756
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/Java13CommandLauncher.java
@@ -0,0 +1,66 @@
+/*
+ * 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.tools.ant.taskdefs.launcher;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * A command launcher for JDK/JRE 1.3 (and higher). Uses the built-in
+ * Runtime.exec() command.
+ */
+public class Java13CommandLauncher extends CommandLauncher {
+
+ /**
+ * Launches the given command in a new process, in the given
+ * working directory.
+ *
+ * @param project
+ * the Ant project.
+ * @param cmd
+ * the command line to execute as an array of strings.
+ * @param env
+ * the environment to set as an array of strings.
+ * @param workingDir
+ * the working directory where the command should run.
+ * @return the created Process.
+ * @throws IOException
+ * probably forwarded from Runtime#exec.
+ */
+ @Override
+ public Process exec(Project project, String[] cmd, String[] env,
+ File workingDir) throws IOException {
+ try {
+ if (project != null) {
+ project.log("Execute:Java13CommandLauncher: "
+ + Commandline.describeCommand(cmd),
+ Project.MSG_DEBUG);
+ }
+ return Runtime.getRuntime().exec(cmd, env, workingDir);
+ } catch(IOException ioex) {
+ throw ioex;
+ } catch(Exception exc) {
+ // IllegalAccess, IllegalArgument, ClassCast
+ throw new BuildException("Unable to execute command", exc);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/MacCommandLauncher.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/MacCommandLauncher.java
new file mode 100644
index 00000000..58d8d71f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/MacCommandLauncher.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.tools.ant.taskdefs.launcher;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.Project;
+
+/**
+ * A command launcher for Mac that uses a dodgy mechanism to change
+ * working directory before launching commands.
+ */
+public class MacCommandLauncher extends CommandLauncherProxy {
+ public MacCommandLauncher(CommandLauncher launcher) {
+ super(launcher);
+ }
+
+ /**
+ * Launches the given command in a new process, in the given
+ * working directory.
+ *
+ * @param project
+ * the Ant project.
+ * @param cmd
+ * the command line to execute as an array of strings.
+ * @param env
+ * the environment to set as an array of strings.
+ * @param workingDir
+ * working directory where the command should run.
+ * @return the created Process.
+ * @throws IOException
+ * forwarded from the exec method of the command launcher.
+ */
+ @Override
+ public Process exec(Project project, String[] cmd, String[] env,
+ File workingDir) throws IOException {
+ if (workingDir == null) {
+ return exec(project, cmd, env);
+ }
+ System.getProperties().put("user.dir", workingDir.getAbsolutePath());
+ try {
+ return exec(project, cmd, env);
+ } finally {
+ System.getProperties().put("user.dir", System.getProperty("user.dir"));
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/OS2CommandLauncher.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/OS2CommandLauncher.java
new file mode 100644
index 00000000..6d6d2aa2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/OS2CommandLauncher.java
@@ -0,0 +1,82 @@
+/*
+ * 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.tools.ant.taskdefs.launcher;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.Project;
+
+/**
+ * A command launcher for OS/2 that uses 'cmd.exe' when launching
+ * commands in directories other than the current working directory.
+ *
+ * <p>Unlike Windows NT and friends, OS/2's cd doesn't support the /d
+ * switch to change drives and directories in one go.</p>
+ */
+public class OS2CommandLauncher extends CommandLauncherProxy {
+ public OS2CommandLauncher(CommandLauncher launcher) {
+ super(launcher);
+ }
+
+ /**
+ * Launches the given command in a new process, in the given
+ * working directory.
+ *
+ * @param project
+ * the Ant project.
+ * @param cmd
+ * the command line to execute as an array of strings.
+ * @param env
+ * the environment to set as an array of strings.
+ * @param workingDir
+ * working directory where the command should run.
+ * @return the created Process.
+ * @throws IOException
+ * forwarded from the exec method of the command launcher.
+ */
+ @Override
+ public Process exec(Project project, String[] cmd, String[] env,
+ File workingDir) throws IOException {
+ File commandDir = workingDir;
+ if (workingDir == null) {
+ if (project != null) {
+ commandDir = project.getBaseDir();
+ } else {
+ return exec(project, cmd, env);
+ }
+ }
+ // Use cmd.exe to change to the specified drive and
+ // directory before running the command
+ final int preCmdLength = 7;
+ final String cmdDir = commandDir.getAbsolutePath();
+ String[] newcmd = new String[cmd.length + preCmdLength];
+ // CheckStyle:MagicNumber OFF - do not bother
+ newcmd[0] = "cmd";
+ newcmd[1] = "/c";
+ newcmd[2] = cmdDir.substring(0, 2);
+ newcmd[3] = "&&";
+ newcmd[4] = "cd";
+ newcmd[5] = cmdDir.substring(2);
+ newcmd[6] = "&&";
+ // CheckStyle:MagicNumber ON
+ System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length);
+
+ return exec(project, newcmd, env);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/PerlScriptCommandLauncher.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/PerlScriptCommandLauncher.java
new file mode 100644
index 00000000..9b76e760
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/PerlScriptCommandLauncher.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.tools.ant.taskdefs.launcher;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+
+/**
+ * A command launcher that uses an auxiliary perl script to launch
+ * commands in directories other than the current working directory.
+ */
+public class PerlScriptCommandLauncher extends CommandLauncherProxy {
+ private final String myScript;
+
+ public PerlScriptCommandLauncher(String script, CommandLauncher launcher) {
+ super(launcher);
+ myScript = script;
+ }
+
+ /**
+ * Launches the given command in a new process, in the given
+ * working directory.
+ *
+ * @param project
+ * the Ant project.
+ * @param cmd
+ * the command line to execute as an array of strings.
+ * @param env
+ * the environment to set as an array of strings.
+ * @param workingDir
+ * working directory where the command should run.
+ * @return the created Process.
+ * @throws IOException
+ * forwarded from the exec method of the command launcher.
+ */
+ @Override
+ public Process exec(Project project, String[] cmd, String[] env,
+ File workingDir) throws IOException {
+ if (project == null) {
+ if (workingDir == null) {
+ return exec(project, cmd, env);
+ }
+ throw new IOException("Cannot locate antRun script: "
+ + "No project provided");
+ }
+ // Locate the auxiliary script
+ String antHome = project.getProperty(MagicNames.ANT_HOME);
+ if (antHome == null) {
+ throw new IOException("Cannot locate antRun script: "
+ + "Property '" + MagicNames.ANT_HOME
+ + "' not found");
+ }
+ String antRun = FILE_UTILS.resolveFile(project.getBaseDir(),
+ antHome + File.separator
+ + myScript).toString();
+
+ // Build the command
+ File commandDir = workingDir;
+ if (workingDir == null) {
+ commandDir = project.getBaseDir();
+ }
+ // CheckStyle:MagicNumber OFF
+ String[] newcmd = new String[cmd.length + 3];
+ newcmd[0] = "perl";
+ newcmd[1] = antRun;
+ newcmd[2] = commandDir.getAbsolutePath();
+ System.arraycopy(cmd, 0, newcmd, 3, cmd.length);
+ // CheckStyle:MagicNumber ON
+
+ return exec(project, newcmd, env);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/ScriptCommandLauncher.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/ScriptCommandLauncher.java
new file mode 100644
index 00000000..fc7e3ebc
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/ScriptCommandLauncher.java
@@ -0,0 +1,88 @@
+/*
+ * 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.tools.ant.taskdefs.launcher;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+
+/**
+ * A command launcher that uses an auxiliary script to launch commands
+ * in directories other than the current working directory.
+ */
+public class ScriptCommandLauncher extends CommandLauncherProxy {
+ private final String myScript;
+
+ public ScriptCommandLauncher(String script, CommandLauncher launcher) {
+ super(launcher);
+ myScript = script;
+ }
+
+ /**
+ * Launches the given command in a new process, in the given
+ * working directory.
+ *
+ * @param project
+ * the Ant project.
+ * @param cmd
+ * the command line to execute as an array of strings.
+ * @param env
+ * the environment to set as an array of strings.
+ * @param workingDir
+ * working directory where the command should run.
+ * @return the created Process.
+ * @throws IOException
+ * forwarded from the exec method of the command launcher.
+ */
+ @Override
+ public Process exec(Project project, String[] cmd, String[] env,
+ File workingDir) throws IOException {
+ if (project == null) {
+ if (workingDir == null) {
+ return exec(project, cmd, env);
+ }
+ throw new IOException("Cannot locate antRun script: "
+ + "No project provided");
+ }
+ // Locate the auxiliary script
+ String antHome = project.getProperty(MagicNames.ANT_HOME);
+ if (antHome == null) {
+ throw new IOException("Cannot locate antRun script: "
+ + "Property '" + MagicNames.ANT_HOME
+ + "' not found");
+ }
+ String antRun = FILE_UTILS.resolveFile(project.getBaseDir(),
+ antHome + File.separator
+ + myScript).toString();
+
+ // Build the command
+ File commandDir = workingDir;
+ if (workingDir == null) {
+ commandDir = project.getBaseDir();
+ }
+ String[] newcmd = new String[cmd.length + 2];
+ newcmd[0] = antRun;
+ newcmd[1] = commandDir.getAbsolutePath();
+ System.arraycopy(cmd, 0, newcmd, 2, cmd.length);
+
+ return exec(project, newcmd, env);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/VmsCommandLauncher.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/VmsCommandLauncher.java
new file mode 100644
index 00000000..c26d9841
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/VmsCommandLauncher.java
@@ -0,0 +1,145 @@
+/*
+ * 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.tools.ant.taskdefs.launcher;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * A command launcher for VMS that writes the command to a temporary
+ * DCL script before launching commands. This is due to limitations of
+ * both the DCL interpreter and the Java VM implementation.
+ */
+public class VmsCommandLauncher extends Java13CommandLauncher {
+
+ public VmsCommandLauncher() {
+ super();
+ }
+
+ /**
+ * Launches the given command in a new process.
+ *
+ * @param project
+ * the Ant project.
+ * @param cmd
+ * the command line to execute as an array of strings.
+ * @param env
+ * the environment to set as an array of strings.
+ * @return the created Process.
+ * @throws IOException
+ * forwarded from the exec method of the command launcher.
+ */
+ @Override
+ public Process exec(Project project, String[] cmd, String[] env)
+ throws IOException {
+ File cmdFile = createCommandFile(cmd, env);
+ Process p = super.exec(project, new String[] {cmdFile.getPath()}, env);
+ deleteAfter(cmdFile, p);
+ return p;
+ }
+
+ /**
+ * Launches the given command in a new process, in the given
+ * working directory. Note that under Java 1.4.0 and 1.4.1 on VMS
+ * this method only works if <code>workingDir</code> is null or
+ * the logical JAVA$FORK_SUPPORT_CHDIR needs to be set to TRUE.
+ *
+ * @param project
+ * the Ant project.
+ * @param cmd
+ * the command line to execute as an array of strings.
+ * @param env
+ * the environment to set as an array of strings.
+ * @param workingDir
+ * working directory where the command should run.
+ * @return the created Process.
+ * @throws IOException
+ * forwarded from the exec method of the command launcher.
+ */
+ @Override
+ public Process exec(Project project, String[] cmd, String[] env,
+ File workingDir) throws IOException {
+ File cmdFile = createCommandFile(cmd, env);
+ Process p = super.exec(project, new String[] {
+ cmdFile.getPath()
+ }, env, workingDir);
+ deleteAfter(cmdFile, p);
+ return p;
+ }
+
+ /*
+ * Writes the command into a temporary DCL script and returns the
+ * corresponding File object. The script will be deleted on exit.
+ * @param cmd the command line to execute as an array of strings.
+ * @param env the environment to set as an array of strings.
+ * @return the command File.
+ * @throws IOException if errors are encountered creating the file.
+ */
+ private File createCommandFile(String[] cmd, String[] env)
+ throws IOException {
+ File script = FILE_UTILS.createTempFile("ANT", ".COM", null, true, true);
+ BufferedWriter out = null;
+ try {
+ out = new BufferedWriter(new FileWriter(script));
+
+ // add the environment as logicals to the DCL script
+ if (env != null) {
+ int eqIndex;
+ for (int i = 0; i < env.length; i++) {
+ eqIndex = env[i].indexOf('=');
+ if (eqIndex != -1) {
+ out.write("$ DEFINE/NOLOG ");
+ out.write(env[i].substring(0, eqIndex));
+ out.write(" \"");
+ out.write(env[i].substring(eqIndex + 1));
+ out.write('\"');
+ out.newLine();
+ }
+ }
+ }
+ out.write("$ " + cmd[0]);
+ for (int i = 1; i < cmd.length; i++) {
+ out.write(" -");
+ out.newLine();
+ out.write(cmd[i]);
+ }
+ } finally {
+ FileUtils.close(out);
+ }
+ return script;
+ }
+
+ private void deleteAfter(final File f, final Process p) {
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ p.waitFor();
+ } catch(InterruptedException e) {
+ // ignore
+ }
+ FileUtils.delete(f);
+ }
+ }.start();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/WinNTCommandLauncher.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/WinNTCommandLauncher.java
new file mode 100644
index 00000000..4ac2af25
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/launcher/WinNTCommandLauncher.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.tools.ant.taskdefs.launcher;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.Project;
+
+/**
+ * A command launcher for Windows XP/2000/NT that uses 'cmd.exe' when
+ * launching commands in directories other than the current working
+ * directory.
+ */
+public class WinNTCommandLauncher extends CommandLauncherProxy {
+ public WinNTCommandLauncher(CommandLauncher launcher) {
+ super(launcher);
+ }
+
+ /**
+ * Launches the given command in a new process, in the given
+ * working directory.
+ *
+ * @param project
+ * the Ant project.
+ * @param cmd
+ * the command line to execute as an array of strings.
+ * @param env
+ * the environment to set as an array of strings.
+ * @param workingDir
+ * working directory where the command should run.
+ * @return the created Process.
+ * @throws IOException
+ * forwarded from the exec method of the command launcher.
+ */
+ @Override
+ public Process exec(Project project, String[] cmd, String[] env,
+ File workingDir) throws IOException {
+ File commandDir = workingDir;
+ if (workingDir == null) {
+ if (project != null) {
+ commandDir = project.getBaseDir();
+ } else {
+ return exec(project, cmd, env);
+ }
+ }
+ // Use cmd.exe to change to the specified directory before running
+ // the command
+ final int preCmdLength = 6;
+ String[] newcmd = new String[cmd.length + preCmdLength];
+ // CheckStyle:MagicNumber OFF - do not bother
+ newcmd[0] = "cmd";
+ newcmd[1] = "/c";
+ newcmd[2] = "cd";
+ newcmd[3] = "/d";
+ newcmd[4] = commandDir.getAbsolutePath();
+ newcmd[5] = "&&";
+ // CheckStyle:MagicNumber ON
+ System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length);
+
+ return exec(project, newcmd, env);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ANTLR.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ANTLR.java
new file mode 100644
index 00000000..605b3368
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ANTLR.java
@@ -0,0 +1,438 @@
+/*
+ * 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.tools.ant.taskdefs.optional;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.taskdefs.PumpStreamHandler;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.LoaderUtils;
+import org.apache.tools.ant.util.TeeOutputStream;
+
+/**
+ * Invokes the ANTLR Translator generator on a grammar file.
+ *
+ */
+public class ANTLR extends Task {
+
+ private CommandlineJava commandline = new CommandlineJava();
+
+ /** the file to process */
+ private File targetFile;
+
+ /** where to output the result */
+ private File outputDirectory;
+
+ /** an optional super grammar file */
+ private File superGrammar;
+
+ /** optional flag to enable html output */
+ private boolean html;
+
+ /** optional flag to print out a diagnostic file */
+ private boolean diagnostic;
+
+ /** optional flag to add trace methods */
+ private boolean trace;
+
+ /** optional flag to add trace methods to the parser only */
+ private boolean traceParser;
+
+ /** optional flag to add trace methods to the lexer only */
+ private boolean traceLexer;
+
+ /** optional flag to add trace methods to the tree walker only */
+ private boolean traceTreeWalker;
+
+ /** working directory */
+ private File workingdir = null;
+
+ /** captures ANTLR's output */
+ private ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+ /** The debug attribute */
+ private boolean debug;
+
+
+ /** Instance of a utility class to use for file operations. */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** Constructor for ANTLR task. */
+ public ANTLR() {
+ commandline.setVm(JavaEnvUtils.getJreExecutable("java"));
+ commandline.setClassname("antlr.Tool");
+ }
+
+ /**
+ * The grammar file to process.
+ * @param target the gramer file
+ */
+ public void setTarget(File target) {
+ log("Setting target to: " + target.toString(), Project.MSG_VERBOSE);
+ this.targetFile = target;
+ }
+
+ /**
+ * The directory to write the generated files to.
+ * @param outputDirectory the output directory
+ */
+ public void setOutputdirectory(File outputDirectory) {
+ log("Setting output directory to: " + outputDirectory.toString(), Project.MSG_VERBOSE);
+ this.outputDirectory = outputDirectory;
+ }
+
+ /**
+ * Sets an optional super grammar file.
+ * Use setGlib(File superGrammar) instead.
+ * @param superGrammar the super grammar filename
+ * @deprecated since ant 1.6
+ */
+ public void setGlib(String superGrammar) {
+ String sg = null;
+ if (Os.isFamily("dos")) {
+ sg = superGrammar.replace('\\', '/');
+ } else {
+ sg = superGrammar;
+ }
+ setGlib(FILE_UTILS.resolveFile(getProject().getBaseDir(), sg));
+ }
+ /**
+ * Sets an optional super grammar file
+ * @param superGrammar the super grammar file
+ * @since ant 1.6
+ */
+ public void setGlib(File superGrammar) {
+ this.superGrammar = superGrammar;
+ }
+ /**
+ * Sets a flag to enable ParseView debugging
+ * @param enable a <code>boolean</code> value
+ */
+ public void setDebug(boolean enable) {
+ this.debug = enable;
+ }
+
+ /**
+ * If true, emit html
+ * @param enable a <code>boolean</code> value
+ */
+ public void setHtml(boolean enable) {
+ html = enable;
+ }
+
+ /**
+ * Sets a flag to emit diagnostic text
+ * @param enable a <code>boolean</code> value
+ */
+ public void setDiagnostic(boolean enable) {
+ diagnostic = enable;
+ }
+
+ /**
+ * If true, enables all tracing.
+ * @param enable a <code>boolean</code> value
+ */
+ public void setTrace(boolean enable) {
+ trace = enable;
+ }
+
+ /**
+ * If true, enables parser tracing.
+ * @param enable a <code>boolean</code> value
+ */
+ public void setTraceParser(boolean enable) {
+ traceParser = enable;
+ }
+
+ /**
+ * If true, enables lexer tracing.
+ * @param enable a <code>boolean</code> value
+ */
+ public void setTraceLexer(boolean enable) {
+ traceLexer = enable;
+ }
+
+ /**
+ * Sets a flag to allow the user to enable tree walker tracing
+ * @param enable a <code>boolean</code> value
+ */
+ public void setTraceTreeWalker(boolean enable) {
+ traceTreeWalker = enable;
+ }
+
+ // we are forced to fork ANTLR since there is a call
+ // to System.exit() and there is nothing we can do
+ // right now to avoid this. :-( (SBa)
+ // I'm not removing this method to keep backward compatibility
+ /**
+ * @ant.attribute ignore="true"
+ * @param s a <code>boolean</code> value
+ */
+ public void setFork(boolean s) {
+ //this.fork = s;
+ }
+
+ /**
+ * The working directory of the process
+ * @param d the working directory
+ */
+ public void setDir(File d) {
+ this.workingdir = d;
+ }
+
+ /**
+ * Adds a classpath to be set
+ * because a directory might be given for Antlr debug.
+ * @return a path to be configured
+ */
+ public Path createClasspath() {
+ return commandline.createClasspath(getProject()).createPath();
+ }
+
+ /**
+ * Adds a new JVM argument.
+ * @return create a new JVM argument so that any argument can be passed to the JVM.
+ * @see #setFork(boolean)
+ */
+ public Commandline.Argument createJvmarg() {
+ return commandline.createVmArgument();
+ }
+
+ /**
+ * Adds the jars or directories containing Antlr
+ * this should make the forked JVM work without having to
+ * specify it directly.
+ * @throws BuildException on error
+ */
+ public void init() throws BuildException {
+ addClasspathEntry("/antlr/ANTLRGrammarParseBehavior.class");
+ }
+
+ /**
+ * Search for the given resource and add the directory or archive
+ * that contains it to the classpath.
+ *
+ * <p>Doesn't work for archives in JDK 1.1 as the URL returned by
+ * getResource doesn't contain the name of the archive.</p>
+ * @param resource the resource name to search for
+ */
+ protected void addClasspathEntry(String resource) {
+ /*
+ * pre Ant 1.6 this method used to call getClass().getResource
+ * while Ant 1.6 will call ClassLoader.getResource().
+ *
+ * The difference is that Class.getResource expects a leading
+ * slash for "absolute" resources and will strip it before
+ * delegating to ClassLoader.getResource - so we now have to
+ * emulate Class's behavior.
+ */
+ if (resource.startsWith("/")) {
+ resource = resource.substring(1);
+ } else {
+ resource = "org/apache/tools/ant/taskdefs/optional/"
+ + resource;
+ }
+
+ File f = LoaderUtils.getResourceSource(getClass().getClassLoader(),
+ resource);
+ if (f != null) {
+ log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG);
+ createClasspath().setLocation(f);
+ } else {
+ log("Couldn\'t find " + resource, Project.MSG_VERBOSE);
+ }
+ }
+
+ /**
+ * Execute the task.
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ validateAttributes();
+
+ //TODO: use ANTLR to parse the grammar file to do this.
+ File generatedFile = getGeneratedFile();
+ boolean targetIsOutOfDate =
+ targetFile.lastModified() > generatedFile.lastModified();
+ boolean superGrammarIsOutOfDate = superGrammar != null
+ && (superGrammar.lastModified() > generatedFile.lastModified());
+ if (targetIsOutOfDate || superGrammarIsOutOfDate) {
+ if (targetIsOutOfDate) {
+ log("Compiling " + targetFile + " as it is newer than "
+ + generatedFile, Project.MSG_VERBOSE);
+ } else {
+ log("Compiling " + targetFile + " as " + superGrammar
+ + " is newer than " + generatedFile, Project.MSG_VERBOSE);
+ }
+ populateAttributes();
+ commandline.createArgument().setValue(targetFile.toString());
+
+ log(commandline.describeCommand(), Project.MSG_VERBOSE);
+ int err = run(commandline.getCommandline());
+ if (err != 0) {
+ throw new BuildException("ANTLR returned: " + err, getLocation());
+ } else {
+ String output = bos.toString();
+ if (output.indexOf("error:") > -1) {
+ throw new BuildException("ANTLR signaled an error: "
+ + output, getLocation());
+ }
+ }
+ } else {
+ log("Skipped grammar file. Generated file " + generatedFile
+ + " is newer.", Project.MSG_VERBOSE);
+ }
+ }
+
+ /**
+ * A refactored method for populating all the command line arguments based
+ * on the user-specified attributes.
+ */
+ private void populateAttributes() {
+ commandline.createArgument().setValue("-o");
+ commandline.createArgument().setValue(outputDirectory.toString());
+ if (superGrammar != null) {
+ commandline.createArgument().setValue("-glib");
+ commandline.createArgument().setValue(superGrammar.toString());
+ }
+ if (html) {
+ commandline.createArgument().setValue("-html");
+ }
+ if (diagnostic) {
+ commandline.createArgument().setValue("-diagnostic");
+ }
+ if (trace) {
+ commandline.createArgument().setValue("-trace");
+ }
+ if (traceParser) {
+ commandline.createArgument().setValue("-traceParser");
+ }
+ if (traceLexer) {
+ commandline.createArgument().setValue("-traceLexer");
+ }
+ if (traceTreeWalker) {
+ if (is272()) {
+ commandline.createArgument().setValue("-traceTreeParser");
+ } else {
+ commandline.createArgument().setValue("-traceTreeWalker");
+ }
+ }
+ if (debug) {
+ commandline.createArgument().setValue("-debug");
+ }
+ }
+
+ private void validateAttributes() throws BuildException {
+ if (targetFile == null || !targetFile.isFile()) {
+ throw new BuildException("Invalid target: " + targetFile);
+ }
+
+ // if no output directory is specified, used the target's directory
+ if (outputDirectory == null) {
+ setOutputdirectory(new File(targetFile.getParent()));
+ }
+ if (!outputDirectory.isDirectory()) {
+ throw new BuildException("Invalid output directory: " + outputDirectory);
+ }
+ }
+
+ private File getGeneratedFile() throws BuildException {
+ String generatedFileName = null;
+ try {
+ BufferedReader in = new BufferedReader(new FileReader(targetFile));
+ String line;
+ while ((line = in.readLine()) != null) {
+ int extendsIndex = line.indexOf(" extends ");
+ if (line.startsWith("class ") && extendsIndex > -1) {
+ generatedFileName = line.substring(
+ "class ".length(), extendsIndex).trim();
+ break;
+ }
+ }
+ in.close();
+ } catch (Exception e) {
+ throw new BuildException("Unable to determine generated class", e);
+ }
+ if (generatedFileName == null) {
+ throw new BuildException("Unable to determine generated class");
+ }
+ return new File(outputDirectory, generatedFileName
+ + (html ? ".html" : ".java"));
+ }
+
+ /** execute in a forked VM */
+ private int run(String[] command) throws BuildException {
+ PumpStreamHandler psh =
+ new PumpStreamHandler(new LogOutputStream(this, Project.MSG_INFO),
+ new TeeOutputStream(
+ new LogOutputStream(this,
+ Project.MSG_WARN),
+ bos)
+ );
+ Execute exe = new Execute(psh, null);
+ exe.setAntRun(getProject());
+ if (workingdir != null) {
+ exe.setWorkingDirectory(workingdir);
+ }
+ exe.setCommandline(command);
+ try {
+ return exe.execute();
+ } catch (IOException e) {
+ throw new BuildException(e, getLocation());
+ } finally {
+ FileUtils.close(bos);
+ }
+ }
+
+ /**
+ * Whether the antlr version is 2.7.2 (or higher).
+ *
+ * @return true if the version of Antlr present is 2.7.2 or later.
+ * @since Ant 1.6
+ */
+ protected boolean is272() {
+ AntClassLoader l = null;
+ try {
+ l = getProject().createClassLoader(commandline.getClasspath());
+ l.loadClass("antlr.Version");
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ } finally {
+ if (l != null) {
+ l.cleanup();
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Cab.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Cab.java
new file mode 100644
index 00000000..11c091a8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Cab.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.tools.ant.taskdefs.optional;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ExecTask;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.taskdefs.StreamPumper;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.FileUtils;
+
+
+/**
+ * Create a CAB archive.
+ *
+ */
+
+public class Cab extends MatchingTask {
+ private static final int DEFAULT_RESULT = -99;
+ private File cabFile;
+ private File baseDir;
+ private Vector filesets = new Vector();
+ private boolean doCompress = true;
+ private boolean doVerbose = false;
+ private String cmdOptions;
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected String archiveType = "cab";
+ // CheckStyle:VisibilityModifier ON
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * The name/location of where to create the .cab file.
+ * @param cabFile the location of the cab file.
+ */
+ public void setCabfile(File cabFile) {
+ this.cabFile = cabFile;
+ }
+
+ /**
+ * Base directory to look in for files to CAB.
+ * @param baseDir base directory for files to cab.
+ */
+ public void setBasedir(File baseDir) {
+ this.baseDir = baseDir;
+ }
+
+ /**
+ * If true, compress the files otherwise only store them.
+ * @param compress a <code>boolean</code> value.
+ */
+ public void setCompress(boolean compress) {
+ doCompress = compress;
+ }
+
+ /**
+ * If true, display cabarc output.
+ * @param verbose a <code>boolean</code> value.
+ */
+ public void setVerbose(boolean verbose) {
+ doVerbose = verbose;
+ }
+
+ /**
+ * Sets additional cabarc options that are not supported directly.
+ * @param options cabarc command line options.
+ */
+ public void setOptions(String options) {
+ cmdOptions = options;
+ }
+
+ /**
+ * Adds a set of files to archive.
+ * @param set a set of files to archive.
+ */
+ public void addFileset(FileSet set) {
+ if (filesets.size() > 0) {
+ throw new BuildException("Only one nested fileset allowed");
+ }
+ filesets.addElement(set);
+ }
+
+ /*
+ * I'm not fond of this pattern: "sub-method expected to throw
+ * task-cancelling exceptions". It feels too much like programming
+ * for side-effects to me...
+ */
+ /**
+ * Check if the attributes and nested elements are correct.
+ * @throws BuildException on error.
+ */
+ protected void checkConfiguration() throws BuildException {
+ if (baseDir == null && filesets.size() == 0) {
+ throw new BuildException("basedir attribute or one "
+ + "nested fileset is required!",
+ getLocation());
+ }
+ if (baseDir != null && !baseDir.exists()) {
+ throw new BuildException("basedir does not exist!", getLocation());
+ }
+ if (baseDir != null && filesets.size() > 0) {
+ throw new BuildException(
+ "Both basedir attribute and a nested fileset is not allowed");
+ }
+ if (cabFile == null) {
+ throw new BuildException("cabfile attribute must be set!",
+ getLocation());
+ }
+ }
+
+ /**
+ * Create a new exec delegate. The delegate task is populated so that
+ * it appears in the logs to be the same task as this one.
+ * @return the delegate.
+ * @throws BuildException on error.
+ */
+ protected ExecTask createExec() throws BuildException {
+ ExecTask exec = new ExecTask(this);
+ return exec;
+ }
+
+ /**
+ * Check to see if the target is up to date with respect to input files.
+ * @param files the list of files to check.
+ * @return true if the cab file is newer than its dependents.
+ */
+ protected boolean isUpToDate(Vector files) {
+ boolean upToDate = true;
+ final int size = files.size();
+ for (int i = 0; i < size && upToDate; i++) {
+ String file = files.elementAt(i).toString();
+ if (FILE_UTILS.resolveFile(baseDir, file).lastModified()
+ > cabFile.lastModified()) {
+ upToDate = false;
+ }
+ }
+ return upToDate;
+ }
+
+ /**
+ * Creates a list file. This temporary file contains a list of all files
+ * to be included in the cab, one file per line.
+ *
+ * <p>This method expects to only be called on Windows and thus
+ * quotes the file names.</p>
+ * @param files the list of files to use.
+ * @return the list file created.
+ * @throws IOException if there is an error.
+ */
+ protected File createListFile(Vector files)
+ throws IOException {
+ File listFile = FILE_UTILS.createTempFile("ant", "", null, true, true);
+
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new FileWriter(listFile));
+
+ final int size = files.size();
+ for (int i = 0; i < size; i++) {
+ writer.write('\"' + files.elementAt(i).toString() + '\"');
+ writer.newLine();
+ }
+ } finally {
+ FileUtils.close(writer);
+ }
+
+ return listFile;
+ }
+
+ /**
+ * Append all files found by a directory scanner to a vector.
+ * @param files the vector to append the files to.
+ * @param ds the scanner to get the files from.
+ */
+ protected void appendFiles(Vector files, DirectoryScanner ds) {
+ String[] dsfiles = ds.getIncludedFiles();
+
+ for (int i = 0; i < dsfiles.length; i++) {
+ files.addElement(dsfiles[i]);
+ }
+ }
+
+ /**
+ * Get the complete list of files to be included in the cab. Filenames
+ * are gathered from the fileset if it has been added, otherwise from the
+ * traditional include parameters.
+ * @return the list of files.
+ * @throws BuildException if there is an error.
+ */
+ protected Vector getFileList() throws BuildException {
+ Vector files = new Vector();
+
+ if (baseDir != null) {
+ // get files from old methods - includes and nested include
+ appendFiles(files, super.getDirectoryScanner(baseDir));
+ } else {
+ FileSet fs = (FileSet) filesets.elementAt(0);
+ baseDir = fs.getDir();
+ appendFiles(files, fs.getDirectoryScanner(getProject()));
+ }
+
+ return files;
+ }
+
+ /**
+ * execute this task.
+ * @throws BuildException on error.
+ */
+ public void execute() throws BuildException {
+
+ checkConfiguration();
+
+ Vector files = getFileList();
+
+ // quick exit if the target is up to date
+ if (isUpToDate(files)) {
+ return;
+ }
+
+ log("Building " + archiveType + ": " + cabFile.getAbsolutePath());
+
+ if (!Os.isFamily("windows")) {
+ log("Using listcab/libcabinet", Project.MSG_VERBOSE);
+
+ StringBuffer sb = new StringBuffer();
+
+ Enumeration fileEnum = files.elements();
+
+ while (fileEnum.hasMoreElements()) {
+ sb.append(fileEnum.nextElement()).append("\n");
+ }
+ sb.append("\n").append(cabFile.getAbsolutePath()).append("\n");
+
+ try {
+ Process p = Execute.launch(getProject(),
+ new String[] {"listcab"}, null,
+ baseDir != null ? baseDir
+ : getProject().getBaseDir(),
+ true);
+ OutputStream out = p.getOutputStream();
+
+ // Create the stream pumpers to forward listcab's stdout and stderr to the log
+ // note: listcab is an interactive program, and issues prompts for every new line.
+ // Therefore, make it show only with verbose logging turned on.
+ LogOutputStream outLog = new LogOutputStream(this, Project.MSG_VERBOSE);
+ LogOutputStream errLog = new LogOutputStream(this, Project.MSG_ERR);
+ StreamPumper outPump = new StreamPumper(p.getInputStream(), outLog);
+ StreamPumper errPump = new StreamPumper(p.getErrorStream(), errLog);
+
+ // Pump streams asynchronously
+ (new Thread(outPump)).start();
+ (new Thread(errPump)).start();
+
+ out.write(sb.toString().getBytes());
+ out.flush();
+ out.close();
+
+ // A wild default for when the thread is interrupted
+ int result = DEFAULT_RESULT;
+
+ try {
+ // Wait for the process to finish
+ result = p.waitFor();
+
+ // Wait for the end of output and error streams
+ outPump.waitFor();
+ outLog.close();
+ errPump.waitFor();
+ errLog.close();
+ } catch (InterruptedException ie) {
+ log("Thread interrupted: " + ie);
+ }
+
+ // Informative summary message in case of errors
+ if (Execute.isFailure(result)) {
+ log("Error executing listcab; error code: " + result);
+ }
+ } catch (IOException ex) {
+ String msg = "Problem creating " + cabFile + " " + ex.getMessage();
+ throw new BuildException(msg, getLocation());
+ }
+ } else {
+ try {
+ File listFile = createListFile(files);
+ ExecTask exec = createExec();
+ File outFile = null;
+
+ // die if cabarc fails
+ exec.setFailonerror(true);
+ exec.setDir(baseDir);
+
+ if (!doVerbose) {
+ outFile = FILE_UTILS.createTempFile("ant", "", null, true, true);
+ exec.setOutput(outFile);
+ }
+
+ exec.setExecutable("cabarc");
+ exec.createArg().setValue("-r");
+ exec.createArg().setValue("-p");
+
+ if (!doCompress) {
+ exec.createArg().setValue("-m");
+ exec.createArg().setValue("none");
+ }
+
+ if (cmdOptions != null) {
+ exec.createArg().setLine(cmdOptions);
+ }
+
+ exec.createArg().setValue("n");
+ exec.createArg().setFile(cabFile);
+ exec.createArg().setValue("@" + listFile.getAbsolutePath());
+
+ exec.execute();
+
+ if (outFile != null) {
+ outFile.delete();
+ }
+
+ listFile.delete();
+ } catch (IOException ioe) {
+ String msg = "Problem creating " + cabFile + " " + ioe.getMessage();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/EchoProperties.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/EchoProperties.java
new file mode 100644
index 00000000..b63ef933
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/EchoProperties.java
@@ -0,0 +1,543 @@
+/*
+ * 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.tools.ant.taskdefs.optional;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.Vector;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.PropertySet;
+import org.apache.tools.ant.util.CollectionUtils;
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Displays all the current properties in the build. The output can be sent to
+ * a file if desired. <P>
+ *
+ * Attribute "destfile" defines a file to send the properties to. This can be
+ * processed as a standard property file later. <P>
+ *
+ * Attribute "prefix" defines a prefix which is used to filter the properties
+ * only those properties starting with this prefix will be echoed. <P>
+ *
+ * By default, the "failonerror" attribute is enabled. If an error occurs while
+ * writing the properties to a file, and this attribute is enabled, then a
+ * BuildException will be thrown. If disabled, then IO errors will be reported
+ * as a log statement, but no error will be thrown. <P>
+ *
+ * Examples: <pre>
+ * &lt;echoproperties /&gt;
+ * </pre> Report the current properties to the log. <P>
+ *
+ * <pre>
+ * &lt;echoproperties destfile="my.properties" /&gt;
+ * </pre> Report the current properties to the file "my.properties", and will
+ * fail the build if the file could not be created or written to. <P>
+ *
+ * <pre>
+ * &lt;echoproperties destfile="my.properties" failonerror="false"
+ * prefix="ant" /&gt;
+ * </pre> Report all properties beginning with 'ant' to the file
+ * "my.properties", and will log a message if the file could not be created or
+ * written to, but will still allow the build to continue.
+ *
+ *@since Ant 1.5
+ */
+public class EchoProperties extends Task {
+
+ /**
+ * the properties element.
+ */
+ private static final String PROPERTIES = "properties";
+
+ /**
+ * the property element.
+ */
+ private static final String PROPERTY = "property";
+
+ /**
+ * name attribute for property, testcase and testsuite elements.
+ */
+ private static final String ATTR_NAME = "name";
+
+ /**
+ * value attribute for property elements.
+ */
+ private static final String ATTR_VALUE = "value";
+
+ /**
+ * the input file.
+ */
+ private File inFile = null;
+
+ /**
+ * File object pointing to the output file. If this is null, then
+ * we output to the project log, not to a file.
+ */
+ private File destfile = null;
+
+ /**
+ * If this is true, then errors generated during file output will become
+ * build errors, and if false, then such errors will be logged, but not
+ * thrown.
+ */
+ private boolean failonerror = true;
+
+ private Vector propertySets = new Vector();
+
+ private String format = "text";
+
+ private String prefix;
+
+ /**
+ * @since Ant 1.7
+ */
+ private String regex;
+
+ /**
+ * Sets the input file.
+ *
+ * @param file the input file
+ */
+ public void setSrcfile(File file) {
+ inFile = file;
+ }
+
+ /**
+ * Set a file to store the property output. If this is never specified,
+ * then the output will be sent to the Ant log.
+ *
+ *@param destfile file to store the property output
+ */
+ public void setDestfile(File destfile) {
+ this.destfile = destfile;
+ }
+
+
+ /**
+ * If true, the task will fail if an error occurs writing the properties
+ * file, otherwise errors are just logged.
+ *
+ *@param failonerror <tt>true</tt> if IO exceptions are reported as build
+ * exceptions, or <tt>false</tt> if IO exceptions are ignored.
+ */
+ public void setFailOnError(boolean failonerror) {
+ this.failonerror = failonerror;
+ }
+
+
+ /**
+ * If the prefix is set, then only properties which start with this
+ * prefix string will be recorded. If regex is not set and if this
+ * is never set, or it is set to an empty string or <tt>null</tt>,
+ * then all properties will be recorded. <P>
+ *
+ * For example, if the attribute is set as:
+ * <PRE>&lt;echoproperties prefix="ant." /&gt;</PRE>
+ * then the property "ant.home" will be recorded, but "ant-example"
+ * will not.
+ *
+ * @param prefix The new prefix value
+ */
+ public void setPrefix(String prefix) {
+ if (prefix != null && prefix.length() != 0) {
+ this.prefix = prefix;
+ PropertySet ps = new PropertySet();
+ ps.setProject(getProject());
+ ps.appendPrefix(prefix);
+ addPropertyset(ps);
+ }
+ }
+
+ /**
+ * If the regex is set, then only properties whose names match it
+ * will be recorded. If prefix is not set and if this is never set,
+ * or it is set to an empty string or <tt>null</tt>, then all
+ * properties will be recorded.<P>
+ *
+ * For example, if the attribute is set as:
+ * <PRE>&lt;echoproperties prefix=".*ant.*" /&gt;</PRE>
+ * then the properties "ant.home" and "user.variant" will be recorded,
+ * but "ant-example" will not.
+ *
+ * @param regex The new regex value
+ *
+ * @since Ant 1.7
+ */
+ public void setRegex(String regex) {
+ if (regex != null && regex.length() != 0) {
+ this.regex = regex;
+ PropertySet ps = new PropertySet();
+ ps.setProject(getProject());
+ ps.appendRegex(regex);
+ addPropertyset(ps);
+ }
+ }
+
+ /**
+ * A set of properties to write.
+ * @param ps the property set to write
+ * @since Ant 1.6
+ */
+ public void addPropertyset(PropertySet ps) {
+ propertySets.addElement(ps);
+ }
+
+ /**
+ * Set the output format - xml or text.
+ * @param ea an enumerated <code>FormatAttribute</code> value
+ */
+ public void setFormat(FormatAttribute ea) {
+ format = ea.getValue();
+ }
+
+ /**
+ * A enumerated type for the format attribute.
+ * The values are "xml" and "text".
+ */
+ public static class FormatAttribute extends EnumeratedAttribute {
+ private String [] formats = new String[]{"xml", "text"};
+
+ /**
+ * @see EnumeratedAttribute#getValues()
+ * @return accepted values
+ */
+ public String[] getValues() {
+ return formats;
+ }
+ }
+
+ /**
+ * Run the task.
+ *
+ *@exception BuildException trouble, probably file IO
+ */
+ public void execute() throws BuildException {
+ if (prefix != null && regex != null) {
+ throw new BuildException("Please specify either prefix"
+ + " or regex, but not both", getLocation());
+ }
+ //copy the properties file
+ Hashtable allProps = new Hashtable();
+
+ /* load properties from file if specified, otherwise
+ use Ant's properties */
+ if (inFile == null && propertySets.size() == 0) {
+ // add ant properties
+ allProps.putAll(getProject().getProperties());
+ } else if (inFile != null) {
+ if (inFile.exists() && inFile.isDirectory()) {
+ String message = "srcfile is a directory!";
+ if (failonerror) {
+ throw new BuildException(message, getLocation());
+ } else {
+ log(message, Project.MSG_ERR);
+ }
+ return;
+ }
+
+ if (inFile.exists() && !inFile.canRead()) {
+ String message = "Can not read from the specified srcfile!";
+ if (failonerror) {
+ throw new BuildException(message, getLocation());
+ } else {
+ log(message, Project.MSG_ERR);
+ }
+ return;
+ }
+
+ FileInputStream in = null;
+ try {
+ in = new FileInputStream(inFile);
+ Properties props = new Properties();
+ props.load(in);
+ allProps.putAll(props);
+ } catch (FileNotFoundException fnfe) {
+ String message =
+ "Could not find file " + inFile.getAbsolutePath();
+ if (failonerror) {
+ throw new BuildException(message, fnfe, getLocation());
+ } else {
+ log(message, Project.MSG_WARN);
+ }
+ return;
+ } catch (IOException ioe) {
+ String message =
+ "Could not read file " + inFile.getAbsolutePath();
+ if (failonerror) {
+ throw new BuildException(message, ioe, getLocation());
+ } else {
+ log(message, Project.MSG_WARN);
+ }
+ return;
+ } finally {
+ FileUtils.close(in);
+ }
+ }
+
+ Enumeration e = propertySets.elements();
+ while (e.hasMoreElements()) {
+ PropertySet ps = (PropertySet) e.nextElement();
+ allProps.putAll(ps.getProperties());
+ }
+
+ OutputStream os = null;
+ try {
+ if (destfile == null) {
+ os = new ByteArrayOutputStream();
+ saveProperties(allProps, os);
+ log(os.toString(), Project.MSG_INFO);
+ } else {
+ if (destfile.exists() && destfile.isDirectory()) {
+ String message = "destfile is a directory!";
+ if (failonerror) {
+ throw new BuildException(message, getLocation());
+ } else {
+ log(message, Project.MSG_ERR);
+ }
+ return;
+ }
+
+ if (destfile.exists() && !destfile.canWrite()) {
+ String message =
+ "Can not write to the specified destfile!";
+ if (failonerror) {
+ throw new BuildException(message, getLocation());
+ } else {
+ log(message, Project.MSG_ERR);
+ }
+ return;
+ }
+ os = new FileOutputStream(this.destfile);
+ saveProperties(allProps, os);
+ }
+ } catch (IOException ioe) {
+ if (failonerror) {
+ throw new BuildException(ioe, getLocation());
+ } else {
+ log(ioe.getMessage(), Project.MSG_INFO);
+ }
+ } finally {
+ if (os != null) {
+ try {
+ os.close();
+ } catch (IOException ex) {
+ //ignore
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Send the key/value pairs in the hashtable to the given output stream.
+ * Only those properties matching the <tt>prefix</tt> constraint will be
+ * sent to the output stream.
+ * The output stream will be closed when this method returns.
+ *
+ * @param allProps propfile to save
+ * @param os output stream
+ * @throws IOException on output errors
+ * @throws BuildException on other errors
+ */
+ protected void saveProperties(Hashtable allProps, OutputStream os)
+ throws IOException, BuildException {
+ final List keyList = new ArrayList(allProps.keySet());
+ Collections.sort(keyList);
+ Properties props = new Properties() {
+ private static final long serialVersionUID = 5090936442309201654L;
+ public Enumeration keys() {
+ return CollectionUtils.asEnumeration(keyList.iterator());
+ }
+ public Set entrySet() {
+ Set result = super.entrySet();
+ if (JavaEnvUtils.isKaffe()) {
+ TreeSet t = new TreeSet(new Comparator() {
+ public int compare(Object o1, Object o2) {
+ String key1 = (String) ((Map.Entry) o1).getKey();
+ String key2 = (String) ((Map.Entry) o2).getKey();
+ return key1.compareTo(key2);
+ }
+ });
+ t.addAll(result);
+ result = t;
+ }
+ return result;
+ }
+ };
+ final int size = keyList.size();
+ for (int i = 0; i < size; i++) {
+ String name = keyList.get(i).toString();
+ String value = allProps.get(name).toString();
+ props.setProperty(name, value);
+ }
+ if ("text".equals(format)) {
+ jdkSaveProperties(props, os, "Ant properties");
+ } else if ("xml".equals(format)) {
+ xmlSaveProperties(props, os);
+ }
+ }
+
+ /**
+ * a tuple for the sort list.
+ */
+ private static final class Tuple implements Comparable {
+ private String key;
+ private String value;
+
+ private Tuple(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ /**
+ * Compares this object with the specified object for order.
+ * @param o the Object to be compared.
+ * @return a negative integer, zero, or a positive integer as this object is
+ * less than, equal to, or greater than the specified object.
+ * @throws ClassCastException if the specified object's type prevents it
+ * from being compared to this Object.
+ */
+ public int compareTo(Object o) {
+ Tuple that = (Tuple) o;
+ return key.compareTo(that.key);
+ }
+ }
+
+ private List sortProperties(Properties props) {
+ //sort the list. Makes SCM and manual diffs easier.
+ List sorted = new ArrayList(props.size());
+ Enumeration e = props.propertyNames();
+ while (e.hasMoreElements()) {
+ String name = (String) e.nextElement();
+ sorted.add(new Tuple(name, props.getProperty(name)));
+ }
+ Collections.sort(sorted);
+ return sorted;
+ }
+
+ /**
+ * Output the properties as xml output.
+ * @param props the properties to save
+ * @param os the output stream to write to (Note this gets closed)
+ * @throws IOException on error in writing to the stream
+ */
+ protected void xmlSaveProperties(Properties props,
+ OutputStream os) throws IOException {
+ // create XML document
+ Document doc = getDocumentBuilder().newDocument();
+ Element rootElement = doc.createElement(PROPERTIES);
+
+ List sorted = sortProperties(props);
+
+
+ // output properties
+ Iterator iten = sorted.iterator();
+ while (iten.hasNext()) {
+ Tuple tuple = (Tuple) iten.next();
+ Element propElement = doc.createElement(PROPERTY);
+ propElement.setAttribute(ATTR_NAME, tuple.key);
+ propElement.setAttribute(ATTR_VALUE, tuple.value);
+ rootElement.appendChild(propElement);
+ }
+
+ Writer wri = null;
+ try {
+ wri = new OutputStreamWriter(os, "UTF8");
+ wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ (new DOMElementWriter()).write(rootElement, wri, 0, "\t");
+ wri.flush();
+ } catch (IOException ioe) {
+ throw new BuildException("Unable to write XML file", ioe);
+ } finally {
+ FileUtils.close(wri);
+ }
+ }
+
+ /**
+ * JDK 1.2 allows for the safer method
+ * <tt>Properties.store(OutputStream, String)</tt>, which throws an
+ * <tt>IOException</tt> on an output error.
+ *
+ *@param props the properties to record
+ *@param os record the properties to this output stream
+ *@param header prepend this header to the property output
+ *@exception IOException on an I/O error during a write.
+ */
+ protected void jdkSaveProperties(Properties props, OutputStream os,
+ String header) throws IOException {
+ try {
+ props.store(os, header);
+
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, getLocation());
+ } finally {
+ if (os != null) {
+ try {
+ os.close();
+ } catch (IOException ioex) {
+ log("Failed to close output stream");
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Uses the DocumentBuilderFactory to get a DocumentBuilder instance.
+ *
+ * @return The DocumentBuilder instance
+ */
+ private static DocumentBuilder getDocumentBuilder() {
+ try {
+ return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ } catch (Exception e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java
new file mode 100644
index 00000000..aaed7832
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java
@@ -0,0 +1,513 @@
+/*
+ * 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.tools.ant.taskdefs.optional;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.optional.javah.JavahAdapter;
+import org.apache.tools.ant.taskdefs.optional.javah.JavahAdapterFactory;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.facade.FacadeTaskHelper;
+import org.apache.tools.ant.util.facade.ImplementationSpecificArgument;
+
+/**
+ * Generates JNI header files using javah.
+ *
+ * This task can take the following arguments:
+ * <ul>
+ * <li>classname - the fully-qualified name of a class</li>
+ * <li>outputFile - Concatenates the resulting header or source files for all
+ * the classes listed into this file</li>
+ * <li>destdir - Sets the directory where javah saves the header files or the
+ * stub files</li>
+ * <li>classpath</li>
+ * <li>bootclasspath</li>
+ * <li>force - Specifies that output files should always be written
+ (JDK1.2 only)</li>
+ * <li>old - Specifies that old JDK1.0-style header files should be generated
+ * (otherwise output file contain JNI-style native method
+ * function prototypes) (JDK1.2 only)</li>
+ * <li>stubs - generate C declarations from the Java object file (used with old)</li>
+ * <li>verbose - causes javah to print a message to stdout concerning the status
+ * of the generated files</li>
+ * <li>extdirs - Override location of installed extensions</li>
+ * </ul>
+ * Of these arguments, either <b>outputFile</b> or <b>destdir</b> is required,
+ * but not both. More than one classname may be specified, using a comma-separated
+ * list or by using <code>&lt;class name="xxx"&gt;</code> elements within the task.
+ * <p>
+ * When this task executes, it will generate C header and source files that
+ * are needed to implement native methods.
+ *
+ */
+
+public class Javah extends Task {
+
+ private Vector classes = new Vector(2);
+ private String cls;
+ private File destDir;
+ private Path classpath = null;
+ private File outputFile = null;
+ private boolean verbose = false;
+ private boolean force = false;
+ private boolean old = false;
+ private boolean stubs = false;
+ private Path bootclasspath;
+ //private Path extdirs;
+ private FacadeTaskHelper facade = null;
+ private Vector files = new Vector();
+ private JavahAdapter nestedAdapter = null;
+
+ /**
+ * No arg constructor.
+ */
+ public Javah() {
+ facade = new FacadeTaskHelper(JavahAdapterFactory.getDefault());
+ }
+
+ /**
+ * the fully-qualified name of the class (or classes, separated by commas).
+ * @param cls the classname (or classnames).
+ */
+ public void setClass(String cls) {
+ this.cls = cls;
+ }
+
+ /**
+ * Adds class to process.
+ * @return a <code>ClassArgument</code> to be configured.
+ */
+ public ClassArgument createClass() {
+ ClassArgument ga = new ClassArgument();
+ classes.addElement(ga);
+ return ga;
+ }
+
+ /**
+ * A class corresponding the the nested "class" element.
+ * It contains a "name" attribute.
+ */
+ public class ClassArgument {
+ private String name;
+
+ /** Constructor for ClassArgument. */
+ public ClassArgument() {
+ }
+
+ /**
+ * Set the name attribute.
+ * @param name the name attribute.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the name attribute.
+ * @return the name attribute.
+ */
+ public String getName() {
+ return name;
+ }
+ }
+
+ /**
+ * Add a fileset.
+ * @param fs the fileset to add.
+ */
+ public void addFileSet(FileSet fs) {
+ files.add(fs);
+ }
+
+ /**
+ * Names of the classes to process.
+ * @return the array of classes.
+ * @since Ant 1.6.3
+ */
+ public String[] getClasses() {
+ ArrayList al = new ArrayList();
+ if (cls != null) {
+ StringTokenizer tok = new StringTokenizer(cls, ",", false);
+ while (tok.hasMoreTokens()) {
+ al.add(tok.nextToken().trim());
+ }
+ }
+
+ if (files.size() > 0) {
+ for (Enumeration e = files.elements(); e.hasMoreElements();) {
+ FileSet fs = (FileSet) e.nextElement();
+ String[] includedClasses = fs.getDirectoryScanner(
+ getProject()).getIncludedFiles();
+ for (int i = 0; i < includedClasses.length; i++) {
+ String className =
+ includedClasses[i].replace('\\', '.').replace('/', '.')
+ .substring(0, includedClasses[i].length() - 6);
+ al.add(className);
+ }
+ }
+ }
+ Enumeration e = classes.elements();
+ while (e.hasMoreElements()) {
+ ClassArgument arg = (ClassArgument) e.nextElement();
+ al.add(arg.getName());
+ }
+ return (String[]) al.toArray(new String[al.size()]);
+ }
+
+ /**
+ * Set the destination directory into which the Java source
+ * files should be compiled.
+ * @param destDir the destination directory.
+ */
+ public void setDestdir(File destDir) {
+ this.destDir = destDir;
+ }
+
+ /**
+ * The destination directory, if any.
+ * @return the destination directory.
+ * @since Ant 1.6.3
+ */
+ public File getDestdir() {
+ return destDir;
+ }
+
+ /**
+ * the classpath to use.
+ * @param src the classpath.
+ */
+ public void setClasspath(Path src) {
+ if (classpath == null) {
+ classpath = src;
+ } else {
+ classpath.append(src);
+ }
+ }
+
+ /**
+ * Path to use for classpath.
+ * @return a path to be configured.
+ */
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(getProject());
+ }
+ return classpath.createPath();
+ }
+
+ /**
+ * Adds a reference to a classpath defined elsewhere.
+ * @param r a reference to a classpath.
+ * @todo this needs to be documented in the HTML docs.
+ */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * The classpath to use.
+ * @return the classpath.
+ * @since Ant 1.6.3
+ */
+ public Path getClasspath() {
+ return classpath;
+ }
+
+ /**
+ * location of bootstrap class files.
+ * @param src the bootstrap classpath.
+ */
+ public void setBootclasspath(Path src) {
+ if (bootclasspath == null) {
+ bootclasspath = src;
+ } else {
+ bootclasspath.append(src);
+ }
+ }
+
+ /**
+ * Adds path to bootstrap class files.
+ * @return a path to be configured.
+ */
+ public Path createBootclasspath() {
+ if (bootclasspath == null) {
+ bootclasspath = new Path(getProject());
+ }
+ return bootclasspath.createPath();
+ }
+
+ /**
+ * To the bootstrap path, this adds a reference to a classpath defined elsewhere.
+ * @param r a reference to a classpath
+ * @todo this needs to be documented in the HTML.
+ */
+ public void setBootClasspathRef(Reference r) {
+ createBootclasspath().setRefid(r);
+ }
+
+ /**
+ * The bootclasspath to use.
+ * @return the bootclass path.
+ * @since Ant 1.6.3
+ */
+ public Path getBootclasspath() {
+ return bootclasspath;
+ }
+
+ /**
+ * Concatenates the resulting header or source files for all
+ * the classes listed into this file.
+ * @param outputFile the output file.
+ */
+ public void setOutputFile(File outputFile) {
+ this.outputFile = outputFile;
+ }
+
+ /**
+ * The destination file, if any.
+ * @return the destination file.
+ * @since Ant 1.6.3
+ */
+ public File getOutputfile() {
+ return outputFile;
+ }
+
+ /**
+ * If true, output files should always be written (JDK1.2 only).
+ * @param force the value to use.
+ */
+ public void setForce(boolean force) {
+ this.force = force;
+ }
+
+ /**
+ * Whether output files should always be written.
+ * @return the force attribute.
+ * @since Ant 1.6.3
+ */
+ public boolean getForce() {
+ return force;
+ }
+
+ /**
+ * If true, specifies that old JDK1.0-style header files should be
+ * generated.
+ * (otherwise output file contain JNI-style native method function
+ * prototypes) (JDK1.2 only).
+ * @param old if true use old 1.0 style header files.
+ */
+ public void setOld(boolean old) {
+ this.old = old;
+ }
+
+ /**
+ * Whether old JDK1.0-style header files should be generated.
+ * @return the old attribute.
+ * @since Ant 1.6.3
+ */
+ public boolean getOld() {
+ return old;
+ }
+
+ /**
+ * If true, generate C declarations from the Java object file (used with old).
+ * @param stubs if true, generated C declarations.
+ */
+ public void setStubs(boolean stubs) {
+ this.stubs = stubs;
+ }
+
+ /**
+ * Whether C declarations from the Java object file should be generated.
+ * @return the stubs attribute.
+ * @since Ant 1.6.3
+ */
+ public boolean getStubs() {
+ return stubs;
+ }
+
+ /**
+ * If true, causes Javah to print a message concerning
+ * the status of the generated files.
+ * @param verbose if true, do verbose printing.
+ */
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ /**
+ * Whether verbose output should get generated.
+ * @return the verbose attribute.
+ * @since Ant 1.6.3
+ */
+ public boolean getVerbose() {
+ return verbose;
+ }
+
+ /**
+ * Choose the implementation for this particular task.
+ * @param impl the name of the implementation.
+ * @since Ant 1.6.3
+ */
+ public void setImplementation(String impl) {
+ if ("default".equals(impl)) {
+ facade.setImplementation(JavahAdapterFactory.getDefault());
+ } else {
+ facade.setImplementation(impl);
+ }
+ }
+
+ /**
+ * Adds an implementation specific command-line argument.
+ * @return a ImplementationSpecificArgument to be configured.
+ *
+ * @since Ant 1.6.3
+ */
+ public ImplementationSpecificArgument createArg() {
+ ImplementationSpecificArgument arg =
+ new ImplementationSpecificArgument();
+ facade.addImplementationArgument(arg);
+ return arg;
+ }
+
+ /**
+ * Returns the (implementation specific) settings given as nested
+ * arg elements.
+ * @return the arguments.
+ * @since Ant 1.6.3
+ */
+ public String[] getCurrentArgs() {
+ return facade.getArgs();
+ }
+
+ /**
+ * The classpath to use when loading the javah implementation
+ * if it is not a built-in one.
+ *
+ * @since Ant 1.8.0
+ */
+ public Path createImplementationClasspath() {
+ return facade.getImplementationClasspath(getProject());
+ }
+
+ /**
+ * Set the adapter explicitly.
+ * @since Ant 1.8.0
+ */
+ public void add(JavahAdapter adapter) {
+ if (nestedAdapter != null) {
+ throw new BuildException("Can't have more than one javah"
+ + " adapter");
+ }
+ nestedAdapter = adapter;
+ }
+
+ /**
+ * Execute the task
+ *
+ * @throws BuildException is there is a problem in the task execution.
+ */
+ public void execute() throws BuildException {
+ // first off, make sure that we've got a srcdir
+
+ if ((cls == null) && (classes.size() == 0) && (files.size() == 0)) {
+ throw new BuildException("class attribute must be set!",
+ getLocation());
+ }
+
+ if ((cls != null) && (classes.size() > 0) && (files.size() > 0)) {
+ throw new BuildException("set class attribute OR class element OR fileset, "
+ + "not 2 or more of them.", getLocation());
+ }
+
+ if (destDir != null) {
+ if (!destDir.isDirectory()) {
+ throw new BuildException("destination directory \"" + destDir
+ + "\" does not exist or is not a directory", getLocation());
+ }
+ if (outputFile != null) {
+ throw new BuildException("destdir and outputFile are mutually "
+ + "exclusive", getLocation());
+ }
+ }
+
+ if (classpath == null) {
+ classpath = (new Path(getProject())).concatSystemClasspath("last");
+ } else {
+ classpath = classpath.concatSystemClasspath("ignore");
+ }
+
+ JavahAdapter ad =
+ nestedAdapter != null ? nestedAdapter :
+ JavahAdapterFactory.getAdapter(facade.getImplementation(),
+ this,
+ createImplementationClasspath());
+ if (!ad.compile(this)) {
+ throw new BuildException("compilation failed");
+ }
+ }
+
+ /**
+ * Logs the compilation parameters, adds the files to compile and logs the
+ * &quot;niceSourceList&quot;
+ * @param cmd the command line.
+ */
+ public void logAndAddFiles(Commandline cmd) {
+ logAndAddFilesToCompile(cmd);
+ }
+
+ /**
+ * Logs the compilation parameters, adds the files to compile and logs the
+ * &quot;niceSourceList&quot;
+ * @param cmd the command line to add parameters to.
+ */
+ protected void logAndAddFilesToCompile(Commandline cmd) {
+ log("Compilation " + cmd.describeArguments(),
+ Project.MSG_VERBOSE);
+
+ StringBuffer niceClassList = new StringBuffer();
+ String[] c = getClasses();
+ for (int i = 0; i < c.length; i++) {
+ cmd.createArgument().setValue(c[i]);
+ niceClassList.append(" ");
+ niceClassList.append(c[i]);
+ niceClassList.append(StringUtils.LINE_SEP);
+ }
+
+ StringBuffer prefix = new StringBuffer("Class");
+ if (c.length > 1) {
+ prefix.append("es");
+ }
+ prefix.append(" to be compiled:");
+ prefix.append(StringUtils.LINE_SEP);
+
+ log(prefix.toString() + niceClassList.toString(), Project.MSG_VERBOSE);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Native2Ascii.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Native2Ascii.java
new file mode 100644
index 00000000..81a386fd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Native2Ascii.java
@@ -0,0 +1,328 @@
+/*
+ * 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.tools.ant.taskdefs.optional;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.taskdefs.optional.native2ascii.Native2AsciiAdapter;
+import org.apache.tools.ant.taskdefs.optional.native2ascii.Native2AsciiAdapterFactory;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.ant.util.SourceFileScanner;
+import org.apache.tools.ant.util.facade.FacadeTaskHelper;
+import org.apache.tools.ant.util.facade.ImplementationSpecificArgument;
+
+/**
+ * Converts files from native encodings to ASCII.
+ *
+ * @since Ant 1.2
+ */
+public class Native2Ascii extends MatchingTask {
+
+ private boolean reverse = false; // convert from ascii back to native
+ private String encoding = null; // encoding to convert to/from
+ private File srcDir = null; // Where to find input files
+ private File destDir = null; // Where to put output files
+ private String extension = null; // Extension of output files if different
+
+ private Mapper mapper;
+ private FacadeTaskHelper facade = null;
+ private Native2AsciiAdapter nestedAdapter = null;
+
+ /** No args constructor */
+ public Native2Ascii() {
+ facade = new FacadeTaskHelper(Native2AsciiAdapterFactory.getDefault());
+ }
+
+ /**
+ * Flag the conversion to run in the reverse sense,
+ * that is Ascii to Native encoding.
+ *
+ * @param reverse True if the conversion is to be reversed,
+ * otherwise false;
+ */
+ public void setReverse(boolean reverse) {
+ this.reverse = reverse;
+ }
+
+ /**
+ * The value of the reverse attribute.
+ * @return the reverse attribute.
+ * @since Ant 1.6.3
+ */
+ public boolean getReverse() {
+ return reverse;
+ }
+
+ /**
+ * Set the encoding to translate to/from.
+ * If unset, the default encoding for the JVM is used.
+ *
+ * @param encoding String containing the name of the Native
+ * encoding to convert from or to.
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * The value of the encoding attribute.
+ * @return the encoding attribute.
+ * @since Ant 1.6.3
+ */
+ public String getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * Set the source directory in which to find files to convert.
+ *
+ * @param srcDir directory to find input file in.
+ */
+ public void setSrc(File srcDir) {
+ this.srcDir = srcDir;
+ }
+
+
+ /**
+ * Set the destination directory to place converted files into.
+ *
+ * @param destDir directory to place output file into.
+ */
+ public void setDest(File destDir) {
+ this.destDir = destDir;
+ }
+
+ /**
+ * Set the extension which converted files should have.
+ * If unset, files will not be renamed.
+ *
+ * @param ext File extension to use for converted files.
+ */
+ public void setExt(String ext) {
+ this.extension = ext;
+ }
+
+ /**
+ * Choose the implementation for this particular task.
+ * @param impl the name of the implementation
+ * @since Ant 1.6.3
+ */
+ public void setImplementation(String impl) {
+ if ("default".equals(impl)) {
+ facade.setImplementation(Native2AsciiAdapterFactory.getDefault());
+ } else {
+ facade.setImplementation(impl);
+ }
+ }
+
+ /**
+ * Defines the FileNameMapper to use (nested mapper element).
+ *
+ * @return the mapper to use for file name translations.
+ *
+ * @throws BuildException if more than one mapper is defined.
+ */
+ public Mapper createMapper() throws BuildException {
+ if (mapper != null) {
+ throw new BuildException("Cannot define more than one mapper",
+ getLocation());
+ }
+ mapper = new Mapper(getProject());
+ return mapper;
+ }
+
+ /**
+ * A nested filenamemapper
+ * @param fileNameMapper the mapper to add
+ * @since Ant 1.6.3
+ */
+ public void add(FileNameMapper fileNameMapper) {
+ createMapper().add(fileNameMapper);
+ }
+
+ /**
+ * Adds an implementation specific command-line argument.
+ * @return a ImplementationSpecificArgument to be configured
+ *
+ * @since Ant 1.6.3
+ */
+ public ImplementationSpecificArgument createArg() {
+ ImplementationSpecificArgument arg =
+ new ImplementationSpecificArgument();
+ facade.addImplementationArgument(arg);
+ return arg;
+ }
+
+ /**
+ * The classpath to use when loading the native2ascii
+ * implementation if it is not a built-in one.
+ *
+ * @since Ant 1.8.0
+ */
+ public Path createImplementationClasspath() {
+ return facade.getImplementationClasspath(getProject());
+ }
+
+ /**
+ * Set the adapter explicitly.
+ * @since Ant 1.8.0
+ */
+ public void add(Native2AsciiAdapter adapter) {
+ if (nestedAdapter != null) {
+ throw new BuildException("Can't have more than one native2ascii"
+ + " adapter");
+ }
+ nestedAdapter = adapter;
+ }
+
+ /**
+ * Execute the task
+ *
+ * @throws BuildException is there is a problem in the task execution.
+ */
+ public void execute() throws BuildException {
+
+ DirectoryScanner scanner = null; // Scanner to find our inputs
+ String[] files; // list of files to process
+
+ // default srcDir to basedir
+ if (srcDir == null) {
+ srcDir = getProject().resolveFile(".");
+ }
+
+ // Require destDir
+ if (destDir == null) {
+ throw new BuildException("The dest attribute must be set.");
+ }
+
+ // if src and dest dirs are the same, require the extension
+ // to be set, so we don't stomp every file. One could still
+ // include a file with the same extension, but ....
+ if (srcDir.equals(destDir) && extension == null && mapper == null) {
+ throw new BuildException("The ext attribute or a mapper must be set if"
+ + " src and dest dirs are the same.");
+ }
+
+ FileNameMapper m = null;
+ if (mapper == null) {
+ if (extension == null) {
+ m = new IdentityMapper();
+ } else {
+ m = new ExtMapper();
+ }
+ } else {
+ m = mapper.getImplementation();
+ }
+
+ scanner = getDirectoryScanner(srcDir);
+ files = scanner.getIncludedFiles();
+ SourceFileScanner sfs = new SourceFileScanner(this);
+ files = sfs.restrict(files, srcDir, destDir, m);
+ int count = files.length;
+ if (count == 0) {
+ return;
+ }
+ String message = "Converting " + count + " file"
+ + (count != 1 ? "s" : "") + " from ";
+ log(message + srcDir + " to " + destDir);
+ for (int i = 0; i < files.length; i++) {
+ convert(files[i], m.mapFileName(files[i])[0]);
+ }
+ }
+
+ /**
+ * Convert a single file.
+ *
+ * @param srcName name of the input file.
+ * @param destName name of the input file.
+ */
+ private void convert(String srcName, String destName)
+ throws BuildException {
+ File srcFile; // File to convert
+ File destFile; // where to put the results
+
+ // Build the full file names
+ srcFile = new File(srcDir, srcName);
+ destFile = new File(destDir, destName);
+
+ // Make sure we're not about to clobber something
+ if (srcFile.equals(destFile)) {
+ throw new BuildException("file " + srcFile
+ + " would overwrite its self");
+ }
+
+ // Make intermediate directories if needed
+ // TODO JDK 1.1 doesn't have File.getParentFile,
+ String parentName = destFile.getParent();
+ if (parentName != null) {
+ File parentFile = new File(parentName);
+
+ if (!parentFile.exists()
+ && !(parentFile.mkdirs() || parentFile.isDirectory())) {
+ throw new BuildException("cannot create parent directory "
+ + parentName);
+ }
+ }
+
+ log("converting " + srcName, Project.MSG_VERBOSE);
+ Native2AsciiAdapter ad =
+ nestedAdapter != null ? nestedAdapter :
+ Native2AsciiAdapterFactory.getAdapter(facade.getImplementation(),
+ this,
+ createImplementationClasspath());
+ if (!ad.convert(this, srcFile, destFile)) {
+ throw new BuildException("conversion failed");
+ }
+ }
+
+ /**
+ * Returns the (implementation specific) settings given as nested
+ * arg elements.
+ * @return the arguments.
+ * @since Ant 1.6.3
+ */
+ public String[] getCurrentArgs() {
+ return facade.getArgs();
+ }
+
+ private class ExtMapper implements FileNameMapper {
+
+ public void setFrom(String s) {
+ }
+ public void setTo(String s) {
+ }
+
+ public String[] mapFileName(String fileName) {
+ int lastDot = fileName.lastIndexOf('.');
+ if (lastDot >= 0) {
+ return new String[] {fileName.substring(0, lastDot)
+ + extension};
+ } else {
+ return new String[] {fileName + extension};
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/NetRexxC.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/NetRexxC.java
new file mode 100644
index 00000000..5ba2a760
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/NetRexxC.java
@@ -0,0 +1,1042 @@
+/*
+ * 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.tools.ant.taskdefs.optional;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import netrexx.lang.Rexx;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.util.FileUtils;
+
+// CheckStyle:InnerAssignmentCheck OFF - used too much in the file to be removed
+/**
+ * Compiles NetRexx source files.
+ * This task can take the following
+ * arguments:
+ * <ul>
+ * <li>binary</li>
+ * <li>classpath</li>
+ * <li>comments</li>
+ * <li>compile</li>
+ * <li>console</li>
+ * <li>crossref</li>
+ * <li>decimal</li>
+ * <li>destdir</li>
+ * <li>diag</li>
+ * <li>explicit</li>
+ * <li>format</li>
+ * <li>keep</li>
+ * <li>logo</li>
+ * <li>replace</li>
+ * <li>savelog</li>
+ * <li>srcdir</li>
+ * <li>sourcedir</li>
+ * <li>strictargs</li>
+ * <li>strictassign</li>
+ * <li>strictcase</li>
+ * <li>strictimport</li>
+ * <li>symbols</li>
+ * <li>time</li>
+ * <li>trace</li>
+ * <li>utf8</li>
+ * <li>verbose</li>
+ * <li>suppressMethodArgumentNotUsed</li>
+ * <li>suppressPrivatePropertyNotUsed</li>
+ * <li>suppressVariableNotUsed</li>
+ * <li>suppressExceptionNotSignalled</li>
+ * <li>suppressDeprecation</li>
+ * <li>removeKeepExtension</li>
+ * </ul>
+ * Of these arguments, the <b>srcdir</b> argument is required.
+ *
+ * <p>When this task executes, it will recursively scan the srcdir
+ * looking for NetRexx source files to compile. This task makes its
+ * compile decision based on timestamp.
+ * <p>Before files are compiled they and any other file in the
+ * srcdir will be copied to the destdir allowing support files to be
+ * located properly in the classpath. The reason for copying the source files
+ * before the compile is that NetRexxC has only two destinations for classfiles:
+ * <ol>
+ * <li>The current directory, and,</li>
+ * <li>The directory the source is in (see sourcedir option)
+ * </ol>
+ *
+ */
+public class NetRexxC extends MatchingTask {
+
+ // variables to hold arguments
+ private boolean binary;
+ private String classpath;
+ private boolean comments;
+ private boolean compact = true; // should be the default, as it integrates better in ant.
+ private boolean compile = true;
+ private boolean console;
+ private boolean crossref;
+ private boolean decimal = true;
+ private File destDir;
+ private boolean diag;
+ private boolean explicit;
+ private boolean format;
+ private boolean keep;
+ private boolean logo = true;
+ private boolean replace;
+ private boolean savelog;
+ private File srcDir;
+ private boolean sourcedir = true; // ?? Should this be the default for ant?
+ private boolean strictargs;
+ private boolean strictassign;
+ private boolean strictcase;
+ private boolean strictimport;
+ private boolean strictprops;
+ private boolean strictsignal;
+ private boolean symbols;
+ private boolean time;
+ private String trace = "trace2";
+ private boolean utf8;
+ private String verbose = "verbose3";
+ private boolean suppressMethodArgumentNotUsed = false;
+ private boolean suppressPrivatePropertyNotUsed = false;
+ private boolean suppressVariableNotUsed = false;
+ private boolean suppressExceptionNotSignalled = false;
+ private boolean suppressDeprecation = false;
+ private boolean removeKeepExtension = false;
+
+ // constants for the messages to suppress by flags and their corresponding properties
+ static final String MSG_METHOD_ARGUMENT_NOT_USED
+ = "Warning: Method argument is not used";
+ static final String MSG_PRIVATE_PROPERTY_NOT_USED
+ = "Warning: Private property is defined but not used";
+ static final String MSG_VARIABLE_NOT_USED
+ = "Warning: Variable is set but not used";
+ static final String MSG_EXCEPTION_NOT_SIGNALLED
+ = "is in SIGNALS list but is not signalled within the method";
+ static final String MSG_DEPRECATION = "has been deprecated";
+
+ // other implementation variables
+ private Vector compileList = new Vector();
+ private Hashtable filecopyList = new Hashtable();
+
+ /**
+ * Set whether literals are treated as binary, rather than NetRexx types.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default is false.
+ * @param binary a <code>boolean</code> value.
+ */
+ public void setBinary(boolean binary) {
+ this.binary = binary;
+ }
+
+
+ /**
+ * Set the classpath used for NetRexx compilation.
+ * @param classpath the classpath to use.
+ */
+ public void setClasspath(String classpath) {
+ this.classpath = classpath;
+ }
+
+
+ /**
+ * Set whether comments are passed through to the generated java source.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param comments a <code>boolean</code> value.
+ */
+ public void setComments(boolean comments) {
+ this.comments = comments;
+ }
+
+
+ /**
+ * Set whether error messages come out in compact or verbose format.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is true.
+ * @param compact a <code>boolean</code> value.
+ */
+ public void setCompact(boolean compact) {
+ this.compact = compact;
+ }
+
+
+ /**
+ * Set whether the NetRexx compiler should compile the generated java code.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is true.
+ * Setting this flag to false, will automatically set the keep flag to true.
+ * @param compile a <code>boolean</code> value.
+ */
+ public void setCompile(boolean compile) {
+ this.compile = compile;
+ if (!this.compile && !this.keep) {
+ this.keep = true;
+ }
+ }
+
+
+ /**
+ * Set whether or not compiler messages should be displayed on the 'console'.
+ * Note that this task will rely on the default value for filtering compile messages.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param console a <code>boolean</code> value.
+ */
+ public void setConsole(boolean console) {
+ this.console = console;
+ }
+
+
+ /**
+ * Whether variable cross references are generated.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param crossref a <code>boolean</code> value.
+ */
+ public void setCrossref(boolean crossref) {
+ this.crossref = crossref;
+ }
+
+
+ /**
+ * Set whether decimal arithmetic should be used for the netrexx code.
+ * Setting this to off will report decimal arithmetic as an error, for
+ * performance critical applications.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is true.
+ * @param decimal a <code>boolean</code> value.
+ */
+ public void setDecimal(boolean decimal) {
+ this.decimal = decimal;
+ }
+
+
+ /**
+ * Set the destination directory into which the NetRexx source files
+ * should be copied and then compiled.
+ * @param destDirName the destination directory.
+ */
+ public void setDestDir(File destDirName) {
+ destDir = destDirName;
+ }
+
+
+ /**
+ * Whether diagnostic information about the compile is generated
+ * @param diag a <code>boolean</code> value.
+ */
+ public void setDiag(boolean diag) {
+ this.diag = diag;
+ }
+
+
+ /**
+ * Sets whether variables must be declared explicitly before use.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param explicit a <code>boolean</code> value.
+ */
+ public void setExplicit(boolean explicit) {
+ this.explicit = explicit;
+ }
+
+
+ /**
+ * Whether the generated java code is formatted nicely or left to match
+ * NetRexx line numbers for call stack debugging.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value false.
+ * @param format a <code>boolean</code> value.
+ */
+ public void setFormat(boolean format) {
+ this.format = format;
+ }
+
+
+ /**
+ * Whether the generated java code is produced.
+ * This is not implemented yet.
+ * @param java a <code>boolean</code> value.
+ */
+ public void setJava(boolean java) {
+ log("The attribute java is currently unused.", Project.MSG_WARN);
+ }
+
+
+ /**
+ * Sets whether the generated java source file should be kept after
+ * compilation. The generated files will have an extension of .java.keep,
+ * <b>not</b> .java. See setRemoveKeepExtension
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param keep a <code>boolean</code> value.
+ * @see #setRemoveKeepExtension(boolean)
+ */
+ public void setKeep(boolean keep) {
+ this.keep = keep;
+ }
+
+
+ /**
+ * Whether the compiler text logo is displayed when compiling.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param logo a <code>boolean</code> value.
+ */
+ public void setLogo(boolean logo) {
+ this.logo = logo;
+ }
+
+
+ /**
+ * Whether the generated .java file should be replaced when compiling.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param replace a <code>boolean</code> value.
+ */
+ public void setReplace(boolean replace) {
+ this.replace = replace;
+ }
+
+
+ /**
+ * Sets whether the compiler messages will be written to NetRexxC.log as
+ * well as to the console.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param savelog a <code>boolean</code> value.
+ */
+ public void setSavelog(boolean savelog) {
+ this.savelog = savelog;
+ }
+
+
+ /**
+ * Tells the NetRexx compiler to store the class files in the same
+ * directory as the source files. The alternative is the working directory.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is true.
+ * @param sourcedir a <code>boolean</code> value.
+ */
+ public void setSourcedir(boolean sourcedir) {
+ this.sourcedir = sourcedir;
+ }
+
+
+ /**
+ * Set the source dir to find the source Java files.
+ * @param srcDirName the source directory.
+ */
+ public void setSrcDir(File srcDirName) {
+ srcDir = srcDirName;
+ }
+
+
+ /**
+ * Tells the NetRexx compiler that method calls always need parentheses,
+ * even if no arguments are needed, e.g. <code>aStringVar.getBytes</code>
+ * vs. <code>aStringVar.getBytes()</code>.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param strictargs a <code>boolean</code> value.
+ */
+ public void setStrictargs(boolean strictargs) {
+ this.strictargs = strictargs;
+ }
+
+
+ /**
+ * Tells the NetRexx compile that assignments must match exactly on type.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param strictassign a <code>boolean</code> value.
+ */
+ public void setStrictassign(boolean strictassign) {
+ this.strictassign = strictassign;
+ }
+
+
+ /**
+ * Specifies whether the NetRexx compiler should be case sensitive or not.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param strictcase a <code>boolean</code> value.
+ */
+ public void setStrictcase(boolean strictcase) {
+ this.strictcase = strictcase;
+ }
+
+
+ /**
+ * Sets whether classes need to be imported explicitly using an <code>import</code>
+ * statement. By default the NetRexx compiler will import certain packages
+ * automatically.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param strictimport a <code>boolean</code> value.
+ */
+ public void setStrictimport(boolean strictimport) {
+ this.strictimport = strictimport;
+ }
+
+
+ /**
+ * Sets whether local properties need to be qualified explicitly using
+ * <code>this</code>.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param strictprops a <code>boolean</code> value.
+ */
+ public void setStrictprops(boolean strictprops) {
+ this.strictprops = strictprops;
+ }
+
+
+ /**
+ * Whether the compiler should force catching of exceptions by explicitly
+ * named types.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false
+ * @param strictsignal a <code>boolean</code> value.
+ */
+ public void setStrictsignal(boolean strictsignal) {
+ this.strictsignal = strictsignal;
+ }
+
+
+ /**
+ * Sets whether debug symbols should be generated into the class file.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param symbols a <code>boolean</code> value.
+ */
+ public void setSymbols(boolean symbols) {
+ this.symbols = symbols;
+ }
+
+
+ /**
+ * Asks the NetRexx compiler to print compilation times to the console
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param time a <code>boolean</code> value.
+ */
+ public void setTime(boolean time) {
+ this.time = time;
+ }
+
+ /**
+ * Turns on or off tracing and directs the resultant trace output Valid
+ * values are: "trace", "trace1", "trace2" and "notrace". "trace" and
+ * "trace2".
+ * @param trace the value to set.
+ */
+ public void setTrace(TraceAttr trace) {
+ this.trace = trace.getValue();
+ }
+
+ /**
+ * Turns on or off tracing and directs the resultant trace output Valid
+ * values are: "trace", "trace1", "trace2" and "notrace". "trace" and
+ * "trace2".
+ * @param trace the value to set.
+ */
+ public void setTrace(String trace) {
+ TraceAttr t = new TraceAttr();
+
+ t.setValue(trace);
+ setTrace(t);
+ }
+
+
+ /**
+ * Tells the NetRexx compiler that the source is in UTF8.
+ * Valid true values are "yes", "on" or "true". Anything else sets the flag to false.
+ * The default value is false.
+ * @param utf8 a <code>boolean</code> value.
+ */
+ public void setUtf8(boolean utf8) {
+ this.utf8 = utf8;
+ }
+
+
+ /**
+ * Whether lots of warnings and error messages should be generated
+ * @param verbose the value to set - verbose&lt;level&gt; or noverbose.
+ */
+ public void setVerbose(VerboseAttr verbose) {
+ this.verbose = verbose.getValue();
+ }
+
+
+ /**
+ * Whether lots of warnings and error messages should be generated
+ * @param verbose the value to set - verbose&lt;level&gt; or noverbose.
+ */
+ public void setVerbose(String verbose) {
+ VerboseAttr v = new VerboseAttr();
+
+ v.setValue(verbose);
+ setVerbose(v);
+ }
+
+ /**
+ * Whether the task should suppress the "Method argument is not used" in
+ * strictargs-Mode, which can not be suppressed by the compiler itself.
+ * The warning is logged as verbose message, though.
+ * @param suppressMethodArgumentNotUsed a <code>boolean</code> value.
+ */
+ public void setSuppressMethodArgumentNotUsed(boolean suppressMethodArgumentNotUsed) {
+ this.suppressMethodArgumentNotUsed = suppressMethodArgumentNotUsed;
+ }
+
+
+ /**
+ * Whether the task should suppress the "Private property is defined but
+ * not used" in strictargs-Mode, which can be quite annoying while
+ * developing. The warning is logged as verbose message, though.
+ * @param suppressPrivatePropertyNotUsed a <code>boolean</code> value.
+ */
+ public void setSuppressPrivatePropertyNotUsed(boolean suppressPrivatePropertyNotUsed) {
+ this.suppressPrivatePropertyNotUsed = suppressPrivatePropertyNotUsed;
+ }
+
+
+ /**
+ * Whether the task should suppress the "Variable is set but not used" in
+ * strictargs-Mode. Be careful with this one! The warning is logged as
+ * verbose message, though.
+ * @param suppressVariableNotUsed a <code>boolean</code> value.
+ */
+ public void setSuppressVariableNotUsed(boolean suppressVariableNotUsed) {
+ this.suppressVariableNotUsed = suppressVariableNotUsed;
+ }
+
+
+ /**
+ * Whether the task should suppress the "FooException is in SIGNALS list
+ * but is not signalled within the method", which is sometimes rather
+ * useless. The warning is logged as verbose message, though.
+ * @param suppressExceptionNotSignalled a <code>boolean</code> value.
+ */
+ public void setSuppressExceptionNotSignalled(boolean suppressExceptionNotSignalled) {
+ this.suppressExceptionNotSignalled = suppressExceptionNotSignalled;
+ }
+
+
+ /**
+ * Tells whether we should filter out any deprecation-messages
+ * of the compiler out.
+ * @param suppressDeprecation a <code>boolean</code> value.
+ */
+ public void setSuppressDeprecation(boolean suppressDeprecation) {
+ this.suppressDeprecation = suppressDeprecation;
+ }
+
+
+ /**
+ * Tells whether the trailing .keep in nocompile-mode should be removed
+ * so that the resulting java source really ends on .java.
+ * This facilitates the use of the javadoc tool lateron.
+ */
+ public void setRemoveKeepExtension(boolean removeKeepExtension) {
+ this.removeKeepExtension = removeKeepExtension;
+ }
+
+
+ /**
+ * init-Method sets defaults from Properties. That way, when ant is called
+ * with arguments like -Dant.netrexxc.verbose=verbose5 one can easily take
+ * control of all netrexxc-tasks.
+ */
+ public void init() {
+ String p;
+
+ if ((p = getProject().getProperty("ant.netrexxc.binary")) != null) {
+ this.binary = Project.toBoolean(p);
+ }
+ // classpath makes no sense
+ if ((p = getProject().getProperty("ant.netrexxc.comments")) != null) {
+ this.comments = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.compact")) != null) {
+ this.compact = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.compile")) != null) {
+ this.compile = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.console")) != null) {
+ this.console = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.crossref")) != null) {
+ this.crossref = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.decimal")) != null) {
+ this.decimal = Project.toBoolean(p);
+ // destDir
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.diag")) != null) {
+ this.diag = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.explicit")) != null) {
+ this.explicit = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.format")) != null) {
+ this.format = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.keep")) != null) {
+ this.keep = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.logo")) != null) {
+ this.logo = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.replace")) != null) {
+ this.replace = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.savelog")) != null) {
+ this.savelog = Project.toBoolean(p);
+ // srcDir
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.sourcedir")) != null) {
+ this.sourcedir = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.strictargs")) != null) {
+ this.strictargs = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.strictassign")) != null) {
+ this.strictassign = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.strictcase")) != null) {
+ this.strictcase = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.strictimport")) != null) {
+ this.strictimport = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.strictprops")) != null) {
+ this.strictprops = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.strictsignal")) != null) {
+ this.strictsignal = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.symbols")) != null) {
+ this.symbols = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.time")) != null) {
+ this.time = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.trace")) != null) {
+ setTrace(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.utf8")) != null) {
+ this.utf8 = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.verbose")) != null) {
+ setVerbose(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.suppressMethodArgumentNotUsed")) != null) {
+ this.suppressMethodArgumentNotUsed = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.suppressPrivatePropertyNotUsed")) != null) {
+ this.suppressPrivatePropertyNotUsed = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.suppressVariableNotUsed")) != null) {
+ this.suppressVariableNotUsed = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.suppressExceptionNotSignalled")) != null) {
+ this.suppressExceptionNotSignalled = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.suppressDeprecation")) != null) {
+ this.suppressDeprecation = Project.toBoolean(p);
+ }
+ if ((p = getProject().getProperty("ant.netrexxc.removeKeepExtension")) != null) {
+ this.removeKeepExtension = Project.toBoolean(p);
+ }
+ }
+
+
+ /**
+ * Executes the task - performs the actual compiler call.
+ * @throws BuildException on error.
+ */
+ public void execute() throws BuildException {
+
+ // first off, make sure that we've got a srcdir and destdir
+ if (srcDir == null || destDir == null) {
+ throw new BuildException("srcDir and destDir attributes must be set!");
+ }
+
+ // scan source and dest dirs to build up both copy lists and
+ // compile lists
+ // scanDir(srcDir, destDir);
+ DirectoryScanner ds = getDirectoryScanner(srcDir);
+
+ String[] files = ds.getIncludedFiles();
+
+ scanDir(srcDir, destDir, files);
+
+ // copy the source and support files
+ copyFilesToDestination();
+
+ // compile the source files
+ if (compileList.size() > 0) {
+ log("Compiling " + compileList.size() + " source file"
+ + (compileList.size() == 1 ? "" : "s")
+ + " to " + destDir);
+ doNetRexxCompile();
+ if (removeKeepExtension && (!compile || keep)) {
+ removeKeepExtensions();
+ }
+ }
+ }
+
+
+ /**
+ * Scans the directory looking for source files to be compiled and support
+ * files to be copied.
+ */
+ private void scanDir(File srcDir, File destDir, String[] files) {
+ for (int i = 0; i < files.length; i++) {
+ File srcFile = new File(srcDir, files[i]);
+ File destFile = new File(destDir, files[i]);
+ String filename = files[i];
+ // if it's a non source file, copy it if a later date than the
+ // dest
+ // if it's a source file, see if the destination class file
+ // needs to be recreated via compilation
+ if (filename.toLowerCase().endsWith(".nrx")) {
+ File classFile =
+ new File(destDir,
+ filename.substring(0, filename.lastIndexOf('.')) + ".class");
+ File javaFile =
+ new File(destDir,
+ filename.substring(0, filename.lastIndexOf('.'))
+ + (removeKeepExtension ? ".java" : ".java.keep"));
+
+ // nocompile case tests against .java[.keep] file
+ if (!compile && srcFile.lastModified() > javaFile.lastModified()) {
+ filecopyList.put(srcFile.getAbsolutePath(), destFile.getAbsolutePath());
+ compileList.addElement(destFile.getAbsolutePath());
+ } else if (compile && srcFile.lastModified() > classFile.lastModified()) {
+ // compile case tests against .class file
+ filecopyList.put(srcFile.getAbsolutePath(), destFile.getAbsolutePath());
+ compileList.addElement(destFile.getAbsolutePath());
+ }
+ } else {
+ if (srcFile.lastModified() > destFile.lastModified()) {
+ filecopyList.put(srcFile.getAbsolutePath(), destFile.getAbsolutePath());
+ }
+ }
+ }
+ }
+
+
+ /** Copy eligible files from the srcDir to destDir */
+ private void copyFilesToDestination() {
+ if (filecopyList.size() > 0) {
+ log("Copying " + filecopyList.size() + " file"
+ + (filecopyList.size() == 1 ? "" : "s")
+ + " to " + destDir.getAbsolutePath());
+
+ Enumeration e = filecopyList.keys();
+
+ while (e.hasMoreElements()) {
+ String fromFile = (String) e.nextElement();
+ String toFile = (String) filecopyList.get(fromFile);
+
+ try {
+ FileUtils.getFileUtils().copyFile(fromFile, toFile);
+ } catch (IOException ioe) {
+ String msg = "Failed to copy " + fromFile + " to " + toFile
+ + " due to " + ioe.getMessage();
+
+ throw new BuildException(msg, ioe);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Rename .java.keep files (back) to .java. The netrexxc renames all
+ * .java files to .java.keep if either -keep or -nocompile option is set.
+ */
+ private void removeKeepExtensions() {
+ if (compileList.size() > 0) {
+ log("Removing .keep extension on " + compileList.size() + " file"
+ + (compileList.size() == 1 ? "" : "s"));
+ Enumeration e = compileList.elements();
+ while (e.hasMoreElements()) {
+ String nrxName = (String) e.nextElement();
+ String baseName = nrxName.substring(0, nrxName.lastIndexOf('.'));
+ File fromFile = new File(baseName + ".java.keep");
+ File toFile = new File(baseName + ".java");
+ if (fromFile.renameTo(toFile)) {
+ log("Successfully renamed " + fromFile + " to " + toFile, Project.MSG_VERBOSE);
+ } else {
+ log("Failed to rename " + fromFile + " to " + toFile);
+ }
+ }
+ }
+ }
+
+
+ /** Performs a compile using the NetRexx 1.1.x compiler */
+ private void doNetRexxCompile() throws BuildException {
+ log("Using NetRexx compiler", Project.MSG_VERBOSE);
+
+ String classpath = getCompileClasspath();
+ StringBuffer compileOptions = new StringBuffer();
+
+ // create an array of strings for input to the compiler: one array
+ // comes from the compile options, the other from the compileList
+ String[] compileOptionsArray = getCompileOptionsAsArray();
+ String[] fileListArray = new String[compileList.size()];
+ Enumeration e = compileList.elements();
+ int j = 0;
+
+ while (e.hasMoreElements()) {
+ fileListArray[j] = (String) e.nextElement();
+ j++;
+ }
+ // create a single array of arguments for the compiler
+ String[] compileArgs = new String[compileOptionsArray.length + fileListArray.length];
+
+ for (int i = 0; i < compileOptionsArray.length; i++) {
+ compileArgs[i] = compileOptionsArray[i];
+ }
+ for (int i = 0; i < fileListArray.length; i++) {
+ compileArgs[i + compileOptionsArray.length] = fileListArray[i];
+ }
+
+ // print nice output about what we are doing for the log
+ compileOptions.append("Compilation args: ");
+ for (int i = 0; i < compileOptionsArray.length; i++) {
+ compileOptions.append(compileOptionsArray[i]);
+ compileOptions.append(" ");
+ }
+ log(compileOptions.toString(), Project.MSG_VERBOSE);
+
+ String eol = System.getProperty("line.separator");
+ StringBuffer niceSourceList = new StringBuffer("Files to be compiled:" + eol);
+
+ final int size = compileList.size();
+ for (int i = 0; i < size; i++) {
+ niceSourceList.append(" ");
+ niceSourceList.append(compileList.elementAt(i).toString());
+ niceSourceList.append(eol);
+ }
+
+ log(niceSourceList.toString(), Project.MSG_VERBOSE);
+
+ // need to set java.class.path property and restore it later
+ // since the NetRexx compiler has no option for the classpath
+ String currentClassPath = System.getProperty("java.class.path");
+ Properties currentProperties = System.getProperties();
+
+ currentProperties.put("java.class.path", classpath);
+
+ try {
+ StringWriter out = new StringWriter();
+ PrintWriter w = null;
+ int rc =
+ COM.ibm.netrexx.process.NetRexxC.main(new Rexx(compileArgs),
+ w = new PrintWriter(out));
+ String sdir = srcDir.getAbsolutePath();
+ String ddir = destDir.getAbsolutePath();
+ boolean doReplace = !(sdir.equals(ddir));
+ int dlen = ddir.length();
+ String l;
+ BufferedReader in = new BufferedReader(new StringReader(out.toString()));
+
+ log("replacing destdir '" + ddir + "' through sourcedir '"
+ + sdir + "'", Project.MSG_VERBOSE);
+ while ((l = in.readLine()) != null) {
+ int idx;
+
+ while (doReplace && ((idx = l.indexOf(ddir)) != -1)) {
+ // path is mentioned in the message
+ l = (new StringBuffer(l)).replace(idx, idx + dlen, sdir).toString();
+ }
+ // verbose level logging for suppressed messages
+ if (suppressMethodArgumentNotUsed
+ && l.indexOf(MSG_METHOD_ARGUMENT_NOT_USED) != -1) {
+ log(l, Project.MSG_VERBOSE);
+ } else if (suppressPrivatePropertyNotUsed
+ && l.indexOf(MSG_PRIVATE_PROPERTY_NOT_USED) != -1) {
+ log(l, Project.MSG_VERBOSE);
+ } else if (suppressVariableNotUsed
+ && l.indexOf(MSG_VARIABLE_NOT_USED) != -1) {
+ log(l, Project.MSG_VERBOSE);
+ } else if (suppressExceptionNotSignalled
+ && l.indexOf(MSG_EXCEPTION_NOT_SIGNALLED) != -1) {
+ log(l, Project.MSG_VERBOSE);
+ } else if (suppressDeprecation
+ && l.indexOf(MSG_DEPRECATION) != -1) {
+ log(l, Project.MSG_VERBOSE);
+ } else if (l.indexOf("Error:") != -1) {
+ // error level logging for compiler errors
+ log(l, Project.MSG_ERR);
+ } else if (l.indexOf("Warning:") != -1) {
+ // warning for all warning messages
+ log(l, Project.MSG_WARN);
+ } else {
+ log(l, Project.MSG_INFO); // info level for the rest.
+ }
+ }
+ if (rc > 1) {
+ throw new BuildException("Compile failed, messages should "
+ + "have been provided.");
+ }
+ if (w.checkError()) {
+ throw new IOException("Encountered an error");
+ }
+ } catch (IOException ioe) {
+ throw new BuildException("Unexpected IOException while "
+ + "playing with Strings", ioe);
+ } finally {
+ // need to reset java.class.path property
+ // since the NetRexx compiler has no option for the classpath
+ currentProperties = System.getProperties();
+ currentProperties.put("java.class.path", currentClassPath);
+ }
+
+ }
+
+
+ /** Builds the compilation classpath. */
+ private String getCompileClasspath() {
+ StringBuffer classpath = new StringBuffer();
+
+ // add dest dir to classpath so that previously compiled and
+ // untouched classes are on classpath
+ classpath.append(destDir.getAbsolutePath());
+
+ // add our classpath to the mix
+ if (this.classpath != null) {
+ addExistingToClasspath(classpath, this.classpath);
+ }
+
+ // add the system classpath
+ // addExistingToClasspath(classpath,System.getProperty("java.class.path"));
+ return classpath.toString();
+ }
+
+
+ /** This */
+ private String[] getCompileOptionsAsArray() {
+ Vector options = new Vector();
+
+ options.addElement(binary ? "-binary" : "-nobinary");
+ options.addElement(comments ? "-comments" : "-nocomments");
+ options.addElement(compile ? "-compile" : "-nocompile");
+ options.addElement(compact ? "-compact" : "-nocompact");
+ options.addElement(console ? "-console" : "-noconsole");
+ options.addElement(crossref ? "-crossref" : "-nocrossref");
+ options.addElement(decimal ? "-decimal" : "-nodecimal");
+ options.addElement(diag ? "-diag" : "-nodiag");
+ options.addElement(explicit ? "-explicit" : "-noexplicit");
+ options.addElement(format ? "-format" : "-noformat");
+ options.addElement(keep ? "-keep" : "-nokeep");
+ options.addElement(logo ? "-logo" : "-nologo");
+ options.addElement(replace ? "-replace" : "-noreplace");
+ options.addElement(savelog ? "-savelog" : "-nosavelog");
+ options.addElement(sourcedir ? "-sourcedir" : "-nosourcedir");
+ options.addElement(strictargs ? "-strictargs" : "-nostrictargs");
+ options.addElement(strictassign ? "-strictassign" : "-nostrictassign");
+ options.addElement(strictcase ? "-strictcase" : "-nostrictcase");
+ options.addElement(strictimport ? "-strictimport" : "-nostrictimport");
+ options.addElement(strictprops ? "-strictprops" : "-nostrictprops");
+ options.addElement(strictsignal ? "-strictsignal" : "-nostrictsignal");
+ options.addElement(symbols ? "-symbols" : "-nosymbols");
+ options.addElement(time ? "-time" : "-notime");
+ options.addElement("-" + trace);
+ options.addElement(utf8 ? "-utf8" : "-noutf8");
+ options.addElement("-" + verbose);
+
+ String[] results = new String[options.size()];
+
+ options.copyInto(results);
+ return results;
+ }
+
+
+ /**
+ * Takes a classpath-like string, and adds each element of this string to
+ * a new classpath, if the components exist. Components that don't exist,
+ * aren't added. We do this, because jikes issues warnings for
+ * non-existent files/dirs in his classpath, and these warnings are pretty
+ * annoying.
+ *
+ * @param target - target classpath
+ * @param source - source classpath to get file objects.
+ */
+ private void addExistingToClasspath(StringBuffer target, String source) {
+ StringTokenizer tok = new StringTokenizer(source,
+ System.getProperty("path.separator"), false);
+
+ while (tok.hasMoreTokens()) {
+ File f = getProject().resolveFile(tok.nextToken());
+
+ if (f.exists()) {
+ target.append(File.pathSeparator);
+ target.append(f.getAbsolutePath());
+ } else {
+ log("Dropping from classpath: "
+ + f.getAbsolutePath(), Project.MSG_VERBOSE);
+ }
+ }
+
+ }
+
+
+ /**
+ * Enumerated class corresponding to the trace attribute.
+ */
+ public static class TraceAttr extends EnumeratedAttribute {
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return new String[]{"trace", "trace1", "trace2", "notrace"};
+ }
+ }
+
+ /**
+ * Enumerated class corresponding to the verbose attribute.
+ */
+ public static class VerboseAttr extends EnumeratedAttribute {
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return new String[]{"verbose", "verbose0", "verbose1",
+ "verbose2", "verbose3", "verbose4",
+ "verbose5", "noverbose"};
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java
new file mode 100644
index 00000000..162cab1a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java
@@ -0,0 +1,726 @@
+/*
+ * 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.tools.ant.taskdefs.optional;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.LayoutPreservingProperties;
+
+/**
+ * Modifies settings in a property file.
+ *
+ * <p>The following is an example of its usage:</p>
+ * <pre>
+ * &lt;target name="setState"&gt;
+ * &lt;property
+ * name="header"
+ * value="##Generated file - do not modify!"/&gt;
+ * &lt;propertyfile file="apropfile.properties" comment="${header}"&gt;
+ * &lt;entry key="product.version.major" type="int" value="5"/&gt;
+ * &lt;entry key="product.version.minor" type="int" value="0"/&gt;
+ * &lt;entry key="product.build.major" type="int" value="0" /&gt;
+ * &lt;entry key="product.build.minor" type="int" operation="+" /&gt;
+ * &lt;entry key="product.build.date" type="date" value="now" /&gt;
+ * &lt;entry key="intSet" type="int" operation="=" value="681"/&gt;
+ * &lt;entry key="intDec" type="int" operation="-"/&gt;
+ * &lt;entry key="StringEquals" type="string" value="testValue"/&gt;
+ * &lt;/propertyfile&gt;
+ * &lt;/target&gt;
+ * </pre>
+ *
+ * The &lt;propertyfile&gt; task must have:
+ * <ul>
+ * <li>file</li>
+ * </ul>
+ * Other parameters are:
+ * <ul>
+ * <li>comment</li>
+ * <li>key</li>
+ * <li>operation</li>
+ * <li>type</li>
+ * <li>value (the final four being eliminated shortly)</li>
+ * </ul>
+ *
+ * The &lt;entry&gt; task must have:
+ * <ul>
+ * <li>key</li>
+ * </ul>
+ * Other parameters are:
+ * <ul>
+ * <li>operation</li>
+ * <li>type</li>
+ * <li>value</li>
+ * <li>default</li>
+ * <li>unit</li>
+ * </ul>
+ *
+ * If type is unspecified, it defaults to string.
+ *
+ * Parameter values:
+ * <ul>
+ * <li>operation:</li>
+ * <ul>
+ * <li>"=" (set -- default)</li>
+ * <li>"-" (dec)</li>
+ * <li>"+" (inc)</li>
+ * </ul>
+ * <li>type:</li>
+ * <ul>
+ * <li>"int"</li>
+ * <li>"date"</li>
+ * <li>"string"</li>
+ * </ul>
+ * <li>value:</li>
+ * <ul>
+ * <li>holds the default value, if the property
+ * was not found in property file</li>
+ * <li>"now" In case of type "date", the
+ * value "now" will be replaced by the current
+ * date/time and used even if a valid date was
+ * found in the property file.</li>
+ * </ul>
+ * </ul>
+ *
+ * <p>String property types can only use the "=" operation.
+ * Int property types can only use the "=", "-" or "+" operations.<p>
+ *
+ * The message property is used for the property file header, with "\\" being
+ * a newline delimiter character.
+ *
+ */
+public class PropertyFile extends Task {
+
+ /* ========================================================================
+ *
+ * Instance variables.
+ */
+
+ // Use this to prepend a message to the properties file
+ private String comment;
+
+ private Properties properties;
+ private File propertyfile;
+ private boolean useJDKProperties;
+
+ private Vector entries = new Vector();
+
+ /* ========================================================================
+ *
+ * Constructors
+ */
+
+ /* ========================================================================
+ *
+ * Methods
+ */
+
+ /**
+ * Execute the task.
+ * @throws BuildException on error.
+ */
+ @Override
+ public void execute() throws BuildException {
+ checkParameters();
+ readFile();
+ executeOperation();
+ writeFile();
+ }
+
+ /**
+ * The entry nested element.
+ * @return an entry nested element to be configured.
+ */
+ public Entry createEntry() {
+ Entry e = new Entry();
+ entries.addElement(e);
+ return e;
+ }
+
+ private void executeOperation() throws BuildException {
+ for (Enumeration e = entries.elements(); e.hasMoreElements();) {
+ Entry entry = (Entry) e.nextElement();
+ entry.executeOn(properties);
+ }
+ }
+
+ private void readFile() throws BuildException {
+ if (useJDKProperties) {
+ // user chose to use standard Java properties, which loose
+ // comments and layout
+ properties = new Properties();
+ } else {
+ properties = new LayoutPreservingProperties();
+ }
+ try {
+ if (propertyfile.exists()) {
+ log("Updating property file: "
+ + propertyfile.getAbsolutePath());
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(propertyfile);
+ BufferedInputStream bis = new BufferedInputStream(fis);
+ properties.load(bis);
+ } finally {
+ if (fis != null) {
+ fis.close();
+ }
+ }
+ } else {
+ log("Creating new property file: "
+ + propertyfile.getAbsolutePath());
+ FileOutputStream out = null;
+ try {
+ out = new FileOutputStream(propertyfile.getAbsolutePath());
+ out.flush();
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ }
+ }
+ } catch (IOException ioe) {
+ throw new BuildException(ioe.toString());
+ }
+ }
+
+ private void checkParameters() throws BuildException {
+ if (!checkParam(propertyfile)) {
+ throw new BuildException("file token must not be null.",
+ getLocation());
+ }
+ }
+
+ /**
+ * Location of the property file to be edited; required.
+ * @param file the property file.
+ */
+ public void setFile(File file) {
+ propertyfile = file;
+ }
+
+ /**
+ * optional header comment for the file
+ * @param hdr the string to use for the comment.
+ */
+ public void setComment(String hdr) {
+ comment = hdr;
+ }
+
+ /**
+ * optional flag to use original Java properties (as opposed to
+ * layout preserving properties)
+ */
+ public void setJDKProperties(boolean val) {
+ useJDKProperties = val;
+ }
+
+ private void writeFile() throws BuildException {
+ // Write to RAM first, as an OOME could otherwise produce a truncated file:
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ properties.store(baos, comment);
+ } catch (IOException x) { // should not happen
+ throw new BuildException(x, getLocation());
+ }
+ try {
+ OutputStream os = new FileOutputStream(propertyfile);
+ try {
+ try {
+ os.write(baos.toByteArray());
+ } finally {
+ os.close();
+ }
+ } catch (IOException x) { // possibly corrupt
+ FileUtils.getFileUtils().tryHardToDelete(propertyfile);
+ throw x;
+ }
+ } catch (IOException x) { // opening, writing, or closing
+ throw new BuildException(x, getLocation());
+ }
+ }
+
+ private boolean checkParam(File param) {
+ return !(param == null);
+ }
+
+ /**
+ * Instance of this class represents nested elements of
+ * a task propertyfile.
+ */
+ public static class Entry {
+ private static final int DEFAULT_INT_VALUE = 0;
+ private static final String DEFAULT_DATE_VALUE = "now";
+ private static final String DEFAULT_STRING_VALUE = "";
+
+ private String key = null;
+ private int type = Type.STRING_TYPE;
+ private int operation = Operation.EQUALS_OPER;
+ private String value = null;
+ private String defaultValue = null;
+ private String newValue = null;
+ private String pattern = null;
+ private int field = Calendar.DATE;
+
+ /**
+ * Name of the property name/value pair
+ * @param value the key.
+ */
+ public void setKey(String value) {
+ this.key = value;
+ }
+
+ /**
+ * Value to set (=), to add (+) or subtract (-)
+ * @param value the value.
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * operation to apply.
+ * &quot;+&quot; or &quot;=&quot;
+ *(default) for all datatypes; &quot;-&quot; for date and int only)\.
+ * @param value the operation enumerated value.
+ */
+ public void setOperation(Operation value) {
+ this.operation = Operation.toOperation(value.getValue());
+ }
+
+ /**
+ * Regard the value as : int, date or string (default)
+ * @param value the type enumerated value.
+ */
+ public void setType(Type value) {
+ this.type = Type.toType(value.getValue());
+ }
+
+ /**
+ * Initial value to set for a property if it is not
+ * already defined in the property file.
+ * For type date, an additional keyword is allowed: &quot;now&quot;
+ * @param value the default value.
+ */
+ public void setDefault(String value) {
+ this.defaultValue = value;
+ }
+
+ /**
+ * For int and date type only. If present, Values will
+ * be parsed and formatted accordingly.
+ * @param value the pattern to use.
+ */
+ public void setPattern(String value) {
+ this.pattern = value;
+ }
+
+ /**
+ * The unit of the value to be applied to date +/- operations.
+ * Valid Values are:
+ * <ul>
+ * <li>millisecond</li>
+ * <li>second</li>
+ * <li>minute</li>
+ * <li>hour</li>
+ * <li>day (default)</li>
+ * <li>week</li>
+ * <li>month</li>
+ * <li>year</li>
+ * </ul>
+ * This only applies to date types using a +/- operation.
+ * @param unit the unit enumerated value.
+ * @since Ant 1.5
+ */
+ public void setUnit(PropertyFile.Unit unit) {
+ field = unit.getCalendarField();
+ }
+
+ /**
+ * Apply the nested element to the properties.
+ * @param props the properties to apply the entry on.
+ * @throws BuildException if there is an error.
+ */
+ protected void executeOn(Properties props) throws BuildException {
+ checkParameters();
+
+ if (operation == Operation.DELETE_OPER) {
+ props.remove(key);
+ return;
+ }
+
+ // type may be null because it wasn't set
+ String oldValue = (String) props.get(key);
+ try {
+ if (type == Type.INTEGER_TYPE) {
+ executeInteger(oldValue);
+ } else if (type == Type.DATE_TYPE) {
+ executeDate(oldValue);
+ } else if (type == Type.STRING_TYPE) {
+ executeString(oldValue);
+ } else {
+ throw new BuildException("Unknown operation type: "
+ + type);
+ }
+ } catch (NullPointerException npe) {
+ // Default to string type
+ // which means do nothing
+ npe.printStackTrace();
+ }
+
+ if (newValue == null) {
+ newValue = "";
+ }
+
+ // Insert as a string by default
+ props.put(key, newValue);
+ }
+
+ /**
+ * Handle operations for type <code>date</code>.
+ *
+ * @param oldValue the current value read from the property file or
+ * <code>null</code> if the <code>key</code> was
+ * not contained in the property file.
+ */
+ private void executeDate(String oldValue) throws BuildException {
+ Calendar currentValue = Calendar.getInstance();
+
+ if (pattern == null) {
+ pattern = "yyyy/MM/dd HH:mm";
+ }
+ DateFormat fmt = new SimpleDateFormat(pattern);
+
+ String currentStringValue = getCurrentValue(oldValue);
+ if (currentStringValue == null) {
+ currentStringValue = DEFAULT_DATE_VALUE;
+ }
+
+ if ("now".equals(currentStringValue)) {
+ currentValue.setTime(new Date());
+ } else {
+ try {
+ currentValue.setTime(fmt.parse(currentStringValue));
+ } catch (ParseException pe) {
+ // swallow
+ }
+ }
+
+ if (operation != Operation.EQUALS_OPER) {
+ int offset = 0;
+ try {
+ offset = Integer.parseInt(value);
+ if (operation == Operation.DECREMENT_OPER) {
+ offset = -1 * offset;
+ }
+ } catch (NumberFormatException e) {
+ throw new BuildException("Value not an integer on " + key);
+ }
+ currentValue.add(field, offset);
+ }
+
+ newValue = fmt.format(currentValue.getTime());
+ }
+
+
+ /**
+ * Handle operations for type <code>int</code>.
+ *
+ * @param oldValue the current value read from the property file or
+ * <code>null</code> if the <code>key</code> was
+ * not contained in the property file.
+ */
+ private void executeInteger(String oldValue) throws BuildException {
+ int currentValue = DEFAULT_INT_VALUE;
+ int newV = DEFAULT_INT_VALUE;
+
+
+ DecimalFormat fmt = (pattern != null) ? new DecimalFormat(pattern)
+ : new DecimalFormat();
+ try {
+ String curval = getCurrentValue(oldValue);
+ if (curval != null) {
+ currentValue = fmt.parse(curval).intValue();
+ } else {
+ currentValue = 0;
+ }
+ } catch (NumberFormatException nfe) {
+ // swallow
+ } catch (ParseException pe) {
+ // swallow
+ }
+
+ if (operation == Operation.EQUALS_OPER) {
+ newV = currentValue;
+ } else {
+ int operationValue = 1;
+ if (value != null) {
+ try {
+ operationValue = fmt.parse(value).intValue();
+ } catch (NumberFormatException nfe) {
+ // swallow
+ } catch (ParseException pe) {
+ // swallow
+ }
+ }
+
+ if (operation == Operation.INCREMENT_OPER) {
+ newV = currentValue + operationValue;
+ } else if (operation == Operation.DECREMENT_OPER) {
+ newV = currentValue - operationValue;
+ }
+ }
+
+ this.newValue = fmt.format(newV);
+ }
+
+ /**
+ * Handle operations for type <code>string</code>.
+ *
+ * @param oldValue the current value read from the property file or
+ * <code>null</code> if the <code>key</code> was
+ * not contained in the property file.
+ */
+ private void executeString(String oldValue) throws BuildException {
+ String newV = DEFAULT_STRING_VALUE;
+
+ String currentValue = getCurrentValue(oldValue);
+
+ if (currentValue == null) {
+ currentValue = DEFAULT_STRING_VALUE;
+ }
+
+ if (operation == Operation.EQUALS_OPER) {
+ newV = currentValue;
+ } else if (operation == Operation.INCREMENT_OPER) {
+ newV = currentValue + value;
+ }
+ this.newValue = newV;
+ }
+
+ /**
+ * Check if parameter combinations can be supported
+ * @todo make sure the 'unit' attribute is only specified on date
+ * fields
+ */
+ private void checkParameters() throws BuildException {
+ if (type == Type.STRING_TYPE
+ && operation == Operation.DECREMENT_OPER) {
+ throw new BuildException("- is not supported for string "
+ + "properties (key:" + key + ")");
+ }
+ if (value == null && defaultValue == null && operation != Operation.DELETE_OPER) {
+ throw new BuildException("\"value\" and/or \"default\" "
+ + "attribute must be specified (key:" + key + ")");
+ }
+ if (key == null) {
+ throw new BuildException("key is mandatory");
+ }
+ if (type == Type.STRING_TYPE && pattern != null) {
+ throw new BuildException("pattern is not supported for string "
+ + "properties (key:" + key + ")");
+ }
+ }
+
+ private String getCurrentValue(String oldValue) {
+ String ret = null;
+ if (operation == Operation.EQUALS_OPER) {
+ // If only value is specified, the property is set to it
+ // regardless of its previous value.
+ if (value != null && defaultValue == null) {
+ ret = value;
+ }
+
+ // If only default is specified and the property previously
+ // existed in the property file, it is unchanged.
+ if (value == null && defaultValue != null && oldValue != null) {
+ ret = oldValue;
+ }
+
+ // If only default is specified and the property did not
+ // exist in the property file, the property is set to default.
+ if (value == null && defaultValue != null && oldValue == null) {
+ ret = defaultValue;
+ }
+
+ // If value and default are both specified and the property
+ // previously existed in the property file, the property
+ // is set to value.
+ if (value != null && defaultValue != null && oldValue != null) {
+ ret = value;
+ }
+
+ // If value and default are both specified and the property
+ // did not exist in the property file, the property is set
+ // to default.
+ if (value != null && defaultValue != null && oldValue == null) {
+ ret = defaultValue;
+ }
+ } else {
+ ret = (oldValue == null) ? defaultValue : oldValue;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Enumerated attribute with the values "+", "-", "="
+ */
+ public static class Operation extends EnumeratedAttribute {
+
+ // Property type operations
+ /** + */
+ public static final int INCREMENT_OPER = 0;
+ /** - */
+ public static final int DECREMENT_OPER = 1;
+ /** = */
+ public static final int EQUALS_OPER = 2;
+ /** del */
+ public static final int DELETE_OPER = 3;
+
+ /** {@inheritDoc}. */
+ @Override
+ public String[] getValues() {
+ return new String[] {"+", "-", "=", "del"};
+ }
+
+ /**
+ * Convert string to index.
+ * @param oper the string to convert.
+ * @return the index.
+ */
+ public static int toOperation(String oper) {
+ if ("+".equals(oper)) {
+ return INCREMENT_OPER;
+ } else if ("-".equals(oper)) {
+ return DECREMENT_OPER;
+ } else if ("del".equals(oper)) {
+ return DELETE_OPER;
+ }
+ return EQUALS_OPER;
+ }
+ }
+
+ /**
+ * Enumerated attribute with the values "int", "date" and "string".
+ */
+ public static class Type extends EnumeratedAttribute {
+
+ // Property types
+ /** int */
+ public static final int INTEGER_TYPE = 0;
+ /** date */
+ public static final int DATE_TYPE = 1;
+ /** string */
+ public static final int STRING_TYPE = 2;
+
+ /** {@inheritDoc} */
+ @Override
+ public String[] getValues() {
+ return new String[] {"int", "date", "string"};
+ }
+
+ /**
+ * Convert string to index.
+ * @param type the string to convert.
+ * @return the index.
+ */
+ public static int toType(String type) {
+ if ("int".equals(type)) {
+ return INTEGER_TYPE;
+ } else if ("date".equals(type)) {
+ return DATE_TYPE;
+ }
+ return STRING_TYPE;
+ }
+ }
+ }
+
+ /**
+ * Borrowed from Tstamp
+ * @todo share all this time stuff across many tasks as a datetime datatype
+ * @since Ant 1.5
+ */
+ public static class Unit extends EnumeratedAttribute {
+
+ private static final String MILLISECOND = "millisecond";
+ private static final String SECOND = "second";
+ private static final String MINUTE = "minute";
+ private static final String HOUR = "hour";
+ private static final String DAY = "day";
+ private static final String WEEK = "week";
+ private static final String MONTH = "month";
+ private static final String YEAR = "year";
+
+ private static final String[] UNITS
+ = {MILLISECOND, SECOND, MINUTE, HOUR,
+ DAY, WEEK, MONTH, YEAR };
+
+ private Map calendarFields = new HashMap();
+
+ /** no arg constructor */
+ public Unit() {
+ calendarFields.put(MILLISECOND,
+ new Integer(Calendar.MILLISECOND));
+ calendarFields.put(SECOND, new Integer(Calendar.SECOND));
+ calendarFields.put(MINUTE, new Integer(Calendar.MINUTE));
+ calendarFields.put(HOUR, new Integer(Calendar.HOUR_OF_DAY));
+ calendarFields.put(DAY, new Integer(Calendar.DATE));
+ calendarFields.put(WEEK, new Integer(Calendar.WEEK_OF_YEAR));
+ calendarFields.put(MONTH, new Integer(Calendar.MONTH));
+ calendarFields.put(YEAR, new Integer(Calendar.YEAR));
+ }
+
+ /**
+ * Convert the value to a Calendar field index.
+ * @return the calendar value.
+ */
+ public int getCalendarField() {
+ String key = getValue().toLowerCase();
+ Integer i = (Integer) calendarFields.get(key);
+ return i.intValue();
+ }
+
+ /** {@inheritDoc}. */
+ @Override
+ public String[] getValues() {
+ return UNITS;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/RenameExtensions.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/RenameExtensions.java
new file mode 100644
index 00000000..c4acadb2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/RenameExtensions.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.
+ *
+ */
+/*
+ * Task to rename files based on extension. This task has the following
+ * properties which can be set:
+ * <ul>
+ * <li>fromExtension: </li>
+ * <li>toExtension: </li>
+ * <li>srcDir: </li>
+ * <li>replace: </li>
+ * </ul>
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.taskdefs.Move;
+import org.apache.tools.ant.types.Mapper;
+
+/**
+ *
+ * @version 1.2
+ *
+ * @deprecated since 1.5.x.
+ * Use &lt;move&gt; instead
+ */
+public class RenameExtensions extends MatchingTask {
+
+ private String fromExtension = "";
+ private String toExtension = "";
+ private boolean replace = false;
+ private File srcDir;
+
+ private Mapper.MapperType globType;
+
+
+ /** Creates new RenameExtensions */
+ public RenameExtensions() {
+ super();
+ globType = new Mapper.MapperType();
+ globType.setValue("glob");
+ }
+
+ /**
+ * The string that files must end in to be renamed
+ *
+ * @param from the extension of files being renamed.
+ */
+ public void setFromExtension(String from) {
+ fromExtension = from;
+ }
+
+ /**
+ * The string that renamed files will end with on
+ * completion
+ *
+ * @param to the extension of the renamed files.
+ */
+ public void setToExtension(String to) {
+ toExtension = to;
+ }
+
+ /**
+ * store replace attribute - this determines whether the target file
+ * should be overwritten if present
+ *
+ * @param replace if true overwrite any target files that exist.
+ */
+ public void setReplace(boolean replace) {
+ this.replace = replace;
+ }
+
+ /**
+ * Set the source dir to find the files to be renamed.
+ *
+ * @param srcDir the source directory.
+ */
+ public void setSrcDir(File srcDir) {
+ this.srcDir = srcDir;
+ }
+
+ /**
+ * Executes the task.
+ *
+ * @throws BuildException is there is a problem in the task execution.
+ */
+ public void execute() throws BuildException {
+
+ // first off, make sure that we've got a from and to extension
+ if (fromExtension == null || toExtension == null || srcDir == null) {
+ throw new BuildException("srcDir, fromExtension and toExtension "
+ + "attributes must be set!");
+ }
+
+ log("DEPRECATED - The renameext task is deprecated. Use move instead.",
+ Project.MSG_WARN);
+ log("Replace this with:", Project.MSG_INFO);
+ log("<move todir=\"" + srcDir + "\" overwrite=\"" + replace + "\">",
+ Project.MSG_INFO);
+ log(" <fileset dir=\"" + srcDir + "\" />", Project.MSG_INFO);
+ log(" <mapper type=\"glob\"", Project.MSG_INFO);
+ log(" from=\"*" + fromExtension + "\"", Project.MSG_INFO);
+ log(" to=\"*" + toExtension + "\" />", Project.MSG_INFO);
+ log("</move>", Project.MSG_INFO);
+ log("using the same patterns on <fileset> as you\'ve used here",
+ Project.MSG_INFO);
+
+ Move move = new Move();
+ move.bindToOwner(this);
+ move.setOwningTarget(getOwningTarget());
+ move.setTaskName(getTaskName());
+ move.setLocation(getLocation());
+ move.setTodir(srcDir);
+ move.setOverwrite(replace);
+
+ fileset.setDir(srcDir);
+ move.addFileset(fileset);
+
+ Mapper me = move.createMapper();
+ me.setType(globType);
+ me.setFrom("*" + fromExtension);
+ me.setTo("*" + toExtension);
+
+ move.execute();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ReplaceRegExp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ReplaceRegExp.java
new file mode 100644
index 00000000..d088096a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ReplaceRegExp.java
@@ -0,0 +1,533 @@
+/*
+ * 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.tools.ant.taskdefs.optional;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.RegularExpression;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.Substitution;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.regexp.Regexp;
+import org.apache.tools.ant.util.regexp.RegexpUtil;
+
+/**
+ * Performs regular expression string replacements in a text
+ * file. The input file(s) must be able to be properly processed by
+ * a Reader instance. That is, they must be text only, no binary.
+ *
+ * The syntax of the regular expression depends on the implementation that
+ * you choose to use. The system property <code>ant.regexp.regexpimpl</code>
+ * will be the classname of the implementation that will be used (the default
+ * is <code>org.apache.tools.ant.util.regexp.JakartaOroRegexp</code> and
+ * requires the Jakarta Oro Package).
+ *
+ * <pre>
+ * Available implementations:
+ *
+ * org.apache.tools.ant.util.regexp.Jdk14RegexpRegexp (default)
+ * Uses Java's built-in regular expression package
+ *
+ * org.apache.tools.ant.util.regexp.JakartaOroRegexp
+ * Requires the jakarta-oro package
+ *
+ * org.apache.tools.ant.util.regexp.JakartaRegexpRegexp
+ * Requires the jakarta-regexp package
+ *
+ * Usage:
+ *
+ * Call Syntax:
+ *
+ * &lt;replaceregexp file="file"
+ * match="pattern"
+ * replace="pattern"
+ * flags="options"?
+ * byline="true|false"? &gt;
+ * regexp?
+ * substitution?
+ * fileset*
+ * &lt;/replaceregexp&gt;
+ *
+ * NOTE: You must have either the file attribute specified, or at least one fileset subelement
+ * to operation on. You may not have the file attribute specified if you nest fileset elements
+ * inside this task. Also, you cannot specify both match and a regular expression subelement at
+ * the same time, nor can you specify the replace attribute and the substitution subelement at
+ * the same time.
+ *
+ * Attributes:
+ *
+ * file --&gt; A single file to operation on (mutually exclusive
+ * with the fileset subelements)
+ * match --&gt; The Regular expression to match
+ * replace --&gt; The Expression replacement string
+ * flags --&gt; The options to give to the replacement
+ * g = Substitute all occurrences. default is to replace only the first one
+ * i = Case insensitive match
+ *
+ * byline --&gt; Should this file be processed a single line at a time (default is false)
+ * "true" indicates to perform replacement on a line by line basis
+ * "false" indicates to perform replacement on the whole file at once.
+ *
+ * Example:
+ *
+ * The following call could be used to replace an old property name in a ".properties"
+ * file with a new name. In the replace attribute, you can refer to any part of the
+ * match expression in parenthesis using backslash followed by a number like '\1'.
+ *
+ * &lt;replaceregexp file="test.properties"
+ * match="MyProperty=(.*)"
+ * replace="NewProperty=\1"
+ * byline="true" /&gt;
+ *
+ * </pre>
+ *
+ */
+public class ReplaceRegExp extends Task {
+
+ private File file;
+ private String flags;
+ private boolean byline;
+ private Union resources;
+ private RegularExpression regex;
+ private Substitution subs;
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private boolean preserveLastModified = false;
+
+ /**
+ * Encoding to assume for the files
+ */
+ private String encoding = null;
+
+ /** Default Constructor */
+ public ReplaceRegExp() {
+ super();
+ this.file = null;
+ this.flags = "";
+ this.byline = false;
+
+ this.regex = null;
+ this.subs = null;
+ }
+
+
+ /**
+ * file for which the regular expression should be replaced;
+ * required unless a nested fileset is supplied.
+ * @param file The file for which the reg exp should be replaced.
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+
+ /**
+ * the regular expression pattern to match in the file(s);
+ * required if no nested &lt;regexp&gt; is used
+ * @param match the match attribute.
+ */
+ public void setMatch(String match) {
+ if (regex != null) {
+ throw new BuildException("Only one regular expression is allowed");
+ }
+
+ regex = new RegularExpression();
+ regex.setPattern(match);
+ }
+
+
+ /**
+ * The substitution pattern to place in the file(s) in place
+ * of the regular expression.
+ * Required if no nested &lt;substitution&gt; is used
+ * @param replace the replace attribute
+ */
+
+ public void setReplace(String replace) {
+ if (subs != null) {
+ throw new BuildException("Only one substitution expression is "
+ + "allowed");
+ }
+
+ subs = new Substitution();
+ subs.setExpression(replace);
+ }
+
+ /**
+ * The flags to use when matching the regular expression. For more
+ * information, consult the Perl5 syntax.
+ * <ul>
+ * <li>g : Global replacement. Replace all occurrences found
+ * <li>i : Case Insensitive. Do not consider case in the match
+ * <li>m : Multiline. Treat the string as multiple lines of input,
+ * using "^" and "$" as the start or end of any line, respectively,
+ * rather than start or end of string.
+ * <li> s : Singleline. Treat the string as a single line of input, using
+ * "." to match any character, including a newline, which normally,
+ * it would not match.
+ *</ul>
+ * @param flags the flags attribute
+ */
+ public void setFlags(String flags) {
+ this.flags = flags;
+ }
+
+
+ /**
+ * Process the file(s) one line at a time, executing the replacement
+ * on one line at a time. This is useful if you
+ * want to only replace the first occurrence of a regular expression on
+ * each line, which is not easy to do when processing the file as a whole.
+ * Defaults to <i>false</i>.
+ *
+ * @param byline the byline attribute as a string
+ * @deprecated since 1.6.x.
+ * Use setByLine(boolean).
+ */
+ @Deprecated
+ public void setByLine(String byline) {
+ Boolean res = Boolean.valueOf(byline);
+
+ if (res == null) {
+ res = Boolean.FALSE;
+ }
+ this.byline = res.booleanValue();
+ }
+
+ /**
+ * Process the file(s) one line at a time, executing the replacement
+ * on one line at a time. This is useful if you
+ * want to only replace the first occurrence of a regular expression on
+ * each line, which is not easy to do when processing the file as a whole.
+ * Defaults to <i>false</i>.
+ *
+ * @param byline the byline attribute
+ */
+ public void setByLine(boolean byline) {
+ this.byline = byline;
+ }
+
+ /**
+ * Specifies the encoding Ant expects the files to be in -
+ * defaults to the platforms default encoding.
+ * @param encoding the encoding attribute
+ *
+ * @since Ant 1.6
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * list files to apply the replacement to
+ * @param set the fileset element
+ */
+ public void addFileset(FileSet set) {
+ addConfigured(set);
+ }
+
+ /**
+ * Support arbitrary file system based resource collections.
+ *
+ * @since Ant 1.8.0
+ */
+ public void addConfigured(ResourceCollection rc) {
+ if (!rc.isFilesystemOnly()) {
+ throw new BuildException("only filesystem resources are supported");
+ }
+ if (resources == null) {
+ resources = new Union();
+ }
+ resources.add(rc);
+ }
+
+ /**
+ * A regular expression.
+ * You can use this element to refer to a previously
+ * defined regular expression datatype instance
+ * @return the regular expression object to be configured as an element
+ */
+ public RegularExpression createRegexp() {
+ if (regex != null) {
+ throw new BuildException("Only one regular expression is allowed.");
+ }
+
+ regex = new RegularExpression();
+ return regex;
+ }
+
+
+ /**
+ * A substitution pattern. You can use this element to refer to a previously
+ * defined substitution pattern datatype instance.
+ * @return the substitution pattern object to be configured as an element
+ */
+ public Substitution createSubstitution() {
+ if (subs != null) {
+ throw new BuildException("Only one substitution expression is "
+ + "allowed");
+ }
+
+ subs = new Substitution();
+ return subs;
+ }
+
+ /**
+ * Whether the file timestamp shall be preserved even if the file
+ * is modified.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setPreserveLastModified(boolean b) {
+ preserveLastModified = b;
+ }
+
+ /**
+ * Invoke a regular expression (r) on a string (input) using
+ * substitutions (s) for a matching regex.
+ *
+ * @param r a regular expression
+ * @param s a Substitution
+ * @param input the string to do the replacement on
+ * @param options The options for the regular expression
+ * @return the replacement result
+ */
+ protected String doReplace(RegularExpression r,
+ Substitution s,
+ String input,
+ int options) {
+ String res = input;
+ Regexp regexp = r.getRegexp(getProject());
+
+ if (regexp.matches(input, options)) {
+ log("Found match; substituting", Project.MSG_DEBUG);
+ res = regexp.substitute(input, s.getExpression(getProject()),
+ options);
+ }
+
+ return res;
+ }
+
+
+ /**
+ * Perform the replacement on a file
+ *
+ * @param f the file to perform the replacement on
+ * @param options the regular expressions options
+ * @exception IOException if an error occurs
+ */
+ protected void doReplace(File f, int options)
+ throws IOException {
+ File temp = FILE_UTILS.createTempFile("replace", ".txt", null, true, true);
+ try {
+ boolean changes = false;
+
+ InputStream is = new FileInputStream(f);
+ try {
+ Reader r = encoding != null ? new InputStreamReader(is, encoding) : new InputStreamReader(is);
+ OutputStream os = new FileOutputStream(temp);
+ try {
+ Writer w = encoding != null ? new OutputStreamWriter(os, encoding) : new OutputStreamWriter(os);
+
+ log("Replacing pattern '" + regex.getPattern(getProject())
+ + "' with '" + subs.getExpression(getProject())
+ + "' in '" + f.getPath() + "'" + (byline ? " by line" : "")
+ + (flags.length() > 0 ? " with flags: '" + flags + "'" : "")
+ + ".", Project.MSG_VERBOSE);
+
+ if (byline) {
+ r = new BufferedReader(r);
+ w = new BufferedWriter(w);
+
+ StringBuffer linebuf = new StringBuffer();
+ int c;
+ boolean hasCR = false;
+
+ do {
+ c = r.read();
+
+ if (c == '\r') {
+ if (hasCR) {
+ // second CR -> EOL + possibly empty line
+ changes |= replaceAndWrite(linebuf.toString(),
+ w, options);
+ w.write('\r');
+
+ linebuf = new StringBuffer();
+ // hasCR is still true (for the second one)
+ } else {
+ // first CR in this line
+ hasCR = true;
+ }
+ } else if (c == '\n') {
+ // LF -> EOL
+ changes |= replaceAndWrite(linebuf.toString(),
+ w, options);
+ if (hasCR) {
+ w.write('\r');
+ hasCR = false;
+ }
+ w.write('\n');
+
+ linebuf = new StringBuffer();
+ } else { // any other char
+ if ((hasCR) || (c < 0)) {
+ // Mac-style linebreak or EOF (or both)
+ changes |= replaceAndWrite(linebuf.toString(),
+ w, options);
+ if (hasCR) {
+ w.write('\r');
+ hasCR = false;
+ }
+
+ linebuf = new StringBuffer();
+ }
+
+ if (c >= 0) {
+ linebuf.append((char) c);
+ }
+ }
+ } while (c >= 0);
+
+ } else {
+ changes = multilineReplace(r, w, options);
+ }
+
+ r.close();
+ w.close();
+
+ } finally {
+ os.close();
+ }
+ } finally {
+ is.close();
+ }
+ if (changes) {
+ log("File has changed; saving the updated file", Project.MSG_VERBOSE);
+ try {
+ long origLastModified = f.lastModified();
+ FILE_UTILS.rename(temp, f);
+ if (preserveLastModified) {
+ FILE_UTILS.setFileLastModified(f, origLastModified);
+ }
+ temp = null;
+ } catch (IOException e) {
+ throw new BuildException("Couldn't rename temporary file "
+ + temp, e, getLocation());
+ }
+ } else {
+ log("No change made", Project.MSG_DEBUG);
+ }
+ } finally {
+ if (temp != null) {
+ temp.delete();
+ }
+ }
+ }
+
+
+ /**
+ * Execute the task
+ *
+ * @throws BuildException is there is a problem in the task execution.
+ */
+ @Override
+ public void execute() throws BuildException {
+ if (regex == null) {
+ throw new BuildException("No expression to match.");
+ }
+ if (subs == null) {
+ throw new BuildException("Nothing to replace expression with.");
+ }
+
+ if (file != null && resources != null) {
+ throw new BuildException("You cannot supply the 'file' attribute "
+ + "and resource collections at the same "
+ + "time.");
+ }
+
+ int options = RegexpUtil.asOptions(flags);
+
+ if (file != null && file.exists()) {
+ try {
+ doReplace(file, options);
+ } catch (IOException e) {
+ log("An error occurred processing file: '"
+ + file.getAbsolutePath() + "': " + e.toString(),
+ Project.MSG_ERR);
+ }
+ } else if (file != null) {
+ log("The following file is missing: '"
+ + file.getAbsolutePath() + "'", Project.MSG_ERR);
+ }
+
+ if (resources != null) {
+ for (Resource r : resources) {
+ FileProvider fp =
+ r.as(FileProvider.class);
+ File f = fp.getFile();
+
+ if (f.exists()) {
+ try {
+ doReplace(f, options);
+ } catch (Exception e) {
+ log("An error occurred processing file: '"
+ + f.getAbsolutePath() + "': " + e.toString(),
+ Project.MSG_ERR);
+ }
+ } else {
+ log("The following file is missing: '"
+ + f.getAbsolutePath() + "'", Project.MSG_ERR);
+ }
+ }
+ }
+ }
+
+ private boolean multilineReplace(Reader r, Writer w, int options)
+ throws IOException {
+ return replaceAndWrite(FileUtils.safeReadFully(r), w, options);
+ }
+
+ private boolean replaceAndWrite(String s, Writer w, int options)
+ throws IOException {
+ String res = doReplace(regex, subs, s, options);
+ w.write(res);
+ return !res.equals(s);
+ }
+}
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Rpm.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Rpm.java
new file mode 100644
index 00000000..b395a16c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Rpm.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.tools.ant.taskdefs.optional;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Map;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.taskdefs.PumpStreamHandler;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Invokes the rpm tool to build a Linux installation file.
+ *
+ */
+public class Rpm extends Task {
+
+ private static final String PATH1 = "PATH";
+ private static final String PATH2 = "Path";
+ private static final String PATH3 = "path";
+
+ /**
+ * the spec file
+ */
+ private String specFile;
+
+ /**
+ * the rpm top dir
+ */
+ private File topDir;
+
+ /**
+ * the rpm command to use
+ */
+ private String command = "-bb";
+
+ /**
+ * The executable to use for building the packages.
+ * @since Ant 1.6
+ */
+ private String rpmBuildCommand = null;
+
+ /**
+ * clean BUILD directory
+ */
+ private boolean cleanBuildDir = false;
+
+ /**
+ * remove spec file
+ */
+ private boolean removeSpec = false;
+
+ /**
+ * remove sources
+ */
+ private boolean removeSource = false;
+
+ /**
+ * the file to direct standard output from the command
+ */
+ private File output;
+
+ /**
+ * the file to direct standard error from the command
+ */
+ private File error;
+
+ /**
+ * Halt on error return value from rpm build.
+ */
+ private boolean failOnError = false;
+
+ /**
+ * Don't show output of RPM build command on console. This does not affect
+ * the printing of output and error messages to files.
+ */
+ private boolean quiet = false;
+
+ /**
+ * Execute the task
+ *
+ * @throws BuildException is there is a problem in the task execution.
+ */
+ public void execute() throws BuildException {
+
+ Commandline toExecute = new Commandline();
+
+ toExecute.setExecutable(rpmBuildCommand == null
+ ? guessRpmBuildCommand()
+ : rpmBuildCommand);
+ if (topDir != null) {
+ toExecute.createArgument().setValue("--define");
+ toExecute.createArgument().setValue("_topdir " + topDir);
+ }
+
+ toExecute.createArgument().setLine(command);
+
+ if (cleanBuildDir) {
+ toExecute.createArgument().setValue("--clean");
+ }
+ if (removeSpec) {
+ toExecute.createArgument().setValue("--rmspec");
+ }
+ if (removeSource) {
+ toExecute.createArgument().setValue("--rmsource");
+ }
+
+ toExecute.createArgument().setValue("SPECS/" + specFile);
+
+ ExecuteStreamHandler streamhandler = null;
+ OutputStream outputstream = null;
+ OutputStream errorstream = null;
+ if (error == null && output == null) {
+ if (!quiet) {
+ streamhandler = new LogStreamHandler(this, Project.MSG_INFO,
+ Project.MSG_WARN);
+ } else {
+ streamhandler = new LogStreamHandler(this, Project.MSG_DEBUG,
+ Project.MSG_DEBUG);
+ }
+ } else {
+ if (output != null) {
+ try {
+ BufferedOutputStream bos
+ = new BufferedOutputStream(new FileOutputStream(output));
+ outputstream = new PrintStream(bos);
+ } catch (IOException e) {
+ throw new BuildException(e, getLocation());
+ }
+ } else if (!quiet) {
+ outputstream = new LogOutputStream(this, Project.MSG_INFO);
+ } else {
+ outputstream = new LogOutputStream(this, Project.MSG_DEBUG);
+ }
+ if (error != null) {
+ try {
+ BufferedOutputStream bos
+ = new BufferedOutputStream(new FileOutputStream(error));
+ errorstream = new PrintStream(bos);
+ } catch (IOException e) {
+ throw new BuildException(e, getLocation());
+ }
+ } else if (!quiet) {
+ errorstream = new LogOutputStream(this, Project.MSG_WARN);
+ } else {
+ errorstream = new LogOutputStream(this, Project.MSG_DEBUG);
+ }
+ streamhandler = new PumpStreamHandler(outputstream, errorstream);
+ }
+
+ Execute exe = getExecute(toExecute, streamhandler);
+ try {
+ log("Building the RPM based on the " + specFile + " file");
+ int returncode = exe.execute();
+ if (Execute.isFailure(returncode)) {
+ String msg = "'" + toExecute.getExecutable()
+ + "' failed with exit code " + returncode;
+ if (failOnError) {
+ throw new BuildException(msg);
+ }
+ log(msg, Project.MSG_ERR);
+ }
+ } catch (IOException e) {
+ throw new BuildException(e, getLocation());
+ } finally {
+ FileUtils.close(outputstream);
+ FileUtils.close(errorstream);
+ }
+ }
+
+ /**
+ * The directory which will have the expected
+ * subdirectories, SPECS, SOURCES, BUILD, SRPMS ; optional.
+ * If this isn't specified,
+ * the <tt>baseDir</tt> value is used
+ *
+ * @param td the directory containing the normal RPM directories.
+ */
+ public void setTopDir(File td) {
+ this.topDir = td;
+ }
+
+ /**
+ * What command to issue to the rpm build tool; optional.
+ * The default is "-bb"
+ * @param c the command to use.
+ */
+ public void setCommand(String c) {
+ this.command = c;
+ }
+
+ /**
+ * The name of the spec File to use; required.
+ * @param sf the spec file name to use.
+ */
+ public void setSpecFile(String sf) {
+ if ((sf == null) || (sf.trim().length() == 0)) {
+ throw new BuildException("You must specify a spec file", getLocation());
+ }
+ this.specFile = sf;
+ }
+
+ /**
+ * Flag (optional, default=false) to remove
+ * the generated files in the BUILD directory
+ * @param cbd a <code>boolean</code> value.
+ */
+ public void setCleanBuildDir(boolean cbd) {
+ cleanBuildDir = cbd;
+ }
+
+ /**
+ * Flag (optional, default=false) to remove the spec file from SPECS
+ * @param rs a <code>boolean</code> value.
+ */
+ public void setRemoveSpec(boolean rs) {
+ removeSpec = rs;
+ }
+
+ /**
+ * Flag (optional, default=false)
+ * to remove the sources after the build.
+ * See the <tt>--rmsource</tt> option of rpmbuild.
+ * @param rs a <code>boolean</code> value.
+ */
+ public void setRemoveSource(boolean rs) {
+ removeSource = rs;
+ }
+
+ /**
+ * Optional file to save stdout to.
+ * @param output the file to save stdout to.
+ */
+ public void setOutput(File output) {
+ this.output = output;
+ }
+
+ /**
+ * Optional file to save stderr to
+ * @param error the file to save error output to.
+ */
+ public void setError(File error) {
+ this.error = error;
+ }
+
+ /**
+ * The executable to run when building; optional.
+ * The default is <code>rpmbuild</code>.
+ *
+ * @since Ant 1.6
+ * @param c the rpm build executable
+ */
+ public void setRpmBuildCommand(String c) {
+ this.rpmBuildCommand = c;
+ }
+
+ /**
+ * If <code>true</code>, stop the build process when the rpmbuild command
+ * exits with an error status.
+ * @param value <code>true</code> if it should halt, otherwise
+ * <code>false</code>. The default is <code>false</code>.
+ *
+ * @since Ant 1.6.3
+ */
+ public void setFailOnError(boolean value) {
+ failOnError = value;
+ }
+
+ /**
+ * If true, output from the RPM build command will only be logged to DEBUG.
+ * @param value <code>false</code> if output should be logged, otherwise
+ * <code>true</code>. The default is <code>false</code>.
+ *
+ * @since Ant 1.6.3
+ */
+ public void setQuiet(boolean value) {
+ quiet = value;
+ }
+
+ /**
+ * Checks whether <code>rpmbuild</code> is on the PATH and returns
+ * the absolute path to it - falls back to <code>rpm</code>
+ * otherwise.
+ *
+ * @return the command used to build RPM's
+ *
+ * @since 1.6
+ */
+ protected String guessRpmBuildCommand() {
+ Map/*<String, String>*/ env = Execute.getEnvironmentVariables();
+ String path = (String) env.get(PATH1);
+ if (path == null) {
+ path = (String) env.get(PATH2);
+ if (path == null) {
+ path = (String) env.get(PATH3);
+ }
+ }
+
+ if (path != null) {
+ Path p = new Path(getProject(), path);
+ String[] pElements = p.list();
+ for (int i = 0; i < pElements.length; i++) {
+ File f = new File(pElements[i],
+ "rpmbuild"
+ + (Os.isFamily("dos") ? ".exe" : ""));
+ if (f.canRead()) {
+ return f.getAbsolutePath();
+ }
+ }
+ }
+
+ return "rpm";
+ }
+
+ /**
+ * Get the execute object.
+ * @param toExecute the command line to use.
+ * @param streamhandler the stream handler to use.
+ * @return the execute object.
+ * @since Ant 1.6.3
+ */
+ protected Execute getExecute(Commandline toExecute,
+ ExecuteStreamHandler streamhandler) {
+ Execute exe = new Execute(streamhandler, null);
+
+ exe.setAntRun(getProject());
+ if (topDir == null) {
+ topDir = getProject().getBaseDir();
+ }
+ exe.setWorkingDirectory(topDir);
+
+ exe.setCommandline(toExecute.getCommandline());
+ return exe;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/SchemaValidate.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/SchemaValidate.java
new file mode 100644
index 00000000..e57d6d22
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/SchemaValidate.java
@@ -0,0 +1,529 @@
+/*
+ * 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.tools.ant.taskdefs.optional;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.XmlConstants;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.XMLReader;
+
+/**
+ * Validate XML Schema documents.
+ * This task validates XML schema documents. It requires an XML parser
+ * that handles the relevant SAX, Xerces or JAXP options.
+ *
+ * To resolve remote referencies, Ant may need its proxy set up, using the
+ * setproxy task.
+ *
+ * Hands off most of the work to its parent, {@link XMLValidateTask}
+ * @since Ant1.7
+ */
+
+public class SchemaValidate extends XMLValidateTask {
+
+ /** map of all declared schemas; we catch and complain about redefinitions */
+ private HashMap schemaLocations = new HashMap();
+
+ /** full checking of a schema */
+ private boolean fullChecking = true;
+
+ /**
+ * flag to disable DTD support. Best left enabled.
+ */
+ private boolean disableDTD = false;
+
+ /**
+ * default URL for nonamespace schemas
+ */
+ private SchemaLocation anonymousSchema;
+
+ // Error strings
+ /** SAX1 not supported */
+ public static final String ERROR_SAX_1 = "SAX1 parsers are not supported";
+
+ /** schema features not supported */
+ public static final String ERROR_NO_XSD_SUPPORT
+ = "Parser does not support Xerces or JAXP schema features";
+
+ /** too many default schemas */
+ public static final String ERROR_TOO_MANY_DEFAULT_SCHEMAS
+ = "Only one of defaultSchemaFile and defaultSchemaURL allowed";
+
+ /** unable to create parser */
+ public static final String ERROR_PARSER_CREATION_FAILURE
+ = "Could not create parser";
+
+ /** adding schema */
+ public static final String MESSAGE_ADDING_SCHEMA = "Adding schema ";
+
+ /** Duplicate declaration of schema */
+ public static final String ERROR_DUPLICATE_SCHEMA
+ = "Duplicate declaration of schema ";
+
+ /**
+ * Called by the project to let the task initialize properly. The default
+ * implementation is a no-op.
+ *
+ * @throws BuildException if something goes wrong with the build
+ */
+ public void init() throws BuildException {
+ super.init();
+ //validating
+ setLenient(false);
+ }
+
+ /**
+ * Turn on XSD support in Xerces.
+ * @return true on success, false on failure
+ */
+ public boolean enableXercesSchemaValidation() {
+ try {
+ setFeature(XmlConstants.FEATURE_XSD, true);
+ //set the schema source for the doc
+ setNoNamespaceSchemaProperty(XmlConstants.PROPERTY_NO_NAMESPACE_SCHEMA_LOCATION);
+ } catch (BuildException e) {
+ log(e.toString(), Project.MSG_VERBOSE);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * set nonamespace handling up for xerces or other parsers
+ * @param property name of the property to set
+ */
+ private void setNoNamespaceSchemaProperty(String property) {
+ String anonSchema = getNoNamespaceSchemaURL();
+ if (anonSchema != null) {
+ setProperty(property, anonSchema);
+ }
+ }
+
+ /**
+ * Set schema attributes in a JAXP 1.2 engine.
+ * @see <A href="http://java.sun.com/xml/jaxp/change-requests-11.html">
+ * JAXP 1.2 Approved CHANGES</A>
+ * @return true on success, false on failure
+ */
+ public boolean enableJAXP12SchemaValidation() {
+ try {
+ //enable XSD
+ setProperty(XmlConstants.FEATURE_JAXP12_SCHEMA_LANGUAGE, XmlConstants.URI_XSD);
+ //set the schema source for the doc
+ setNoNamespaceSchemaProperty(XmlConstants.FEATURE_JAXP12_SCHEMA_SOURCE);
+ } catch (BuildException e) {
+ log(e.toString(), Project.MSG_VERBOSE);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * add the schema
+ * @param location the schema location.
+ * @throws BuildException if there is no namespace, or if there already
+ * is a declaration of this schema with a different value
+ */
+ public void addConfiguredSchema(SchemaLocation location) {
+ log("adding schema " + location, Project.MSG_DEBUG);
+ location.validateNamespace();
+ SchemaLocation old = (SchemaLocation) schemaLocations.get(location.getNamespace());
+ if (old != null && !old.equals(location)) {
+ throw new BuildException(ERROR_DUPLICATE_SCHEMA + location);
+ }
+ schemaLocations.put(location.getNamespace(), location);
+ }
+
+ /**
+ * enable full schema checking. Slower but better.
+ * @param fullChecking a <code>boolean</code> value.
+ */
+ public void setFullChecking(boolean fullChecking) {
+ this.fullChecking = fullChecking;
+ }
+
+ /**
+ * create a schema location to hold the anonymous
+ * schema
+ */
+ protected void createAnonymousSchema() {
+ if (anonymousSchema == null) {
+ anonymousSchema = new SchemaLocation();
+ }
+ anonymousSchema.setNamespace("(no namespace)");
+ }
+
+ /**
+ * identify the URL of the default schema
+ * @param defaultSchemaURL the URL of the default schema.
+ */
+ public void setNoNamespaceURL(String defaultSchemaURL) {
+ createAnonymousSchema();
+ this.anonymousSchema.setUrl(defaultSchemaURL);
+ }
+
+ /**
+ * identify a file containing the default schema
+ * @param defaultSchemaFile the location of the default schema.
+ */
+ public void setNoNamespaceFile(File defaultSchemaFile) {
+ createAnonymousSchema();
+ this.anonymousSchema.setFile(defaultSchemaFile);
+ }
+
+ /**
+ * flag to disable DTD support.
+ * @param disableDTD a <code>boolean</code> value.
+ */
+ public void setDisableDTD(boolean disableDTD) {
+ this.disableDTD = disableDTD;
+ }
+
+ /**
+ * init the parser : load the parser class, and set features if necessary It
+ * is only after this that the reader is valid
+ *
+ * @throws BuildException if something went wrong
+ */
+ protected void initValidator() {
+ super.initValidator();
+ //validate the parser type
+ if (isSax1Parser()) {
+ throw new BuildException(ERROR_SAX_1);
+ }
+
+ //enable schema
+ //setFeature(XmlConstants.FEATURE_VALIDATION, false);
+ setFeature(XmlConstants.FEATURE_NAMESPACES, true);
+ if (!enableXercesSchemaValidation() && !enableJAXP12SchemaValidation()) {
+ //couldnt use the xerces or jaxp calls
+ throw new BuildException(ERROR_NO_XSD_SUPPORT);
+ }
+
+ //enable schema checking
+ setFeature(XmlConstants.FEATURE_XSD_FULL_VALIDATION, fullChecking);
+
+ //turn off DTDs if desired
+ setFeatureIfSupported(XmlConstants.FEATURE_DISALLOW_DTD, disableDTD);
+
+ //schema declarations go in next
+ addSchemaLocations();
+ }
+
+ /**
+ * Create a reader if the use of the class did not specify another one.
+ * The reason to not use {@link org.apache.tools.ant.util.JAXPUtils#getXMLReader()} was to
+ * create our own factory with our own options.
+ * @return a default XML parser
+ */
+ protected XMLReader createDefaultReader() {
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setValidating(true);
+ factory.setNamespaceAware(true);
+ XMLReader reader = null;
+ try {
+ SAXParser saxParser = factory.newSAXParser();
+ reader = saxParser.getXMLReader();
+ } catch (ParserConfigurationException e) {
+ throw new BuildException(ERROR_PARSER_CREATION_FAILURE, e);
+ } catch (SAXException e) {
+ throw new BuildException(ERROR_PARSER_CREATION_FAILURE, e);
+ }
+ return reader;
+ }
+
+ /**
+ * build a string list of all schema locations, then set the relevant
+ * property.
+ */
+ protected void addSchemaLocations() {
+ Iterator it = schemaLocations.values().iterator();
+ StringBuffer buffer = new StringBuffer();
+ int count = 0;
+ while (it.hasNext()) {
+ if (count > 0) {
+ buffer.append(' ');
+ }
+ SchemaLocation schemaLocation = (SchemaLocation) it.next();
+ String tuple = schemaLocation.getURIandLocation();
+ buffer.append(tuple);
+ log("Adding schema " + tuple, Project.MSG_VERBOSE);
+ count++;
+ }
+ if (count > 0) {
+ setProperty(XmlConstants.PROPERTY_SCHEMA_LOCATION, buffer.toString());
+ }
+
+ }
+
+ /**
+ * get the URL of the no namespace schema
+ * @return the schema URL
+ */
+ protected String getNoNamespaceSchemaURL() {
+ if (anonymousSchema == null) {
+ return null;
+ } else {
+ return anonymousSchema.getSchemaLocationURL();
+ }
+ }
+
+ /**
+ * set a feature if it is supported, log at verbose level if
+ * not
+ * @param feature the feature.
+ * @param value a <code>boolean</code> value.
+ */
+ protected void setFeatureIfSupported(String feature, boolean value) {
+ try {
+ getXmlReader().setFeature(feature, value);
+ } catch (SAXNotRecognizedException e) {
+ log("Not recognizied: " + feature, Project.MSG_VERBOSE);
+ } catch (SAXNotSupportedException e) {
+ log("Not supported: " + feature, Project.MSG_VERBOSE);
+ }
+ }
+
+ /**
+ * handler called on successful file validation.
+ *
+ * @param fileProcessed number of files processed.
+ */
+ protected void onSuccessfulValidation(int fileProcessed) {
+ log(fileProcessed + MESSAGE_FILES_VALIDATED, Project.MSG_VERBOSE);
+ }
+
+ /**
+ * representation of a schema location. This is a URI plus either a file or
+ * a url
+ */
+ public static class SchemaLocation {
+ private String namespace;
+
+ private File file;
+
+ private String url;
+
+ /** No namespace URI */
+ public static final String ERROR_NO_URI = "No namespace URI";
+
+ /** Both URL and File were given for schema */
+ public static final String ERROR_TWO_LOCATIONS
+ = "Both URL and File were given for schema ";
+
+ /** File not found */
+ public static final String ERROR_NO_FILE = "File not found: ";
+
+ /** Cannot make URL */
+ public static final String ERROR_NO_URL_REPRESENTATION
+ = "Cannot make a URL of ";
+
+ /** No location provided */
+ public static final String ERROR_NO_LOCATION
+ = "No file or URL supplied for the schema ";
+
+ /** No arg constructor */
+ public SchemaLocation() {
+ }
+
+ /**
+ * Get the namespace.
+ * @return the namespace.
+ */
+ public String getNamespace() {
+ return namespace;
+ }
+
+ /**
+ * set the namespace of this schema. Any URI
+ * @param namespace the namespace to use.
+ */
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ /**
+ * Get the file.
+ * @return the file containing the schema.
+ */
+ public File getFile() {
+ return file;
+ }
+
+ /**
+ * identify a file that contains this namespace's schema.
+ * The file must exist.
+ * @param file the file contains the schema.
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * The URL containing the schema.
+ * @return the URL string.
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * identify a URL that hosts the schema.
+ * @param url the URL string.
+ */
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ /**
+ * get the URL of the schema
+ * @return a URL to the schema
+ * @throws BuildException if not
+ */
+ public String getSchemaLocationURL() {
+ boolean hasFile = file != null;
+ boolean hasURL = isSet(url);
+ //error if both are empty, or both are set
+ if (!hasFile && !hasURL) {
+ throw new BuildException(ERROR_NO_LOCATION + namespace);
+ }
+ if (hasFile && hasURL) {
+ throw new BuildException(ERROR_TWO_LOCATIONS + namespace);
+ }
+ String schema = url;
+ if (hasFile) {
+ if (!file.exists()) {
+ throw new BuildException(ERROR_NO_FILE + file);
+ }
+
+ try {
+ schema = FileUtils.getFileUtils().getFileURL(file).toString();
+ } catch (MalformedURLException e) {
+ //this is almost implausible, but required handling
+ throw new BuildException(ERROR_NO_URL_REPRESENTATION + file, e);
+ }
+ }
+ return schema;
+ }
+
+ /**
+ * validate the fields then create a "uri location" string
+ *
+ * @return string of uri and location
+ * @throws BuildException if there is an error.
+ */
+ public String getURIandLocation() throws BuildException {
+ validateNamespace();
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(namespace);
+ buffer.append(' ');
+ buffer.append(getSchemaLocationURL());
+ return new String(buffer);
+ }
+
+ /**
+ * assert that a namespace is valid
+ * @throws BuildException if not
+ */
+ public void validateNamespace() {
+ if (!isSet(getNamespace())) {
+ throw new BuildException(ERROR_NO_URI);
+ }
+ }
+
+ /**
+ * check that a property is set
+ * @param property string to check
+ * @return true if it is not null or empty
+ */
+ private boolean isSet(String property) {
+ return property != null && property.length() != 0;
+ }
+
+ /**
+ * equality test checks namespace, location and filename. All must match,
+ * @param o object to compare against
+ * @return true iff the objects are considered equal in value
+ */
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof SchemaLocation)) {
+ return false;
+ }
+
+ final SchemaLocation schemaLocation = (SchemaLocation) o;
+
+ if (file != null ? !file.equals(schemaLocation.file) : schemaLocation.file != null) {
+ return false;
+ }
+ if (namespace != null ? !namespace.equals(schemaLocation.namespace)
+ : schemaLocation.namespace != null) {
+ return false;
+ }
+ if (url != null ? !url.equals(schemaLocation.url) : schemaLocation.url != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Generate a hashcode depending on the namespace, url and file name.
+ * @return the hashcode.
+ */
+ public int hashCode() {
+ int result;
+ // CheckStyle:MagicNumber OFF
+ result = (namespace != null ? namespace.hashCode() : 0);
+ result = 29 * result + (file != null ? file.hashCode() : 0);
+ result = 29 * result + (url != null ? url.hashCode() : 0);
+ // CheckStyle:MagicNumber OFF
+ return result;
+ }
+
+ /**
+ * Returns a string representation of the object for error messages
+ * and the like
+ * @return a string representation of the object.
+ */
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(namespace != null ? namespace : "(anonymous)");
+ buffer.append(' ');
+ buffer.append(url != null ? (url + " ") : "");
+ buffer.append(file != null ? file.getAbsolutePath() : "");
+ return buffer.toString();
+ }
+ } //SchemaLocation
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Script.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Script.java
new file mode 100644
index 00000000..2b8d9f65
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Script.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.tools.ant.taskdefs.optional;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.ScriptRunnerHelper;
+
+/**
+ * Executes a script.
+ *
+ * @ant.task name="script"
+ */
+public class Script extends Task {
+
+ private ScriptRunnerHelper helper = new ScriptRunnerHelper();
+
+ /**
+ * Set the project.
+ * @param project the project that this task belongs to.
+ */
+ public void setProject(Project project) {
+ super.setProject(project);
+ helper.setProjectComponent(this);
+ }
+
+ /**
+ * Run the script using the helper object.
+ *
+ * @exception BuildException if something goes wrong with the build
+ */
+ public void execute() throws BuildException {
+ helper.getScriptRunner().executeScript("ANT");
+ }
+
+ /**
+ * Defines the manager.
+ *
+ * @param manager the scripting manager.
+ */
+ public void setManager(String manager) {
+ helper.setManager(manager);
+ }
+
+ /**
+ * Defines the language (required).
+ *
+ * @param language the scripting language name for the script.
+ */
+ public void setLanguage(String language) {
+ helper.setLanguage(language);
+ }
+
+ /**
+ * Load the script from an external file ; optional.
+ *
+ * @param fileName the name of the file containing the script source.
+ */
+ public void setSrc(String fileName) {
+ helper.setSrc(new File(fileName));
+ }
+
+ /**
+ * Set the script text.
+ *
+ * @param text a component of the script text to be added.
+ */
+ public void addText(String text) {
+ helper.addText(text);
+ }
+
+ /**
+ * Set the classpath to be used when searching for classes and resources.
+ *
+ * @param classpath an Ant Path object containing the search path.
+ */
+ public void setClasspath(Path classpath) {
+ helper.setClasspath(classpath);
+ }
+
+ /**
+ * Classpath to be used when searching for classes and resources.
+ *
+ * @return an empty Path instance to be configured by Ant.
+ */
+ public Path createClasspath() {
+ return helper.createClasspath();
+ }
+
+ /**
+ * Set the classpath by reference.
+ *
+ * @param r a Reference to a Path instance to be used as the classpath
+ * value.
+ */
+ public void setClasspathRef(Reference r) {
+ helper.setClasspathRef(r);
+ }
+
+ /**
+ * Set the setbeans attribute.
+ * If this is true, &lt;script&gt; will create variables in the
+ * script instance for all
+ * properties, targets and references of the current project.
+ * It this is false, only the project and self variables will
+ * be set.
+ * The default is true.
+ * @param setBeans the value to set.
+ */
+ public void setSetBeans(boolean setBeans) {
+ helper.setSetBeans(setBeans);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java
new file mode 100644
index 00000000..8d9a44a6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java
@@ -0,0 +1,650 @@
+/*
+ * 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.tools.ant.taskdefs.optional;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.XSLTLiaison4;
+import org.apache.tools.ant.taskdefs.XSLTLogger;
+import org.apache.tools.ant.taskdefs.XSLTLoggerAware;
+import org.apache.tools.ant.taskdefs.XSLTProcess;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.XMLCatalog;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.URLProvider;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+/**
+ * Concrete liaison for XSLT processor implementing TraX. (ie JAXP 1.1)
+ *
+ * @since Ant 1.3
+ */
+public class TraXLiaison implements XSLTLiaison4, ErrorListener, XSLTLoggerAware {
+
+ /**
+ * Helper for transforming filenames to URIs.
+ *
+ * @since Ant 1.7
+ */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * The current <code>Project</code>
+ */
+ private Project project;
+
+ /**
+ * the name of the factory implementation class to use
+ * or null for default JAXP lookup.
+ */
+ private String factoryName = null;
+
+ /** The trax TransformerFactory */
+ private TransformerFactory tfactory = null;
+
+ /** stylesheet to use for transformation */
+ private Resource stylesheet;
+
+ private XSLTLogger logger;
+
+ /** possible resolver for publicIds */
+ private EntityResolver entityResolver;
+
+ /** transformer to use for processing files */
+ private Transformer transformer;
+
+ /** The In memory version of the stylesheet */
+ private Templates templates;
+
+ /**
+ * The modification time of the stylesheet from which the templates
+ * are read
+ */
+ private long templatesModTime;
+
+ /** possible resolver for URIs */
+ private URIResolver uriResolver;
+
+ /** transformer output properties */
+ private final Vector outputProperties = new Vector();
+
+ /** stylesheet parameters */
+ private final Hashtable<String, Object> params = new Hashtable<String, Object>();
+
+ /** factory attributes */
+ private final Vector attributes = new Vector();
+
+ /** whether to suppress warnings */
+ private boolean suppressWarnings = false;
+
+ /** optional trace configuration. */
+ private XSLTProcess.TraceConfiguration traceConfiguration = null;
+
+ /**
+ * Constructor for TraXLiaison.
+ * @throws Exception never
+ */
+ public TraXLiaison() throws Exception {
+ }
+
+ /**
+ * Set the stylesheet file.
+ * @param stylesheet a <code>File</code> value
+ * @throws Exception on error
+ */
+ public void setStylesheet(final File stylesheet) throws Exception {
+ final FileResource fr = new FileResource();
+ fr.setProject(project);
+ fr.setFile(stylesheet);
+ setStylesheet(fr);
+ }
+
+ /**
+ * Set the stylesheet file.
+ * @param stylesheet a {@link org.apache.tools.ant.types.Resource} value
+ * @throws Exception on error
+ */
+ public void setStylesheet(final Resource stylesheet) throws Exception {
+ if (this.stylesheet != null) {
+ // resetting the stylesheet - reset transformer
+ transformer = null;
+
+ // do we need to reset templates as well
+ if (!this.stylesheet.equals(stylesheet)
+ || (stylesheet.getLastModified() != templatesModTime)) {
+ templates = null;
+ }
+ }
+ this.stylesheet = stylesheet;
+ }
+
+ /**
+ * Transform an input file.
+ * @param infile the file to transform
+ * @param outfile the result file
+ * @throws Exception on error
+ */
+ public void transform(final File infile, final File outfile) throws Exception {
+ if (transformer == null) {
+ createTransformer();
+ }
+
+ InputStream fis = null;
+ OutputStream fos = null;
+ try {
+ fis = new BufferedInputStream(new FileInputStream(infile));
+ fos = new BufferedOutputStream(new FileOutputStream(outfile));
+ final StreamResult res = new StreamResult(fos);
+ // not sure what could be the need of this...
+ res.setSystemId(JAXPUtils.getSystemId(outfile));
+ final Source src = getSource(fis, infile);
+
+ // set parameters on each transformation, maybe something has changed
+ //(e.g. value of file name parameter)
+ setTransformationParameters();
+
+ transformer.transform(src, res);
+ } finally {
+ // make sure to close all handles, otherwise the garbage
+ // collector will close them...whenever possible and
+ // Windows may complain about not being able to delete files.
+ FileUtils.close(fis);
+ FileUtils.close(fos);
+ }
+ }
+
+ /**
+ * Get the source instance from the stream and id of the file.
+ * @param is the stream containing the stylesheet data.
+ * @param infile the file that will be used for the systemid.
+ * @return the configured source instance matching the stylesheet.
+ * @throws ParserConfigurationException if a parser cannot be created which
+ * satisfies the requested configuration.
+ * @throws SAXException in case of problem detected by the SAX parser.
+ */
+ private Source getSource(final InputStream is, final File infile)
+ throws ParserConfigurationException, SAXException {
+ // todo: is this comment still relevant ??
+ // FIXME: need to use a SAXSource as the source for the transform
+ // so we can plug in our own entity resolver
+ Source src = null;
+ if (entityResolver != null) {
+ if (getFactory().getFeature(SAXSource.FEATURE)) {
+ final SAXParserFactory spFactory = SAXParserFactory.newInstance();
+ spFactory.setNamespaceAware(true);
+ final XMLReader reader = spFactory.newSAXParser().getXMLReader();
+ reader.setEntityResolver(entityResolver);
+ src = new SAXSource(reader, new InputSource(is));
+ } else {
+ throw new IllegalStateException("xcatalog specified, but "
+ + "parser doesn't support SAX");
+ }
+ } else {
+ // WARN: Don't use the StreamSource(File) ctor. It won't work with
+ // xalan prior to 2.2 because of systemid bugs.
+ src = new StreamSource(is);
+ }
+ src.setSystemId(JAXPUtils.getSystemId(infile));
+ return src;
+ }
+
+ private Source getSource(final InputStream is, final Resource resource)
+ throws ParserConfigurationException, SAXException {
+ // todo: is this comment still relevant ??
+ // FIXME: need to use a SAXSource as the source for the transform
+ // so we can plug in our own entity resolver
+ Source src = null;
+ if (entityResolver != null) {
+ if (getFactory().getFeature(SAXSource.FEATURE)) {
+ final SAXParserFactory spFactory = SAXParserFactory.newInstance();
+ spFactory.setNamespaceAware(true);
+ final XMLReader reader = spFactory.newSAXParser().getXMLReader();
+ reader.setEntityResolver(entityResolver);
+ src = new SAXSource(reader, new InputSource(is));
+ } else {
+ throw new IllegalStateException("xcatalog specified, but "
+ + "parser doesn't support SAX");
+ }
+ } else {
+ // WARN: Don't use the StreamSource(File) ctor. It won't work with
+ // xalan prior to 2.2 because of systemid bugs.
+ src = new StreamSource(is);
+ }
+ // The line below is a hack: the system id must an URI, but it is not
+ // cleat to get the URI of an resource, so just set the name of the
+ // resource as a system id
+ src.setSystemId(resourceToURI(resource));
+ return src;
+ }
+
+ private String resourceToURI(final Resource resource) {
+ final FileProvider fp = resource.as(FileProvider.class);
+ if (fp != null) {
+ return FILE_UTILS.toURI(fp.getFile().getAbsolutePath());
+ }
+ final URLProvider up = resource.as(URLProvider.class);
+ if (up != null) {
+ final URL u = up.getURL();
+ return String.valueOf(u);
+ } else {
+ return resource.getName();
+ }
+ }
+
+ /**
+ * Read in templates from the stylesheet
+ */
+ private void readTemplates()
+ throws IOException, TransformerConfigurationException,
+ ParserConfigurationException, SAXException {
+
+ // Use a stream so that you can close it yourself quickly
+ // and avoid keeping the handle until the object is garbaged.
+ // (always keep control), otherwise you won't be able to delete
+ // the file quickly on windows.
+ InputStream xslStream = null;
+ try {
+ xslStream
+ = new BufferedInputStream(stylesheet.getInputStream());
+ templatesModTime = stylesheet.getLastModified();
+ final Source src = getSource(xslStream, stylesheet);
+ templates = getFactory().newTemplates(src);
+ } finally {
+ if (xslStream != null) {
+ xslStream.close();
+ }
+ }
+ }
+
+ /**
+ * Create a new transformer based on the liaison settings
+ * @throws Exception thrown if there is an error during creation.
+ * @see #setStylesheet(java.io.File)
+ * @see #addParam(java.lang.String, java.lang.String)
+ * @see #setOutputProperty(java.lang.String, java.lang.String)
+ */
+ private void createTransformer() throws Exception {
+ if (templates == null) {
+ readTemplates();
+ }
+
+ transformer = templates.newTransformer();
+
+ // configure the transformer...
+ transformer.setErrorListener(this);
+ if (uriResolver != null) {
+ transformer.setURIResolver(uriResolver);
+ }
+ final int size = outputProperties.size();
+ for (int i = 0; i < size; i++) {
+ final String[] pair = (String[]) outputProperties.elementAt(i);
+ transformer.setOutputProperty(pair[0], pair[1]);
+ }
+
+ if (traceConfiguration != null) {
+ if ("org.apache.xalan.transformer.TransformerImpl"
+ .equals(transformer.getClass().getName())) {
+ try {
+ final Class traceSupport =
+ Class.forName("org.apache.tools.ant.taskdefs.optional."
+ + "Xalan2TraceSupport", true,
+ Thread.currentThread()
+ .getContextClassLoader());
+ final XSLTTraceSupport ts =
+ (XSLTTraceSupport) traceSupport.newInstance();
+ ts.configureTrace(transformer, traceConfiguration);
+ } catch (final Exception e) {
+ final String msg = "Failed to enable tracing because of " + e;
+ if (project != null) {
+ project.log(msg, Project.MSG_WARN);
+ } else {
+ System.err.println(msg);
+ }
+ }
+ } else {
+ final String msg = "Not enabling trace support for transformer"
+ + " implementation" + transformer.getClass().getName();
+ if (project != null) {
+ project.log(msg, Project.MSG_WARN);
+ } else {
+ System.err.println(msg);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the parameters for the transformer.
+ */
+ private void setTransformationParameters() {
+ for (final Enumeration enumeration = params.keys();
+ enumeration.hasMoreElements();) {
+ final String name = (String) enumeration.nextElement();
+ final Object value = params.get(name);
+ transformer.setParameter(name, value);
+ }
+ }
+
+ /**
+ * return the Transformer factory associated to this liaison.
+ * @return the Transformer factory associated to this liaison.
+ * @throws BuildException thrown if there is a problem creating
+ * the factory.
+ * @see #setFactory(String)
+ * @since Ant 1.5.2
+ */
+ private TransformerFactory getFactory() throws BuildException {
+ if (tfactory != null) {
+ return tfactory;
+ }
+ // not initialized yet, so create the factory
+ if (factoryName == null) {
+ tfactory = TransformerFactory.newInstance();
+ } else {
+ try {
+ Class clazz = null;
+ try {
+ clazz =
+ Class.forName(factoryName, true,
+ Thread.currentThread()
+ .getContextClassLoader());
+ } catch (final ClassNotFoundException cnfe) {
+ final String msg = "Failed to load " + factoryName
+ + " via the configured classpath, will try"
+ + " Ant's classpath instead.";
+ if (logger != null) {
+ logger.log(msg);
+ } else if (project != null) {
+ project.log(msg, Project.MSG_WARN);
+ } else {
+ System.err.println(msg);
+ }
+ }
+
+ if (clazz == null) {
+ clazz = Class.forName(factoryName);
+ }
+ tfactory = (TransformerFactory) clazz.newInstance();
+ } catch (final Exception e) {
+ throw new BuildException(e);
+ }
+ }
+
+ try { // #51668, #52382
+ final Field _isNotSecureProcessing = tfactory.getClass().getDeclaredField("_isNotSecureProcessing");
+ _isNotSecureProcessing.setAccessible(true);
+ _isNotSecureProcessing.set(tfactory, Boolean.TRUE);
+ } catch (final Exception x) {
+ if (project != null) {
+ project.log(x.toString(), Project.MSG_DEBUG);
+ }
+ }
+
+ tfactory.setErrorListener(this);
+
+ // specific attributes for the transformer
+ final int size = attributes.size();
+ for (int i = 0; i < size; i++) {
+ final Object[] pair = (Object[]) attributes.elementAt(i);
+ tfactory.setAttribute((String) pair[0], pair[1]);
+ }
+
+ if (uriResolver != null) {
+ tfactory.setURIResolver(uriResolver);
+ }
+ return tfactory;
+ }
+
+
+ /**
+ * Set the factory name to use instead of JAXP default lookup.
+ * @param name the fully qualified class name of the factory to use
+ * or null for the default JAXP look up mechanism.
+ * @since Ant 1.6
+ */
+ public void setFactory(final String name) {
+ factoryName = name;
+ }
+
+ /**
+ * Set a custom attribute for the JAXP factory implementation.
+ * @param name the attribute name.
+ * @param value the value of the attribute, usually a boolean
+ * string or object.
+ * @since Ant 1.6
+ */
+ public void setAttribute(final String name, final Object value) {
+ final Object[] pair = new Object[]{name, value};
+ attributes.addElement(pair);
+ }
+
+ /**
+ * Set the output property for the current transformer.
+ * Note that the stylesheet must be set prior to calling
+ * this method.
+ * @param name the output property name.
+ * @param value the output property value.
+ * @since Ant 1.5
+ * @since Ant 1.5
+ */
+ public void setOutputProperty(final String name, final String value) {
+ final String[] pair = new String[]{name, value};
+ outputProperties.addElement(pair);
+ }
+
+ /**
+ * Set the class to resolve entities during the transformation.
+ * @param aResolver the resolver class.
+ */
+ public void setEntityResolver(final EntityResolver aResolver) {
+ entityResolver = aResolver;
+ }
+
+ /**
+ * Set the class to resolve URIs during the transformation
+ * @param aResolver a <code>EntityResolver</code> value
+ */
+ public void setURIResolver(final URIResolver aResolver) {
+ uriResolver = aResolver;
+ }
+
+ /**
+ * Add a parameter.
+ * @param name the name of the parameter
+ * @param value the value of the parameter
+ */
+ public void addParam(final String name, final String value) {
+ params.put(name, value);
+ }
+
+ /**
+ * Add a parameter.
+ * @param name the name of the parameter
+ * @param value the value of the parameter
+ * @since Ant 1.9.3
+ */
+ public void addParam(final String name, final Object value) {
+ params.put(name, value);
+ }
+
+ /**
+ * Set a logger.
+ * @param l a logger.
+ */
+ public void setLogger(final XSLTLogger l) {
+ logger = l;
+ }
+
+ /**
+ * Log an error.
+ * @param e the exception to log.
+ */
+ public void error(final TransformerException e) {
+ logError(e, "Error");
+ }
+
+ /**
+ * Log a fatal error.
+ * @param e the exception to log.
+ */
+ public void fatalError(final TransformerException e) {
+ logError(e, "Fatal Error");
+ throw new BuildException("Fatal error during transformation using " + stylesheet + ": " + e.getMessageAndLocation(), e);
+ }
+
+ /**
+ * Log a warning.
+ * @param e the exception to log.
+ */
+ public void warning(final TransformerException e) {
+ if (!suppressWarnings) {
+ logError(e, "Warning");
+ }
+ }
+
+ private void logError(final TransformerException e, final String type) {
+ if (logger == null) {
+ return;
+ }
+
+ final StringBuffer msg = new StringBuffer();
+ final SourceLocator locator = e.getLocator();
+ if (locator != null) {
+ final String systemid = locator.getSystemId();
+ if (systemid != null) {
+ String url = systemid;
+ if (url.startsWith("file:")) {
+ url = FileUtils.getFileUtils().fromURI(url);
+ }
+ msg.append(url);
+ } else {
+ msg.append("Unknown file");
+ }
+ final int line = locator.getLineNumber();
+ if (line != -1) {
+ msg.append(":");
+ msg.append(line);
+ final int column = locator.getColumnNumber();
+ if (column != -1) {
+ msg.append(":");
+ msg.append(column);
+ }
+ }
+ }
+ msg.append(": ");
+ msg.append(type);
+ msg.append("! ");
+ msg.append(e.getMessage());
+ if (e.getCause() != null) {
+ msg.append(" Cause: ");
+ msg.append(e.getCause());
+ }
+
+ logger.log(msg.toString());
+ }
+
+ // kept for backwards compatibility
+ /**
+ * @param file the filename to use for the systemid
+ * @return the systemid
+ * @deprecated since 1.5.x.
+ * Use org.apache.tools.ant.util.JAXPUtils#getSystemId instead.
+ */
+ @Deprecated
+ protected String getSystemId(final File file) {
+ return JAXPUtils.getSystemId(file);
+ }
+
+
+ /**
+ * Specific configuration for the TRaX liaison.
+ * @param xsltTask the XSLTProcess task instance from which this liaison
+ * is to be configured.
+ */
+ public void configure(final XSLTProcess xsltTask) {
+ project = xsltTask.getProject();
+ final XSLTProcess.Factory factory = xsltTask.getFactory();
+ if (factory != null) {
+ setFactory(factory.getName());
+
+ // configure factory attributes
+ for (final Enumeration attrs = factory.getAttributes();
+ attrs.hasMoreElements();) {
+ final XSLTProcess.Factory.Attribute attr =
+ (XSLTProcess.Factory.Attribute) attrs.nextElement();
+ setAttribute(attr.getName(), attr.getValue());
+ }
+ }
+
+ final XMLCatalog xmlCatalog = xsltTask.getXMLCatalog();
+ // use XMLCatalog as the entity resolver and URI resolver
+ if (xmlCatalog != null) {
+ setEntityResolver(xmlCatalog);
+ setURIResolver(xmlCatalog);
+ }
+
+
+ // configure output properties
+ for (final Enumeration props = xsltTask.getOutputProperties();
+ props.hasMoreElements();) {
+ final XSLTProcess.OutputProperty prop
+ = (XSLTProcess.OutputProperty) props.nextElement();
+ setOutputProperty(prop.getName(), prop.getValue());
+ }
+
+ suppressWarnings = xsltTask.getSuppressWarnings();
+
+ traceConfiguration = xsltTask.getTraceConfiguration();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XMLValidateTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XMLValidateTask.java
new file mode 100644
index 00000000..05c043df
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XMLValidateTask.java
@@ -0,0 +1,764 @@
+/*
+ * 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.tools.ant.taskdefs.optional;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Vector;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.DTDLocation;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.XMLCatalog;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.apache.tools.ant.util.XmlConstants;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.Parser;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.ParserAdapter;
+
+/**
+ * Checks XML files are valid (or only well formed). The
+ * task uses the SAX2 parser implementation provided by JAXP by default
+ * (probably the one that is used by Ant itself), but one can specify any
+ * SAX1/2 parser if needed.
+ *
+ */
+public class XMLValidateTask extends Task {
+
+ /**
+ * helper for path -> URI and URI -> path conversions.
+ */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ protected static final String INIT_FAILED_MSG =
+ "Could not start xml validation: ";
+
+ // ant task properties
+ // defaults
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected boolean failOnError = true;
+ protected boolean warn = true;
+ protected boolean lenient = false;
+ protected String readerClassName = null;
+
+ /** file to be validated */
+ protected File file = null;
+ /** sets of file to be validated */
+ protected Vector filesets = new Vector();
+ protected Path classpath;
+
+ /**
+ * the parser is viewed as a SAX2 XMLReader. If a SAX1 parser is specified,
+ * it's wrapped in an adapter that make it behave as a XMLReader.
+ * a more 'standard' way of doing this would be to use the JAXP1.1 SAXParser
+ * interface.
+ */
+ protected XMLReader xmlReader = null;
+ // XMLReader used to validation process
+ protected ValidatorErrorHandler errorHandler = new ValidatorErrorHandler();
+ // to report sax parsing errors
+ // CheckStyle:VisibilityModifier ON
+
+ /** The vector to store all attributes (features) to be set on the parser. **/
+ private Vector attributeList = new Vector();
+
+ /**
+ * List of properties.
+ */
+ private final Vector propertyList = new Vector();
+
+ private XMLCatalog xmlCatalog = new XMLCatalog();
+ /** Message for successful validation */
+ public static final String MESSAGE_FILES_VALIDATED
+ = " file(s) have been successfully validated.";
+
+ private AntClassLoader readerLoader = null;
+
+ /**
+ * Specify how parser error are to be handled.
+ * Optional, default is <code>true</code>.
+ * <p>
+ * If set to <code>true</code> (default), throw a buildException if the
+ * parser yields an error.
+ * @param fail if set to <code>false</code> do not fail on error
+ */
+ public void setFailOnError(boolean fail) {
+ failOnError = fail;
+ }
+
+ /**
+ * Specify how parser error are to be handled.
+ * <p>
+ * If set to <code>true</code> (default), log a warn message for each SAX warn event.
+ * @param bool if set to <code>false</code> do not send warnings
+ */
+ public void setWarn(boolean bool) {
+ warn = bool;
+ }
+
+ /**
+ * Specify whether the parser should be validating. Default
+ * is <code>true</code>.
+ * <p>
+ * If set to false, the validation will fail only if the parsed document
+ * is not well formed XML.
+ * <p>
+ * this option is ignored if the specified class
+ * with {@link #setClassName(String)} is not a SAX2 XMLReader.
+ * @param bool if set to <code>false</code> only fail on malformed XML
+ */
+ public void setLenient(boolean bool) {
+ lenient = bool;
+ }
+
+ /**
+ * Specify the class name of the SAX parser to be used. (optional)
+ * @param className should be an implementation of SAX2
+ * <code>org.xml.sax.XMLReader</code> or SAX2 <code>org.xml.sax.Parser</code>.
+ * <p> if className is an implementation of
+ * <code>org.xml.sax.Parser</code>, {@link #setLenient(boolean)},
+ * will be ignored.
+ * <p> if not set, the default will be used.
+ * @see org.xml.sax.XMLReader
+ * @see org.xml.sax.Parser
+ */
+ public void setClassName(String className) {
+ readerClassName = className;
+ }
+
+ /**
+ * Specify the classpath to be searched to load the parser (optional)
+ * @param classpath the classpath to load the parser
+ */
+ public void setClasspath(Path classpath) {
+ if (this.classpath == null) {
+ this.classpath = classpath;
+ } else {
+ this.classpath.append(classpath);
+ }
+ }
+
+ /**
+ * @see #setClasspath
+ * @return the classpath created
+ */
+ public Path createClasspath() {
+ if (this.classpath == null) {
+ this.classpath = new Path(getProject());
+ }
+ return this.classpath.createPath();
+ }
+
+ /**
+ * Where to find the parser class; optional.
+ * @see #setClasspath
+ * @param r reference to a classpath defined elsewhere
+ */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * specify the file to be checked; optional.
+ * @param file the file to be checked
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * add an XMLCatalog as a nested element; optional.
+ * @param catalog XMLCatalog to use
+ */
+ public void addConfiguredXMLCatalog(XMLCatalog catalog) {
+ xmlCatalog.addConfiguredXMLCatalog(catalog);
+ }
+
+ /**
+ * specify a set of file to be checked
+ * @param set the fileset to check
+ */
+ public void addFileset(FileSet set) {
+ filesets.addElement(set);
+ }
+
+ /**
+ * Add an attribute nested element. This is used for setting arbitrary
+ * features of the SAX parser.
+ * Valid attributes
+ * <a href=
+ * "http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html#package_description"
+ * >include</a>
+ * @return attribute created
+ * @since ant1.6
+ */
+ public Attribute createAttribute() {
+ final Attribute feature = new Attribute();
+ attributeList.addElement(feature);
+ return feature;
+ }
+
+ /**
+ * Creates a property.
+ *
+ * @return a property.
+ * @since ant 1.6.2
+ */
+ public Property createProperty() {
+ final Property prop = new Property();
+ propertyList.addElement(prop);
+ return prop;
+ }
+
+ /**
+ * Called by the project to let the task initialize properly.
+ *
+ * @exception BuildException if something goes wrong with the build
+ */
+ public void init() throws BuildException {
+ super.init();
+ xmlCatalog.setProject(getProject());
+ }
+
+ /**
+ * Create a DTD location record; optional.
+ * This stores the location of a DTD. The DTD is identified
+ * by its public Id.
+ * @return created DTD location
+ */
+ public DTDLocation createDTD() {
+ DTDLocation dtdLocation = new DTDLocation();
+ xmlCatalog.addDTD(dtdLocation);
+ return dtdLocation;
+ }
+ /**
+ * accessor to the xmlCatalog used in the task
+ * @return xmlCatalog reference
+ */
+ protected EntityResolver getEntityResolver() {
+ return xmlCatalog;
+ }
+
+ /**
+ * get the XML reader. Non-null only after {@link #initValidator()}.
+ * If the reader is an instance of {@link ParserAdapter} then
+ * the parser is a SAX1 parser, and you cannot call
+ * {@link #setFeature(String, boolean)} or {@link #setProperty(String, String)}
+ * on it.
+ * @return the XML reader or null.
+ */
+ protected XMLReader getXmlReader() {
+ return xmlReader;
+ }
+
+ /**
+ * execute the task
+ * @throws BuildException if <code>failonerror</code> is true and an error happens
+ */
+ public void execute() throws BuildException {
+ try {
+ int fileProcessed = 0;
+ if (file == null && (filesets.size() == 0)) {
+ throw new BuildException(
+ "Specify at least one source - " + "a file or a fileset.");
+ }
+
+
+
+ if (file != null) {
+ if (file.exists() && file.canRead() && file.isFile()) {
+ doValidate(file);
+ fileProcessed++;
+ } else {
+ String errorMsg = "File " + file + " cannot be read";
+ if (failOnError) {
+ throw new BuildException(errorMsg);
+ } else {
+ log(errorMsg, Project.MSG_ERR);
+ }
+ }
+ }
+
+ final int size = filesets.size();
+ for (int i = 0; i < size; i++) {
+
+ FileSet fs = (FileSet) filesets.elementAt(i);
+ DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+ String[] files = ds.getIncludedFiles();
+
+ for (int j = 0; j < files.length; j++) {
+ File srcFile = new File(fs.getDir(getProject()), files[j]);
+ doValidate(srcFile);
+ fileProcessed++;
+ }
+ }
+ onSuccessfulValidation(fileProcessed);
+ } finally {
+ cleanup();
+ }
+ }
+
+ /**
+ * handler called on successful file validation.
+ * @param fileProcessed number of files processed.
+ */
+ protected void onSuccessfulValidation(int fileProcessed) {
+ log(fileProcessed + MESSAGE_FILES_VALIDATED);
+ }
+
+ /**
+ * init the parser :
+ * load the parser class, and set features if necessary
+ * It is only after this that the reader is valid
+ * @throws BuildException if something went wrong
+ */
+ protected void initValidator() {
+
+ xmlReader = createXmlReader();
+
+ xmlReader.setEntityResolver(getEntityResolver());
+ xmlReader.setErrorHandler(errorHandler);
+
+ if (!isSax1Parser()) {
+ // turn validation on
+ if (!lenient) {
+ setFeature(XmlConstants.FEATURE_VALIDATION, true);
+ }
+ // set the feature from the attribute list
+ final int attSize = attributeList.size();
+ for (int i = 0; i < attSize; i++) {
+ Attribute feature = (Attribute) attributeList.elementAt(i);
+ setFeature(feature.getName(), feature.getValue());
+
+ }
+ // Sets properties
+ final int propSize = propertyList.size();
+ for (int i = 0; i < propSize; i++) {
+ final Property prop = (Property) propertyList.elementAt(i);
+ setProperty(prop.getName(), prop.getValue());
+ }
+ }
+ }
+
+ /**
+ * test that returns true if we are using a SAX1 parser.
+ * @return true when a SAX1 parser is in use
+ */
+ protected boolean isSax1Parser() {
+ return (xmlReader instanceof ParserAdapter);
+ }
+
+ /**
+ * create the XML reader.
+ * This is one by instantiating anything specified by {@link #readerClassName},
+ * falling back to a default reader if not.
+ * If the returned reader is an instance of {@link ParserAdapter} then
+ * we have created and wrapped a SAX1 parser.
+ * @return the new XMLReader.
+ */
+ protected XMLReader createXmlReader() {
+ Object reader = null;
+ if (readerClassName == null) {
+ reader = createDefaultReaderOrParser();
+ } else {
+
+ Class readerClass = null;
+ try {
+ // load the parser class
+ if (classpath != null) {
+ readerLoader = getProject().createClassLoader(classpath);
+ readerClass = Class.forName(readerClassName, true,
+ readerLoader);
+ } else {
+ readerClass = Class.forName(readerClassName);
+ }
+
+ reader = readerClass.newInstance();
+ } catch (ClassNotFoundException e) {
+ throw new BuildException(INIT_FAILED_MSG + readerClassName, e);
+ } catch (InstantiationException e) {
+ throw new BuildException(INIT_FAILED_MSG + readerClassName, e);
+ } catch (IllegalAccessException e) {
+ throw new BuildException(INIT_FAILED_MSG + readerClassName, e);
+ }
+ }
+
+ // then check it implements XMLReader
+ XMLReader newReader;
+ if (reader instanceof XMLReader) {
+ newReader = (XMLReader) reader;
+ log(
+ "Using SAX2 reader " + reader.getClass().getName(),
+ Project.MSG_VERBOSE);
+ } else {
+
+ // see if it is a SAX1 Parser
+ if (reader instanceof Parser) {
+ newReader = new ParserAdapter((Parser) reader);
+ log(
+ "Using SAX1 parser " + reader.getClass().getName(),
+ Project.MSG_VERBOSE);
+ } else {
+ throw new BuildException(
+ INIT_FAILED_MSG
+ + reader.getClass().getName()
+ + " implements nor SAX1 Parser nor SAX2 XMLReader.");
+ }
+ }
+ return newReader;
+ }
+
+ /**
+ * Cleans up resources.
+ *
+ * @since Ant 1.8.0
+ */
+ protected void cleanup() {
+ if (readerLoader != null) {
+ readerLoader.cleanup();
+ readerLoader = null;
+ }
+ }
+
+ /**
+ * Returns a SAX-based XMLReader or a SAX-based Parser.
+ * @return reader or parser
+ */
+ private Object createDefaultReaderOrParser() {
+ Object reader;
+ try {
+ reader = createDefaultReader();
+ } catch (BuildException exc) {
+ reader = JAXPUtils.getParser();
+ }
+ return reader;
+ }
+
+ /**
+ * Create a reader if the use of the class did not specify another one.
+ * If a BuildException is thrown, the caller may revert to an alternate
+ * reader.
+ * @return a new reader.
+ * @throws BuildException if something went wrong
+ */
+ protected XMLReader createDefaultReader() {
+ return JAXPUtils.getXMLReader();
+ }
+
+ /**
+ * Set a feature on the parser.
+ * @param feature the name of the feature to set
+ * @param value the value of the feature
+ * @throws BuildException if the feature was not supported
+ */
+ protected void setFeature(String feature, boolean value)
+ throws BuildException {
+ log("Setting feature " + feature + "=" + value, Project.MSG_DEBUG);
+ try {
+ xmlReader.setFeature(feature, value);
+ } catch (SAXNotRecognizedException e) {
+ throw new BuildException(
+ "Parser "
+ + xmlReader.getClass().getName()
+ + " doesn't recognize feature "
+ + feature,
+ e,
+ getLocation());
+ } catch (SAXNotSupportedException e) {
+ throw new BuildException(
+ "Parser "
+ + xmlReader.getClass().getName()
+ + " doesn't support feature "
+ + feature,
+ e,
+ getLocation());
+ }
+ }
+
+ /**
+ * Sets a property.
+ *
+ * @param name a property name
+ * @param value a property value.
+ * @throws BuildException if an error occurs.
+ * @throws BuildException if the property was not supported
+ */
+ protected void setProperty(String name, String value) throws BuildException {
+ // Validates property
+ if (name == null || value == null) {
+ throw new BuildException("Property name and value must be specified.");
+ }
+
+ try {
+ xmlReader.setProperty(name, value);
+ } catch (SAXNotRecognizedException e) {
+ throw new BuildException(
+ "Parser "
+ + xmlReader.getClass().getName()
+ + " doesn't recognize property "
+ + name,
+ e,
+ getLocation());
+ } catch (SAXNotSupportedException e) {
+ throw new BuildException(
+ "Parser "
+ + xmlReader.getClass().getName()
+ + " doesn't support property "
+ + name,
+ e,
+ getLocation());
+ }
+ }
+
+ /**
+ * parse the file
+ * @param afile the file to validate.
+ * @return true if the file validates.
+ */
+ protected boolean doValidate(File afile) {
+ //for every file, we have a new instance of the validator
+ initValidator();
+ boolean result = true;
+ try {
+ log("Validating " + afile.getName() + "... ", Project.MSG_VERBOSE);
+ errorHandler.init(afile);
+ InputSource is = new InputSource(new FileInputStream(afile));
+ String uri = FILE_UTILS.toURI(afile.getAbsolutePath());
+ is.setSystemId(uri);
+ xmlReader.parse(is);
+ } catch (SAXException ex) {
+ log("Caught when validating: " + ex.toString(), Project.MSG_DEBUG);
+ if (failOnError) {
+ throw new BuildException(
+ "Could not validate document " + afile);
+ }
+ log("Could not validate document " + afile + ": " + ex.toString());
+ result = false;
+ } catch (IOException ex) {
+ throw new BuildException(
+ "Could not validate document " + afile,
+ ex);
+ }
+ if (errorHandler.getFailure()) {
+ if (failOnError) {
+ throw new BuildException(
+ afile + " is not a valid XML document.");
+ }
+ result = false;
+ log(afile + " is not a valid XML document", Project.MSG_ERR);
+ }
+ return result;
+ }
+
+ /**
+ * ValidatorErrorHandler role :
+ * <ul>
+ * <li> log SAX parse exceptions,
+ * <li> remember if an error occurred
+ * </ul>
+ */
+ protected class ValidatorErrorHandler implements ErrorHandler {
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected File currentFile = null;
+ protected String lastErrorMessage = null;
+ protected boolean failed = false;
+ // CheckStyle:VisibilityModifier ON
+ /**
+ * initialises the class
+ * @param file file used
+ */
+ public void init(File file) {
+ currentFile = file;
+ failed = false;
+ }
+ /**
+ * did an error happen during last parsing ?
+ * @return did an error happen during last parsing ?
+ */
+ public boolean getFailure() {
+ return failed;
+ }
+
+ /**
+ * record a fatal error
+ * @param exception the fatal error
+ */
+ public void fatalError(SAXParseException exception) {
+ failed = true;
+ doLog(exception, Project.MSG_ERR);
+ }
+ /**
+ * receive notification of a recoverable error
+ * @param exception the error
+ */
+ public void error(SAXParseException exception) {
+ failed = true;
+ doLog(exception, Project.MSG_ERR);
+ }
+ /**
+ * receive notification of a warning
+ * @param exception the warning
+ */
+ public void warning(SAXParseException exception) {
+ // depending on implementation, XMLReader can yield hips of warning,
+ // only output then if user explicitly asked for it
+ if (warn) {
+ doLog(exception, Project.MSG_WARN);
+ }
+ }
+
+ private void doLog(SAXParseException e, int logLevel) {
+
+ log(getMessage(e), logLevel);
+ }
+
+ private String getMessage(SAXParseException e) {
+ String sysID = e.getSystemId();
+ if (sysID != null) {
+ String name = sysID;
+ if (sysID.startsWith("file:")) {
+ try {
+ name = FILE_UTILS.fromURI(sysID);
+ } catch (Exception ex) {
+ // if this is not a valid file: just use the uri
+ }
+ }
+ int line = e.getLineNumber();
+ int col = e.getColumnNumber();
+ return name
+ + (line == -1
+ ? ""
+ : (":" + line + (col == -1 ? "" : (":" + col))))
+ + ": "
+ + e.getMessage();
+ }
+ return e.getMessage();
+ }
+ }
+
+ /**
+ * The class to create to set a feature of the parser.
+ * @since ant1.6
+ */
+ public static class Attribute {
+ /** The name of the attribute to set.
+ *
+ * Valid attributes <a href=
+ * "http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html#package_description"
+ * >include.</a>
+ */
+ private String attributeName = null;
+
+ /**
+ * The value of the feature.
+ **/
+ private boolean attributeValue;
+
+ /**
+ * Set the feature name.
+ * @param name the name to set
+ */
+ public void setName(String name) {
+ attributeName = name;
+ }
+ /**
+ * Set the feature value to true or false.
+ * @param value feature value
+ */
+ public void setValue(boolean value) {
+ attributeValue = value;
+ }
+
+ /**
+ * Gets the attribute name.
+ * @return the feature name
+ */
+ public String getName() {
+ return attributeName;
+ }
+
+ /**
+ * Gets the attribute value.
+ * @return the feature value
+ */
+ public boolean getValue() {
+ return attributeValue;
+ }
+ }
+
+ /**
+ * A Parser property.
+ * See <a href="http://xml.apache.org/xerces-j/properties.html">
+ * XML parser properties</a> for usable properties
+ * @since ant 1.6.2
+ */
+ public static final class Property {
+
+ private String name;
+ private String value;
+ /**
+ * accessor to the name of the property
+ * @return name of the property
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * setter for the name of the property
+ * @param name name of the property
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * getter for the value of the property
+ * @return value of the property
+ */
+ public String getValue() {
+ return value;
+ }
+ /**
+ * sets the value of the property
+ * @param value value of the property
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ } // Property
+
+
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XSLTTraceSupport.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XSLTTraceSupport.java
new file mode 100644
index 00000000..8b684d83
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/XSLTTraceSupport.java
@@ -0,0 +1,32 @@
+/*
+ * 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.tools.ant.taskdefs.optional;
+
+import javax.xml.transform.Transformer;
+
+import org.apache.tools.ant.taskdefs.XSLTProcess;
+
+/**
+ * Sets up trace support for a given transformer.
+ *
+ * @since Ant 1.8.0
+ */
+public interface XSLTTraceSupport {
+ void configureTrace(Transformer t, XSLTProcess.TraceConfiguration conf);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Xalan2TraceSupport.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Xalan2TraceSupport.java
new file mode 100644
index 00000000..e5da0185
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/Xalan2TraceSupport.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.tools.ant.taskdefs.optional;
+
+import java.io.PrintWriter;
+import java.util.TooManyListenersException;
+
+import javax.xml.transform.Transformer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.XSLTProcess;
+import org.apache.xalan.trace.PrintTraceListener;
+import org.apache.xalan.transformer.TransformerImpl;
+
+/**
+ * Sets up trace support for a given transformer.
+ *
+ * @since Ant 1.8.0
+ */
+public class Xalan2TraceSupport implements XSLTTraceSupport {
+ public void configureTrace(final Transformer t,
+ final XSLTProcess.TraceConfiguration conf) {
+ if (t instanceof TransformerImpl && conf != null) {
+ final PrintWriter w = new PrintWriter(conf.getOutputStream(), false);
+ final PrintTraceListener tl = new PrintTraceListener(w);
+ tl.m_traceElements = conf.getElements();
+ tl.m_traceExtension = conf.getExtension();
+ tl.m_traceGeneration = conf.getGeneration();
+ tl.m_traceSelection = conf.getSelection();
+ tl.m_traceTemplates = conf.getTemplates();
+ try {
+ ((TransformerImpl) t).getTraceManager().addTraceListener(tl);
+ } catch (final TooManyListenersException tml) {
+ throw new BuildException(tml);
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheck.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheck.java
new file mode 100644
index 00000000..f6a94b52
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheck.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.tools.ant.taskdefs.optional.ccm;
+
+
+import java.io.File;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.FileSet;
+
+
+/**
+ * Class common to all check commands (checkout, checkin,checkin default task);
+ * @ant.task ignore="true"
+ */
+public class CCMCheck extends Continuus {
+
+ private File file = null;
+ private String comment = null;
+ private String task = null;
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ protected Vector filesets = new Vector();
+
+ // CheckStyle:VisibilityModifier ON
+
+ /** Constructor for CCMCheck. */
+ public CCMCheck() {
+ super();
+ }
+
+ /**
+ * Get the value of file.
+ * @return value of file.
+ */
+ public File getFile() {
+ return file;
+ }
+
+ /**
+ * Sets the path to the file that the command will operate on.
+ * @param v Value to assign to file.
+ */
+ public void setFile(File v) {
+ log("working file " + v, Project.MSG_VERBOSE);
+ this.file = v;
+ }
+
+ /**
+ * Get the value of comment.
+ * @return value of comment.
+ */
+ public String getComment() {
+ return comment;
+ }
+
+ /**
+ * Specifies a comment.
+ * @param v Value to assign to comment.
+ */
+ public void setComment(String v) {
+ this.comment = v;
+ }
+
+
+ /**
+ * Get the value of task.
+ * @return value of task.
+ */
+ public String getTask() {
+ return task;
+ }
+
+ /**
+ * Specifies the task number used to check
+ * in the file (may use 'default').
+ * @param v Value to assign to task.
+ */
+ public void setTask(String v) {
+ this.task = v;
+ }
+
+
+ /**
+ * Adds a set of files to copy.
+ * @param set the set of files
+ */
+ public void addFileset(FileSet set) {
+ filesets.addElement(set);
+ }
+
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute ccm and then calls Exec's run method
+ * to execute the command line.
+ * </p>
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+
+ if (file == null && filesets.size() == 0) {
+ throw new BuildException(
+ "Specify at least one source - a file or a fileset.");
+ }
+
+ if (file != null && file.exists() && file.isDirectory()) {
+ throw new BuildException("CCMCheck cannot be generated for directories");
+ }
+
+ if (file != null && filesets.size() > 0) {
+ throw new BuildException("Choose between file and fileset !");
+ }
+
+ if (getFile() != null) {
+ doit();
+ return;
+ }
+
+ int sizeofFileSet = filesets.size();
+ for (int i = 0; i < sizeofFileSet; i++) {
+ FileSet fs = (FileSet) filesets.elementAt(i);
+ DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+ String[] srcFiles = ds.getIncludedFiles();
+ for (int j = 0; j < srcFiles.length; j++) {
+ File src = new File(fs.getDir(getProject()), srcFiles[j]);
+ setFile(src);
+ doit();
+ }
+ }
+ }
+
+ /**
+ * check the file given by getFile().
+ */
+ private void doit() {
+ Commandline commandLine = new Commandline();
+
+ // build the command line from what we got the format is
+ // ccm co /t .. files
+ // as specified in the CCM.EXE help
+
+ commandLine.setExecutable(getCcmCommand());
+ commandLine.createArgument().setValue(getCcmAction());
+
+ checkOptions(commandLine);
+
+ int result = run(commandLine);
+ if (Execute.isFailure(result)) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ if (getComment() != null) {
+ cmd.createArgument().setValue(FLAG_COMMENT);
+ cmd.createArgument().setValue(getComment());
+ }
+
+ if (getTask() != null) {
+ cmd.createArgument().setValue(FLAG_TASK);
+ cmd.createArgument().setValue(getTask());
+ }
+
+ if (getFile() != null) {
+ cmd.createArgument().setValue(file.getAbsolutePath());
+ }
+ }
+
+ /**
+ * -comment flag -- comment to attach to the file
+ */
+ public static final String FLAG_COMMENT = "/comment";
+
+ /**
+ * -task flag -- associate checkout task with task
+ */
+ public static final String FLAG_TASK = "/task";
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckin.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckin.java
new file mode 100644
index 00000000..ff7472c9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckin.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.tools.ant.taskdefs.optional.ccm;
+
+import java.util.Date;
+
+/**
+ * Performs Continuus checkin command.
+ *
+ */
+public class CCMCheckin extends CCMCheck {
+
+ /**
+ * Default constructor - setup checkin command
+ */
+ public CCMCheckin() {
+ super();
+ setCcmAction(COMMAND_CHECKIN);
+ setComment("Checkin " + new Date());
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckinDefault.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckinDefault.java
new file mode 100644
index 00000000..2fe2a2a1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckinDefault.java
@@ -0,0 +1,38 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ccm;
+
+/**
+ * Performs Continuus Checkin Default task command.
+ *
+ * @ant.task name="ccmcheckintask" category="scm"
+ */
+public class CCMCheckinDefault extends CCMCheck {
+
+ /** Constructor for CCMCheckinDefault. */
+ public CCMCheckinDefault() {
+ super();
+ setCcmAction(COMMAND_CHECKIN);
+ setTask(DEFAULT_TASK);
+ }
+
+ /** The default task */
+ public static final String DEFAULT_TASK = "default";
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckout.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckout.java
new file mode 100644
index 00000000..44bbc6bf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckout.java
@@ -0,0 +1,35 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ccm;
+
+/**
+ * Performs Continuus checkout command.
+ *
+ */
+public class CCMCheckout extends CCMCheck {
+
+ /**
+ * default constructor
+ */
+ public CCMCheckout() {
+ super();
+ setCcmAction(COMMAND_CHECKOUT);
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCreateTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCreateTask.java
new file mode 100644
index 00000000..cda13a5c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCreateTask.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.tools.ant.taskdefs.optional.ccm;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+
+
+/**
+ * Creates new Continuus ccm task and sets it as the default.
+ *
+ * @ant.task name="ccmcreatetask" category="scm"
+ */
+public class CCMCreateTask extends Continuus implements ExecuteStreamHandler {
+
+ private String comment = null;
+ private String platform = null;
+ private String resolver = null;
+ private String release = null;
+ private String subSystem = null;
+ private String task = null;
+
+ /**
+ * Constructor for CCMCreateTask.
+ */
+ public CCMCreateTask() {
+ super();
+ setCcmAction(COMMAND_CREATE_TASK);
+ }
+
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute ccm and then calls Exec's run method
+ * to execute the command line.
+ * </p>
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ int result = 0;
+
+ // build the command line from what we got the format
+ // as specified in the CCM.EXE help
+ commandLine.setExecutable(getCcmCommand());
+ commandLine.createArgument().setValue(getCcmAction());
+
+ checkOptions(commandLine);
+
+ result = run(commandLine, this);
+ if (Execute.isFailure(result)) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+
+ //create task ok, set this task as the default one
+ Commandline commandLine2 = new Commandline();
+ commandLine2.setExecutable(getCcmCommand());
+ commandLine2.createArgument().setValue(COMMAND_DEFAULT_TASK);
+ commandLine2.createArgument().setValue(getTask());
+
+ log(commandLine.describeCommand(), Project.MSG_DEBUG);
+
+ result = run(commandLine2);
+ if (result != 0) {
+ String msg = "Failed executing: " + commandLine2.toString();
+ throw new BuildException(msg, getLocation());
+ }
+
+ }
+
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ if (getComment() != null) {
+ cmd.createArgument().setValue(FLAG_COMMENT);
+ cmd.createArgument().setValue("\"" + getComment() + "\"");
+ }
+
+ if (getPlatform() != null) {
+ cmd.createArgument().setValue(FLAG_PLATFORM);
+ cmd.createArgument().setValue(getPlatform());
+ } // end of if ()
+
+ if (getResolver() != null) {
+ cmd.createArgument().setValue(FLAG_RESOLVER);
+ cmd.createArgument().setValue(getResolver());
+ } // end of if ()
+
+ if (getSubSystem() != null) {
+ cmd.createArgument().setValue(FLAG_SUBSYSTEM);
+ cmd.createArgument().setValue("\"" + getSubSystem() + "\"");
+ } // end of if ()
+
+ if (getRelease() != null) {
+ cmd.createArgument().setValue(FLAG_RELEASE);
+ cmd.createArgument().setValue(getRelease());
+ } // end of if ()
+ }
+
+
+ /**
+ * Get the value of comment.
+ * @return value of comment.
+ */
+ public String getComment() {
+ return comment;
+ }
+
+ /**
+ * Specifies a comment.
+ *
+ * @param v Value to assign to comment.
+ */
+ public void setComment(String v) {
+ this.comment = v;
+ }
+
+
+ /**
+ * Get the value of platform.
+ * @return value of platform.
+ */
+ public String getPlatform() {
+ return platform;
+ }
+
+ /**
+ * Specifies the target platform.
+ *
+ * @param v Value to assign to platform.
+ */
+ public void setPlatform(String v) {
+ this.platform = v;
+ }
+
+
+ /**
+ * Get the value of resolver.
+ * @return value of resolver.
+ */
+ public String getResolver() {
+ return resolver;
+ }
+
+ /**
+ * Specifies the resolver.
+ *
+ * @param v Value to assign to resolver.
+ */
+ public void setResolver(String v) {
+ this.resolver = v;
+ }
+
+
+ /**
+ * Get the value of release.
+ * @return value of release.
+ */
+ public String getRelease() {
+ return release;
+ }
+
+ /**
+ * Specify the CCM release.
+ *
+ * @param v Value to assign to release.
+ */
+ public void setRelease(String v) {
+ this.release = v;
+ }
+
+ /**
+ * Get the value of subSystem.
+ * @return value of subSystem.
+ */
+ public String getSubSystem() {
+ return subSystem;
+ }
+
+ /**
+ * Specifies the subsystem.
+ *
+ * @param v Value to assign to subSystem.
+ */
+ public void setSubSystem(String v) {
+ this.subSystem = v;
+ }
+
+
+ /**
+ * Get the value of task.
+ * @return value of task.
+ */
+ public String getTask() {
+ return task;
+ }
+
+ /**
+ * Specifies the task number used to checkin
+ * the file (may use 'default').
+ *
+ * @param v Value to assign to task.
+ */
+ public void setTask(String v) {
+ this.task = v;
+ }
+
+ /**
+ * /comment -- comments associated to the task
+ */
+ public static final String FLAG_COMMENT = "/synopsis";
+
+ /**
+ * /platform flag -- target platform
+ */
+ public static final String FLAG_PLATFORM = "/plat";
+
+ /**
+ * /resolver flag
+ */
+ public static final String FLAG_RESOLVER = "/resolver";
+
+ /**
+ * /release flag
+ */
+ public static final String FLAG_RELEASE = "/release";
+
+ /**
+ * /release flag
+ */
+ public static final String FLAG_SUBSYSTEM = "/subsystem";
+
+ /**
+ * -task flag -- associate checkout task with task
+ */
+ public static final String FLAG_TASK = "/task";
+
+
+ // implementation of org.apache.tools.ant.taskdefs.ExecuteStreamHandler interface
+
+ /**
+ *
+ * @throws IOException on error
+ */
+ public void start() throws IOException {
+ }
+
+ /**
+ *
+ */
+ public void stop() {
+ }
+
+ /**
+ *
+ * @param param1 the output stream
+ * @exception java.io.IOException on error
+ */
+ public void setProcessInputStream(OutputStream param1) throws IOException {
+ }
+
+ /**
+ *
+ * @param is the input stream
+ * @exception java.io.IOException on error
+ */
+ public void setProcessErrorStream(InputStream is) throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ String s = reader.readLine();
+ if (s != null) {
+ log("err " + s, Project.MSG_DEBUG);
+ } // end of if ()
+ }
+
+ /**
+ * read the output stream to retrieve the new task number.
+ * @param is InputStream
+ * @throws IOException on error
+ */
+ public void setProcessOutputStream(InputStream is) throws IOException {
+
+ String buffer = "";
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ buffer = reader.readLine();
+ if (buffer != null) {
+ log("buffer:" + buffer, Project.MSG_DEBUG);
+ String taskstring = buffer.substring(buffer.indexOf(' ')).trim();
+ taskstring = taskstring.substring(0, taskstring.lastIndexOf(' ')).trim();
+ setTask(taskstring);
+ log("task is " + getTask(), Project.MSG_DEBUG);
+ } // end of if ()
+ } catch (NullPointerException npe) {
+ log("error procession stream , null pointer exception", Project.MSG_ERR);
+ npe.printStackTrace();
+ throw new BuildException(npe.getClass().getName());
+ } catch (Exception e) {
+ log("error procession stream " + e.getMessage(), Project.MSG_ERR);
+ throw new BuildException(e.getMessage());
+ } // end of try-catch
+
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMReconfigure.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMReconfigure.java
new file mode 100644
index 00000000..28f80645
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMReconfigure.java
@@ -0,0 +1,159 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ccm;
+
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+
+/**
+ * Task allows to reconfigure a project, recursively or not
+ */
+public class CCMReconfigure extends Continuus {
+
+ private String ccmProject = null;
+ private boolean recurse = false;
+ private boolean verbose = false;
+
+ /** Constructor for CCMReconfigure. */
+ public CCMReconfigure() {
+ super();
+ setCcmAction(COMMAND_RECONFIGURE);
+ }
+
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute ccm and then calls Exec's run method
+ * to execute the command line.
+ * </p>
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ int result = 0;
+
+ // build the command line from what we got the format
+ // as specified in the CCM.EXE help
+ commandLine.setExecutable(getCcmCommand());
+ commandLine.createArgument().setValue(getCcmAction());
+
+ checkOptions(commandLine);
+
+ result = run(commandLine);
+ if (Execute.isFailure(result)) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+
+ if (isRecurse()) {
+ cmd.createArgument().setValue(FLAG_RECURSE);
+ } // end of if ()
+
+ if (isVerbose()) {
+ cmd.createArgument().setValue(FLAG_VERBOSE);
+ } // end of if ()
+
+ if (getCcmProject() != null) {
+ cmd.createArgument().setValue(FLAG_PROJECT);
+ cmd.createArgument().setValue(getCcmProject());
+ }
+
+ }
+
+ /**
+ * Get the value of project.
+ * @return value of project.
+ */
+ public String getCcmProject() {
+ return ccmProject;
+ }
+
+ /**
+ * Sets the ccm project on which the operation is applied.
+ * @param v Value to assign to project.
+ */
+ public void setCcmProject(String v) {
+ this.ccmProject = v;
+ }
+
+
+ /**
+ * Get the value of recurse.
+ * @return value of recurse.
+ */
+ public boolean isRecurse() {
+ return recurse;
+ }
+
+ /**
+ * If true, recurse on subproject (default false).
+ *
+ * @param v Value to assign to recurse.
+ */
+ public void setRecurse(boolean v) {
+ this.recurse = v;
+ }
+
+
+ /**
+ * Get the value of verbose.
+ * @return value of verbose.
+ */
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ /**
+ * If true, do a verbose reconfigure operation (default false).
+ * @param v Value to assign to verbose.
+ */
+ public void setVerbose(boolean v) {
+ this.verbose = v;
+ }
+
+
+ /**
+ * /recurse --
+ */
+ public static final String FLAG_RECURSE = "/recurse";
+
+ /**
+ * /recurse --
+ */
+ public static final String FLAG_VERBOSE = "/verbose";
+
+
+ /**
+ * /project flag -- target project
+ */
+ public static final String FLAG_PROJECT = "/project";
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/Continuus.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/Continuus.java
new file mode 100644
index 00000000..5618dd6a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ccm/Continuus.java
@@ -0,0 +1,144 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ccm;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.FileUtils;
+
+
+/**
+ * A base class for creating tasks for executing commands on Continuus 5.1.
+ * <p>
+ * The class extends the task as it operates by executing the ccm.exe program
+ * supplied with Continuus/Synergy. By default the task expects the ccm executable to be
+ * in the path,
+ * you can override this be specifying the ccmdir attribute.
+ * </p>
+ *
+ */
+public abstract class Continuus extends Task {
+
+ private String ccmDir = "";
+ private String ccmAction = "";
+
+ /**
+ * Get the value of ccmAction.
+ * @return value of ccmAction.
+ */
+ public String getCcmAction() {
+ return ccmAction;
+ }
+
+ /**
+ * Set the value of ccmAction.
+ * @param v Value to assign to ccmAction.
+ * @ant.attribute ignore="true"
+ */
+ public void setCcmAction(String v) {
+ this.ccmAction = v;
+ }
+
+
+ /**
+ * Set the directory where the ccm executable is located.
+ *
+ * @param dir the directory containing the ccm executable
+ */
+ public final void setCcmDir(String dir) {
+ ccmDir = FileUtils.translatePath(dir);
+ }
+
+ /**
+ * Builds and returns the command string to execute ccm
+ * @return String containing path to the executable
+ */
+ protected final String getCcmCommand() {
+ String toReturn = ccmDir;
+ if (!toReturn.equals("") && !toReturn.endsWith("/")) {
+ toReturn += "/";
+ }
+
+ toReturn += CCM_EXE;
+
+ return toReturn;
+ }
+
+
+ /**
+ * Run the command.
+ * @param cmd the command line
+ * @param handler an execute stream handler
+ * @return the exit status of the command
+ */
+ protected int run(Commandline cmd, ExecuteStreamHandler handler) {
+ try {
+ Execute exe = new Execute(handler);
+ exe.setAntRun(getProject());
+ exe.setWorkingDirectory(getProject().getBaseDir());
+ exe.setCommandline(cmd.getCommandline());
+ return exe.execute();
+ } catch (java.io.IOException e) {
+ throw new BuildException(e, getLocation());
+ }
+ }
+
+ /**
+ * Run the command.
+ * @param cmd the command line
+ * @return the exit status of the command
+ */
+ protected int run(Commandline cmd) {
+ return run(cmd, new LogStreamHandler(this, Project.MSG_VERBOSE, Project.MSG_WARN));
+ }
+
+ /**
+ * Constant for the thing to execute
+ */
+ private static final String CCM_EXE = "ccm";
+
+ /**
+ * The 'CreateTask' command
+ */
+ public static final String COMMAND_CREATE_TASK = "create_task";
+ /**
+ * The 'Checkout' command
+ */
+ public static final String COMMAND_CHECKOUT = "co";
+ /**
+ * The 'Checkin' command
+ */
+ public static final String COMMAND_CHECKIN = "ci";
+ /**
+ * The 'Reconfigure' command
+ */
+ public static final String COMMAND_RECONFIGURE = "reconfigure";
+
+ /**
+ * The 'Reconfigure' command
+ */
+ public static final String COMMAND_DEFAULT_TASK = "default_task";
+
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckin.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckin.java
new file mode 100644
index 00000000..371d418b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckin.java
@@ -0,0 +1,343 @@
+/*
+ * 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.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs ClearCase checkin.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ * <tr>
+ * <th>Attribute</th>
+ * <th>Values</th>
+ * <th>Required</th>
+ * </tr>
+ * <tr>
+ * <td>viewpath</td>
+ * <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ * <td>No</td>
+ * </tr>
+ * <tr>
+ * <td>comment</td>
+ * <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * </tr>
+ * <tr>
+ * <td>commentfile</td>
+ * <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * </tr>
+ * <tr>
+ * <td>nowarn</td>
+ * <td>Suppress warning messages</td>
+ * <td>No</td>
+ * </tr>
+ * <tr>
+ * <td>preservetime</td>
+ * <td>Preserve the modification time</td>
+ * <td>No</td>
+ * </tr>
+ * <tr>
+ * <td>keepcopy</td>
+ * <td>Keeps a copy of the file with a .keep extension</td>
+ * <td>No</td>
+ * </tr>
+ * <tr>
+ * <td>identical</td>
+ * <td>Allows the file to be checked in even if it is identical to the original</td>
+ * <td>No</td>
+ * </tr>
+ * <tr>
+ * <td>failonerr</td>
+ * <td>Throw an exception if the command fails. Default is true</td>
+ * <td>No</td>
+ * </tr>
+ * </table>
+ *
+ */
+public class CCCheckin extends ClearCase {
+ private String mComment = null;
+ private String mCfile = null;
+ private boolean mNwarn = false;
+ private boolean mPtime = false;
+ private boolean mKeep = false;
+ private boolean mIdentical = true;
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute cleartool and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command fails and failonerr is set to true
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ Project aProj = getProject();
+ int result = 0;
+
+ // Default the viewpath to basedir if it is not specified
+ if (getViewPath() == null) {
+ setViewPath(aProj.getBaseDir().getPath());
+ }
+
+ // build the command line from what we got. the format is
+ // cleartool checkin [options...] [viewpath ...]
+ // as specified in the CLEARTOOL.EXE help
+ commandLine.setExecutable(getClearToolCommand());
+ commandLine.createArgument().setValue(COMMAND_CHECKIN);
+
+ checkOptions(commandLine);
+
+ if (!getFailOnErr()) {
+ getProject().log("Ignoring any errors that occur for: "
+ + getViewPathBasename(), Project.MSG_VERBOSE);
+ }
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnErr()) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ if (getComment() != null) {
+ // -c
+ getCommentCommand(cmd);
+ } else {
+ if (getCommentFile() != null) {
+ // -cfile
+ getCommentFileCommand(cmd);
+ } else {
+ cmd.createArgument().setValue(FLAG_NOCOMMENT);
+ }
+ }
+
+ if (getNoWarn()) {
+ // -nwarn
+ cmd.createArgument().setValue(FLAG_NOWARN);
+ }
+
+ if (getPreserveTime()) {
+ // -ptime
+ cmd.createArgument().setValue(FLAG_PRESERVETIME);
+ }
+
+ if (getKeepCopy()) {
+ // -keep
+ cmd.createArgument().setValue(FLAG_KEEPCOPY);
+ }
+
+ if (getIdentical()) {
+ // -identical
+ cmd.createArgument().setValue(FLAG_IDENTICAL);
+ }
+
+ // viewpath
+ cmd.createArgument().setValue(getViewPath());
+ }
+
+
+ /**
+ * Sets the comment string.
+ *
+ * @param comment the comment string
+ */
+ public void setComment(String comment) {
+ mComment = comment;
+ }
+
+ /**
+ * Get comment string
+ *
+ * @return String containing the comment
+ */
+ public String getComment() {
+ return mComment;
+ }
+
+ /**
+ * Specifies a file containing a comment.
+ *
+ * @param cfile the path to the comment file
+ */
+ public void setCommentFile(String cfile) {
+ mCfile = cfile;
+ }
+
+ /**
+ * Get comment file
+ *
+ * @return String containing the path to the comment file
+ */
+ public String getCommentFile() {
+ return mCfile;
+ }
+
+ /**
+ * If true, suppress warning messages.
+ *
+ * @param nwarn the status to set the flag to
+ */
+ public void setNoWarn(boolean nwarn) {
+ mNwarn = nwarn;
+ }
+
+ /**
+ * Get nowarn flag status
+ *
+ * @return boolean containing status of nwarn flag
+ */
+ public boolean getNoWarn() {
+ return mNwarn;
+ }
+
+ /**
+ * If true, preserve the modification time.
+ *
+ * @param ptime the status to set the flag to
+ */
+ public void setPreserveTime(boolean ptime) {
+ mPtime = ptime;
+ }
+
+ /**
+ * Get preservetime flag status
+ *
+ * @return boolean containing status of preservetime flag
+ */
+ public boolean getPreserveTime() {
+ return mPtime;
+ }
+
+ /**
+ * If true, keeps a copy of the file with a .keep extension.
+ *
+ * @param keep the status to set the flag to
+ */
+ public void setKeepCopy(boolean keep) {
+ mKeep = keep;
+ }
+
+ /**
+ * Get keepcopy flag status
+ *
+ * @return boolean containing status of keepcopy flag
+ */
+ public boolean getKeepCopy() {
+ return mKeep;
+ }
+
+ /**
+ * If true, allows the file to be checked in even
+ * if it is identical to the original.
+ *
+ * @param identical the status to set the flag to
+ */
+ public void setIdentical(boolean identical) {
+ mIdentical = identical;
+ }
+
+ /**
+ * Get identical flag status
+ *
+ * @return boolean containing status of identical flag
+ */
+ public boolean getIdentical() {
+ return mIdentical;
+ }
+
+
+ /**
+ * Get the 'comment' command
+ *
+ * @param cmd containing the command line string with or
+ * without the comment flag and string appended
+ */
+ private void getCommentCommand(Commandline cmd) {
+ if (getComment() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENT);
+ cmd.createArgument().setValue(getComment());
+ }
+ }
+
+ /**
+ * Get the 'commentfile' command
+ *
+ * @param cmd containing the command line string with or
+ * without the commentfile flag and file appended
+ */
+ private void getCommentFileCommand(Commandline cmd) {
+ if (getCommentFile() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENTFILE);
+ cmd.createArgument().setValue(getCommentFile());
+ }
+ }
+
+
+ /**
+ * -c flag -- comment to attach to the file
+ */
+ public static final String FLAG_COMMENT = "-c";
+ /**
+ * -cfile flag -- file containing a comment to attach to the file
+ */
+ public static final String FLAG_COMMENTFILE = "-cfile";
+ /**
+ * -nc flag -- no comment is specified
+ */
+ public static final String FLAG_NOCOMMENT = "-nc";
+ /**
+ * -nwarn flag -- suppresses warning messages
+ */
+ public static final String FLAG_NOWARN = "-nwarn";
+ /**
+ * -ptime flag -- preserves the modification time
+ */
+ public static final String FLAG_PRESERVETIME = "-ptime";
+ /**
+ * -keep flag -- keeps a copy of the file with a .keep extension
+ */
+ public static final String FLAG_KEEPCOPY = "-keep";
+ /**
+ * -identical flag -- allows the file to be checked in even if it is identical to the original
+ */
+ public static final String FLAG_IDENTICAL = "-identical";
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckout.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckout.java
new file mode 100644
index 00000000..0eaf09ac
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckout.java
@@ -0,0 +1,516 @@
+/*
+ * 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.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+
+/**
+ * Performs ClearCase checkout.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ * <tr>
+ * <th>Attribute</th>
+ * <th>Values</th>
+ * <th>Required</th>
+ * </tr>
+ * <tr>
+ * <td>viewpath</td>
+ * <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>reserved</td>
+ * <td>Specifies whether to check out the file as reserved or not</td>
+ * <td>Yes</td>
+ * <tr>
+ * <tr>
+ * <td>out</td>
+ * <td>Creates a writable file under a different filename</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>nodata</td>
+ * <td>Checks out the file but does not create an editable file containing its data</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>branch</td>
+ * <td>Specify a branch to check out the file to</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>version</td>
+ * <td>Allows checkout of a version other than main latest</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>nowarn</td>
+ * <td>Suppress warning messages</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>comment</td>
+ * <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>commentfile</td>
+ * <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>notco</td>
+ * <td>Fail if it's already checked out to the current view. Set to false to ignore it.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>failonerr</td>
+ * <td>Throw an exception if the command fails. Default is true</td>
+ * <td>No</td>
+ * <tr>
+ * </table>
+ *
+ */
+public class CCCheckout extends ClearCase {
+ private boolean mReserved = true;
+ private String mOut = null;
+ private boolean mNdata = false;
+ private String mBranch = null;
+ private boolean mVersion = false;
+ private boolean mNwarn = false;
+ private String mComment = null;
+ private String mCfile = null;
+ private boolean mNotco = true;
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute cleartool and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command fails and failonerr is set to true
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ Project aProj = getProject();
+ int result = 0;
+
+ // Default the viewpath to basedir if it is not specified
+ if (getViewPath() == null) {
+ setViewPath(aProj.getBaseDir().getPath());
+ }
+
+ // build the command line from what we got the format is
+ // cleartool checkout [options...] [viewpath ...]
+ // as specified in the CLEARTOOL.EXE help
+ commandLine.setExecutable(getClearToolCommand());
+ commandLine.createArgument().setValue(COMMAND_CHECKOUT);
+
+ checkOptions(commandLine);
+ /*
+ * If configured to not care about whether the element is
+ * already checked out to the current view.
+ * Then check to see if it is checked out.
+ */
+ if (!getNotco() && lsCheckout()) {
+ getProject().log("Already checked out in this view: "
+ + getViewPathBasename(), Project.MSG_VERBOSE);
+ return;
+ }
+ if (!getFailOnErr()) {
+ getProject().log("Ignoring any errors that occur for: "
+ + getViewPathBasename(), Project.MSG_VERBOSE);
+ }
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnErr()) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+ /**
+ * Check to see if the element is checked out in the current view.
+ */
+ private boolean lsCheckout() {
+ Commandline cmdl = new Commandline();
+ String result;
+
+ // build the command line from what we got the format is
+ // cleartool lsco [options...] [viewpath ...]
+ // as specified in the CLEARTOOL.EXE help
+ cmdl.setExecutable(getClearToolCommand());
+ cmdl.createArgument().setValue(COMMAND_LSCO);
+ cmdl.createArgument().setValue("-cview");
+ cmdl.createArgument().setValue("-short");
+ cmdl.createArgument().setValue("-d");
+ // viewpath
+ cmdl.createArgument().setValue(getViewPath());
+
+ result = runS(cmdl);
+
+ // System.out.println( "lsCheckout: " + result );
+
+ return (result != null && result.length() > 0) ? true : false;
+ }
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ // ClearCase items
+ if (getReserved()) {
+ // -reserved
+ cmd.createArgument().setValue(FLAG_RESERVED);
+ } else {
+ // -unreserved
+ cmd.createArgument().setValue(FLAG_UNRESERVED);
+ }
+
+ if (getOut() != null) {
+ // -out
+ getOutCommand(cmd);
+ } else {
+ if (getNoData()) {
+ // -ndata
+ cmd.createArgument().setValue(FLAG_NODATA);
+ }
+
+ }
+
+ if (getBranch() != null) {
+ // -branch
+ getBranchCommand(cmd);
+ } else {
+ if (getVersion()) {
+ // -version
+ cmd.createArgument().setValue(FLAG_VERSION);
+ }
+
+ }
+
+ if (getNoWarn()) {
+ // -nwarn
+ cmd.createArgument().setValue(FLAG_NOWARN);
+ }
+
+ if (getComment() != null) {
+ // -c
+ getCommentCommand(cmd);
+ } else {
+ if (getCommentFile() != null) {
+ // -cfile
+ getCommentFileCommand(cmd);
+ } else {
+ cmd.createArgument().setValue(FLAG_NOCOMMENT);
+ }
+ }
+
+ // viewpath
+ cmd.createArgument().setValue(getViewPath());
+
+ // Print out info about the notco option
+ // System.out.println( "Notco: " + (getNotco() ? "yes" : "no") );
+ }
+
+ /**
+ * If true, checks out the file as reserved.
+ *
+ * @param reserved the status to set the flag to
+ */
+ public void setReserved(boolean reserved) {
+ mReserved = reserved;
+ }
+
+ /**
+ * Get reserved flag status
+ *
+ * @return boolean containing status of reserved flag
+ */
+ public boolean getReserved() {
+ return mReserved;
+ }
+
+ /**
+ * If true, checkout fails if the element is already checked out to the current view.
+ *
+ * @param notco the status to set the flag to
+ * @since ant 1.6.1
+ */
+ public void setNotco(boolean notco) {
+ mNotco = notco;
+ }
+
+ /**
+ * Get notco flag status
+ *
+ * @return boolean containing status of notco flag
+ * @since ant 1.6.1
+ */
+ public boolean getNotco() {
+ return mNotco;
+ }
+
+
+ /**
+ * Creates a writable file under a different filename.
+ *
+ * @param outf the path to the out file
+ */
+ public void setOut(String outf) {
+ mOut = outf;
+ }
+
+ /**
+ * Get out file
+ *
+ * @return String containing the path to the out file
+ */
+ public String getOut() {
+ return mOut;
+ }
+
+ /**
+ * If true, checks out the file but does not create an
+ * editable file containing its data.
+ *
+ * @param ndata the status to set the flag to
+ */
+ public void setNoData(boolean ndata) {
+ mNdata = ndata;
+ }
+
+ /**
+ * Get nodata flag status
+ *
+ * @return boolean containing status of ndata flag
+ */
+ public boolean getNoData() {
+ return mNdata;
+ }
+
+ /**
+ * Specify a branch to check out the file to.
+ *
+ * @param branch the name of the branch
+ */
+ public void setBranch(String branch) {
+ mBranch = branch;
+ }
+
+ /**
+ * Get branch name
+ *
+ * @return String containing the name of the branch
+ */
+ public String getBranch() {
+ return mBranch;
+ }
+
+ /**
+ * If true, allows checkout of a version other than main latest.
+ *
+ * @param version the status to set the flag to
+ */
+ public void setVersion(boolean version) {
+ mVersion = version;
+ }
+
+ /**
+ * Get version flag status
+ *
+ * @return boolean containing status of version flag
+ */
+ public boolean getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * If true, warning messages are suppressed.
+ *
+ * @param nwarn the status to set the flag to
+ */
+ public void setNoWarn(boolean nwarn) {
+ mNwarn = nwarn;
+ }
+
+ /**
+ * Get nowarn flag status
+ *
+ * @return boolean containing status of nwarn flag
+ */
+ public boolean getNoWarn() {
+ return mNwarn;
+ }
+
+ /**
+ * Sets the comment string.
+ *
+ * @param comment the comment string
+ */
+ public void setComment(String comment) {
+ mComment = comment;
+ }
+
+ /**
+ * Get comment string
+ *
+ * @return String containing the comment
+ */
+ public String getComment() {
+ return mComment;
+ }
+
+ /**
+ * Specifies a file containing a comment.
+ *
+ * @param cfile the path to the comment file
+ */
+ public void setCommentFile(String cfile) {
+ mCfile = cfile;
+ }
+
+ /**
+ * Get comment file
+ *
+ * @return String containing the path to the comment file
+ */
+ public String getCommentFile() {
+ return mCfile;
+ }
+
+ /**
+ * Get the 'out' command
+ *
+ * @param cmd containing the command line string with or
+ * without the out flag and path appended
+ */
+ private void getOutCommand(Commandline cmd) {
+ if (getOut() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_OUT);
+ cmd.createArgument().setValue(getOut());
+ }
+ }
+
+ /**
+ * Get the 'branch' command
+ *
+ * @param cmd containing the command line string with or
+ without the branch flag and name appended
+ */
+ private void getBranchCommand(Commandline cmd) {
+ if (getBranch() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_BRANCH);
+ cmd.createArgument().setValue(getBranch());
+ }
+ }
+
+
+ /**
+ * Get the 'comment' command
+ *
+ * @param cmd containing the command line string with or
+ * without the comment flag and string appended
+ */
+ private void getCommentCommand(Commandline cmd) {
+ if (getComment() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENT);
+ cmd.createArgument().setValue(getComment());
+ }
+ }
+
+ /**
+ * Get the 'cfile' command
+ *
+ * @param cmd containing the command line string with or
+ * without the cfile flag and file appended
+ */
+ private void getCommentFileCommand(Commandline cmd) {
+ if (getCommentFile() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENTFILE);
+ cmd.createArgument().setValue(getCommentFile());
+ }
+ }
+
+ /**
+ * -reserved flag -- check out the file as reserved
+ */
+ public static final String FLAG_RESERVED = "-reserved";
+ /**
+ * -reserved flag -- check out the file as unreserved
+ */
+ public static final String FLAG_UNRESERVED = "-unreserved";
+ /**
+ * -out flag -- create a writable file under a different filename
+ */
+ public static final String FLAG_OUT = "-out";
+ /**
+ * -ndata flag -- checks out the file but does not create an editable file containing its data
+ */
+ public static final String FLAG_NODATA = "-ndata";
+ /**
+ * -branch flag -- checks out the file on a specified branch
+ */
+ public static final String FLAG_BRANCH = "-branch";
+ /**
+ * -version flag -- allows checkout of a version that is not main latest
+ */
+ public static final String FLAG_VERSION = "-version";
+ /**
+ * -nwarn flag -- suppresses warning messages
+ */
+ public static final String FLAG_NOWARN = "-nwarn";
+ /**
+ * -c flag -- comment to attach to the file
+ */
+ public static final String FLAG_COMMENT = "-c";
+ /**
+ * -cfile flag -- file containing a comment to attach to the file
+ */
+ public static final String FLAG_COMMENTFILE = "-cfile";
+ /**
+ * -nc flag -- no comment is specified
+ */
+ public static final String FLAG_NOCOMMENT = "-nc";
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCLock.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCLock.java
new file mode 100644
index 00000000..c273554e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCLock.java
@@ -0,0 +1,378 @@
+/*
+ * 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.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+
+/**
+ * TODO:
+ * comment field doesn't include all options yet
+ */
+
+
+
+/**
+ * Performs a ClearCase Lock command.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ * <tr>
+ * <th>Attribute</th>
+ * <th>Values</th>
+ * <th>Required</th>
+ * </tr>
+ * <tr>
+ * <td>replace</td>
+ * <td>Specifies replacing an existing lock</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>nusers</td>
+ * <td>Specifies user(s) who can still modify the object/pname</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>obsolete</td>
+ * <td>Specifies that the object/pname should be marked obsolete</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>comment</td>
+ * <td>Specifies how to populate comments fields</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>pname</td>
+ * <td>Specifies the pathname to be locked.</td>
+ * <td>No</td>
+ * <tr>
+ * <td>objselect</td>
+ * <td>This variable is obsolete. Should use <i>objsel</i> instead.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>objsel</td>
+ * <td>Specifies the object(s) to be unlocked.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>failonerr</td>
+ * <td>Throw an exception if the command fails. Default is true</td>
+ * <td>No</td>
+ * <tr>
+ * </table>
+ *
+ */
+public class CCLock extends ClearCase {
+ private boolean mReplace = false;
+ private boolean mObsolete = false;
+ private String mComment = null;
+ private String mNusers = null;
+ private String mPname = null;
+ private String mObjselect = null;
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute cleartool and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command fails and failonerr is set to true
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ Project aProj = getProject();
+ int result = 0;
+
+ // Default the viewpath to basedir if it is not specified
+ if (getViewPath() == null) {
+ setViewPath(aProj.getBaseDir().getPath());
+ }
+
+ // build the command line from what we got the format is
+ // cleartool lock [options...]
+ // as specified in the CLEARTOOL.EXE help
+ commandLine.setExecutable(getClearToolCommand());
+ commandLine.createArgument().setValue(COMMAND_LOCK);
+
+ // Check the command line options
+ checkOptions(commandLine);
+
+ // For debugging
+ // System.out.println(commandLine.toString());
+
+ if (!getFailOnErr()) {
+ getProject().log("Ignoring any errors that occur for: "
+ + getOpType(), Project.MSG_VERBOSE);
+ }
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnErr()) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+ /**
+ * Check the command line options.
+ */
+private void checkOptions(Commandline cmd) {
+ // ClearCase items
+ if (getReplace()) {
+ // -replace
+ cmd.createArgument().setValue(FLAG_REPLACE);
+ }
+ if (getObsolete()) {
+ // -obsolete
+ cmd.createArgument().setValue(FLAG_OBSOLETE);
+ } else {
+ getNusersCommand(cmd);
+ }
+ getCommentCommand(cmd);
+
+ if (getObjselect() == null && getPname() == null) {
+ throw new BuildException("Should select either an element "
+ + "(pname) or an object (objselect)");
+ }
+ getPnameCommand(cmd);
+ // object selector
+ if (getObjselect() != null) {
+ cmd.createArgument().setValue(getObjselect());
+ }
+}
+
+ /**
+ * If true, replace an existing lock.
+ *
+ * @param replace the status to set the flag to
+ */
+ public void setReplace(boolean replace) {
+ mReplace = replace;
+ }
+
+ /**
+ * Get replace flag status
+ *
+ * @return boolean containing status of replace flag
+ */
+ public boolean getReplace() {
+ return mReplace;
+ }
+
+ /**
+ * If true, mark object as obsolete.
+ *
+ * @param obsolete the status to set the flag to
+ */
+ public void setObsolete(boolean obsolete) {
+ mObsolete = obsolete;
+ }
+
+ /**
+ * Get obsolete flag status
+ *
+ * @return boolean containing status of obsolete flag
+ */
+ public boolean getObsolete() {
+ return mObsolete;
+ }
+
+ /**
+ * Sets the users who may continue to
+ * edit the object while it is locked.
+ *
+ * @param nusers users excluded from lock
+ */
+ public void setNusers(String nusers) {
+ mNusers = nusers;
+ }
+
+ /**
+ * Get nusers list
+ *
+ * @return String containing the list of users excluded from lock
+ */
+ public String getNusers() {
+ return mNusers;
+ }
+
+ /**
+ * Sets how comments should be written
+ * for the event record(s)
+ *
+ * @param comment comment method to use
+ */
+ public void setComment(String comment) {
+ mComment = comment;
+ }
+
+ /**
+ * Get comment method
+ *
+ * @return String containing the desired comment method
+ */
+ public String getComment() {
+ return mComment;
+ }
+
+ /**
+ * Sets the pathname to be locked
+ *
+ * @param pname pathname to be locked
+ */
+ public void setPname(String pname) {
+ mPname = pname;
+ }
+
+ /**
+ * Get the pathname to be locked
+ *
+ * @return String containing the pathname to be locked
+ */
+ public String getPname() {
+ return mPname;
+ }
+
+ /**
+ * Sets the object(s) to be locked
+ *
+ * @param objsel objects to be locked
+ * @since ant 1.6.1
+ */
+ public void setObjSel(String objsel) {
+ mObjselect = objsel;
+ }
+
+ /**
+ * Sets the object(s) to be locked
+ *
+ * @param objselect objects to be locked
+ */
+ public void setObjselect(String objselect) {
+ mObjselect = objselect;
+ }
+
+ /**
+ * Get list of objects to be locked
+ *
+ * @return String containing the objects to be locked
+ */
+ public String getObjselect() {
+ return mObjselect;
+ }
+
+ /**
+ * Get the 'nusers' command
+ *
+ * @param cmd containing the command line string with or
+ * without the nusers flag and value appended
+ */
+ private void getNusersCommand(Commandline cmd) {
+ if (getNusers() == null) {
+ return;
+ } else {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_NUSERS);
+ cmd.createArgument().setValue(getNusers());
+ }
+ }
+
+ /**
+ * Get the 'comment' command
+ *
+ * @param cmd containing the command line string with or without the
+ * comment flag and value appended
+ */
+ private void getCommentCommand(Commandline cmd) {
+ if (getComment() == null) {
+ return;
+ } else {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENT);
+ cmd.createArgument().setValue(getComment());
+ }
+ }
+
+ /**
+ * Get the 'pname' command
+ *
+ * @param cmd containing the command line string with or
+ * without the pname flag and value appended
+ */
+ private void getPnameCommand(Commandline cmd) {
+ if (getPname() == null) {
+ return;
+ } else {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_PNAME);
+ cmd.createArgument().setValue(getPname());
+ }
+ }
+
+ /**
+ * Return which object/pname is being operated on
+ *
+ * @return String containing the object/pname being worked on
+ */
+ private String getOpType() {
+
+ if (getPname() != null) {
+ return getPname();
+ } else {
+ return getObjselect();
+ }
+ }
+
+ /**
+ * -replace flag -- replace existing lock on object(s)
+ */
+ public static final String FLAG_REPLACE = "-replace";
+ /**
+ * -nusers flag -- list of users to exclude from lock
+ */
+ public static final String FLAG_NUSERS = "-nusers";
+ /**
+ * -obsolete flag -- mark locked object as obsolete
+ */
+ public static final String FLAG_OBSOLETE = "-obsolete";
+ /**
+ * -comment flag -- method to use for commenting events
+ */
+ public static final String FLAG_COMMENT = "-comment";
+ /**
+ * -pname flag -- pathname to lock
+ */
+ public static final String FLAG_PNAME = "-pname";
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkattr.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkattr.java
new file mode 100644
index 00000000..128ea16b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkattr.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.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Task to perform mkattr command to ClearCase.
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ * <tr>
+ * <th>Attribute</th>
+ * <th>Values</th>
+ * <th>Required</th>
+ * </tr>
+ * <tr>
+ * <td>viewpath</td>
+ * <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ * <td>Yes</td>
+ * <tr>
+ * <tr>
+ * <td>replace</td>
+ * <td>Replace the value of the attribute if it already exists</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>recurse</td>
+ * <td>Process each subdirectory under viewpath</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>version</td>
+ * <td>Identify a specific version to attach the attribute to</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>typename</td>
+ * <td>Name of the attribute type</td>
+ * <td>Yes</td>
+ * <tr>
+ * <tr>
+ * <td>typevalue</td>
+ * <td>Value to attach to the attribute type</td>
+ * <td>Yes</td>
+ * <tr>
+ * <tr>
+ * <td>comment</td>
+ * <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>commentfile</td>
+ * <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>failonerr</td>
+ * <td>Throw an exception if the command fails. Default is true</td>
+ * <td>No</td>
+ * <tr>
+ * </table>
+ *
+ */
+public class CCMkattr extends ClearCase {
+ private boolean mReplace = false;
+ private boolean mRecurse = false;
+ private String mVersion = null;
+ private String mTypeName = null;
+ private String mTypeValue = null;
+ private String mComment = null;
+ private String mCfile = null;
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute cleartool and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command fails and failonerr is set to true
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ Project aProj = getProject();
+ int result = 0;
+
+ // Check for required attributes
+ if (getTypeName() == null) {
+ throw new BuildException("Required attribute TypeName not specified");
+ }
+ if (getTypeValue() == null) {
+ throw new BuildException("Required attribute TypeValue not specified");
+ }
+ // Default the viewpath to basedir if it is not specified
+ if (getViewPath() == null) {
+ setViewPath(aProj.getBaseDir().getPath());
+ }
+
+ // build the command line from what we got. the format is
+ // cleartool mkattr [options...] [viewpath ...]
+ // as specified in the CLEARTOOL help
+ commandLine.setExecutable(getClearToolCommand());
+ commandLine.createArgument().setValue(COMMAND_MKATTR);
+
+ checkOptions(commandLine);
+
+ if (!getFailOnErr()) {
+ getProject().log("Ignoring any errors that occur for: "
+ + getViewPathBasename(), Project.MSG_VERBOSE);
+ }
+
+ // For debugging
+ // System.out.println(commandLine.toString());
+
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnErr()) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ if (getReplace()) {
+ // -replace
+ cmd.createArgument().setValue(FLAG_REPLACE);
+ }
+
+ if (getRecurse()) {
+ // -recurse
+ cmd.createArgument().setValue(FLAG_RECURSE);
+ }
+
+ if (getVersion() != null) {
+ // -version
+ getVersionCommand(cmd);
+ }
+
+ if (getComment() != null) {
+ // -c
+ getCommentCommand(cmd);
+ } else {
+ if (getCommentFile() != null) {
+ // -cfile
+ getCommentFileCommand(cmd);
+ } else {
+ cmd.createArgument().setValue(FLAG_NOCOMMENT);
+ }
+ }
+
+ if (getTypeName() != null) {
+ // type
+ getTypeCommand(cmd);
+ }
+ if (getTypeValue() != null) {
+ // type value
+ getTypeValueCommand(cmd);
+ }
+ // viewpath
+ cmd.createArgument().setValue(getViewPath());
+ }
+
+
+ /**
+ * Set the replace flag
+ *
+ * @param replace the status to set the flag to
+ */
+ public void setReplace(boolean replace) {
+ mReplace = replace;
+ }
+
+ /**
+ * Get replace flag status
+ *
+ * @return boolean containing status of replace flag
+ */
+ public boolean getReplace() {
+ return mReplace;
+ }
+
+ /**
+ * Set recurse flag
+ *
+ * @param recurse the status to set the flag to
+ */
+ public void setRecurse(boolean recurse) {
+ mRecurse = recurse;
+ }
+
+ /**
+ * Get recurse flag status
+ *
+ * @return boolean containing status of recurse flag
+ */
+ public boolean getRecurse() {
+ return mRecurse;
+ }
+
+ /**
+ * Set the version flag
+ *
+ * @param version the status to set the flag to
+ */
+ public void setVersion(String version) {
+ mVersion = version;
+ }
+
+ /**
+ * Get version flag status
+ *
+ * @return boolean containing status of version flag
+ */
+ public String getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * Set comment string
+ *
+ * @param comment the comment string
+ */
+ public void setComment(String comment) {
+ mComment = comment;
+ }
+
+ /**
+ * Get comment string
+ *
+ * @return String containing the comment
+ */
+ public String getComment() {
+ return mComment;
+ }
+
+ /**
+ * Set comment file
+ *
+ * @param cfile the path to the comment file
+ */
+ public void setCommentFile(String cfile) {
+ mCfile = cfile;
+ }
+
+ /**
+ * Get comment file
+ *
+ * @return String containing the path to the comment file
+ */
+ public String getCommentFile() {
+ return mCfile;
+ }
+
+ /**
+ * Set the attribute type-name
+ *
+ * @param tn the type name
+ */
+ public void setTypeName(String tn) {
+ mTypeName = tn;
+ }
+
+ /**
+ * Get attribute type-name
+ *
+ * @return String containing type name
+ */
+ public String getTypeName() {
+ return mTypeName;
+ }
+
+ /**
+ * Set the attribute type-value
+ *
+ * @param tv the type value
+ */
+ public void setTypeValue(String tv) {
+ mTypeValue = tv;
+ }
+
+ /**
+ * Get the attribute type-value
+ *
+ * @return String containing type value
+ */
+ public String getTypeValue() {
+ return mTypeValue;
+ }
+
+
+ /**
+ * Get the 'version' command
+ *
+ * @param cmd CommandLine containing the command line string with or
+ * without the version flag and string appended
+ */
+ private void getVersionCommand(Commandline cmd) {
+ if (getVersion() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_VERSION);
+ cmd.createArgument().setValue(getVersion());
+ }
+ }
+
+ /**
+ * Get the 'comment' command
+ *
+ * @param cmd containing the command line string with or
+ * without the comment flag and string appended
+ */
+ private void getCommentCommand(Commandline cmd) {
+ if (getComment() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENT);
+ cmd.createArgument().setValue(getComment());
+ }
+ }
+
+ /**
+ * Get the 'commentfile' command
+ *
+ * @param cmd containing the command line string with or
+ * without the commentfile flag and file appended
+ */
+ private void getCommentFileCommand(Commandline cmd) {
+ if (getCommentFile() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENTFILE);
+ cmd.createArgument().setValue(getCommentFile());
+ }
+ }
+
+ /**
+ * Get the attribute type-name
+ *
+ * @param cmd containing the command line string with or
+ * without the type-name
+ */
+ private void getTypeCommand(Commandline cmd) {
+ String typenm = getTypeName();
+
+ if (typenm != null) {
+ cmd.createArgument().setValue(typenm);
+ }
+ }
+
+ /**
+ * Get the attribute type-value
+ *
+ * @param cmd containing the command line string with or
+ * without the type-value
+ */
+ private void getTypeValueCommand(Commandline cmd) {
+ String typevl = getTypeValue();
+
+ if (typevl != null) {
+ if (Os.isFamily("windows")) {
+ typevl = "\\\"" + typevl + "\\\""; // Windows quoting of the value
+ } else {
+ typevl = "\"" + typevl + "\"";
+ }
+ cmd.createArgument().setValue(typevl);
+ }
+ }
+
+ /**
+ * -replace flag -- replace the existing value of the attribute
+ */
+ public static final String FLAG_REPLACE = "-replace";
+ /**
+ * -recurse flag -- process all subdirectories
+ */
+ public static final String FLAG_RECURSE = "-recurse";
+ /**
+ * -version flag -- attach attribute to specified version
+ */
+ public static final String FLAG_VERSION = "-version";
+ /**
+ * -c flag -- comment to attach to the element
+ */
+ public static final String FLAG_COMMENT = "-c";
+ /**
+ * -cfile flag -- file containing a comment to attach to the file
+ */
+ public static final String FLAG_COMMENTFILE = "-cfile";
+ /**
+ * -nc flag -- no comment is specified
+ */
+ public static final String FLAG_NOCOMMENT = "-nc";
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkbl.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkbl.java
new file mode 100644
index 00000000..82c96005
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkbl.java
@@ -0,0 +1,365 @@
+/*
+ * 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.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Task to CreateBaseline command to ClearCase.
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ * <tr>
+ * <th>Attribute</th>
+ * <th>Values</th>
+ * <th>Required</th>
+ * </tr>
+ * <tr>
+ * <td>comment</td>
+ * <td>Specify a comment. Only one of comment or cfile may be
+used.</td>
+ * <td>No</td>
+ * </tr>
+ * <tr>
+ * <td>commentfile</td>
+ * <td>Specify a file containing a comment. Only one of comment or
+cfile may be used.</td>
+ * <td>No</td>
+ * </tr>
+ * <tr>
+ * <td>baselinerootname</td>
+ * <td>Specify the name to be associated with the baseline.</td>
+ * <td>Yes</td>
+ * </tr>
+ * <tr>
+ * <td>nowarn</td>
+ * <td>Suppress warning messages</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>identical</td>
+ * <td>Allows the baseline to be created even if it is identical to the
+previous baseline.</td>
+ * <td>No</td>
+ * </tr>
+ * <tr>
+ * <td>full</td>
+ * <td>Creates a full baseline.</td>
+ * <td>No</td>
+ * </tr>
+ * <tr>
+ * <td>nlabel</td>
+ * <td>Allows the baseline to be created without a label.</td>
+ * <td>No</td>
+ * </tr>
+ * <tr>
+ * <td>failonerr</td>
+ * <td>Throw an exception if the command fails. Default is true</td>
+ * <td>No</td>
+ * <tr>
+ * </table>
+ *
+ */
+public class CCMkbl extends ClearCase {
+ private String mComment = null;
+ private String mCfile = null;
+ private String mBaselineRootName = null;
+ private boolean mNwarn = false;
+ private boolean mIdentical = true;
+ private boolean mFull = false;
+ private boolean mNlabel = false;
+
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute cleartool and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command fails and failonerr is set to true
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ Project aProj = getProject();
+ int result = 0;
+
+ // Default the viewpath to basedir if it is not specified
+ if (getViewPath() == null) {
+ setViewPath(aProj.getBaseDir().getPath());
+ }
+
+ // build the command line from what we got. the format is
+ // cleartool checkin [options...] [viewpath ...]
+ // as specified in the CLEARTOOL.EXE help
+ commandLine.setExecutable(getClearToolCommand());
+ commandLine.createArgument().setValue(COMMAND_MKBL);
+
+ checkOptions(commandLine);
+
+ if (!getFailOnErr()) {
+ getProject().log("Ignoring any errors that occur for: "
+ + getBaselineRootName(), Project.MSG_VERBOSE);
+ }
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnErr()) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ if (getComment() != null) {
+ // -c
+ getCommentCommand(cmd);
+ } else {
+ if (getCommentFile() != null) {
+ // -cfile
+ getCommentFileCommand(cmd);
+ } else {
+ cmd.createArgument().setValue(FLAG_NOCOMMENT);
+ }
+ }
+
+ if (getIdentical()) {
+ // -identical
+ cmd.createArgument().setValue(FLAG_IDENTICAL);
+ }
+
+ if (getFull()) {
+ // -full
+ cmd.createArgument().setValue(FLAG_FULL);
+ } else {
+ // -incremental
+ cmd.createArgument().setValue(FLAG_INCREMENTAL);
+ }
+
+ if (getNlabel()) {
+ // -nlabel
+ cmd.createArgument().setValue(FLAG_NLABEL);
+ }
+
+ // baseline_root_name
+ cmd.createArgument().setValue(getBaselineRootName());
+
+ }
+
+
+ /**
+ * Set comment string
+ *
+ * @param comment the comment string
+ */
+ public void setComment(String comment) {
+ mComment = comment;
+ }
+
+ /**
+ * Get comment string
+ *
+ * @return String containing the comment
+ */
+ public String getComment() {
+ return mComment;
+ }
+
+ /**
+ * Set comment file
+ *
+ * @param cfile the path to the comment file
+ */
+ public void setCommentFile(String cfile) {
+ mCfile = cfile;
+ }
+
+ /**
+ * Get comment file
+ *
+ * @return String containing the path to the comment file
+ */
+ public String getCommentFile() {
+ return mCfile;
+ }
+
+ /**
+ * Set baseline_root_name
+ *
+ * @param baselineRootName the name of the baseline
+ */
+ public void setBaselineRootName(String baselineRootName) {
+ mBaselineRootName = baselineRootName;
+ }
+
+ /**
+ * Get baseline_root_name
+ *
+ * @return String containing the name of the baseline
+ */
+ public String getBaselineRootName() {
+ return mBaselineRootName;
+ }
+
+ /**
+
+ /**
+ * Set the nowarn flag
+ *
+ * @param nwarn the status to set the flag to
+ */
+ public void setNoWarn(boolean nwarn) {
+ mNwarn = nwarn;
+ }
+
+ /**
+ * Get nowarn flag status
+ *
+ * @return boolean containing status of nwarn flag
+ */
+ public boolean getNoWarn() {
+ return mNwarn;
+ }
+
+ /**
+ * Set the identical flag
+ *
+ * @param identical the status to set the flag to
+ */
+ public void setIdentical(boolean identical) {
+ mIdentical = identical;
+ }
+
+ /**
+ * Get identical flag status
+ *
+ * @return boolean containing status of identical flag
+ */
+ public boolean getIdentical() {
+ return mIdentical;
+ }
+
+ /**
+ * Set the full flag
+ *
+ * @param full the status to set the flag to
+ */
+ public void setFull(boolean full) {
+ mFull = full;
+ }
+
+ /**
+ * Get full flag status
+ *
+ * @return boolean containing status of full flag
+ */
+ public boolean getFull() {
+ return mFull;
+ }
+
+ /**
+ * Set the nlabel flag
+ *
+ * @param nlabel the status to set the flag to
+ */
+ public void setNlabel(boolean nlabel) {
+ mNlabel = nlabel;
+ }
+
+ /**
+ * Get nlabel status
+ *
+ * @return boolean containing status of nlabel flag
+ */
+ public boolean getNlabel() {
+ return mNlabel;
+ }
+
+
+ /**
+ * Get the 'comment' command
+ *
+ * @param cmd containing the command line string with or
+ * without the comment flag and string appended
+ */
+ private void getCommentCommand(Commandline cmd) {
+ if (getComment() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENT);
+ cmd.createArgument().setValue(getComment());
+ }
+ }
+
+ /**
+ * Get the 'commentfile' command
+ *
+ * @param cmd CommandLine containing the command line string with or
+ * without the commentfile flag and file appended
+ */
+ private void getCommentFileCommand(Commandline cmd) {
+ if (getCommentFile() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENTFILE);
+ cmd.createArgument().setValue(getCommentFile());
+ }
+ }
+
+
+ /**
+ * -c flag -- comment to attach to the file
+ */
+ public static final String FLAG_COMMENT = "-c";
+ /**
+ * -cfile flag -- file containing a comment to attach to the file
+ */
+ public static final String FLAG_COMMENTFILE = "-cfile";
+ /**
+ * -nc flag -- no comment is specified
+ */
+ public static final String FLAG_NOCOMMENT = "-nc";
+ /**
+ * -identical flag -- allows the file to be checked in even if it is identical to the original
+ */
+ public static final String FLAG_IDENTICAL = "-identical";
+ /**
+ * -incremental flag -- baseline to be created is incremental
+ */
+ public static final String FLAG_INCREMENTAL = "-incremental";
+ /**
+ * -full flag -- baseline to be created is full
+ */
+ public static final String FLAG_FULL = "-full";
+ /**
+ * -nlabel -- baseline to be created without a label
+ */
+ public static final String FLAG_NLABEL = "-nlabel";
+
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkdir.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkdir.java
new file mode 100644
index 00000000..4c89539f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkdir.java
@@ -0,0 +1,237 @@
+/*
+ * 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.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs ClearCase mkdir.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ * <tr>
+ * <th>Attribute</th>
+ * <th>Values</th>
+ * <th>Required</th>
+ * </tr>
+ * <tr>
+ * <td>viewpath</td>
+ * <td>Path to the ClearCase view directory that the command will operate on</td>
+ * <td>Yes</td>
+ * <tr>
+ * <tr>
+ * <td>comment</td>
+ * <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>commentfile</td>
+ * <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>nocheckout</td>
+ * <td>Do not checkout after element creation</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>failonerr</td>
+ * <td>Throw an exception if the command fails. Default is true</td>
+ * <td>No</td>
+ * <tr>
+ * </table>
+ *
+ */
+public class CCMkdir extends ClearCase {
+ private String mComment = null;
+ private String mCfile = null;
+ private boolean mNoco = false;
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute cleartool and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command fails and failonerr is set to true
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ Project aProj = getProject();
+ int result = 0;
+
+ // Default the viewpath to basedir if it is not specified
+ if (getViewPath() == null) {
+ setViewPath(aProj.getBaseDir().getPath());
+ }
+
+ // build the command line from what we got. the format is
+ // cleartool mkelem [options...] [viewpath ...]
+ // as specified in the CLEARTOOL.EXE help
+ commandLine.setExecutable(getClearToolCommand());
+ commandLine.createArgument().setValue(COMMAND_MKDIR);
+
+ checkOptions(commandLine);
+
+ if (!getFailOnErr()) {
+ getProject().log("Ignoring any errors that occur for: "
+ + getViewPathBasename(), Project.MSG_VERBOSE);
+ }
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnErr()) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ if (getComment() != null) {
+ // -c
+ getCommentCommand(cmd);
+ } else {
+ if (getCommentFile() != null) {
+ // -cfile
+ getCommentFileCommand(cmd);
+ } else {
+ cmd.createArgument().setValue(FLAG_NOCOMMENT);
+ }
+ }
+ if (getNoCheckout()) {
+ // -nco
+ cmd.createArgument().setValue(FLAG_NOCHECKOUT);
+ }
+ // viewpath
+ cmd.createArgument().setValue(getViewPath());
+ }
+
+ /**
+ * Sets the comment string.
+ *
+ * @param comment the comment string
+ */
+ public void setComment(String comment) {
+ mComment = comment;
+ }
+
+ /**
+ * Get comment string
+ *
+ * @return String containing the comment
+ */
+ public String getComment() {
+ return mComment;
+ }
+
+ /**
+ * Specifies a file containing a comment.
+ *
+ * @param cfile the path to the comment file
+ */
+ public void setCommentFile(String cfile) {
+ mCfile = cfile;
+ }
+
+ /**
+ * Get comment file
+ *
+ * @return String containing the path to the comment file
+ */
+ public String getCommentFile() {
+ return mCfile;
+ }
+
+ /**
+ * If true, do not checkout element after creation.
+ *
+ * @param co the status to set the flag to
+ */
+ public void setNoCheckout(boolean co) {
+ mNoco = co;
+ }
+
+ /**
+ * Get no checkout flag status
+ *
+ * @return boolean containing status of noco flag
+ */
+ public boolean getNoCheckout() {
+ return mNoco;
+ }
+
+
+ /**
+ * Get the 'comment' command
+ *
+ * @param cmd containing the command line string with or
+ * without the comment flag and string appended
+ */
+ private void getCommentCommand(Commandline cmd) {
+ if (getComment() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENT);
+ cmd.createArgument().setValue(getComment());
+ }
+ }
+
+ /**
+ * Get the 'commentfile' command
+ *
+ * @param cmd containing the command line string with or
+ * without the commentfile flag and file appended
+ */
+ private void getCommentFileCommand(Commandline cmd) {
+ if (getCommentFile() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENTFILE);
+ cmd.createArgument().setValue(getCommentFile());
+ }
+ }
+
+ /**
+ * -c flag -- comment to attach to the directory
+ */
+ public static final String FLAG_COMMENT = "-c";
+ /**
+ * -cfile flag -- file containing a comment to attach to the directory
+ */
+ public static final String FLAG_COMMENTFILE = "-cfile";
+ /**
+ * -nc flag -- no comment is specified
+ */
+ public static final String FLAG_NOCOMMENT = "-nc";
+ /**
+ * -nco flag -- do not checkout element after creation
+ */
+ public static final String FLAG_NOCHECKOUT = "-nco";
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkelem.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkelem.java
new file mode 100644
index 00000000..94faa5a6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkelem.java
@@ -0,0 +1,424 @@
+/*
+ * 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.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs ClearCase mkelem.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ * <tr>
+ * <th>Attribute</th>
+ * <th>Values</th>
+ * <th>Required</th>
+ * </tr>
+ * <tr>
+ * <td>viewpath</td>
+ * <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ * <td>Yes</td>
+ * <tr>
+ * <tr>
+ * <td>comment</td>
+ * <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>commentfile</td>
+ * <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>nowarn</td>
+ * <td>Suppress warning messages</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>nocheckout</td>
+ * <td>Do not checkout after element creation</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>checkin</td>
+ * <td>Checkin element after creation</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>preservetime</td>
+ * <td>Preserve the modification time (for checkin)</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>master</td>
+ * <td>Assign mastership of the main branch to the current site</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>eltype</td>
+ * <td>Element type to use during element creation</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>failonerr</td>
+ * <td>Throw an exception if the command fails. Default is true</td>
+ * <td>No</td>
+ * <tr>
+ * </table>
+ *
+ */
+public class CCMkelem extends ClearCase {
+ private String mComment = null;
+ private String mCfile = null;
+ private boolean mNwarn = false;
+ private boolean mPtime = false;
+ private boolean mNoco = false;
+ private boolean mCheckin = false;
+ private boolean mMaster = false;
+ private String mEltype = null;
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute cleartool and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command fails and failonerr is set to true
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ Project aProj = getProject();
+ int result = 0;
+
+ // Default the viewpath to basedir if it is not specified
+ if (getViewPath() == null) {
+ setViewPath(aProj.getBaseDir().getPath());
+ }
+
+ // build the command line from what we got. the format is
+ // cleartool mkelem [options...] [viewpath ...]
+ // as specified in the CLEARTOOL.EXE help
+ commandLine.setExecutable(getClearToolCommand());
+ commandLine.createArgument().setValue(COMMAND_MKELEM);
+
+ checkOptions(commandLine);
+
+ if (!getFailOnErr()) {
+ getProject().log("Ignoring any errors that occur for: "
+ + getViewPathBasename(), Project.MSG_VERBOSE);
+ }
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnErr()) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ if (getComment() != null) {
+ // -c
+ getCommentCommand(cmd);
+ } else {
+ if (getCommentFile() != null) {
+ // -cfile
+ getCommentFileCommand(cmd);
+ } else {
+ cmd.createArgument().setValue(FLAG_NOCOMMENT);
+ }
+ }
+
+ if (getNoWarn()) {
+ // -nwarn
+ cmd.createArgument().setValue(FLAG_NOWARN);
+ }
+ /*
+ * Should choose either -ci or -nco.
+ */
+ if (getNoCheckout() && getCheckin()) {
+ throw new BuildException("Should choose either [nocheckout | checkin]");
+ }
+ if (getNoCheckout()) {
+ // -nco
+ cmd.createArgument().setValue(FLAG_NOCHECKOUT);
+ }
+ if (getCheckin()) {
+ // -ci
+ cmd.createArgument().setValue(FLAG_CHECKIN);
+ if (getPreserveTime()) {
+ // -ptime
+ cmd.createArgument().setValue(FLAG_PRESERVETIME);
+ }
+ }
+ if (getMaster()) {
+ // -master
+ cmd.createArgument().setValue(FLAG_MASTER);
+ }
+ if (getEltype() != null) {
+ // -eltype
+ getEltypeCommand(cmd);
+ }
+ // viewpath
+ cmd.createArgument().setValue(getViewPath());
+ }
+
+ /**
+ * Sets the comment string.
+ *
+ * @param comment the comment string
+ */
+ public void setComment(String comment) {
+ mComment = comment;
+ }
+
+ /**
+ * Get comment string
+ *
+ * @return String containing the comment
+ */
+ public String getComment() {
+ return mComment;
+ }
+
+ /**
+ * Specifies a file containing a comment.
+ *
+ * @param cfile the path to the comment file
+ */
+ public void setCommentFile(String cfile) {
+ mCfile = cfile;
+ }
+
+ /**
+ * Get comment file
+ *
+ * @return String containing the path to the comment file
+ */
+ public String getCommentFile() {
+ return mCfile;
+ }
+
+ /**
+ * If true, suppress warning messages.
+ *
+ * @param nwarn the status to set the flag to
+ */
+ public void setNoWarn(boolean nwarn) {
+ mNwarn = nwarn;
+ }
+
+ /**
+ * Get nowarn flag status
+ *
+ * @return boolean containing status of nwarn flag
+ */
+ public boolean getNoWarn() {
+ return mNwarn;
+ }
+
+ /**
+ * If true, preserve the modification time.
+ *
+ * @param ptime the status to set the flag to
+ */
+ public void setPreserveTime(boolean ptime) {
+ mPtime = ptime;
+ }
+
+ /**
+ * Get preservetime flag status
+ *
+ * @return boolean containing status of preservetime flag
+ */
+ public boolean getPreserveTime() {
+ return mPtime;
+ }
+
+ /**
+ * If true, do not checkout element after creation.
+ *
+ * @param co the status to set the flag to
+ */
+ public void setNoCheckout(boolean co) {
+ mNoco = co;
+ }
+
+ /**
+ * Get no checkout flag status
+ *
+ * @return boolean containing status of noco flag
+ */
+ public boolean getNoCheckout() {
+ return mNoco;
+ }
+
+ /**
+ * If true, checkin the element after creation
+ *
+ * @param ci the status to set the flag to
+ */
+ public void setCheckin(boolean ci) {
+ mCheckin = ci;
+ }
+
+ /**
+ * Get ci flag status
+ *
+ * @return boolean containing status of ci flag
+ */
+ public boolean getCheckin() {
+ return mCheckin;
+ }
+
+ /**
+ * If true, changes mastership of the main branch
+ * to the current site
+ *
+ * @param master the status to set the flag to
+ */
+ public void setMaster(boolean master) {
+ mMaster = master;
+ }
+
+ /**
+ * Get master flag status
+ *
+ * @return boolean containing status of master flag
+ */
+ public boolean getMaster() {
+ return mMaster;
+ }
+
+ /**
+ * Specifies the element type to use.
+ *
+ * @param eltype to create element
+ */
+ public void setEltype(String eltype) {
+ mEltype = eltype;
+ }
+
+ /**
+ * Get element type
+ *
+ * @return String containing the element type
+ */
+ public String getEltype() {
+ return mEltype;
+ }
+
+
+ /**
+ * Get the 'comment' command
+ *
+ * @param cmd containing the command line string with or
+ * without the comment flag and string appended
+ */
+ private void getCommentCommand(Commandline cmd) {
+ if (getComment() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENT);
+ cmd.createArgument().setValue(getComment());
+ }
+ }
+
+ /**
+ * Get the 'commentfile' command
+ *
+ * @param cmd containing the command line string with or
+ * without the commentfile flag and file appended
+ */
+ private void getCommentFileCommand(Commandline cmd) {
+ if (getCommentFile() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENTFILE);
+ cmd.createArgument().setValue(getCommentFile());
+ }
+ }
+
+ /**
+ * Get the 'element type' command
+ *
+ * @param cmd containing the command line string with or
+ * without the comment flag and string appended
+ */
+ private void getEltypeCommand(Commandline cmd) {
+ if (getEltype() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_ELTYPE);
+ cmd.createArgument().setValue(getEltype());
+ }
+ }
+
+ /**
+ * -c flag -- comment to attach to the file
+ */
+ public static final String FLAG_COMMENT = "-c";
+ /**
+ * -cfile flag -- file containing a comment to attach to the file
+ */
+ public static final String FLAG_COMMENTFILE = "-cfile";
+ /**
+ * -nc flag -- no comment is specified
+ */
+ public static final String FLAG_NOCOMMENT = "-nc";
+ /**
+ * -nwarn flag -- suppresses warning messages
+ */
+ public static final String FLAG_NOWARN = "-nwarn";
+ /**
+ * -ptime flag -- preserves the modification time on checkin
+ */
+ public static final String FLAG_PRESERVETIME = "-ptime";
+ /**
+ * -nco flag -- do not checkout element after creation
+ */
+ public static final String FLAG_NOCHECKOUT = "-nco";
+ /**
+ * -ci flag -- checkin element after creation
+ */
+ public static final String FLAG_CHECKIN = "-ci";
+ /**
+ * -master flag -- change mastership of main branch to current site
+ */
+ public static final String FLAG_MASTER = "-master";
+ /**
+ * -eltype flag -- element type to use during creation
+ */
+ public static final String FLAG_ELTYPE = "-eltype";
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklabel.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklabel.java
new file mode 100644
index 00000000..e3d288df
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklabel.java
@@ -0,0 +1,402 @@
+/*
+ * 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.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Task to perform mklabel command to ClearCase.
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ * <tr>
+ * <th>Attribute</th>
+ * <th>Values</th>
+ * <th>Required</th>
+ * </tr>
+ * <tr>
+ * <td>viewpath</td>
+ * <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>replace</td>
+ * <td>Replace a label of the same type on the same branch</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>recurse</td>
+ * <td>Process each subdirectory under viewpath</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>version</td>
+ * <td>Identify a specific version to attach the label to</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>typename</td>
+ * <td>Name of the label type</td>
+ * <td>Yes</td>
+ * <tr>
+ * <tr>
+ * <td>vob</td>
+ * <td>Name of the VOB</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>comment</td>
+ * <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>commentfile</td>
+ * <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>failonerr</td>
+ * <td>Throw an exception if the command fails. Default is true</td>
+ * <td>No</td>
+ * <tr>
+ * </table>
+ *
+ */
+public class CCMklabel extends ClearCase {
+ private boolean mReplace = false;
+ private boolean mRecurse = false;
+ private String mVersion = null;
+ private String mTypeName = null;
+ private String mVOB = null;
+ private String mComment = null;
+ private String mCfile = null;
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute cleartool and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command fails and failonerr is set to true
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ Project aProj = getProject();
+ int result = 0;
+
+ // Check for required attributes
+ if (getTypeName() == null) {
+ throw new BuildException("Required attribute TypeName not specified");
+ }
+
+ // Default the viewpath to basedir if it is not specified
+ if (getViewPath() == null) {
+ setViewPath(aProj.getBaseDir().getPath());
+ }
+
+ // build the command line from what we got. the format is
+ // cleartool mklabel [options...] [viewpath ...]
+ // as specified in the CLEARTOOL help
+ commandLine.setExecutable(getClearToolCommand());
+ commandLine.createArgument().setValue(COMMAND_MKLABEL);
+
+ checkOptions(commandLine);
+
+ if (!getFailOnErr()) {
+ getProject().log("Ignoring any errors that occur for: "
+ + getViewPathBasename(), Project.MSG_VERBOSE);
+ }
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnErr()) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ if (getReplace()) {
+ // -replace
+ cmd.createArgument().setValue(FLAG_REPLACE);
+ }
+
+ if (getRecurse()) {
+ // -recurse
+ cmd.createArgument().setValue(FLAG_RECURSE);
+ }
+
+ if (getVersion() != null) {
+ // -version
+ getVersionCommand(cmd);
+ }
+
+ if (getComment() != null) {
+ // -c
+ getCommentCommand(cmd);
+ } else {
+ if (getCommentFile() != null) {
+ // -cfile
+ getCommentFileCommand(cmd);
+ } else {
+ cmd.createArgument().setValue(FLAG_NOCOMMENT);
+ }
+ }
+
+ if (getTypeName() != null) {
+ // type
+ getTypeCommand(cmd);
+ }
+
+ // viewpath
+ cmd.createArgument().setValue(getViewPath());
+ }
+
+
+ /**
+ * Set the replace flag
+ *
+ * @param replace the status to set the flag to
+ */
+ public void setReplace(boolean replace) {
+ mReplace = replace;
+ }
+
+ /**
+ * Get replace flag status
+ *
+ * @return boolean containing status of replace flag
+ */
+ public boolean getReplace() {
+ return mReplace;
+ }
+
+ /**
+ * Set recurse flag
+ *
+ * @param recurse the status to set the flag to
+ */
+ public void setRecurse(boolean recurse) {
+ mRecurse = recurse;
+ }
+
+ /**
+ * Get recurse flag status
+ *
+ * @return boolean containing status of recurse flag
+ */
+ public boolean getRecurse() {
+ return mRecurse;
+ }
+
+ /**
+ * Set the version flag
+ *
+ * @param version the status to set the flag to
+ */
+ public void setVersion(String version) {
+ mVersion = version;
+ }
+
+ /**
+ * Get version flag status
+ *
+ * @return boolean containing status of version flag
+ */
+ public String getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * Set comment string
+ *
+ * @param comment the comment string
+ */
+ public void setComment(String comment) {
+ mComment = comment;
+ }
+
+ /**
+ * Get comment string
+ *
+ * @return String containing the comment
+ */
+ public String getComment() {
+ return mComment;
+ }
+
+ /**
+ * Set comment file
+ *
+ * @param cfile the path to the comment file
+ */
+ public void setCommentFile(String cfile) {
+ mCfile = cfile;
+ }
+
+ /**
+ * Get comment file
+ *
+ * @return String containing the path to the comment file
+ */
+ public String getCommentFile() {
+ return mCfile;
+ }
+
+ /**
+ * Set the type-name
+ *
+ * @param tn the type name
+ */
+ public void setTypeName(String tn) {
+ mTypeName = tn;
+ }
+
+ /**
+ * Get type-name
+ *
+ * @return String containing type name
+ */
+ public String getTypeName() {
+ return mTypeName;
+ }
+
+ /**
+ * Set the VOB name
+ *
+ * @param vob the VOB name
+ */
+ public void setVOB(String vob) {
+ mVOB = vob;
+ }
+
+ /**
+ * Get VOB name
+ *
+ * @return String containing VOB name
+ */
+ public String getVOB() {
+ return mVOB;
+ }
+
+
+ /**
+ * Get the 'version' command
+ *
+ * @param cmd CommandLine containing the command line string with or
+ * without the version flag and string appended
+ */
+ private void getVersionCommand(Commandline cmd) {
+ if (getVersion() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_VERSION);
+ cmd.createArgument().setValue(getVersion());
+ }
+ }
+
+ /**
+ * Get the 'comment' command
+ *
+ * @param cmd containing the command line string with or
+ * without the comment flag and string appended
+ */
+ private void getCommentCommand(Commandline cmd) {
+ if (getComment() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENT);
+ cmd.createArgument().setValue(getComment());
+ }
+ }
+
+ /**
+ * Get the 'commentfile' command
+ *
+ * @param cmd containing the command line string with or
+ * without the commentfile flag and file appended
+ */
+ private void getCommentFileCommand(Commandline cmd) {
+ if (getCommentFile() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENTFILE);
+ cmd.createArgument().setValue(getCommentFile());
+ }
+ }
+
+ /**
+ * Get the type-name
+ *
+ * @param cmd containing the command line string with or
+ * without the type-name
+ */
+ private void getTypeCommand(Commandline cmd) {
+ String typenm = null;
+
+ if (getTypeName() != null) {
+ typenm = getTypeName();
+ if (getVOB() != null) {
+ typenm += "@" + getVOB();
+ }
+ cmd.createArgument().setValue(typenm);
+ }
+ }
+
+
+ /**
+ * -replace flag -- replace another label of the same type
+ */
+ public static final String FLAG_REPLACE = "-replace";
+ /**
+ * -recurse flag -- process all subdirectories
+ */
+ public static final String FLAG_RECURSE = "-recurse";
+ /**
+ * -version flag -- attach label to specified version
+ */
+ public static final String FLAG_VERSION = "-version";
+ /**
+ * -c flag -- comment to attach to the file
+ */
+ public static final String FLAG_COMMENT = "-c";
+ /**
+ * -cfile flag -- file containing a comment to attach to the file
+ */
+ public static final String FLAG_COMMENTFILE = "-cfile";
+ /**
+ * -nc flag -- no comment is specified
+ */
+ public static final String FLAG_NOCOMMENT = "-nc";
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklbtype.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklbtype.java
new file mode 100644
index 00000000..7bb7192e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklbtype.java
@@ -0,0 +1,440 @@
+/*
+ * 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.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Task to perform mklbtype command to ClearCase.
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ * <tr>
+ * <th>Attribute</th>
+ * <th>Values</th>
+ * <th>Required</th>
+ * </tr>
+ * <tr>
+ * <td>typename</td>
+ * <td>Name of the label type to create</td>
+ * <td>Yes</td>
+ * <tr>
+ * <tr>
+ * <td>vob</td>
+ * <td>Name of the VOB</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>replace</td>
+ * <td>Replace an existing label definition of the same type</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>global</td>
+ * <td>Either global or ordinary can be specified, not both.
+ * Creates a label type that is global to the VOB or to
+ * VOBs that use this VOB</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>ordinary</td>
+ * <td>Either global or ordinary can be specified, not both.
+ * Creates a label type that can be used only in the current
+ * VOB. <B>Default</B></td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>pbranch</td>
+ * <td>Allows the label type to be used once per branch in a given
+ * element's version tree</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>shared</td>
+ * <td>Sets the way mastership is checked by ClearCase. See ClearCase
+ * documentation for details</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>comment</td>
+ * <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>commentfile</td>
+ * <td>Specify a file containing a comment. Only one of comment or
+ * cfile may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>failonerr</td>
+ * <td>Throw an exception if the command fails. Default is true</td>
+ * <td>No</td>
+ * <tr>
+ * </table>
+ *
+ */
+public class CCMklbtype extends ClearCase {
+ private String mTypeName = null;
+ private String mVOB = null;
+ private String mComment = null;
+ private String mCfile = null;
+ private boolean mReplace = false;
+ private boolean mGlobal = false;
+ private boolean mOrdinary = true;
+ private boolean mPbranch = false;
+ private boolean mShared = false;
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute cleartool and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command fails and failonerr is set to true
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ int result = 0;
+
+ // Check for required attributes
+ if (getTypeName() == null) {
+ throw new BuildException("Required attribute TypeName not specified");
+ }
+
+ // build the command line from what we got. the format is
+ // cleartool mklbtype [options...] type-selector...
+ // as specified in the CLEARTOOL help
+ commandLine.setExecutable(getClearToolCommand());
+ commandLine.createArgument().setValue(COMMAND_MKLBTYPE);
+
+ checkOptions(commandLine);
+
+ if (!getFailOnErr()) {
+ getProject().log("Ignoring any errors that occur for: "
+ + getTypeSpecifier(), Project.MSG_VERBOSE);
+ }
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnErr()) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ if (getReplace()) {
+ // -replace
+ cmd.createArgument().setValue(FLAG_REPLACE);
+ }
+
+ if (getOrdinary()) {
+ // -ordinary
+ cmd.createArgument().setValue(FLAG_ORDINARY);
+ } else {
+ if (getGlobal()) {
+ // -global
+ cmd.createArgument().setValue(FLAG_GLOBAL);
+ }
+ }
+
+ if (getPbranch()) {
+ // -pbranch
+ cmd.createArgument().setValue(FLAG_PBRANCH);
+ }
+
+ if (getShared()) {
+ // -shared
+ cmd.createArgument().setValue(FLAG_SHARED);
+ }
+
+ if (getComment() != null) {
+ // -c
+ getCommentCommand(cmd);
+ } else {
+ if (getCommentFile() != null) {
+ // -cfile
+ getCommentFileCommand(cmd);
+ } else {
+ cmd.createArgument().setValue(FLAG_NOCOMMENT);
+ }
+ }
+
+ // type-name@vob
+ cmd.createArgument().setValue(getTypeSpecifier());
+ }
+
+
+ /**
+ * Set type-name string
+ *
+ * @param tn the type-name string
+ */
+ public void setTypeName(String tn) {
+ mTypeName = tn;
+ }
+
+ /**
+ * Get type-name string
+ *
+ * @return String containing the type-name
+ */
+ public String getTypeName() {
+ return mTypeName;
+ }
+
+ /**
+ * Set the VOB name
+ *
+ * @param vob the VOB name
+ */
+ public void setVOB(String vob) {
+ mVOB = vob;
+ }
+
+ /**
+ * Get VOB name
+ *
+ * @return String containing VOB name
+ */
+ public String getVOB() {
+ return mVOB;
+ }
+
+ /**
+ * Set the replace flag
+ *
+ * @param repl the status to set the flag to
+ */
+ public void setReplace(boolean repl) {
+ mReplace = repl;
+ }
+
+ /**
+ * Get replace flag status
+ *
+ * @return boolean containing status of replace flag
+ */
+ public boolean getReplace() {
+ return mReplace;
+ }
+
+ /**
+ * Set the global flag
+ *
+ * @param glob the status to set the flag to
+ */
+ public void setGlobal(boolean glob) {
+ mGlobal = glob;
+ }
+
+ /**
+ * Get global flag status
+ *
+ * @return boolean containing status of global flag
+ */
+ public boolean getGlobal() {
+ return mGlobal;
+ }
+
+ /**
+ * Set the ordinary flag
+ *
+ * @param ordinary the status to set the flag to
+ */
+ public void setOrdinary(boolean ordinary) {
+ mOrdinary = ordinary;
+ }
+
+ /**
+ * Get ordinary flag status
+ *
+ * @return boolean containing status of ordinary flag
+ */
+ public boolean getOrdinary() {
+ return mOrdinary;
+ }
+
+ /**
+ * Set the pbranch flag
+ *
+ * @param pbranch the status to set the flag to
+ */
+ public void setPbranch(boolean pbranch) {
+ mPbranch = pbranch;
+ }
+
+ /**
+ * Get pbranch flag status
+ *
+ * @return boolean containing status of pbranch flag
+ */
+ public boolean getPbranch() {
+ return mPbranch;
+ }
+
+ /**
+ * Set the shared flag
+ *
+ * @param shared the status to set the flag to
+ */
+ public void setShared(boolean shared) {
+ mShared = shared;
+ }
+
+ /**
+ * Get shared flag status
+ *
+ * @return boolean containing status of shared flag
+ */
+ public boolean getShared() {
+ return mShared;
+ }
+
+ /**
+ * Set comment string
+ *
+ * @param comment the comment string
+ */
+ public void setComment(String comment) {
+ mComment = comment;
+ }
+
+ /**
+ * Get comment string
+ *
+ * @return String containing the comment
+ */
+ public String getComment() {
+ return mComment;
+ }
+
+ /**
+ * Set comment file
+ *
+ * @param cfile the path to the comment file
+ */
+ public void setCommentFile(String cfile) {
+ mCfile = cfile;
+ }
+
+ /**
+ * Get comment file
+ *
+ * @return String containing the path to the comment file
+ */
+ public String getCommentFile() {
+ return mCfile;
+ }
+
+
+ /**
+ * Get the 'comment' command
+ *
+ * @param cmd containing the command line string with or
+ * without the comment flag and string appended
+ */
+ private void getCommentCommand(Commandline cmd) {
+ if (getComment() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENT);
+ cmd.createArgument().setValue(getComment());
+ }
+ }
+
+ /**
+ * Get the 'commentfile' command
+ *
+ * @param cmd containing the command line string with or
+ * without the commentfile flag and file appended
+ */
+ private void getCommentFileCommand(Commandline cmd) {
+ if (getCommentFile() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENTFILE);
+ cmd.createArgument().setValue(getCommentFile());
+ }
+ }
+
+ /**
+ * Get the type-name specifier
+ *
+ * @return the 'type-name-specifier' command if the attribute was
+ * specified, otherwise an empty string
+ */
+ private String getTypeSpecifier() {
+ String typenm = null;
+
+ typenm = getTypeName();
+ if (getVOB() != null) {
+ typenm += "@" + getVOB();
+ }
+
+ return typenm;
+ }
+
+
+ /**
+ * -replace flag -- replace existing label definition of the same type
+ */
+ public static final String FLAG_REPLACE = "-replace";
+ /**
+ * -global flag -- creates a label type that is global to the VOB or to VOBs that use this VOB
+ */
+ public static final String FLAG_GLOBAL = "-global";
+ /**
+ * -ordinary flag -- creates a label type that can be used only in the current VOB
+ */
+ public static final String FLAG_ORDINARY = "-ordinary";
+ /**
+ * -pbranch flag -- allows label type to be used once per branch
+ */
+ public static final String FLAG_PBRANCH = "-pbranch";
+ /**
+ * -shared flag -- sets the way mastership is checked by ClearCase
+ */
+ public static final String FLAG_SHARED = "-shared";
+ /**
+ * -c flag -- comment to attach to the file
+ */
+ public static final String FLAG_COMMENT = "-c";
+ /**
+ * -cfile flag -- file containing a comment to attach to the file
+ */
+ public static final String FLAG_COMMENTFILE = "-cfile";
+ /**
+ * -nc flag -- no comment is specified
+ */
+ public static final String FLAG_NOCOMMENT = "-nc";
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCRmtype.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCRmtype.java
new file mode 100644
index 00000000..cef0c3a5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCRmtype.java
@@ -0,0 +1,373 @@
+/*
+ * 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.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Task to perform rmtype command to ClearCase.
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ * <tr>
+ * <th>Attribute</th>
+ * <th>Values</th>
+ * <th>Required</th>
+ * </tr>
+ * <tr>
+ * <td>typekind</td>
+ * <td>The kind of type to create. Valid types are:<br>
+ * attype attribute type<br>
+ * brtype branch type<br>
+ * eltype element type<br>
+ * hltype hyperlink type<br>
+ * lbtype label type<br>
+ * trtype trigger type<br>
+ * </td>
+ * <td>Yes</td>
+ * <tr>
+ * <tr>
+ * <td>typename</td>
+ * <td>The name of the type to remove</td>
+ * <td>Yes</td>
+ * <tr>
+ * <tr>
+ * <td>vob</td>
+ * <td>Name of the VOB</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>ignore</td>
+ * <td>Used with trigger types only. Forces removal of trigger type
+ * even if a pre-operation trigger would prevent its removal</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>rmall</td>
+ * <td>Removes all instances of a type and the type object itself</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>comment</td>
+ * <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>commentfile</td>
+ * <td>Specify a file containing a comment. Only one of comment or cfile
+ * may be used.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>failonerr</td>
+ * <td>Throw an exception if the command fails. Default is true</td>
+ * <td>No</td>
+ * <tr>
+ * </table>
+ *
+ */
+public class CCRmtype extends ClearCase {
+ private String mTypeKind = null;
+ private String mTypeName = null;
+ private String mVOB = null;
+ private String mComment = null;
+ private String mCfile = null;
+ private boolean mRmall = false;
+ private boolean mIgnore = false;
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute cleartool and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command fails and failonerr is set to true
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ int result = 0;
+
+ // Check for required attributes
+ if (getTypeKind() == null) {
+ throw new BuildException("Required attribute TypeKind not specified");
+ }
+ if (getTypeName() == null) {
+ throw new BuildException("Required attribute TypeName not specified");
+ }
+
+ // build the command line from what we got. the format is
+ // cleartool rmtype [options...] type-selector...
+ // as specified in the CLEARTOOL help
+ commandLine.setExecutable(getClearToolCommand());
+ commandLine.createArgument().setValue(COMMAND_RMTYPE);
+
+ checkOptions(commandLine);
+
+ if (!getFailOnErr()) {
+ getProject().log("Ignoring any errors that occur for: "
+ + getTypeSpecifier(), Project.MSG_VERBOSE);
+ }
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnErr()) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ if (getIgnore()) {
+ // -ignore
+ cmd.createArgument().setValue(FLAG_IGNORE);
+ }
+ if (getRmAll()) {
+ // -rmall -force
+ cmd.createArgument().setValue(FLAG_RMALL);
+ cmd.createArgument().setValue(FLAG_FORCE);
+ }
+ if (getComment() != null) {
+ // -c
+ getCommentCommand(cmd);
+ } else {
+ if (getCommentFile() != null) {
+ // -cfile
+ getCommentFileCommand(cmd);
+ } else {
+ cmd.createArgument().setValue(FLAG_NOCOMMENT);
+ }
+ }
+
+ // type-kind:type-name
+ cmd.createArgument().setValue(getTypeSpecifier());
+ }
+
+ /**
+ * Set the ignore flag
+ *
+ * @param ignore the status to set the flag to
+ */
+ public void setIgnore(boolean ignore) {
+ mIgnore = ignore;
+ }
+
+ /**
+ * Get ignore flag status
+ *
+ * @return boolean containing status of ignore flag
+ */
+ public boolean getIgnore() {
+ return mIgnore;
+ }
+
+ /**
+ * Set rmall flag
+ *
+ * @param rmall the status to set the flag to
+ */
+ public void setRmAll(boolean rmall) {
+ mRmall = rmall;
+ }
+
+ /**
+ * Get rmall flag status
+ *
+ * @return boolean containing status of rmall flag
+ */
+ public boolean getRmAll() {
+ return mRmall;
+ }
+
+ /**
+ * Set comment string
+ *
+ * @param comment the comment string
+ */
+ public void setComment(String comment) {
+ mComment = comment;
+ }
+
+ /**
+ * Get comment string
+ *
+ * @return String containing the comment
+ */
+ public String getComment() {
+ return mComment;
+ }
+
+ /**
+ * Set comment file
+ *
+ * @param cfile the path to the comment file
+ */
+ public void setCommentFile(String cfile) {
+ mCfile = cfile;
+ }
+
+ /**
+ * Get comment file
+ *
+ * @return String containing the path to the comment file
+ */
+ public String getCommentFile() {
+ return mCfile;
+ }
+
+ /**
+ * Set type-kind string
+ *
+ * @param tk the type-kind string
+ */
+ public void setTypeKind(String tk) {
+ mTypeKind = tk;
+ }
+
+ /**
+ * Get type-kind string
+ *
+ * @return String containing the type-kind
+ */
+ public String getTypeKind() {
+ return mTypeKind;
+ }
+
+ /**
+ * Set type-name string
+ *
+ * @param tn the type-name string
+ */
+ public void setTypeName(String tn) {
+ mTypeName = tn;
+ }
+
+ /**
+ * Get type-name string
+ *
+ * @return String containing the type-name
+ */
+ public String getTypeName() {
+ return mTypeName;
+ }
+
+ /**
+ * Set the VOB name
+ *
+ * @param vob the VOB name
+ */
+ public void setVOB(String vob) {
+ mVOB = vob;
+ }
+
+ /**
+ * Get VOB name
+ *
+ * @return String containing VOB name
+ */
+ public String getVOB() {
+ return mVOB;
+ }
+
+ /**
+ * Get the 'type-specifier' string
+ *
+ * @return the 'type-kind:type-name@vob' specifier
+ *
+ */
+ private String getTypeSpecifier() {
+ String tkind = getTypeKind();
+ String tname = getTypeName();
+ String typeSpec = null;
+
+ // Return the type-selector
+ typeSpec = tkind + ":" + tname;
+ if (getVOB() != null) {
+ typeSpec += "@" + getVOB();
+ }
+ return typeSpec;
+ }
+
+ /**
+ * Get the 'comment' command
+ *
+ * @param cmd containing the command line string with or
+ * without the comment flag and string appended
+ */
+ private void getCommentCommand(Commandline cmd) {
+ if (getComment() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENT);
+ cmd.createArgument().setValue(getComment());
+ }
+ }
+
+ /**
+ * Get the 'commentfile' command
+ *
+ * @param cmd containing the command line string with or
+ * without the commentfile flag and file appended
+ */
+ private void getCommentFileCommand(Commandline cmd) {
+ if (getCommentFile() != null) {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENTFILE);
+ cmd.createArgument().setValue(getCommentFile());
+ }
+ }
+
+
+ /**
+ * -ignore flag -- ignore pre-trigger operations when removing a trigger type
+ */
+ public static final String FLAG_IGNORE = "-ignore";
+ /**
+ * -rmall flag -- removes all instances of a type and the type object itself
+ */
+ public static final String FLAG_RMALL = "-rmall";
+ /**
+ * -force flag -- suppresses confirmation prompts
+ */
+ public static final String FLAG_FORCE = "-force";
+ /**
+ * -c flag -- comment to attach to the file
+ */
+ public static final String FLAG_COMMENT = "-c";
+ /**
+ * -cfile flag -- file containing a comment to attach to the file
+ */
+ public static final String FLAG_COMMENTFILE = "-cfile";
+ /**
+ * -nc flag -- no comment is specified
+ */
+ public static final String FLAG_NOCOMMENT = "-nc";
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnCheckout.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnCheckout.java
new file mode 100644
index 00000000..3c00e1af
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnCheckout.java
@@ -0,0 +1,141 @@
+/*
+ * 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.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs ClearCase UnCheckout command.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ * <tr>
+ * <th>Attribute</th>
+ * <th>Values</th>
+ * <th>Required</th>
+ * </tr>
+ * <tr>
+ * <td>viewpath</td>
+ * <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>keepcopy</td>
+ * <td>Specifies whether to keep a copy of the file with a .keep extension or not</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>failonerr</td>
+ * <td>Throw an exception if the command fails. Default is true</td>
+ * <td>No</td>
+ * <tr>
+ * </table>
+ *
+ */
+public class CCUnCheckout extends ClearCase {
+ private boolean mKeep = false;
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute cleartool and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command fails and failonerr is set to true
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ Project aProj = getProject();
+ int result = 0;
+
+ // Default the viewpath to basedir if it is not specified
+ if (getViewPath() == null) {
+ setViewPath(aProj.getBaseDir().getPath());
+ }
+
+ // build the command line from what we got the format is
+ // cleartool uncheckout [options...] [viewpath ...]
+ // as specified in the CLEARTOOL.EXE help
+ commandLine.setExecutable(getClearToolCommand());
+ commandLine.createArgument().setValue(COMMAND_UNCHECKOUT);
+
+ checkOptions(commandLine);
+
+ if (!getFailOnErr()) {
+ getProject().log("Ignoring any errors that occur for: "
+ + getViewPathBasename(), Project.MSG_VERBOSE);
+ }
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnErr()) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ // ClearCase items
+ if (getKeepCopy()) {
+ // -keep
+ cmd.createArgument().setValue(FLAG_KEEPCOPY);
+ } else {
+ // -rm
+ cmd.createArgument().setValue(FLAG_RM);
+ }
+
+ // viewpath
+ cmd.createArgument().setValue(getViewPath());
+ }
+
+ /**
+ * If true, keep a copy of the file with a .keep extension.
+ *
+ * @param keep the status to set the flag to
+ */
+ public void setKeepCopy(boolean keep) {
+ mKeep = keep;
+ }
+
+ /**
+ * Get keepcopy flag status
+ *
+ * @return boolean containing status of keep flag
+ */
+ public boolean getKeepCopy() {
+ return mKeep;
+ }
+
+
+ /**
+ * -keep flag -- keep a copy of the file with .keep extension
+ */
+ public static final String FLAG_KEEPCOPY = "-keep";
+ /**
+ * -rm flag -- remove the copy of the file
+ */
+ public static final String FLAG_RM = "-rm";
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnlock.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnlock.java
new file mode 100644
index 00000000..4ca3e890
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnlock.java
@@ -0,0 +1,260 @@
+/*
+ * 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.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * TODO:
+ * comment field doesn't include all options yet
+ */
+
+/**
+ * Performs a ClearCase Unlock command.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ * <tr>
+ * <th>Attribute</th>
+ * <th>Values</th>
+ * <th>Required</th>
+ * </tr>
+ * <tr>
+ * <td>comment</td>
+ * <td>Specifies how to populate comments fields</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>pname</td>
+ * <td>Specifies the object pathname to be unlocked.</td>
+ * <td>No</td>
+ * <tr>
+ * <td>objselect</td>
+ * <td>This variable is obsolete. Should use <i>objsel</i> instead.</td>
+ * <td>no</td>
+ * <tr>
+ * <tr>
+ * <td>objsel</td>
+ * <td>Specifies the object(s) to be unlocked.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>failonerr</td>
+ * <td>Throw an exception if the command fails. Default is true</td>
+ * <td>No</td>
+ * <tr>
+ *
+ * </table>
+ *
+ */
+public class CCUnlock extends ClearCase {
+ private String mComment = null;
+ private String mPname = null;
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute cleartool and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command fails and failonerr is set to true
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ Project aProj = getProject();
+ int result = 0;
+
+ // Default the viewpath to basedir if it is not specified
+ if (getViewPath() == null) {
+ setViewPath(aProj.getBaseDir().getPath());
+ }
+
+ // build the command line from what we got the format is
+ // cleartool lock [options...]
+ // as specified in the CLEARTOOL.EXE help
+ commandLine.setExecutable(getClearToolCommand());
+ commandLine.createArgument().setValue(COMMAND_UNLOCK);
+
+ // Check the command line options
+ checkOptions(commandLine);
+
+ // For debugging
+ // System.out.println(commandLine.toString());
+
+ if (!getFailOnErr()) {
+ getProject().log("Ignoring any errors that occur for: "
+ + getOpType(), Project.MSG_VERBOSE);
+ }
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnErr()) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ // ClearCase items
+ getCommentCommand(cmd);
+
+ if (getObjSelect() == null && getPname() == null) {
+ throw new BuildException("Should select either an element "
+ + "(pname) or an object (objselect)");
+ }
+ getPnameCommand(cmd);
+ // object selector
+ if (getObjSelect() != null) {
+ cmd.createArgument().setValue(getObjSelect());
+ }
+ }
+
+ /**
+ * Sets how comments should be written
+ * for the event record(s)
+ *
+ * @param comment comment method to use
+ */
+ public void setComment(String comment) {
+ mComment = comment;
+ }
+
+ /**
+ * Get comment method
+ *
+ * @return String containing the desired comment method
+ */
+ public String getComment() {
+ return mComment;
+ }
+
+ /**
+ * Sets the pathname to be locked
+ *
+ * @param pname pathname to be locked
+ */
+ public void setPname(String pname) {
+ mPname = pname;
+ }
+
+ /**
+ * Get the pathname to be locked
+ *
+ * @return String containing the pathname to be locked
+ */
+ public String getPname() {
+ return mPname;
+ }
+
+ /**
+ * Sets the object(s) to be locked
+ *
+ * @param objselect objects to be locked
+ */
+ public void setObjselect(String objselect) {
+ setObjSelect(objselect);
+ }
+
+ /**
+ * Sets the object(s) to be locked
+ *
+ * @param objsel objects to be locked
+ * @since ant 1.6.1
+ */
+ public void setObjSel(String objsel) {
+ setObjSelect(objsel);
+ }
+
+ /**
+ * Get list of objects to be locked
+ *
+ * @return String containing the objects to be locked
+ */
+ public String getObjselect() {
+ return getObjSelect();
+ }
+
+ /**
+ * Get the 'comment' command
+ *
+ * @param cmd containing the command line string with or without the
+ * comment flag and value appended
+ */
+ private void getCommentCommand(Commandline cmd) {
+ if (getComment() == null) {
+ return;
+ } else {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_COMMENT);
+ cmd.createArgument().setValue(getComment());
+ }
+ }
+
+ /**
+ * Get the 'pname' command
+ *
+ * @param cmd containing the command line string with or without the
+ * pname flag and value appended
+ */
+ private void getPnameCommand(Commandline cmd) {
+ if (getPname() == null) {
+ return;
+ } else {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_PNAME);
+ cmd.createArgument().setValue(getPname());
+ }
+ }
+
+ /**
+ * Return which object/pname is being operated on
+ *
+ * @return String containing the object/pname being worked on
+ */
+ private String getOpType() {
+
+ if (getPname() != null) {
+ return getPname();
+ } else {
+ return getObjSelect();
+ }
+ }
+
+ /**
+ * -comment flag -- method to use for commenting events
+ */
+ public static final String FLAG_COMMENT = "-comment";
+ /**
+ * -pname flag -- pathname to lock
+ */
+ public static final String FLAG_PNAME = "-pname";
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUpdate.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUpdate.java
new file mode 100644
index 00000000..712efdca
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUpdate.java
@@ -0,0 +1,331 @@
+/*
+ * 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.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs a ClearCase Update command.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ * <tr>
+ * <th>Attribute</th>
+ * <th>Values</th>
+ * <th>Required</th>
+ * </tr>
+ * <tr>
+ * <td>viewpath</td>
+ * <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>graphical</td>
+ * <td>Displays a graphical dialog during the update</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>log</td>
+ * <td>Specifies a log file for ClearCase to write to</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>overwrite</td>
+ * <td>Specifies whether to overwrite hijacked files or not</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>rename</td>
+ * <td>Specifies that hijacked files should be renamed with a .keep extension</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>currenttime</td>
+ * <td>Specifies that modification time should be written as the current
+ * time. Either currenttime or preservetime can be specified.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>preservetime</td>
+ * <td>Specifies that modification time should preserved from the VOB
+ * time. Either currenttime or preservetime can be specified.</td>
+ * <td>No</td>
+ * <tr>
+ * <tr>
+ * <td>failonerr</td>
+ * <td>Throw an exception if the command fails. Default is true</td>
+ * <td>No</td>
+ * <tr>
+ * </table>
+ *
+ */
+public class CCUpdate extends ClearCase {
+ private boolean mGraphical = false;
+ private boolean mOverwrite = false;
+ private boolean mRename = false;
+ private boolean mCtime = false;
+ private boolean mPtime = false;
+ private String mLog = null;
+
+ /**
+ * Executes the task.
+ * <p>
+ * Builds a command line to execute cleartool and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command fails and failonerr is set to true
+ */
+ public void execute() throws BuildException {
+ Commandline commandLine = new Commandline();
+ Project aProj = getProject();
+ int result = 0;
+
+ // Default the viewpath to basedir if it is not specified
+ if (getViewPath() == null) {
+ setViewPath(aProj.getBaseDir().getPath());
+ }
+
+ // build the command line from what we got the format is
+ // cleartool update [options...] [viewpath ...]
+ // as specified in the CLEARTOOL.EXE help
+ commandLine.setExecutable(getClearToolCommand());
+ commandLine.createArgument().setValue(COMMAND_UPDATE);
+
+ // Check the command line options
+ checkOptions(commandLine);
+
+ // For debugging
+ getProject().log(commandLine.toString(), Project.MSG_DEBUG);
+
+ if (!getFailOnErr()) {
+ getProject().log("Ignoring any errors that occur for: "
+ + getViewPathBasename(), Project.MSG_VERBOSE);
+ }
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnErr()) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+ /**
+ * Check the command line options.
+ */
+ private void checkOptions(Commandline cmd) {
+ // ClearCase items
+ if (getGraphical()) {
+ // -graphical
+ cmd.createArgument().setValue(FLAG_GRAPHICAL);
+ } else {
+ if (getOverwrite()) {
+ // -overwrite
+ cmd.createArgument().setValue(FLAG_OVERWRITE);
+ } else {
+ if (getRename()) {
+ // -rename
+ cmd.createArgument().setValue(FLAG_RENAME);
+ } else {
+ // -noverwrite
+ cmd.createArgument().setValue(FLAG_NOVERWRITE);
+ }
+ }
+
+ if (getCurrentTime()) {
+ // -ctime
+ cmd.createArgument().setValue(FLAG_CURRENTTIME);
+ } else {
+ if (getPreserveTime()) {
+ // -ptime
+ cmd.createArgument().setValue(FLAG_PRESERVETIME);
+ }
+ }
+
+ // -log logname
+ getLogCommand(cmd);
+ }
+
+ // viewpath
+ cmd.createArgument().setValue(getViewPath());
+ }
+
+ /**
+ * If true, displays a graphical dialog during the update.
+ *
+ * @param graphical the status to set the flag to
+ */
+ public void setGraphical(boolean graphical) {
+ mGraphical = graphical;
+ }
+
+ /**
+ * Get graphical flag status
+ *
+ * @return boolean containing status of graphical flag
+ */
+ public boolean getGraphical() {
+ return mGraphical;
+ }
+
+ /**
+ * If true, overwrite hijacked files.
+ *
+ * @param ow the status to set the flag to
+ */
+ public void setOverwrite(boolean ow) {
+ mOverwrite = ow;
+ }
+
+ /**
+ * Get overwrite hijacked files status
+ *
+ * @return boolean containing status of overwrite flag
+ */
+ public boolean getOverwrite() {
+ return mOverwrite;
+ }
+
+ /**
+ * If true, hijacked files are renamed with a .keep extension.
+ *
+ * @param ren the status to set the flag to
+ */
+ public void setRename(boolean ren) {
+ mRename = ren;
+ }
+
+ /**
+ * Get rename hijacked files status
+ *
+ * @return boolean containing status of rename flag
+ */
+ public boolean getRename() {
+ return mRename;
+ }
+
+ /**
+ * If true, modification time should be written as the current time.
+ * Either currenttime or preservetime can be specified.
+ *
+ * @param ct the status to set the flag to
+ */
+ public void setCurrentTime(boolean ct) {
+ mCtime = ct;
+ }
+
+ /**
+ * Get current time status
+ *
+ * @return boolean containing status of current time flag
+ */
+ public boolean getCurrentTime() {
+ return mCtime;
+ }
+
+ /**
+ * If true, modification time should be preserved from the VOB time.
+ * Either currenttime or preservetime can be specified.
+ *
+ * @param pt the status to set the flag to
+ */
+ public void setPreserveTime(boolean pt) {
+ mPtime = pt;
+ }
+
+ /**
+ * Get preserve time status
+ *
+ * @return boolean containing status of preserve time flag
+ */
+ public boolean getPreserveTime() {
+ return mPtime;
+ }
+
+ /**
+ * Sets the log file where cleartool records
+ * the status of the command.
+ *
+ * @param log the path to the log file
+ */
+ public void setLog(String log) {
+ mLog = log;
+ }
+
+ /**
+ * Get log file
+ *
+ * @return String containing the path to the log file
+ */
+ public String getLog() {
+ return mLog;
+ }
+
+
+ /**
+ * Get the 'log' command
+ *
+ * @param cmd containing the command line string with or without the log flag and path appended
+ */
+ private void getLogCommand(Commandline cmd) {
+ if (getLog() == null) {
+ return;
+ } else {
+ /* Had to make two separate commands here because if a space is
+ inserted between the flag and the value, it is treated as a
+ Windows filename with a space and it is enclosed in double
+ quotes ("). This breaks clearcase.
+ */
+ cmd.createArgument().setValue(FLAG_LOG);
+ cmd.createArgument().setValue(getLog());
+ }
+ }
+
+ /**
+ * -graphical flag -- display graphical dialog during update operation
+ */
+ public static final String FLAG_GRAPHICAL = "-graphical";
+ /**
+ * -log flag -- file to log status to
+ */
+ public static final String FLAG_LOG = "-log";
+ /**
+ * -overwrite flag -- overwrite hijacked files
+ */
+ public static final String FLAG_OVERWRITE = "-overwrite";
+ /**
+ * -noverwrite flag -- do not overwrite hijacked files
+ */
+ public static final String FLAG_NOVERWRITE = "-noverwrite";
+ /**
+ * -rename flag -- rename hijacked files with .keep extension
+ */
+ public static final String FLAG_RENAME = "-rename";
+ /**
+ * -ctime flag -- modified time is written as the current time
+ */
+ public static final String FLAG_CURRENTTIME = "-ctime";
+ /**
+ * -ptime flag -- modified time is written as the VOB time
+ */
+ public static final String FLAG_PRESERVETIME = "-ptime";
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/ClearCase.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/ClearCase.java
new file mode 100644
index 00000000..d81e505c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/ClearCase.java
@@ -0,0 +1,242 @@
+/*
+ * 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.tools.ant.taskdefs.optional.clearcase;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.ExecTask;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.FileUtils;
+
+
+
+/**
+ * A base class for creating tasks for executing commands on ClearCase.
+ * <p>
+ * The class extends the 'exec' task as it operates by executing the cleartool program
+ * supplied with ClearCase. By default the task expects the cleartool executable to be
+ * in the path, * you can override this be specifying the cleartooldir attribute.
+ * </p>
+ * <p>
+ * This class provides set and get methods for the 'viewpath' and 'objselect'
+ * attribute. It also contains constants for the flags that can be passed to
+ * cleartool.
+ * </p>
+ *
+ */
+public abstract class ClearCase extends Task {
+ private String mClearToolDir = "";
+ private String mviewPath = null;
+ private String mobjSelect = null;
+ private static int pcnt = 0;
+ private boolean mFailonerr = true;
+ /**
+ * Set the directory where the cleartool executable is located.
+ *
+ * @param dir the directory containing the cleartool executable
+ */
+ public final void setClearToolDir(String dir) {
+ mClearToolDir = FileUtils.translatePath(dir);
+ }
+
+ /**
+ * Builds and returns the command string to execute cleartool
+ *
+ * @return String containing path to the executable
+ */
+ protected final String getClearToolCommand() {
+ String toReturn = mClearToolDir;
+ if (!toReturn.equals("") && !toReturn.endsWith("/")) {
+ toReturn += "/";
+ }
+
+ toReturn += CLEARTOOL_EXE;
+
+ return toReturn;
+ }
+
+ /**
+ * Set the path to the item in a ClearCase view to operate on.
+ *
+ * @param viewPath Path to the view directory or file
+ */
+ public final void setViewPath(String viewPath) {
+ mviewPath = viewPath;
+ }
+
+ /**
+ * Get the path to the item in a clearcase view
+ *
+ * @return mviewPath
+ */
+ public String getViewPath() {
+ return mviewPath;
+ }
+
+ /**
+ * Get the basename path of the item in a clearcase view
+ *
+ * @return basename
+ */
+ public String getViewPathBasename() {
+ return (new File(mviewPath)).getName();
+ }
+
+ /**
+ * Set the object to operate on.
+ *
+ * @param objSelect object to operate on
+ */
+ public final void setObjSelect(String objSelect) {
+ mobjSelect = objSelect;
+ }
+
+ /**
+ * Get the object to operate on
+ *
+ * @return mobjSelect
+ */
+ public String getObjSelect() {
+ return mobjSelect;
+ }
+
+ /**
+ * Execute the given command are return success or failure
+ * @param cmd command line to execute
+ * @return the exit status of the subprocess or <code>INVALID</code>
+ */
+ protected int run(Commandline cmd) {
+ try {
+ Project aProj = getProject();
+ Execute exe
+ = new Execute(new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN));
+ exe.setAntRun(aProj);
+ exe.setWorkingDirectory(aProj.getBaseDir());
+ exe.setCommandline(cmd.getCommandline());
+ return exe.execute();
+ } catch (java.io.IOException e) {
+ throw new BuildException(e, getLocation());
+ }
+ }
+
+ /**
+ * Execute the given command, and return it's output
+ * @param cmdline command line to execute
+ * @return output of the command line
+ */
+ protected String runS(Commandline cmdline) {
+ String outV = "opts.cc.runS.output" + pcnt++;
+ ExecTask exe = new ExecTask(this);
+ Commandline.Argument arg = exe.createArg();
+
+ exe.setExecutable(cmdline.getExecutable());
+ arg.setLine(Commandline.toString(cmdline.getArguments()));
+ exe.setOutputproperty(outV);
+ exe.execute();
+
+ return getProject().getProperty(outV);
+ }
+ /**
+ * If true, command will throw an exception on failure.
+ *
+ * @param failonerr the status to set the flag to
+ * @since ant 1.6.1
+ */
+ public void setFailOnErr(boolean failonerr) {
+ mFailonerr = failonerr;
+ }
+
+ /**
+ * Get failonerr flag status
+ *
+ * @return boolean containing status of failonerr flag
+ * @since ant 1.6.1
+ */
+ public boolean getFailOnErr() {
+ return mFailonerr;
+ }
+
+ /**
+ * Constant for the thing to execute
+ */
+ private static final String CLEARTOOL_EXE = "cleartool";
+ /**
+ * The 'Update' command
+ */
+ public static final String COMMAND_UPDATE = "update";
+ /**
+ * The 'Checkout' command
+ */
+ public static final String COMMAND_CHECKOUT = "checkout";
+ /**
+ * The 'Checkin' command
+ */
+ public static final String COMMAND_CHECKIN = "checkin";
+ /**
+ * The 'UndoCheckout' command
+ */
+ public static final String COMMAND_UNCHECKOUT = "uncheckout";
+ /**
+ * The 'Lock' command
+ */
+ public static final String COMMAND_LOCK = "lock";
+ /**
+ * The 'Unlock' command
+ */
+ public static final String COMMAND_UNLOCK = "unlock";
+ /**
+ * The 'Mkbl' command
+ */
+ public static final String COMMAND_MKBL = "mkbl";
+ /**
+ * The 'Mklabel' command
+ */
+ public static final String COMMAND_MKLABEL = "mklabel";
+ /**
+ * The 'Mklbtype' command
+ */
+ public static final String COMMAND_MKLBTYPE = "mklbtype";
+ /**
+ * The 'Rmtype' command
+ */
+ public static final String COMMAND_RMTYPE = "rmtype";
+ /**
+ * The 'LsCheckout' command
+ */
+ public static final String COMMAND_LSCO = "lsco";
+ /**
+ * The 'Mkelem' command
+ */
+ public static final String COMMAND_MKELEM = "mkelem";
+ /**
+ * The 'Mkattr' command
+ */
+ public static final String COMMAND_MKATTR = "mkattr";
+ /**
+ * The 'Mkdir' command
+ */
+ public static final String COMMAND_MKDIR = "mkdir";
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/AntAnalyzer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/AntAnalyzer.java
new file mode 100644
index 00000000..341f6707
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/AntAnalyzer.java
@@ -0,0 +1,144 @@
+/*
+ * 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.tools.ant.taskdefs.optional.depend;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.depend.AbstractAnalyzer;
+
+/**
+ * An analyzer which uses the depend task's bytecode classes to analyze
+ * dependencies
+ *
+ */
+public class AntAnalyzer extends AbstractAnalyzer {
+ /**
+ * Default constructor
+ */
+ public AntAnalyzer() {
+ }
+
+ /**
+ * Determine the dependencies of the configured root classes.
+ *
+ * @param files a vector to be populated with the files which contain
+ * the dependency classes
+ * @param classes a vector to be populated with the names of the
+ * dependency classes.
+ */
+ protected void determineDependencies(Vector<File> files, Vector<String> classes) {
+ // we get the root classes and build up a set of
+ // classes upon which they depend
+ Hashtable<String, String> dependencies = new Hashtable<String, String>();
+ Hashtable<File, File> containers = new Hashtable<File, File>();
+ Hashtable<String, String> toAnalyze = new Hashtable<String, String>();
+ for (Enumeration<String> e = getRootClasses(); e.hasMoreElements();) {
+ String classname = e.nextElement();
+ toAnalyze.put(classname, classname);
+ }
+
+ int count = 0;
+ int maxCount = isClosureRequired() ? MAX_LOOPS : 1;
+ Hashtable<String, String> analyzedDeps = null;
+ while (toAnalyze.size() != 0 && count++ < maxCount) {
+ analyzedDeps = new Hashtable<String, String>();
+ for (Enumeration<String> e = toAnalyze.keys(); e.hasMoreElements();) {
+ String classname = e.nextElement();
+ dependencies.put(classname, classname);
+ try {
+ File container = getClassContainer(classname);
+ if (container == null) {
+ continue;
+ }
+ containers.put(container, container);
+
+ ZipFile zipFile = null;
+ InputStream inStream = null;
+ try {
+ if (container.getName().endsWith(".class")) {
+ inStream = new FileInputStream(container.getPath());
+ } else {
+ zipFile = new ZipFile(container.getPath());
+ String entryName
+ = classname.replace('.', '/') + ".class";
+ ZipEntry entry = new ZipEntry(entryName);
+ inStream
+ = zipFile.getInputStream(entry);
+ }
+ ClassFile classFile = new ClassFile();
+ classFile.read(inStream);
+ for (String dependency : classFile.getClassRefs()) {
+ analyzedDeps.put(dependency, dependency);
+ }
+ } finally {
+ FileUtils.close(inStream);
+ if (zipFile != null) {
+ zipFile.close();
+ }
+ }
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+
+ toAnalyze.clear();
+
+ // now recover all the dependencies collected and add to the list.
+ for (String className : analyzedDeps.values()) {
+ if (!dependencies.containsKey(className)) {
+ toAnalyze.put(className, className);
+ }
+ }
+ }
+
+ // pick up the last round of dependencies that were determined
+ for (String className : analyzedDeps.values()) {
+ dependencies.put(className, className);
+ }
+
+ files.removeAllElements();
+ for (File f : containers.keySet()) {
+ files.add(f);
+ }
+
+ classes.removeAllElements();
+ for (String dependency :dependencies.keySet()) {
+ classes.add(dependency);
+ }
+ }
+
+ /**
+ * Indicate if this analyzer can determine dependent files.
+ *
+ * @return true if the analyzer provides dependency file information.
+ */
+ protected boolean supportsFileDependencies() {
+ return true;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFile.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFile.java
new file mode 100644
index 00000000..858ce03e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFile.java
@@ -0,0 +1,121 @@
+/*
+ * 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.tools.ant.taskdefs.optional.depend;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+
+import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ClassCPInfo;
+import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ConstantPool;
+import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ConstantPoolEntry;
+
+/**
+ * A ClassFile object stores information about a Java class. The class may
+ * be read from a DataInputStream.and written to a DataOutputStream. These
+ * are usually streams from a Java class file or a class file component of a
+ * Jar file.
+ *
+ */
+public class ClassFile {
+
+ /** The Magic Value that marks the start of a Java class file */
+ private static final int CLASS_MAGIC = 0xCAFEBABE;
+
+ /** This class' constant pool. */
+ private ConstantPool constantPool;
+
+ /** The class name for this class. */
+ private String className;
+
+ /**
+ * Read the class from a data stream. This method takes an InputStream
+ * as input and parses the class from the stream. <p>
+ *
+ *
+ *
+ * @param stream an InputStream from which the class will be read
+ * @exception IOException if there is a problem reading from the given
+ * stream.
+ * @exception ClassFormatError if the class cannot be parsed correctly
+ */
+ public void read(InputStream stream) throws IOException, ClassFormatError {
+ DataInputStream classStream = new DataInputStream(stream);
+
+ if (classStream.readInt() != CLASS_MAGIC) {
+ throw new ClassFormatError("No Magic Code Found "
+ + "- probably not a Java class file.");
+ }
+
+ // right we have a good looking class file.
+ /* int minorVersion = */ classStream.readUnsignedShort();
+ /* int majorVersion = */ classStream.readUnsignedShort();
+
+ // read the constant pool in and resolve it
+ constantPool = new ConstantPool();
+
+ constantPool.read(classStream);
+ constantPool.resolve();
+
+ /* int accessFlags = */ classStream.readUnsignedShort();
+ int thisClassIndex = classStream.readUnsignedShort();
+ /* int superClassIndex = */ classStream.readUnsignedShort();
+ ClassCPInfo classInfo
+ = (ClassCPInfo) constantPool.getEntry(thisClassIndex);
+ className = classInfo.getClassName();
+ }
+
+
+ /**
+ * Get the classes which this class references.
+ *
+ * @return a vector of class names which this class references
+ */
+ public Vector<String> getClassRefs() {
+
+ Vector<String> classRefs = new Vector<String>();
+
+ final int size = constantPool.size();
+ for (int i = 0; i < size; ++i) {
+ ConstantPoolEntry entry = constantPool.getEntry(i);
+
+ if (entry != null
+ && entry.getTag() == ConstantPoolEntry.CONSTANT_CLASS) {
+ ClassCPInfo classEntry = (ClassCPInfo) entry;
+
+ if (!classEntry.getClassName().equals(className)) {
+ classRefs.add(
+ ClassFileUtils.convertSlashName(classEntry.getClassName()));
+ }
+ }
+ }
+
+ return classRefs;
+ }
+
+ /**
+ * Get the class' fully qualified name in dot format.
+ *
+ * @return the class name in dot format (eg. java.lang.Object)
+ */
+ public String getFullClassName() {
+ return ClassFileUtils.convertSlashName(className);
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileIterator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileIterator.java
new file mode 100644
index 00000000..92fc191b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileIterator.java
@@ -0,0 +1,33 @@
+/*
+ * 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.tools.ant.taskdefs.optional.depend;
+
+/**
+ * Iterator interface for iterating over a set of class files
+ *
+ */
+public interface ClassFileIterator {
+
+ /**
+ * Get the next class file in the iteration
+ *
+ * @return the next class file in the iteration
+ */
+ ClassFile getNextClassFile();
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileUtils.java
new file mode 100644
index 00000000..c6eec6cc
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileUtils.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.tools.ant.taskdefs.optional.depend;
+
+/**
+ * Utility class file routines. This class provides a number of static
+ * utility methods to convert between the formats used in the Java class
+ * file format and those commonly used in Java programming.
+ *
+ *
+ */
+// CheckStyle:HideUtilityClassConstructorCheck OFF (bc)
+public class ClassFileUtils {
+
+ /**
+ * Convert a class name from class file slash notation to java source
+ * file dot notation.
+ *
+ * @param name the class name in slash notation org/apache/ant
+ * @return the class name in dot notation (eg. java.lang.Object).
+ */
+ public static String convertSlashName(String name) {
+ return name.replace('\\', '.').replace('/', '.');
+ }
+
+ /**
+ * Convert a class name from java source file dot notation to class file
+ * slash notation..
+ *
+ * @param dotName the class name in dot notation (eg. java.lang.Object).
+ * @return the class name in slash notation (eg. java/lang/Object).
+ */
+ public static String convertDotName(String dotName) {
+ return dotName.replace('.', '/');
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java
new file mode 100644
index 00000000..cc65129f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java
@@ -0,0 +1,917 @@
+/*
+ * 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.tools.ant.taskdefs.optional.depend;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.taskdefs.rmic.DefaultRmicAdapter;
+import org.apache.tools.ant.taskdefs.rmic.WLRmic;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.depend.DependencyAnalyzer;
+
+/**
+ * Generates a dependency file for a given set of classes.
+ *
+ */
+public class Depend extends MatchingTask {
+ private static final int ONE_SECOND = 1000;
+
+ /**
+ * A class (struct) user to manage information about a class
+ *
+ */
+ private static class ClassFileInfo {
+ /** The file where the class file is stored in the file system */
+ private File absoluteFile;
+
+ /** The Java class name of this class */
+ private String className;
+
+ /** The source File containing this class */
+ private File sourceFile;
+
+ /** if user has been warned about this file not having a source file */
+ private boolean isUserWarned = false;
+ }
+
+ /** The path where source files exist */
+ private Path srcPath;
+
+ /** The path where compiled class files exist. */
+ private Path destPath;
+
+ /** The directory which contains the dependency cache. */
+ private File cache;
+
+ /** The list of source paths derived from the srcPath field. */
+ private String[] srcPathList;
+
+ /**
+ * A map which gives for every class a list of the class which it
+ * affects.
+ */
+ private Hashtable affectedClassMap;
+
+ /** A map which gives information about a class */
+ private Hashtable classFileInfoMap;
+
+ /**
+ * A map which gives the list of jars and classes from the classpath
+ * that a class depends upon
+ */
+ private Hashtable classpathDependencies;
+
+ /** The list of classes which are out of date. */
+ private Hashtable outOfDateClasses;
+
+ /**
+ * indicates that the dependency relationships should be extended beyond
+ * direct dependencies to include all classes. So if A directly affects
+ * B and B directly affects C, then A indirectly affects C.
+ */
+ private boolean closure = false;
+
+ /**
+ * flag to enable warning if we encounter RMI stubs
+ */
+ private boolean warnOnRmiStubs = true;
+
+ /**
+ * Flag which controls whether the reversed dependencies should be
+ * dumped to the log
+ */
+ private boolean dump = false;
+
+ /** The classpath to look for additional dependencies */
+ private Path dependClasspath;
+
+ /** constants used with the cache file */
+ private static final String CACHE_FILE_NAME = "dependencies.txt";
+ /** String Used to separate classnames in the dependency file */
+ private static final String CLASSNAME_PREPEND = "||:";
+
+ /**
+ * Set the classpath to be used for this dependency check.
+ *
+ * @param classpath the classpath to be used when checking for
+ * dependencies on elements in the classpath
+ */
+ public void setClasspath(Path classpath) {
+ if (dependClasspath == null) {
+ dependClasspath = classpath;
+ } else {
+ dependClasspath.append(classpath);
+ }
+ }
+
+ /**
+ * Gets the classpath to be used for this dependency check.
+ *
+ * @return the current dependency classpath
+ */
+ public Path getClasspath() {
+ return dependClasspath;
+ }
+
+ /**
+ * Adds a classpath to be used for this dependency check.
+ *
+ * @return A path object to be configured by Ant
+ */
+ public Path createClasspath() {
+ if (dependClasspath == null) {
+ dependClasspath = new Path(getProject());
+ }
+ return dependClasspath.createPath();
+ }
+
+ /**
+ * Adds a reference to a classpath defined elsewhere.
+ *
+ * @param r a reference to a path object to be used as the depend
+ * classpath
+ */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * Flag to set to true if you want dependency issues with RMI
+ * stubs to appear at warning level.
+ * @param warnOnRmiStubs if true set dependency issues to appear at warning level.
+ * @since Ant1.7
+ */
+ public void setWarnOnRmiStubs(boolean warnOnRmiStubs) {
+ this.warnOnRmiStubs = warnOnRmiStubs;
+ }
+
+ /**
+ * Read the dependencies from cache file
+ *
+ * @return a collection of class dependencies
+ * @exception IOException if the dependency file cannot be read
+ */
+ private Hashtable readCachedDependencies(File depFile) throws IOException {
+ Hashtable dependencyMap = new Hashtable();
+
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(new FileReader(depFile));
+ String line = null;
+ Vector dependencyList = null;
+ String className = null;
+ int prependLength = CLASSNAME_PREPEND.length();
+ while ((line = in.readLine()) != null) {
+ if (line.startsWith(CLASSNAME_PREPEND)) {
+ dependencyList = new Vector();
+ className = line.substring(prependLength);
+ dependencyMap.put(className, dependencyList);
+ } else {
+ dependencyList.addElement(line);
+ }
+ }
+ } finally {
+ FileUtils.close(in);
+ }
+
+ return dependencyMap;
+ }
+
+ /**
+ * Write the dependencies to cache file
+ *
+ * @param dependencyMap the map of dependencies to be written out.
+ * @exception IOException if the dependency file cannot be written out.
+ */
+ private void writeCachedDependencies(Hashtable dependencyMap)
+ throws IOException {
+ if (cache != null) {
+ BufferedWriter pw = null;
+ try {
+ cache.mkdirs();
+ File depFile = new File(cache, CACHE_FILE_NAME);
+
+ pw = new BufferedWriter(new FileWriter(depFile));
+ Enumeration e = dependencyMap.keys();
+ while (e.hasMoreElements()) {
+ String className = (String) e.nextElement();
+
+ pw.write(CLASSNAME_PREPEND + className);
+ pw.newLine();
+
+ Vector dependencyList
+ = (Vector) dependencyMap.get(className);
+ int size = dependencyList.size();
+ for (int x = 0; x < size; x++) {
+ pw.write(String.valueOf(dependencyList.elementAt(x)));
+ pw.newLine();
+ }
+ }
+ } finally {
+ FileUtils.close(pw);
+ }
+ }
+ }
+
+ /**
+ * Get the classpath for dependency checking.
+ *
+ * This method removes the dest dirs if it is given from the dependency classpath
+ */
+ private Path getCheckClassPath() {
+ if (dependClasspath == null) {
+ return null;
+ }
+
+ String[] destPathElements = destPath.list();
+ String[] classpathElements = dependClasspath.list();
+ String checkPath = "";
+ for (int i = 0; i < classpathElements.length; ++i) {
+ String element = classpathElements[i];
+ boolean inDestPath = false;
+ for (int j = 0; j < destPathElements.length && !inDestPath; ++j) {
+ inDestPath = destPathElements[j].equals(element);
+ }
+ if (!inDestPath) {
+ if (checkPath.length() == 0) {
+ checkPath = element;
+ } else {
+ checkPath += ":" + element;
+ }
+ }
+ }
+
+ Path p = null;
+ if (checkPath.length() > 0) {
+ p = new Path(getProject(), checkPath);
+ }
+
+ log("Classpath without dest dir is " + p, Project.MSG_DEBUG);
+ return p;
+ }
+
+ /**
+ * Determine the dependencies between classes. Class dependencies are
+ * determined by examining the class references in a class file to other
+ * classes.
+ *
+ * This method sets up the following fields
+ * <ul>
+ * <li>affectedClassMap - the list of classes each class affects</li>
+ * <li>classFileInfoMap - information about each class</li>
+ * <li>classpathDependencies - the list of jars and classes from the
+ * classpath that each class depends upon.</li>
+ * </ul>
+ *
+ * If required, the dependencies are written to the cache.
+ *
+ * @exception IOException if either the dependencies cache or the class
+ * files cannot be read or written
+ */
+ private void determineDependencies() throws IOException {
+ affectedClassMap = new Hashtable();
+ classFileInfoMap = new Hashtable();
+ boolean cacheDirty = false;
+
+ Hashtable dependencyMap = new Hashtable();
+ File cacheFile = null;
+ boolean cacheFileExists = true;
+ long cacheLastModified = Long.MAX_VALUE;
+
+ // read the dependency cache from the disk
+ if (cache != null) {
+ cacheFile = new File(cache, CACHE_FILE_NAME);
+ cacheFileExists = cacheFile.exists();
+ cacheLastModified = cacheFile.lastModified();
+ if (cacheFileExists) {
+ dependencyMap = readCachedDependencies(cacheFile);
+ }
+ }
+ Enumeration classfileEnum = getClassFiles(destPath).elements();
+ while (classfileEnum.hasMoreElements()) {
+ ClassFileInfo info = (ClassFileInfo) classfileEnum.nextElement();
+ log("Adding class info for " + info.className, Project.MSG_DEBUG);
+ classFileInfoMap.put(info.className, info);
+
+ Vector dependencyList = null;
+
+ if (cache != null) {
+ // try to read the dependency info from the map if it is
+ // not out of date
+ if (cacheFileExists
+ && cacheLastModified > info.absoluteFile.lastModified()) {
+ // depFile exists and is newer than the class file
+ // need to get dependency list from the map.
+ dependencyList = (Vector) dependencyMap.get(info.className);
+ }
+ }
+
+ if (dependencyList == null) {
+ // not cached - so need to read directly from the class file
+ DependencyAnalyzer analyzer = new AntAnalyzer();
+ analyzer.addRootClass(info.className);
+ analyzer.addClassPath(destPath);
+ analyzer.setClosure(false);
+ dependencyList = new Vector();
+ Enumeration depEnum = analyzer.getClassDependencies();
+ while (depEnum.hasMoreElements()) {
+ Object o = depEnum.nextElement();
+ dependencyList.addElement(o);
+ log("Class " + info.className + " depends on " + o,
+ Project.MSG_DEBUG);
+ }
+ cacheDirty = true;
+ dependencyMap.put(info.className, dependencyList);
+ }
+
+ // This class depends on each class in the dependency list. For each
+ // one of those, add this class into their affected classes list
+ Enumeration depEnum = dependencyList.elements();
+ while (depEnum.hasMoreElements()) {
+ String dependentClass = (String) depEnum.nextElement();
+
+ Hashtable affectedClasses
+ = (Hashtable) affectedClassMap.get(dependentClass);
+ if (affectedClasses == null) {
+ affectedClasses = new Hashtable();
+ affectedClassMap.put(dependentClass, affectedClasses);
+ }
+
+ affectedClasses.put(info.className, info);
+ log(dependentClass + " affects " + info.className,
+ Project.MSG_DEBUG);
+ }
+ }
+
+ classpathDependencies = null;
+ Path checkPath = getCheckClassPath();
+ if (checkPath != null) {
+ // now determine which jars each class depends upon
+ classpathDependencies = new Hashtable();
+ AntClassLoader loader = null;
+ try {
+ loader = getProject().createClassLoader(checkPath);
+
+ Hashtable classpathFileCache = new Hashtable();
+ Object nullFileMarker = new Object();
+ for (Enumeration e = dependencyMap.keys(); e.hasMoreElements();) {
+ String className = (String) e.nextElement();
+ log("Determining classpath dependencies for " + className,
+ Project.MSG_DEBUG);
+ Vector dependencyList = (Vector) dependencyMap.get(className);
+ Hashtable dependencies = new Hashtable();
+ classpathDependencies.put(className, dependencies);
+ Enumeration e2 = dependencyList.elements();
+ while (e2.hasMoreElements()) {
+ String dependency = (String) e2.nextElement();
+ log("Looking for " + dependency, Project.MSG_DEBUG);
+ Object classpathFileObject
+ = classpathFileCache.get(dependency);
+ if (classpathFileObject == null) {
+ classpathFileObject = nullFileMarker;
+
+ if (!dependency.startsWith("java.")
+ && !dependency.startsWith("javax.")) {
+ URL classURL
+ = loader.getResource(dependency.replace('.', '/') + ".class");
+ log("URL is " + classURL, Project.MSG_DEBUG);
+ if (classURL != null) {
+ if (classURL.getProtocol().equals("jar")) {
+ String jarFilePath = classURL.getFile();
+ int classMarker = jarFilePath.indexOf('!');
+ jarFilePath = jarFilePath.substring(0, classMarker);
+ if (jarFilePath.startsWith("file:")) {
+ classpathFileObject = new File(
+ FileUtils.getFileUtils().fromURI(jarFilePath));
+ } else {
+ throw new IOException(
+ "Bizarre nested path in jar: protocol: "
+ + jarFilePath);
+ }
+ } else if (classURL.getProtocol().equals("file")) {
+ classpathFileObject = new File(
+ FileUtils.getFileUtils()
+ .fromURI(classURL.toExternalForm()));
+ }
+ log("Class " + className
+ + " depends on " + classpathFileObject
+ + " due to " + dependency, Project.MSG_DEBUG);
+ }
+ } else {
+ log("Ignoring base classlib dependency "
+ + dependency, Project.MSG_DEBUG);
+ }
+ classpathFileCache.put(dependency, classpathFileObject);
+ }
+ if (classpathFileObject != nullFileMarker) {
+ // we need to add this jar to the list for this class.
+ File jarFile = (File) classpathFileObject;
+ log("Adding a classpath dependency on " + jarFile,
+ Project.MSG_DEBUG);
+ dependencies.put(jarFile, jarFile);
+ }
+ }
+ }
+ } finally {
+ if (loader != null) {
+ loader.cleanup();
+ }
+ }
+ } else {
+ log("No classpath to check", Project.MSG_DEBUG);
+ }
+
+ // write the dependency cache to the disk
+ if (cache != null && cacheDirty) {
+ writeCachedDependencies(dependencyMap);
+ }
+ }
+
+ /**
+ * Delete all the class files which are out of date, by way of their
+ * dependency on a class which is out of date
+ *
+ * @return the number of files deleted.
+ */
+ private int deleteAllAffectedFiles() {
+ int count = 0;
+ for (Enumeration e = outOfDateClasses.elements(); e.hasMoreElements();) {
+ String className = (String) e.nextElement();
+ count += deleteAffectedFiles(className);
+ ClassFileInfo classInfo
+ = (ClassFileInfo) classFileInfoMap.get(className);
+ if (classInfo != null && classInfo.absoluteFile.exists()) {
+ if (classInfo.sourceFile == null) {
+ warnOutOfDateButNotDeleted(classInfo, className, className);
+ } else {
+ classInfo.absoluteFile.delete();
+ count++;
+ }
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Delete all the class files of classes which depend on the given class
+ *
+ * @param className the name of the class whose dependent classes will be
+ * deleted
+ * @return the number of class files removed
+ */
+ private int deleteAffectedFiles(String className) {
+ int count = 0;
+
+ Hashtable affectedClasses = (Hashtable) affectedClassMap.get(className);
+ if (affectedClasses == null) {
+ return count;
+ }
+ for (Enumeration e = affectedClasses.keys(); e.hasMoreElements();) {
+ String affectedClass = (String) e.nextElement();
+ ClassFileInfo affectedClassInfo
+ = (ClassFileInfo) affectedClasses.get(affectedClass);
+
+ if (!affectedClassInfo.absoluteFile.exists()) {
+ continue;
+ }
+
+ if (affectedClassInfo.sourceFile == null) {
+ warnOutOfDateButNotDeleted(affectedClassInfo, affectedClass, className);
+ continue;
+ }
+
+ log("Deleting file " + affectedClassInfo.absoluteFile.getPath()
+ + " since " + className + " out of date", Project.MSG_VERBOSE);
+
+ affectedClassInfo.absoluteFile.delete();
+ count++;
+ if (closure) {
+ count += deleteAffectedFiles(affectedClass);
+ } else {
+ // without closure we may delete an inner class but not the
+ // top level class which would not trigger a recompile.
+
+ if (affectedClass.indexOf("$") == -1) {
+ continue;
+ }
+ // need to delete the main class
+ String topLevelClassName
+ = affectedClass.substring(0, affectedClass.indexOf("$"));
+ log("Top level class = " + topLevelClassName,
+ Project.MSG_VERBOSE);
+ ClassFileInfo topLevelClassInfo
+ = (ClassFileInfo) classFileInfoMap.get(topLevelClassName);
+ if (topLevelClassInfo != null
+ && topLevelClassInfo.absoluteFile.exists()) {
+ log("Deleting file "
+ + topLevelClassInfo.absoluteFile.getPath()
+ + " since one of its inner classes was removed",
+ Project.MSG_VERBOSE);
+ topLevelClassInfo.absoluteFile.delete();
+ count++;
+ if (closure) {
+ count += deleteAffectedFiles(topLevelClassName);
+ }
+ }
+ }
+ }
+ return count;
+ }
+
+ /**
+ * warn when a class is out of date, but not deleted as its source is unknown.
+ * MSG_WARN is the normal level, but we downgrade to MSG_VERBOSE for RMI files
+ * if {@link #warnOnRmiStubs is false}
+ * @param affectedClassInfo info about the affectd class
+ * @param affectedClass the name of the affected .class file
+ * @param className the file that is triggering the out of dateness
+ */
+ private void warnOutOfDateButNotDeleted(
+ ClassFileInfo affectedClassInfo, String affectedClass,
+ String className) {
+ if (affectedClassInfo.isUserWarned) {
+ return;
+ }
+ int level = Project.MSG_WARN;
+ if (!warnOnRmiStubs) {
+ //downgrade warnings on RMI stublike classes, as they are generated
+ //by rmic, so there is no need to tell the user that their source is
+ //missing.
+ if (isRmiStub(affectedClass, className)) {
+ level = Project.MSG_VERBOSE;
+ }
+ }
+ log("The class " + affectedClass + " in file "
+ + affectedClassInfo.absoluteFile.getPath()
+ + " is out of date due to " + className
+ + " but has not been deleted because its source file"
+ + " could not be determined", level);
+ affectedClassInfo.isUserWarned = true;
+ }
+
+ /**
+ * test for being an RMI stub
+ * @param affectedClass class being tested
+ * @param className possible origin of the RMI stub
+ * @return whether the class affectedClass is a RMI stub
+ */
+ private boolean isRmiStub(String affectedClass, String className) {
+ return isStub(affectedClass, className, DefaultRmicAdapter.RMI_STUB_SUFFIX)
+ || isStub(affectedClass, className, DefaultRmicAdapter.RMI_SKEL_SUFFIX)
+ || isStub(affectedClass, className, WLRmic.RMI_STUB_SUFFIX)
+ || isStub(affectedClass, className, WLRmic.RMI_SKEL_SUFFIX);
+ }
+
+ private boolean isStub(String affectedClass, String baseClass, String suffix) {
+ return (baseClass + suffix).equals(affectedClass);
+ }
+
+ /**
+ * Dump the dependency information loaded from the classes to the Ant log
+ */
+ private void dumpDependencies() {
+ log("Reverse Dependency Dump for " + affectedClassMap.size()
+ + " classes:", Project.MSG_DEBUG);
+
+ Enumeration classEnum = affectedClassMap.keys();
+ while (classEnum.hasMoreElements()) {
+ String className = (String) classEnum.nextElement();
+ log(" Class " + className + " affects:", Project.MSG_DEBUG);
+ Hashtable affectedClasses
+ = (Hashtable) affectedClassMap.get(className);
+ Enumeration affectedClassEnum = affectedClasses.keys();
+ while (affectedClassEnum.hasMoreElements()) {
+ String affectedClass = (String) affectedClassEnum.nextElement();
+ ClassFileInfo info
+ = (ClassFileInfo) affectedClasses.get(affectedClass);
+ log(" " + affectedClass + " in "
+ + info.absoluteFile.getPath(), Project.MSG_DEBUG);
+ }
+ }
+
+ if (classpathDependencies != null) {
+ log("Classpath file dependencies (Forward):", Project.MSG_DEBUG);
+
+ Enumeration classpathEnum = classpathDependencies.keys();
+ while (classpathEnum.hasMoreElements()) {
+ String className = (String) classpathEnum.nextElement();
+ log(" Class " + className + " depends on:", Project.MSG_DEBUG);
+ Hashtable dependencies
+ = (Hashtable) classpathDependencies.get(className);
+
+ Enumeration classpathFileEnum = dependencies.elements();
+ while (classpathFileEnum.hasMoreElements()) {
+ File classpathFile = (File) classpathFileEnum.nextElement();
+ log(" " + classpathFile.getPath(), Project.MSG_DEBUG);
+ }
+ }
+ }
+ }
+
+ private void determineOutOfDateClasses() {
+ outOfDateClasses = new Hashtable();
+ for (int i = 0; i < srcPathList.length; i++) {
+ File srcDir = getProject().resolveFile(srcPathList[i]);
+ if (srcDir.exists()) {
+ DirectoryScanner ds = this.getDirectoryScanner(srcDir);
+ String[] files = ds.getIncludedFiles();
+ scanDir(srcDir, files);
+ }
+ }
+
+ // now check classpath file dependencies
+ if (classpathDependencies == null) {
+ return;
+ }
+
+ Enumeration classpathDepsEnum = classpathDependencies.keys();
+ while (classpathDepsEnum.hasMoreElements()) {
+ String className = (String) classpathDepsEnum.nextElement();
+ if (outOfDateClasses.containsKey(className)) {
+ continue;
+ }
+ ClassFileInfo info
+ = (ClassFileInfo) classFileInfoMap.get(className);
+
+ // if we have no info about the class - it may have been deleted already and we
+ // are using cached info.
+ if (info != null) {
+ Hashtable dependencies
+ = (Hashtable) classpathDependencies.get(className);
+ for (Enumeration e2 = dependencies.elements(); e2.hasMoreElements();) {
+ File classpathFile = (File) e2.nextElement();
+ if (classpathFile.lastModified()
+ > info.absoluteFile.lastModified()) {
+ log("Class " + className
+ + " is out of date with respect to "
+ + classpathFile, Project.MSG_DEBUG);
+ outOfDateClasses.put(className, className);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Does the work.
+ *
+ * @exception BuildException Thrown in case of an unrecoverable error.
+ */
+ public void execute() throws BuildException {
+ try {
+ long start = System.currentTimeMillis();
+ if (srcPath == null) {
+ throw new BuildException("srcdir attribute must be set",
+ getLocation());
+ }
+
+ srcPathList = srcPath.list();
+ if (srcPathList.length == 0) {
+ throw new BuildException("srcdir attribute must be non-empty",
+ getLocation());
+ }
+
+ if (destPath == null) {
+ destPath = srcPath;
+ }
+
+ if (cache != null && cache.exists() && !cache.isDirectory()) {
+ throw new BuildException("The cache, if specified, must "
+ + "point to a directory");
+ }
+
+ if (cache != null && !cache.exists()) {
+ cache.mkdirs();
+ }
+
+ determineDependencies();
+ if (dump) {
+ dumpDependencies();
+ }
+ determineOutOfDateClasses();
+ int count = deleteAllAffectedFiles();
+
+ long duration = (System.currentTimeMillis() - start) / ONE_SECOND;
+
+ final int summaryLogLevel;
+ if (count > 0) {
+ summaryLogLevel = Project.MSG_INFO;
+ } else {
+ summaryLogLevel = Project.MSG_DEBUG;
+ }
+
+ log("Deleted " + count + " out of date files in "
+ + duration + " seconds", summaryLogLevel);
+ } catch (Exception e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Scans the directory looking for source files that are newer than
+ * their class files. The results are returned in the class variable
+ * compileList
+ *
+ * @param srcDir the source directory
+ * @param files the names of the files in the source dir which are to be
+ * checked.
+ */
+ protected void scanDir(File srcDir, String[] files) {
+
+ for (int i = 0; i < files.length; i++) {
+ File srcFile = new File(srcDir, files[i]);
+ if (files[i].endsWith(".java")) {
+ String filePath = srcFile.getPath();
+ String className
+ = filePath.substring(srcDir.getPath().length() + 1,
+ filePath.length() - ".java".length());
+ className = ClassFileUtils.convertSlashName(className);
+ ClassFileInfo info
+ = (ClassFileInfo) classFileInfoMap.get(className);
+ if (info == null) {
+ // there was no class file. add this class to the list
+ outOfDateClasses.put(className, className);
+ } else {
+ if (srcFile.lastModified()
+ > info.absoluteFile.lastModified()) {
+ outOfDateClasses.put(className, className);
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Get the list of class files we are going to analyse.
+ *
+ * @param classLocations a path structure containing all the directories
+ * where classes can be found.
+ * @return a vector containing the classes to analyse.
+ */
+ private Vector getClassFiles(Path classLocations) {
+ // break the classLocations into its components.
+ String[] classLocationsList = classLocations.list();
+
+ Vector classFileList = new Vector();
+
+ for (int i = 0; i < classLocationsList.length; ++i) {
+ File dir = new File(classLocationsList[i]);
+ if (dir.isDirectory()) {
+ addClassFiles(classFileList, dir, dir);
+ }
+ }
+
+ return classFileList;
+ }
+
+ /**
+ * Find the source file for a given class
+ *
+ * @param classname the classname in slash format.
+ * @param sourceFileKnownToExist if not null, a file already known to exist
+ * (saves call to .exists())
+ */
+ private File findSourceFile(String classname, File sourceFileKnownToExist) {
+ String sourceFilename;
+ int innerIndex = classname.indexOf("$");
+ if (innerIndex != -1) {
+ sourceFilename = classname.substring(0, innerIndex) + ".java";
+ } else {
+ sourceFilename = classname + ".java";
+ }
+
+ // search the various source path entries
+ for (int i = 0; i < srcPathList.length; ++i) {
+ File sourceFile = new File(srcPathList[i], sourceFilename);
+ if (sourceFile.equals(sourceFileKnownToExist) || sourceFile.exists()) {
+ return sourceFile;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Add the list of class files from the given directory to the class
+ * file vector, including any subdirectories.
+ *
+ * @param classFileList a list of ClassFileInfo objects for all the
+ * files in the directory tree
+ * @param dir the directory tree to be searched, recursively, for class
+ * files
+ * @param root the root of the source tree. This is used to determine
+ * the absolute class name from the relative position in the
+ * source tree
+ */
+ private void addClassFiles(Vector classFileList, File dir, File root) {
+ String[] filesInDir = dir.list();
+
+ if (filesInDir == null) {
+ return;
+ }
+ int length = filesInDir.length;
+
+ int rootLength = root.getPath().length();
+ File sourceFileKnownToExist = null; // speed optimization
+ for (int i = 0; i < length; ++i) {
+ File file = new File(dir, filesInDir[i]);
+ if (filesInDir[i].endsWith(".class")) {
+ ClassFileInfo info = new ClassFileInfo();
+ info.absoluteFile = file;
+ String relativeName = file.getPath().substring(
+ rootLength + 1,
+ file.getPath().length() - ".class".length());
+ info.className
+ = ClassFileUtils.convertSlashName(relativeName);
+ info.sourceFile = sourceFileKnownToExist = findSourceFile(
+ relativeName, sourceFileKnownToExist);
+ classFileList.addElement(info);
+ } else {
+ addClassFiles(classFileList, file, root);
+ }
+ }
+ }
+
+
+ /**
+ * Set the directories path to find the Java source files.
+ *
+ * @param srcPath the source path
+ */
+ public void setSrcdir(Path srcPath) {
+ this.srcPath = srcPath;
+ }
+
+ /**
+ * Set the destination directory where the compiled Java files exist.
+ *
+ * @param destPath the destination areas where build files are written
+ */
+ public void setDestDir(Path destPath) {
+ this.destPath = destPath;
+ }
+
+ /**
+ * Sets the dependency cache file.
+ *
+ * @param cache the dependency cache file
+ */
+ public void setCache(File cache) {
+ this.cache = cache;
+ }
+
+ /**
+ * If true, transitive dependencies are followed until the
+ * closure of the dependency set if reached.
+ * When not set, the depend task will only follow
+ * direct dependencies between classes.
+ *
+ * @param closure indicate if dependency closure is required.
+ */
+ public void setClosure(boolean closure) {
+ this.closure = closure;
+ }
+
+ /**
+ * If true, the dependency information will be written
+ * to the debug level log.
+ *
+ * @param dump set to true to dump dependency information to the log
+ */
+ public void setDump(boolean dump) {
+ this.dump = dump;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/DirectoryIterator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/DirectoryIterator.java
new file mode 100644
index 00000000..ebf244a5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/DirectoryIterator.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.tools.ant.taskdefs.optional.depend;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Stack;
+import java.util.Vector;
+
+/**
+ * An iterator which iterates through the contents of a java directory. The
+ * iterator should be created with the directory at the root of the Java
+ * namespace.
+ *
+ */
+public class DirectoryIterator implements ClassFileIterator {
+
+ /**
+ * This is a stack of current iterators supporting the depth first
+ * traversal of the directory tree.
+ */
+ private Stack enumStack;
+
+ /**
+ * The current directory iterator. As directories encounter lower level
+ * directories, the current iterator is pushed onto the iterator stack
+ * and a new iterator over the sub directory becomes the current
+ * directory. This implements a depth first traversal of the directory
+ * namespace.
+ */
+ private Enumeration currentEnum;
+
+ /**
+ * Creates a directory iterator. The directory iterator is created to
+ * scan the root directory. If the changeInto flag is given, then the
+ * entries returned will be relative to this directory and not the
+ * current directory.
+ *
+ * @param rootDirectory the root if the directory namespace which is to
+ * be iterated over
+ * @param changeInto if true then the returned entries will be relative
+ * to the rootDirectory and not the current directory.
+ * @exception IOException if there is a problem reading the directory
+ * information.
+ */
+ public DirectoryIterator(File rootDirectory, boolean changeInto)
+ throws IOException {
+ super();
+
+ enumStack = new Stack();
+
+ Vector filesInRoot = getDirectoryEntries(rootDirectory);
+
+ currentEnum = filesInRoot.elements();
+ }
+
+ /**
+ * Get a vector covering all the entries (files and subdirectories in a
+ * directory).
+ *
+ * @param directory the directory to be scanned.
+ * @return a vector containing File objects for each entry in the
+ * directory.
+ */
+ private Vector getDirectoryEntries(File directory) {
+ Vector files = new Vector();
+
+ // File[] filesInDir = directory.listFiles();
+ String[] filesInDir = directory.list();
+
+ if (filesInDir != null) {
+ int length = filesInDir.length;
+
+ for (int i = 0; i < length; ++i) {
+ files.addElement(new File(directory, filesInDir[i]));
+ }
+ }
+
+ return files;
+ }
+
+ /**
+ * Template method to allow subclasses to supply elements for the
+ * iteration. The directory iterator maintains a stack of iterators
+ * covering each level in the directory hierarchy. The current iterator
+ * covers the current directory being scanned. If the next entry in that
+ * directory is a subdirectory, the current iterator is pushed onto the
+ * stack and a new iterator is created for the subdirectory. If the
+ * entry is a file, it is returned as the next element and the iterator
+ * remains valid. If there are no more entries in the current directory,
+ * the topmost iterator on the stack is popped off to become the
+ * current iterator.
+ *
+ * @return the next ClassFile in the iteration.
+ */
+ public ClassFile getNextClassFile() {
+ ClassFile nextElement = null;
+
+ try {
+ while (nextElement == null) {
+ if (currentEnum.hasMoreElements()) {
+ File element = (File) currentEnum.nextElement();
+
+ if (element.isDirectory()) {
+
+ // push the current iterator onto the stack and then
+ // iterate through this directory.
+ enumStack.push(currentEnum);
+
+ Vector files = getDirectoryEntries(element);
+
+ currentEnum = files.elements();
+ } else {
+
+ // we have a file. create a stream for it
+ FileInputStream inFileStream
+ = new FileInputStream(element);
+
+ if (element.getName().endsWith(".class")) {
+
+ // create a data input stream from the jar
+ // input stream
+ ClassFile javaClass = new ClassFile();
+
+ javaClass.read(inFileStream);
+
+ nextElement = javaClass;
+ }
+ }
+ } else {
+ // this iterator is exhausted. Can we pop one off the stack
+ if (enumStack.empty()) {
+ break;
+ } else {
+ currentEnum = (Enumeration) enumStack.pop();
+ }
+ }
+ }
+ } catch (IOException e) {
+ nextElement = null;
+ }
+
+ return nextElement;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/JarFileIterator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/JarFileIterator.java
new file mode 100644
index 00000000..bc315d2e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/JarFileIterator.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.tools.ant.taskdefs.optional.depend;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * A class file iterator which iterates through the contents of a Java jar
+ * file.
+ *
+ */
+public class JarFileIterator implements ClassFileIterator {
+ /** The jar stream from the jar file being iterated over*/
+ private ZipInputStream jarStream;
+
+ /**
+ * Construct an iterator over a jar stream
+ *
+ * @param stream the basic input stream from which the Jar is received
+ * @exception IOException if the jar stream cannot be created
+ */
+ public JarFileIterator(InputStream stream) throws IOException {
+ super();
+
+ jarStream = new ZipInputStream(stream);
+ }
+
+ /**
+ * Get the next ClassFile object from the jar
+ *
+ * @return a ClassFile object describing the class from the jar
+ */
+ public ClassFile getNextClassFile() {
+ ZipEntry jarEntry;
+ ClassFile nextElement = null;
+
+ try {
+ jarEntry = jarStream.getNextEntry();
+
+ while (nextElement == null && jarEntry != null) {
+ String entryName = jarEntry.getName();
+
+ if (!jarEntry.isDirectory() && entryName.endsWith(".class")) {
+
+ // create a data input stream from the jar input stream
+ ClassFile javaClass = new ClassFile();
+
+ javaClass.read(jarStream);
+
+ nextElement = javaClass;
+ } else {
+
+ jarEntry = jarStream.getNextEntry();
+ }
+ }
+ } catch (IOException e) {
+ String message = e.getMessage();
+ String text = e.getClass().getName();
+
+ if (message != null) {
+ text += ": " + message;
+ }
+
+ throw new RuntimeException("Problem reading JAR file: " + text);
+ }
+
+ return nextElement;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ClassCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ClassCPInfo.java
new file mode 100644
index 00000000..8abbfc82
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ClassCPInfo.java
@@ -0,0 +1,93 @@
+/*
+ * 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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * The constant pool entry which stores class information.
+ *
+ */
+public class ClassCPInfo extends ConstantPoolEntry {
+
+ /**
+ * The class' name. This will be only valid if the entry has been
+ * resolved against the constant pool.
+ */
+ private String className;
+
+ /**
+ * The index into the constant pool where this class' name is stored. If
+ * the class name is changed, this entry is invalid until this entry is
+ * connected to a constant pool.
+ */
+ private int index;
+
+ /**
+ * Constructor. Sets the tag value for this entry to type Class
+ */
+ public ClassCPInfo() {
+ super(CONSTANT_CLASS, 1);
+ }
+
+ /**
+ * Read the entry from a stream.
+ *
+ * @param cpStream the stream containing the constant pool entry to be
+ * read.
+ * @exception IOException thrown if there is a problem reading the entry
+ * from the stream.
+ */
+ public void read(DataInputStream cpStream) throws IOException {
+ index = cpStream.readUnsignedShort();
+ className = "unresolved";
+ }
+
+ /**
+ * Generate a string readable version of this entry
+ *
+ * @return string representation of this constant pool entry
+ */
+ public String toString() {
+ return "Class Constant Pool Entry for " + className + "[" + index + "]";
+ }
+
+ /**
+ * Resolve this class info against the given constant pool.
+ *
+ * @param constantPool the constant pool with which to resolve the
+ * class.
+ */
+ public void resolve(ConstantPool constantPool) {
+ className = ((Utf8CPInfo) constantPool.getEntry(index)).getValue();
+
+ super.resolve(constantPool);
+ }
+
+ /**
+ * Get the class name of this entry.
+ *
+ * @return the class' name.
+ */
+ public String getClassName() {
+ return className;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantCPInfo.java
new file mode 100644
index 00000000..6103422e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantCPInfo.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.tools.ant.taskdefs.optional.depend.constantpool;
+
+/**
+ * A Constant Pool entry which represents a constant value.
+ *
+ */
+public abstract class ConstantCPInfo extends ConstantPoolEntry {
+
+ /**
+ * The entry's untyped value. Each subclass interprets the constant
+ * value based on the subclass's type. The value here must be
+ * compatible.
+ */
+ private Object value;
+
+ /**
+ * Initialise the constant entry.
+ *
+ * @param tagValue the constant pool entry type to be used.
+ * @param entries the number of constant pool entry slots occupied by
+ * this entry.
+ */
+ protected ConstantCPInfo(int tagValue, int entries) {
+ super(tagValue, entries);
+ }
+
+ /**
+ * Get the value of the constant.
+ *
+ * @return the value of the constant (untyped).
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * Set the constant value.
+ *
+ * @param newValue the new untyped value of this constant.
+ */
+ public void setValue(Object newValue) {
+ value = newValue;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPool.java
new file mode 100644
index 00000000..67d366b8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPool.java
@@ -0,0 +1,358 @@
+/*
+ * 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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The constant pool of a Java class. The constant pool is a collection of
+ * constants used in a Java class file. It stores strings, constant values,
+ * class names, method names, field names etc.
+ *
+ * @see <a href="http://java.sun.com/docs/books/vmspec/">The Java Virtual
+ * Machine Specification</a>
+ */
+public class ConstantPool {
+
+ /** The entries in the constant pool. */
+ private final List<ConstantPoolEntry> entries = new ArrayList<ConstantPoolEntry>();
+
+ /**
+ * A Hashtable of UTF8 entries - used to get constant pool indexes of
+ * the UTF8 values quickly
+ */
+ private final Map<String, Integer> utf8Indexes = new HashMap<String, Integer>();
+
+ /** Initialise the constant pool. */
+ public ConstantPool() {
+ // The zero index is never present in the constant pool itself so
+ // we add a null entry for it
+ entries.add(null);
+ }
+
+ /**
+ * Read the constant pool from a class input stream.
+ *
+ * @param classStream the DataInputStream of a class file.
+ * @exception IOException if there is a problem reading the constant pool
+ * from the stream
+ */
+ public void read(DataInputStream classStream) throws IOException {
+ int numEntries = classStream.readUnsignedShort();
+
+ for (int i = 1; i < numEntries;) {
+ ConstantPoolEntry nextEntry
+ = ConstantPoolEntry.readEntry(classStream);
+
+ i += nextEntry.getNumEntries();
+
+ addEntry(nextEntry);
+ }
+ }
+
+ /**
+ * Get the size of the constant pool.
+ *
+ * @return the size of the constant pool
+ */
+ public int size() {
+ return entries.size();
+ }
+
+ /**
+ * Add an entry to the constant pool.
+ *
+ * @param entry the new entry to be added to the constant pool.
+ * @return the index into the constant pool at which the entry is
+ * stored.
+ */
+ public int addEntry(ConstantPoolEntry entry) {
+ int index = entries.size();
+
+ entries.add(entry);
+
+ int numSlots = entry.getNumEntries();
+
+ // add null entries for any additional slots required.
+ for (int j = 0; j < numSlots - 1; ++j) {
+ entries.add(null);
+ }
+
+ if (entry instanceof Utf8CPInfo) {
+ Utf8CPInfo utf8Info = (Utf8CPInfo) entry;
+
+ utf8Indexes.put(utf8Info.getValue(), new Integer(index));
+ }
+
+ return index;
+ }
+
+ /**
+ * Resolve the entries in the constant pool. Resolution of the constant
+ * pool involves transforming indexes to other constant pool entries
+ * into the actual data for that entry.
+ */
+ public void resolve() {
+ for (ConstantPoolEntry poolInfo : entries) {
+ if (poolInfo != null && !poolInfo.isResolved()) {
+ poolInfo.resolve(this);
+ }
+ }
+ }
+
+
+ /**
+ * Get an constant pool entry at a particular index.
+ *
+ * @param index the index into the constant pool.
+ * @return the constant pool entry at that index.
+ */
+ public ConstantPoolEntry getEntry(int index) {
+ return entries.get(index);
+ }
+
+ /**
+ * Get the index of a given UTF8 constant pool entry.
+ *
+ * @param value the string value of the UTF8 entry.
+ * @return the index at which the given string occurs in the constant
+ * pool or -1 if the value does not occur.
+ */
+ public int getUTF8Entry(String value) {
+ int index = -1;
+ Integer indexInteger = utf8Indexes.get(value);
+
+ if (indexInteger != null) {
+ index = indexInteger.intValue();
+ }
+
+ return index;
+ }
+
+ /**
+ * Get the index of a given CONSTANT_CLASS entry in the constant pool.
+ *
+ * @param className the name of the class for which the class entry
+ * index is required.
+ * @return the index at which the given class entry occurs in the
+ * constant pool or -1 if the value does not occur.
+ */
+ public int getClassEntry(String className) {
+ int index = -1;
+
+ final int size = entries.size();
+ for (int i = 0; i < size && index == -1; ++i) {
+ Object element = entries.get(i);
+
+ if (element instanceof ClassCPInfo) {
+ ClassCPInfo classinfo = (ClassCPInfo) element;
+
+ if (classinfo.getClassName().equals(className)) {
+ index = i;
+ }
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * Get the index of a given constant value entry in the constant pool.
+ *
+ * @param constantValue the constant value for which the index is
+ * required.
+ * @return the index at which the given value entry occurs in the
+ * constant pool or -1 if the value does not occur.
+ */
+ public int getConstantEntry(Object constantValue) {
+ int index = -1;
+
+ final int size = entries.size();
+ for (int i = 0; i < size && index == -1; ++i) {
+ Object element = entries.get(i);
+
+ if (element instanceof ConstantCPInfo) {
+ ConstantCPInfo constantEntry = (ConstantCPInfo) element;
+
+ if (constantEntry.getValue().equals(constantValue)) {
+ index = i;
+ }
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * Get the index of a given CONSTANT_METHODREF entry in the constant
+ * pool.
+ *
+ * @param methodClassName the name of the class which contains the
+ * method being referenced.
+ * @param methodName the name of the method being referenced.
+ * @param methodType the type descriptor of the method being referenced.
+ * @return the index at which the given method ref entry occurs in the
+ * constant pool or -1 if the value does not occur.
+ */
+ public int getMethodRefEntry(String methodClassName, String methodName,
+ String methodType) {
+ int index = -1;
+
+ final int size = entries.size();
+ for (int i = 0; i < size && index == -1; ++i) {
+ Object element = entries.get(i);
+
+ if (element instanceof MethodRefCPInfo) {
+ MethodRefCPInfo methodRefEntry = (MethodRefCPInfo) element;
+
+ if (methodRefEntry.getMethodClassName().equals(methodClassName)
+ && methodRefEntry.getMethodName().equals(methodName)
+ && methodRefEntry.getMethodType().equals(methodType)) {
+ index = i;
+ }
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * Get the index of a given CONSTANT_INTERFACEMETHODREF entry in the
+ * constant pool.
+ *
+ * @param interfaceMethodClassName the name of the interface which
+ * contains the method being referenced.
+ * @param interfaceMethodName the name of the method being referenced.
+ * @param interfaceMethodType the type descriptor of the method being
+ * referenced.
+ * @return the index at which the given method ref entry occurs in the
+ * constant pool or -1 if the value does not occur.
+ */
+ public int getInterfaceMethodRefEntry(String interfaceMethodClassName,
+ String interfaceMethodName,
+ String interfaceMethodType) {
+ int index = -1;
+
+ final int size = entries.size();
+ for (int i = 0; i < size && index == -1; ++i) {
+ Object element = entries.get(i);
+
+ if (element instanceof InterfaceMethodRefCPInfo) {
+ InterfaceMethodRefCPInfo interfaceMethodRefEntry
+ = (InterfaceMethodRefCPInfo) element;
+
+ if (interfaceMethodRefEntry.getInterfaceMethodClassName().equals(
+ interfaceMethodClassName)
+ && interfaceMethodRefEntry.getInterfaceMethodName().equals(
+ interfaceMethodName)
+ && interfaceMethodRefEntry.getInterfaceMethodType().equals(
+ interfaceMethodType)) {
+ index = i;
+ }
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * Get the index of a given CONSTANT_FIELDREF entry in the constant
+ * pool.
+ *
+ * @param fieldClassName the name of the class which contains the field
+ * being referenced.
+ * @param fieldName the name of the field being referenced.
+ * @param fieldType the type descriptor of the field being referenced.
+ * @return the index at which the given field ref entry occurs in the
+ * constant pool or -1 if the value does not occur.
+ */
+ public int getFieldRefEntry(String fieldClassName, String fieldName,
+ String fieldType) {
+ int index = -1;
+
+ final int size = entries.size();
+ for (int i = 0; i < size && index == -1; ++i) {
+ Object element = entries.get(i);
+
+ if (element instanceof FieldRefCPInfo) {
+ FieldRefCPInfo fieldRefEntry = (FieldRefCPInfo) element;
+
+ if (fieldRefEntry.getFieldClassName().equals(fieldClassName)
+ && fieldRefEntry.getFieldName().equals(fieldName)
+ && fieldRefEntry.getFieldType().equals(fieldType)) {
+ index = i;
+ }
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * Get the index of a given CONSTANT_NAMEANDTYPE entry in the constant
+ * pool.
+ *
+ * @param name the name
+ * @param type the type
+ * @return the index at which the given NameAndType entry occurs in the
+ * constant pool or -1 if the value does not occur.
+ */
+ public int getNameAndTypeEntry(String name, String type) {
+ int index = -1;
+
+ final int size = entries.size();
+ for (int i = 0; i < size && index == -1; ++i) {
+ Object element = entries.get(i);
+
+ if (element instanceof NameAndTypeCPInfo) {
+ NameAndTypeCPInfo nameAndTypeEntry
+ = (NameAndTypeCPInfo) element;
+
+ if (nameAndTypeEntry.getName().equals(name)
+ && nameAndTypeEntry.getType().equals(type)) {
+ index = i;
+ }
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * Dump the constant pool to a string.
+ *
+ * @return the constant pool entries as strings
+ */
+ public String toString() {
+ StringBuilder sb = new StringBuilder("\n");
+ final int size = entries.size();
+
+ for (int i = 0; i < size; ++i) {
+ sb.append("[" + i + "] = " + getEntry(i) + "\n");
+ }
+
+ return sb.toString();
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPoolEntry.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPoolEntry.java
new file mode 100644
index 00000000..26a0d094
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPoolEntry.java
@@ -0,0 +1,242 @@
+/*
+ * 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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * An entry in the constant pool. This class contains a representation of the
+ * constant pool entries. It is an abstract base class for all the different
+ * forms of constant pool entry.
+ *
+ * @see ConstantPool
+ */
+public abstract class ConstantPoolEntry {
+
+ /** Tag value for UTF8 entries. */
+ public static final int CONSTANT_UTF8 = 1;
+
+ /** Tag value for Integer entries. */
+ public static final int CONSTANT_INTEGER = 3;
+
+ /** Tag value for Float entries. */
+ public static final int CONSTANT_FLOAT = 4;
+
+ /** Tag value for Long entries. */
+ public static final int CONSTANT_LONG = 5;
+
+ /** Tag value for Double entries. */
+ public static final int CONSTANT_DOUBLE = 6;
+
+ /** Tag value for Class entries. */
+ public static final int CONSTANT_CLASS = 7;
+
+ /** Tag value for String entries. */
+ public static final int CONSTANT_STRING = 8;
+
+ /** Tag value for Field Reference entries. */
+ public static final int CONSTANT_FIELDREF = 9;
+
+ /** Tag value for Method Reference entries. */
+ public static final int CONSTANT_METHODREF = 10;
+
+ /** Tag value for Interface Method Reference entries. */
+ public static final int CONSTANT_INTERFACEMETHODREF = 11;
+
+ /** Tag value for Name and Type entries. */
+ public static final int CONSTANT_NAMEANDTYPE = 12;
+
+ /** Tag value for Method Handle entries */
+ public static final int CONSTANT_METHODHANDLE = 15;
+
+ /** Tag value for Method Type entries */
+ public static final int CONSTANT_METHODTYPE = 16;
+
+ /** Tag value for InvokeDynamic entries*/
+ public static final int CONSTANT_INVOKEDYNAMIC = 18;
+
+ /**
+ * This entry's tag which identifies the type of this constant pool
+ * entry.
+ */
+ private int tag;
+
+ /**
+ * The number of slots in the constant pool, occupied by this entry.
+ */
+ private int numEntries;
+
+ /**
+ * A flag which indicates if this entry has been resolved or not.
+ */
+ private boolean resolved;
+
+ /**
+ * Initialise the constant pool entry.
+ *
+ * @param tagValue the tag value which identifies which type of constant
+ * pool entry this is.
+ * @param entries the number of constant pool entry slots this entry
+ * occupies.
+ */
+ public ConstantPoolEntry(int tagValue, int entries) {
+ tag = tagValue;
+ numEntries = entries;
+ resolved = false;
+ }
+
+ /**
+ * Read a constant pool entry from a stream. This is a factory method
+ * which reads a constant pool entry form a stream and returns the
+ * appropriate subclass for the entry.
+ *
+ * @param cpStream the stream from which the constant pool entry is to
+ * be read.
+ * @return the appropriate ConstantPoolEntry subclass representing the
+ * constant pool entry from the stream.
+ * @exception IOException if the constant pool entry cannot be read
+ * from the stream
+ */
+ public static ConstantPoolEntry readEntry(DataInputStream cpStream)
+ throws IOException {
+ ConstantPoolEntry cpInfo = null;
+ int cpTag = cpStream.readUnsignedByte();
+
+ switch (cpTag) {
+
+ case CONSTANT_UTF8:
+ cpInfo = new Utf8CPInfo();
+
+ break;
+ case CONSTANT_INTEGER:
+ cpInfo = new IntegerCPInfo();
+
+ break;
+ case CONSTANT_FLOAT:
+ cpInfo = new FloatCPInfo();
+
+ break;
+ case CONSTANT_LONG:
+ cpInfo = new LongCPInfo();
+
+ break;
+ case CONSTANT_DOUBLE:
+ cpInfo = new DoubleCPInfo();
+
+ break;
+ case CONSTANT_CLASS:
+ cpInfo = new ClassCPInfo();
+
+ break;
+ case CONSTANT_STRING:
+ cpInfo = new StringCPInfo();
+
+ break;
+ case CONSTANT_FIELDREF:
+ cpInfo = new FieldRefCPInfo();
+
+ break;
+ case CONSTANT_METHODREF:
+ cpInfo = new MethodRefCPInfo();
+
+ break;
+ case CONSTANT_INTERFACEMETHODREF:
+ cpInfo = new InterfaceMethodRefCPInfo();
+
+ break;
+ case CONSTANT_NAMEANDTYPE:
+ cpInfo = new NameAndTypeCPInfo();
+
+ break;
+ case CONSTANT_METHODHANDLE:
+ cpInfo = new MethodHandleCPInfo();
+
+ break;
+ case CONSTANT_METHODTYPE:
+ cpInfo = new MethodTypeCPInfo();
+
+ break;
+ case CONSTANT_INVOKEDYNAMIC:
+ cpInfo = new InvokeDynamicCPInfo();
+
+ break;
+ default:
+ throw new ClassFormatError("Invalid Constant Pool entry Type "
+ + cpTag);
+ }
+
+ cpInfo.read(cpStream);
+
+ return cpInfo;
+ }
+
+ /**
+ * Indicates whether this entry has been resolved. In general a constant
+ * pool entry can reference another constant pool entry by its index
+ * value. Resolution involves replacing this index value with the
+ * constant pool entry at that index.
+ *
+ * @return true if this entry has been resolved.
+ */
+ public boolean isResolved() {
+ return resolved;
+ }
+
+ /**
+ * Resolve this constant pool entry with respect to its dependents in
+ * the constant pool.
+ *
+ * @param constantPool the constant pool of which this entry is a member
+ * and against which this entry is to be resolved.
+ */
+ public void resolve(ConstantPool constantPool) {
+ resolved = true;
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception IOException if there is a problem reading the entry from
+ * the stream.
+ */
+ public abstract void read(DataInputStream cpStream) throws IOException;
+
+ /**
+ * Get the Entry's type tag.
+ *
+ * @return The Tag value of this entry
+ */
+ public int getTag() {
+ return tag;
+ }
+
+ /**
+ * Get the number of Constant Pool Entry slots within the constant pool
+ * occupied by this entry.
+ *
+ * @return the number of slots used.
+ */
+ public final int getNumEntries() {
+ return numEntries;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/DoubleCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/DoubleCPInfo.java
new file mode 100644
index 00000000..a21c0d66
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/DoubleCPInfo.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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * The constant pool entry subclass used to represent double constant
+ * values.
+ *
+ */
+public class DoubleCPInfo extends ConstantCPInfo {
+ /**
+ * Constructor
+ */
+ public DoubleCPInfo() {
+ super(CONSTANT_DOUBLE, 2);
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception IOException if there is a problem reading the entry from the
+ * stream.
+ */
+ public void read(DataInputStream cpStream) throws IOException {
+ setValue(new Double(cpStream.readDouble()));
+ }
+
+ /**
+ * Print a readable version of the constant pool entry.
+ *
+ * @return the string representation of this constant pool entry.
+ */
+ public String toString() {
+ return "Double Constant Pool Entry: " + getValue();
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FieldRefCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FieldRefCPInfo.java
new file mode 100644
index 00000000..06c0925d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FieldRefCPInfo.java
@@ -0,0 +1,130 @@
+/*
+ * 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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A FieldRef CP Info
+ *
+ */
+public class FieldRefCPInfo extends ConstantPoolEntry {
+ /** Name of the field's class */
+ private String fieldClassName;
+ /** name of the field in that class */
+ private String fieldName;
+ /** The type of the field */
+ private String fieldType;
+ /** Index into the constant pool for the class */
+ private int classIndex;
+ /** Index into the constant pool for the name and type entry */
+ private int nameAndTypeIndex;
+
+ /** Constructor. */
+ public FieldRefCPInfo() {
+ super(CONSTANT_FIELDREF, 1);
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception IOException if there is a problem reading the entry from
+ * the stream.
+ */
+ public void read(DataInputStream cpStream) throws IOException {
+ classIndex = cpStream.readUnsignedShort();
+ nameAndTypeIndex = cpStream.readUnsignedShort();
+ }
+
+ /**
+ * Resolve this constant pool entry with respect to its dependents in
+ * the constant pool.
+ *
+ * @param constantPool the constant pool of which this entry is a member
+ * and against which this entry is to be resolved.
+ */
+ public void resolve(ConstantPool constantPool) {
+ ClassCPInfo fieldClass
+ = (ClassCPInfo) constantPool.getEntry(classIndex);
+
+ fieldClass.resolve(constantPool);
+
+ fieldClassName = fieldClass.getClassName();
+
+ NameAndTypeCPInfo nt
+ = (NameAndTypeCPInfo) constantPool.getEntry(nameAndTypeIndex);
+
+ nt.resolve(constantPool);
+
+ fieldName = nt.getName();
+ fieldType = nt.getType();
+
+ super.resolve(constantPool);
+ }
+
+ /**
+ * Print a readable version of the constant pool entry.
+ *
+ * @return the string representation of this constant pool entry.
+ */
+ public String toString() {
+ String value;
+
+ if (isResolved()) {
+ value = "Field : Class = " + fieldClassName + ", name = "
+ + fieldName + ", type = " + fieldType;
+ } else {
+ value = "Field : Class index = " + classIndex
+ + ", name and type index = " + nameAndTypeIndex;
+ }
+
+ return value;
+ }
+
+ /**
+ * Gets the name of the class defining the field
+ *
+ * @return the name of the class defining the field
+ */
+ public String getFieldClassName() {
+ return fieldClassName;
+ }
+
+ /**
+ * Get the name of the field
+ *
+ * @return the field's name
+ */
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ /**
+ * Get the type of the field
+ *
+ * @return the field's type in string format
+ */
+ public String getFieldType() {
+ return fieldType;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FloatCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FloatCPInfo.java
new file mode 100644
index 00000000..532b6725
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FloatCPInfo.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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A Float CP Info
+ *
+ */
+public class FloatCPInfo extends ConstantCPInfo {
+
+ /** Constructor. */
+ public FloatCPInfo() {
+ super(CONSTANT_FLOAT, 1);
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception IOException if there is a problem reading the entry from
+ * the stream.
+ */
+ public void read(DataInputStream cpStream) throws IOException {
+ setValue(new Float(cpStream.readFloat()));
+ }
+
+ /**
+ * Print a readable version of the constant pool entry.
+ *
+ * @return the string representation of this constant pool entry.
+ */
+ public String toString() {
+ return "Float Constant Pool Entry: " + getValue();
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/IntegerCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/IntegerCPInfo.java
new file mode 100644
index 00000000..3beaa8cd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/IntegerCPInfo.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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * An Integer CP Info
+ *
+ */
+public class IntegerCPInfo extends ConstantCPInfo {
+
+ /** Constructor. */
+ public IntegerCPInfo() {
+ super(CONSTANT_INTEGER, 1);
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception IOException if there is a problem reading the entry from
+ * the stream.
+ */
+ public void read(DataInputStream cpStream) throws IOException {
+ setValue(new Integer(cpStream.readInt()));
+ }
+
+ /**
+ * Print a readable version of the constant pool entry.
+ *
+ * @return the string representation of this constant pool entry.
+ */
+ public String toString() {
+ return "Integer Constant Pool Entry: " + getValue();
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InterfaceMethodRefCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InterfaceMethodRefCPInfo.java
new file mode 100644
index 00000000..fbc23c1c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InterfaceMethodRefCPInfo.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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A InterfaceMethodRef CP Info
+ *
+ */
+public class InterfaceMethodRefCPInfo extends ConstantPoolEntry {
+ /** the class name of the class defining the interface method */
+ private String interfaceMethodClassName;
+ /** the name of the interface nmethod */
+ private String interfaceMethodName;
+ /** the method signature of the interface method */
+ private String interfaceMethodType;
+ /**
+ * the index into the constant pool of the class entry for the interface
+ * class
+ */
+ private int classIndex;
+ /**
+ * the index into the constant pool of the name and type entry
+ * describing the method
+ */
+ private int nameAndTypeIndex;
+
+ /** Constructor. */
+ public InterfaceMethodRefCPInfo() {
+ super(CONSTANT_INTERFACEMETHODREF, 1);
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception IOException if there is a problem reading the entry from
+ * the stream.
+ */
+ public void read(DataInputStream cpStream) throws IOException {
+ classIndex = cpStream.readUnsignedShort();
+ nameAndTypeIndex = cpStream.readUnsignedShort();
+ }
+
+ /**
+ * Resolve this constant pool entry with respect to its dependents in
+ * the constant pool.
+ *
+ * @param constantPool the constant pool of which this entry is a member
+ * and against which this entry is to be resolved.
+ */
+ public void resolve(ConstantPool constantPool) {
+ ClassCPInfo interfaceMethodClass
+ = (ClassCPInfo) constantPool.getEntry(classIndex);
+
+ interfaceMethodClass.resolve(constantPool);
+
+ interfaceMethodClassName = interfaceMethodClass.getClassName();
+
+ NameAndTypeCPInfo nt
+ = (NameAndTypeCPInfo) constantPool.getEntry(nameAndTypeIndex);
+
+ nt.resolve(constantPool);
+
+ interfaceMethodName = nt.getName();
+ interfaceMethodType = nt.getType();
+
+ super.resolve(constantPool);
+ }
+
+ /**
+ * Print a readable version of the constant pool entry.
+ *
+ * @return the string representation of this constant pool entry.
+ */
+ public String toString() {
+ String value;
+
+ if (isResolved()) {
+ value = "InterfaceMethod : Class = " + interfaceMethodClassName
+ + ", name = " + interfaceMethodName + ", type = "
+ + interfaceMethodType;
+ } else {
+ value = "InterfaceMethod : Class index = " + classIndex
+ + ", name and type index = " + nameAndTypeIndex;
+ }
+
+ return value;
+ }
+
+ /**
+ * Gets the name of the class defining the interface method
+ *
+ * @return the name of the class defining the interface method
+ */
+ public String getInterfaceMethodClassName() {
+ return interfaceMethodClassName;
+ }
+
+ /**
+ * Get the name of the interface method
+ *
+ * @return the name of the interface method
+ */
+ public String getInterfaceMethodName() {
+ return interfaceMethodName;
+ }
+
+ /**
+ * Gets the type of the interface method
+ *
+ * @return the interface method's type signature
+ */
+ public String getInterfaceMethodType() {
+ return interfaceMethodType;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InvokeDynamicCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InvokeDynamicCPInfo.java
new file mode 100644
index 00000000..3795db75
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InvokeDynamicCPInfo.java
@@ -0,0 +1,85 @@
+/*
+ * 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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * An InvokeDynamic CP Info
+ *
+ */
+public class InvokeDynamicCPInfo extends ConstantCPInfo {
+
+ /** Index into the bootstrap methods for the class */
+ private int bootstrapMethodAttrIndex;
+ /** the value of the method descriptor pointed to */
+ private int nameAndTypeIndex;
+ /** the name and type CP info pointed to */
+ private NameAndTypeCPInfo nameAndTypeCPInfo;
+ /** */
+ /** Constructor. */
+ public InvokeDynamicCPInfo() {
+ super(CONSTANT_INVOKEDYNAMIC, 1);
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception java.io.IOException if there is a problem reading the entry from
+ * the stream.
+ */
+ public void read(DataInputStream cpStream) throws IOException {
+ bootstrapMethodAttrIndex = cpStream.readUnsignedShort();
+ nameAndTypeIndex = cpStream.readUnsignedShort();
+ }
+
+ /**
+ * Print a readable version of the constant pool entry.
+ *
+ * @return the string representation of this constant pool entry.
+ */
+ public String toString() {
+ String value;
+ if (isResolved()) {
+ value = "Name = " + nameAndTypeCPInfo.getName() + ", type = " + nameAndTypeCPInfo.getType();
+ } else {
+ value = "BootstrapMethodAttrIndex inx = " + bootstrapMethodAttrIndex
+ + "NameAndType index = " + nameAndTypeIndex;
+ }
+
+ return value;
+ }
+ /**
+ * Resolve this constant pool entry with respect to its dependents in
+ * the constant pool.
+ *
+ * @param constantPool the constant pool of which this entry is a member
+ * and against which this entry is to be resolved.
+ */
+ public void resolve(ConstantPool constantPool) {
+ nameAndTypeCPInfo
+ = (NameAndTypeCPInfo) constantPool.getEntry(nameAndTypeIndex);
+ nameAndTypeCPInfo.resolve(constantPool);
+ super.resolve(constantPool);
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/LongCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/LongCPInfo.java
new file mode 100644
index 00000000..e854f04f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/LongCPInfo.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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A Long CP Info
+ *
+ */
+public class LongCPInfo extends ConstantCPInfo {
+
+ /** Constructor. */
+ public LongCPInfo() {
+ super(CONSTANT_LONG, 2);
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception IOException if there is a problem reading the entry from
+ * the stream.
+ */
+ public void read(DataInputStream cpStream) throws IOException {
+ setValue(new Long(cpStream.readLong()));
+ }
+
+ /**
+ * Print a readable version of the constant pool entry.
+ *
+ * @return the string representation of this constant pool entry.
+ */
+ public String toString() {
+ return "Long Constant Pool Entry: " + getValue();
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodHandleCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodHandleCPInfo.java
new file mode 100644
index 00000000..e11e3aab
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodHandleCPInfo.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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A MethodHandle CP Info
+ *
+ */
+public class MethodHandleCPInfo extends ConstantPoolEntry {
+ private ConstantPoolEntry reference;
+
+ /** reference kind **/
+ private ReferenceKind referenceKind;
+ /** Must be a valid index into the constant pool tabel. */
+ private int referenceIndex;
+ /**
+ * the index into the constant pool which defined the name and type
+ * signature of the method
+ */
+ private int nameAndTypeIndex;
+ public enum ReferenceKind {
+ REF_getField(1),
+ REF_getStatic(2),
+ REF_putField(3),
+ REF_putStatic(4),
+ REF_invokeVirtual(5),
+ REF_invokeStatic(6),
+ REF_invokeSpecial(7),
+ REF_newInvokeSpecial(8),
+ REF_invokeInterface(9);
+ private final int referenceKind;
+ ReferenceKind(int referenceKind) {
+ this.referenceKind = referenceKind;
+ }
+
+ }
+ /** Constructor. */
+ public MethodHandleCPInfo() {
+ super(CONSTANT_METHODHANDLE, 1);
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception java.io.IOException if there is a problem reading the entry from
+ * the stream.
+ */
+ public void read(DataInputStream cpStream) throws IOException {
+ referenceKind = ReferenceKind.values()[cpStream.readUnsignedByte() - 1];
+
+ referenceIndex = cpStream.readUnsignedShort();
+ }
+
+ /**
+ * Print a readable version of the constant pool entry.
+ *
+ * @return the string representation of this constant pool entry.
+ */
+ public String toString() {
+ String value;
+
+ if (isResolved()) {
+ value = "MethodHandle : " + reference.toString();
+ } else {
+ value = "MethodHandle : Reference kind = " + referenceKind
+ + "Reference index = " + referenceIndex;
+ }
+
+ return value;
+ }
+
+ /**
+ * Resolve this constant pool entry with respect to its dependents in
+ * the constant pool.
+ *
+ * @param constantPool the constant pool of which this entry is a member
+ * and against which this entry is to be resolved.
+ */
+ public void resolve(ConstantPool constantPool) {
+ reference = constantPool.getEntry(referenceIndex);
+ reference.resolve(constantPool);
+ super.resolve(constantPool);
+ }
+
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodRefCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodRefCPInfo.java
new file mode 100644
index 00000000..6b335212
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodRefCPInfo.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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A MethodRef CP Info
+ *
+ */
+public class MethodRefCPInfo extends ConstantPoolEntry {
+ /** the name of the class defining this method */
+ private String methodClassName;
+ /** the name of the method */
+ private String methodName;
+ /** the method's type descriptor */
+ private String methodType;
+ /** The index into the constant pool which defines the class of this method. */
+ private int classIndex;
+ /**
+ * the index into the constant pool which defined the name and type
+ * signature of the method
+ */
+ private int nameAndTypeIndex;
+
+ /** Constructor. */
+ public MethodRefCPInfo() {
+ super(CONSTANT_METHODREF, 1);
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception IOException if there is a problem reading the entry from
+ * the stream.
+ */
+ public void read(DataInputStream cpStream) throws IOException {
+ classIndex = cpStream.readUnsignedShort();
+ nameAndTypeIndex = cpStream.readUnsignedShort();
+ }
+
+ /**
+ * Print a readable version of the constant pool entry.
+ *
+ * @return the string representation of this constant pool entry.
+ */
+ public String toString() {
+ String value;
+
+ if (isResolved()) {
+ value = "Method : Class = " + methodClassName + ", name = "
+ + methodName + ", type = " + methodType;
+ } else {
+ value = "Method : Class index = " + classIndex
+ + ", name and type index = " + nameAndTypeIndex;
+ }
+
+ return value;
+ }
+
+ /**
+ * Resolve this constant pool entry with respect to its dependents in
+ * the constant pool.
+ *
+ * @param constantPool the constant pool of which this entry is a member
+ * and against which this entry is to be resolved.
+ */
+ public void resolve(ConstantPool constantPool) {
+ ClassCPInfo methodClass
+ = (ClassCPInfo) constantPool.getEntry(classIndex);
+
+ methodClass.resolve(constantPool);
+
+ methodClassName = methodClass.getClassName();
+
+ NameAndTypeCPInfo nt
+ = (NameAndTypeCPInfo) constantPool.getEntry(nameAndTypeIndex);
+
+ nt.resolve(constantPool);
+
+ methodName = nt.getName();
+ methodType = nt.getType();
+
+ super.resolve(constantPool);
+ }
+
+ /**
+ * Get the name of the class defining the method
+ *
+ * @return the name of the class defining this method
+ */
+ public String getMethodClassName() {
+ return methodClassName;
+ }
+
+ /**
+ * Get the name of the method.
+ *
+ * @return the name of the method.
+ */
+ public String getMethodName() {
+ return methodName;
+ }
+
+ /**
+ * Get the type signature of the method.
+ *
+ * @return the type signature of the method.
+ */
+ public String getMethodType() {
+ return methodType;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodTypeCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodTypeCPInfo.java
new file mode 100644
index 00000000..d3c35cee
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodTypeCPInfo.java
@@ -0,0 +1,82 @@
+/*
+ * 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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A Method Type CP Info
+ *
+ */
+public class MethodTypeCPInfo extends ConstantCPInfo {
+
+ /** Index into the constant pool for the class */
+ private int methodDescriptorIndex;
+ /** the value of the method descriptor pointed to */
+ private String methodDescriptor;
+ /** Constructor. */
+ public MethodTypeCPInfo() {
+ super(CONSTANT_METHODTYPE, 1);
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception java.io.IOException if there is a problem reading the entry from
+ * the stream.
+ */
+ @Override
+ public void read(final DataInputStream cpStream) throws IOException {
+ methodDescriptorIndex = cpStream.readUnsignedShort();
+ }
+
+ /**
+ * Resolve this constant pool entry with respect to its dependents in
+ * the constant pool.
+ *
+ * @param constantPool the constant pool of which this entry is a member
+ * and against which this entry is to be resolved.
+ */
+ @Override
+ public void resolve(final ConstantPool constantPool) {
+ final Utf8CPInfo methodClass
+ = (Utf8CPInfo) constantPool.getEntry(methodDescriptorIndex);
+ methodClass.resolve(constantPool);
+ methodDescriptor = methodClass.getValue();
+ super.resolve(constantPool);
+ }
+ /**
+ * Print a readable version of the constant pool entry.
+ *
+ * @return the string representation of this constant pool entry.
+ */
+ @Override
+ public String toString() {
+ if (!isResolved()) {
+ return "MethodDescriptorIndex: " + methodDescriptorIndex;
+ } else {
+ return "MethodDescriptor: " + methodDescriptor;
+
+ }
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/NameAndTypeCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/NameAndTypeCPInfo.java
new file mode 100644
index 00000000..47f454d2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/NameAndTypeCPInfo.java
@@ -0,0 +1,112 @@
+/*
+ * 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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A NameAndType CP Info
+ *
+ */
+public class NameAndTypeCPInfo extends ConstantPoolEntry {
+
+ /** Constructor. */
+ public NameAndTypeCPInfo() {
+ super(CONSTANT_NAMEANDTYPE, 1);
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception IOException if there is a problem reading the entry from
+ * the stream.
+ */
+ public void read(DataInputStream cpStream) throws IOException {
+ nameIndex = cpStream.readUnsignedShort();
+ descriptorIndex = cpStream.readUnsignedShort();
+ }
+
+ /**
+ * Print a readable version of the constant pool entry.
+ *
+ * @return the string representation of this constant pool entry.
+ */
+ public String toString() {
+ String value;
+
+ if (isResolved()) {
+ value = "Name = " + name + ", type = " + type;
+ } else {
+ value = "Name index = " + nameIndex
+ + ", descriptor index = " + descriptorIndex;
+ }
+
+ return value;
+ }
+
+ /**
+ * Resolve this constant pool entry with respect to its dependents in
+ * the constant pool.
+ *
+ * @param constantPool the constant pool of which this entry is a member
+ * and against which this entry is to be resolved.
+ */
+ public void resolve(ConstantPool constantPool) {
+ name = ((Utf8CPInfo) constantPool.getEntry(nameIndex)).getValue();
+ type = ((Utf8CPInfo) constantPool.getEntry(descriptorIndex)).getValue();
+
+ super.resolve(constantPool);
+ }
+
+ /**
+ * Get the name component of this entry
+ *
+ * @return the name of this name and type entry
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the type signature of this entry
+ *
+ * @return the type signature of this entry
+ */
+ public String getType() {
+ return type;
+ }
+
+ /** the name component of this entry */
+ private String name;
+ /** the type component of this entry */
+ private String type;
+ /**
+ * the index into the constant pool at which the name component's string
+ * value is stored
+ */
+ private int nameIndex;
+ /**
+ * the index into the constant pool where the type descriptor string is
+ * stored.
+ */
+ private int descriptorIndex;
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/StringCPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/StringCPInfo.java
new file mode 100644
index 00000000..bc9ee24b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/StringCPInfo.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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A String Constant Pool Entry. The String info contains an index into the
+ * constant pool where a UTF8 string is stored.
+ *
+ */
+public class StringCPInfo extends ConstantCPInfo {
+
+ /** Constructor. */
+ public StringCPInfo() {
+ super(CONSTANT_STRING, 1);
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception IOException if there is a problem reading the entry from
+ * the stream.
+ */
+ public void read(DataInputStream cpStream) throws IOException {
+ index = cpStream.readUnsignedShort();
+
+ setValue("unresolved");
+ }
+
+ /**
+ * Print a readable version of the constant pool entry.
+ *
+ * @return the string representation of this constant pool entry.
+ */
+ public String toString() {
+ return "String Constant Pool Entry for "
+ + getValue() + "[" + index + "]";
+ }
+
+ /**
+ * Resolve this constant pool entry with respect to its dependents in
+ * the constant pool.
+ *
+ * @param constantPool the constant pool of which this entry is a member
+ * and against which this entry is to be resolved.
+ */
+ public void resolve(ConstantPool constantPool) {
+ setValue(((Utf8CPInfo) constantPool.getEntry(index)).getValue());
+ super.resolve(constantPool);
+ }
+
+ /** the index into the constant pool containing the string's content */
+ private int index;
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/Utf8CPInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/Utf8CPInfo.java
new file mode 100644
index 00000000..5471ccde
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/Utf8CPInfo.java
@@ -0,0 +1,67 @@
+/*
+ * 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.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A UTF8 Constant Pool Entry.
+ *
+ */
+public class Utf8CPInfo extends ConstantPoolEntry {
+ /** The String value of the UTF-8 entry */
+ private String value;
+
+ /** Constructor. */
+ public Utf8CPInfo() {
+ super(CONSTANT_UTF8, 1);
+ }
+
+ /**
+ * read a constant pool entry from a class stream.
+ *
+ * @param cpStream the DataInputStream which contains the constant pool
+ * entry to be read.
+ * @exception IOException if there is a problem reading the entry from
+ * the stream.
+ */
+ public void read(DataInputStream cpStream) throws IOException {
+ value = cpStream.readUTF();
+ }
+
+ /**
+ * Print a readable version of the constant pool entry.
+ *
+ * @return the string representation of this constant pool entry.
+ */
+ public String toString() {
+ return "UTF8 Value = " + value;
+ }
+
+ /**
+ * Get the string value of the UTF-8 entry
+ *
+ * @return the UTF-8 value as a Java string
+ */
+ public String getValue() {
+ return value;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandDeploymentTool.java
new file mode 100644
index 00000000..4eefaebf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandDeploymentTool.java
@@ -0,0 +1,559 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ejb;
+
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ExecTask;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+
+/**
+ * BorlandDeploymentTool is dedicated to the Borland Application Server 4.5 and 4.5.1
+ * This task generates and compiles the stubs and skeletons for all ejb described into the
+ * Deployment Descriptor, builds the jar file including the support files and verify
+ * whether the produced jar is valid or not.
+ * The supported options are:
+ * <ul>
+ * <li>debug (boolean) : turn on the debug mode for generation of
+ * stubs and skeletons (default:false)</li>
+ * <li>verify (boolean) : turn on the verification at the end of the jar
+ * production (default:true) </li>
+ * <li>verifyargs (String) : add optional argument to verify command
+ * (see vbj com.inprise.ejb.util.Verify)</li>
+ * <li>basdtd (String) : location of the BAS DTD </li>
+ * <li>generateclient (boolean) : turn on the client jar file generation </li>
+ * <li>version (int) : tell what is the Borland appserver version 4 or 5 </li>
+ * </ul>
+ *
+ *<PRE>
+ *
+ * &lt;ejbjar srcdir=&quot;${build.classes}&quot;
+ * basejarname=&quot;vsmp&quot;
+ * descriptordir=&quot;${rsc.dir}/hrmanager&quot;&gt;
+ * &lt;borland destdir=&quot;tstlib&quot;&gt;
+ * &lt;classpath refid=&quot;classpath&quot; /&gt;
+ * &lt;/borland&gt;
+ * &lt;include name=&quot;**\ejb-jar.xml&quot;/&gt;
+ * &lt;support dir=&quot;${build.classes}&quot;&gt;
+ * &lt;include name=&quot;demo\smp\*.class&quot;/&gt;
+ * &lt;include name=&quot;demo\helper\*.class&quot;/&gt;
+ * &lt;/support&gt;
+ * &lt;/ejbjar&gt;
+ *</PRE>
+ *
+ */
+public class BorlandDeploymentTool extends GenericDeploymentTool
+ implements ExecuteStreamHandler {
+ /** Borland 1.1 ejb id */
+ public static final String PUBLICID_BORLAND_EJB
+ = "-//Inprise Corporation//DTD Enterprise JavaBeans 1.1//EN";
+
+ protected static final String DEFAULT_BAS45_EJB11_DTD_LOCATION
+ = "/com/inprise/j2ee/xml/dtds/ejb-jar.dtd";
+
+ protected static final String DEFAULT_BAS_DTD_LOCATION
+ = "/com/inprise/j2ee/xml/dtds/ejb-inprise.dtd";
+
+ protected static final String BAS_DD = "ejb-inprise.xml";
+ protected static final String BES_DD = "ejb-borland.xml";
+
+
+ /** Java2iiop executable **/
+ protected static final String JAVA2IIOP = "java2iiop";
+
+ /** Verify class */
+ protected static final String VERIFY = "com.inprise.ejb.util.Verify";
+
+ /** Instance variable that stores the suffix for the borland jarfile. */
+ private String jarSuffix = "-ejb.jar";
+
+ /** Instance variable that stores the location of the borland DTD file. */
+ private String borlandDTD;
+
+ /** Instance variable that determines whether the debug mode is on */
+ private boolean java2iiopdebug = false;
+
+ /** store additional param for java2iiop command used to build EJB Stubs */
+ private String java2iioparams = null;
+
+ /** Instance variable that determines whether the client jar file is generated */
+ private boolean generateclient = false;
+
+ /** Borland Enterprise Server = version 5 */
+ static final int BES = 5;
+ /** Borland Application Server or Inprise Application Server = version 4 */
+ static final int BAS = 4;
+
+ /** borland appserver version 4 or 5 */
+ private int version = BAS;
+
+
+ /**
+ * Instance variable that determines whether it is necessary to verify the
+ * produced jar
+ */
+ private boolean verify = true;
+ private String verifyArgs = "";
+
+ private Hashtable genfiles = new Hashtable();
+
+ /**
+ * set the debug mode for java2iiop (default false)
+ * @param debug the setting to use.
+ **/
+ public void setDebug(boolean debug) {
+ this.java2iiopdebug = debug;
+ }
+
+ /**
+ * set the verify mode for the produced jar (default true)
+ * @param verify the setting to use.
+ **/
+ public void setVerify(boolean verify) {
+ this.verify = verify;
+ }
+
+
+ /**
+ * Setter used to store the suffix for the generated borland jar file.
+ * @param inString the string to use as the suffix.
+ */
+ public void setSuffix(String inString) {
+ this.jarSuffix = inString;
+ }
+
+
+ /**
+ * sets some additional args to send to verify command
+ * @param args additional command line parameters
+ */
+ public void setVerifyArgs(String args) {
+ this.verifyArgs = args;
+ }
+
+ /**
+ * Setter used to store the location of the borland DTD. This can be a file on the system
+ * or a resource on the classpath.
+ * @param inString the string to use as the DTD location.
+ */
+ public void setBASdtd(String inString) {
+ this.borlandDTD = inString;
+ }
+
+
+ /**
+ * setter used to store whether the task will include the generate client task.
+ * (see : BorlandGenerateClient task)
+ * @param b if true generate the client task.
+ */
+ public void setGenerateclient(boolean b) {
+ this.generateclient = b;
+ }
+
+ /**
+ * setter used to store the borland appserver version [4 or 5]
+ * @param version app server version 4 or 5
+ */
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ /**
+ * If filled, the params are added to the java2iiop command.
+ * (ex: -no_warn_missing_define)
+ * @param params additional params for java2iiop
+ */
+ public void setJava2iiopParams(String params) {
+ this.java2iioparams = params;
+ }
+
+
+ /**
+ * Get the borland descriptor handler.
+ * @param srcDir the source directory.
+ * @return the descriptor.
+ */
+ protected DescriptorHandler getBorlandDescriptorHandler(final File srcDir) {
+ DescriptorHandler handler =
+ new DescriptorHandler(getTask(), srcDir) {
+ protected void processElement() {
+ if (currentElement.equals("type-storage")) {
+ // Get the filename of vendor specific descriptor
+ String fileNameWithMETA = currentText;
+ //trim the META_INF\ off of the file name
+ String fileName
+ = fileNameWithMETA.substring(META_DIR.length(),
+ fileNameWithMETA.length());
+ File descriptorFile = new File(srcDir, fileName);
+
+ ejbFiles.put(fileNameWithMETA, descriptorFile);
+ }
+ }
+ };
+ handler.registerDTD(PUBLICID_BORLAND_EJB,
+ borlandDTD == null ? DEFAULT_BAS_DTD_LOCATION : borlandDTD);
+
+ for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) {
+ EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
+ handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
+ }
+ return handler;
+ }
+
+ /**
+ * Add any vendor specific files which should be included in the
+ * EJB Jar.
+ * @param ejbFiles the map to add the files to.
+ * @param ddPrefix the prefix to use.
+ */
+ protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+
+ //choose the right vendor DD
+ if (!(version == BES || version == BAS)) {
+ throw new BuildException("version " + version + " is not supported");
+ }
+
+ String dd = (version == BES ? BES_DD : BAS_DD);
+
+ log("vendor file : " + ddPrefix + dd, Project.MSG_DEBUG);
+
+ File borlandDD = new File(getConfig().descriptorDir, ddPrefix + dd);
+ if (borlandDD.exists()) {
+ log("Borland specific file found " + borlandDD, Project.MSG_VERBOSE);
+ ejbFiles.put(META_DIR + dd , borlandDD);
+ } else {
+ log("Unable to locate borland deployment descriptor. "
+ + "It was expected to be in "
+ + borlandDD.getPath(), Project.MSG_WARN);
+ return;
+ }
+ }
+
+ /**
+ * Get the vendor specific name of the Jar that will be output. The modification date
+ * of this jar will be checked against the dependent bean classes.
+ */
+ File getVendorOutputJarFile(String baseName) {
+ return new File(getDestDir(), baseName + jarSuffix);
+ }
+
+ /**
+ * Verify the produced jar file by invoking the Borland verify tool
+ * @param sourceJar java.io.File representing the produced jar file
+ */
+ private void verifyBorlandJar(File sourceJar) {
+ if (version == BAS) {
+ verifyBorlandJarV4(sourceJar);
+ return;
+ }
+ if (version == BES) {
+ verifyBorlandJarV5(sourceJar);
+ return;
+ }
+ log("verify jar skipped because the version is invalid ["
+ + version + "]", Project.MSG_WARN);
+ }
+
+ /**
+ * Verify the produced jar file by invoking the Borland iastool tool
+ * @param sourceJar java.io.File representing the produced jar file
+ */
+ private void verifyBorlandJarV5(File sourceJar) {
+ log("verify BES " + sourceJar, Project.MSG_INFO);
+ try {
+ ExecTask execTask = null;
+ execTask = new ExecTask(getTask());
+ execTask.setDir(new File("."));
+ execTask.setExecutable("iastool");
+ //classpath
+ if (getCombinedClasspath() != null) {
+ execTask.createArg().setValue("-VBJclasspath");
+ execTask.createArg().setValue(getCombinedClasspath().toString());
+ }
+
+ if (java2iiopdebug) {
+ execTask.createArg().setValue("-debug");
+ }
+ execTask.createArg().setValue("-verify");
+ execTask.createArg().setValue("-src");
+ // ejb jar file to verify
+ execTask.createArg().setValue(sourceJar.getPath());
+ log("Calling iastool", Project.MSG_VERBOSE);
+ execTask.execute();
+ } catch (Exception e) {
+ // Have to catch this because of the semantics of calling main()
+ String msg = "Exception while calling generateclient Details: "
+ + e.toString();
+ throw new BuildException(msg, e);
+ }
+ }
+
+ /**
+ * Verify the produced jar file by invoking the Borland verify tool
+ * @param sourceJar java.io.File representing the produced jar file
+ */
+ private void verifyBorlandJarV4(File sourceJar) {
+ org.apache.tools.ant.taskdefs.Java javaTask = null;
+ log("verify BAS " + sourceJar, Project.MSG_INFO);
+ try {
+ String args = verifyArgs;
+ args += " " + sourceJar.getPath();
+
+ javaTask = new Java(getTask());
+ javaTask.setTaskName("verify");
+ javaTask.setClassname(VERIFY);
+ Commandline.Argument arguments = javaTask.createArg();
+ arguments.setLine(args);
+ Path classpath = getCombinedClasspath();
+ if (classpath != null) {
+ javaTask.setClasspath(classpath);
+ javaTask.setFork(true);
+ }
+
+ log("Calling " + VERIFY + " for " + sourceJar.toString(),
+ Project.MSG_VERBOSE);
+ javaTask.execute();
+ } catch (Exception e) {
+ //TO DO : delete the file if it is not a valid file.
+ String msg = "Exception while calling " + VERIFY + " Details: "
+ + e.toString();
+ throw new BuildException(msg, e);
+ }
+ }
+
+
+ /**
+ * Generate the client jar corresponding to the jar file passed as parameter
+ * the method uses the BorlandGenerateClient task.
+ * @param sourceJar java.io.File representing the produced jar file
+ */
+ private void generateClient(File sourceJar) {
+ getTask().getProject().addTaskDefinition("internal_bas_generateclient",
+ org.apache.tools.ant.taskdefs.optional.ejb.BorlandGenerateClient.class);
+
+ org.apache.tools.ant.taskdefs.optional.ejb.BorlandGenerateClient gentask = null;
+ log("generate client for " + sourceJar, Project.MSG_INFO);
+ try {
+ Project project = getTask().getProject();
+ gentask
+ = (BorlandGenerateClient) project.createTask("internal_bas_generateclient");
+ gentask.setEjbjar(sourceJar);
+ gentask.setDebug(java2iiopdebug);
+ Path classpath = getCombinedClasspath();
+ if (classpath != null) {
+ gentask.setClasspath(classpath);
+ }
+ gentask.setVersion(version);
+ gentask.setTaskName("generate client");
+ gentask.execute();
+ } catch (Exception e) {
+ //TO DO : delete the file if it is not a valid file.
+ String msg = "Exception while calling " + VERIFY + " Details: "
+ + e.toString();
+ throw new BuildException(msg, e);
+ }
+ }
+
+ /**
+ * Generate stubs & skeleton for each home found into the DD
+ * Add all the generate class file into the ejb files
+ * @param ithomes : iterator on home class
+ */
+ private void buildBorlandStubs(Iterator ithomes) {
+ Execute execTask = null;
+
+ execTask = new Execute(this);
+ Project project = getTask().getProject();
+ execTask.setAntRun(project);
+ execTask.setWorkingDirectory(project.getBaseDir());
+
+ Commandline commandline = new Commandline();
+ commandline.setExecutable(JAVA2IIOP);
+ //debug ?
+ if (java2iiopdebug) {
+ commandline.createArgument().setValue("-VBJdebug");
+ }
+ //set the classpath
+ commandline.createArgument().setValue("-VBJclasspath");
+ commandline.createArgument().setPath(getCombinedClasspath());
+ //list file
+ commandline.createArgument().setValue("-list_files");
+ //no TIE classes
+ commandline.createArgument().setValue("-no_tie");
+
+ if (java2iioparams != null) {
+ log("additional " + java2iioparams + " to java2iiop ", 0);
+ commandline.createArgument().setLine(java2iioparams);
+ }
+
+
+ //root dir
+ commandline.createArgument().setValue("-root_dir");
+ commandline.createArgument().setValue(getConfig().srcDir.getAbsolutePath());
+ //compiling order
+ commandline.createArgument().setValue("-compile");
+ //add the home class
+ while (ithomes.hasNext()) {
+ commandline.createArgument().setValue(ithomes.next().toString());
+ }
+
+ try {
+ log("Calling java2iiop", Project.MSG_VERBOSE);
+ log(commandline.describeCommand(), Project.MSG_DEBUG);
+ execTask.setCommandline(commandline.getCommandline());
+ int result = execTask.execute();
+ if (Execute.isFailure(result)) {
+ String msg = "Failed executing java2iiop (ret code is "
+ + result + ")";
+ throw new BuildException(msg, getTask().getLocation());
+ }
+ } catch (java.io.IOException e) {
+ log("java2iiop exception :" + e.getMessage(), Project.MSG_ERR);
+ throw new BuildException(e, getTask().getLocation());
+ }
+ }
+
+ /**
+ * Method used to encapsulate the writing of the JAR file. Iterates over the
+ * filenames/java.io.Files in the Hashtable stored on the instance variable
+ * ejbFiles.
+ * @param baseName the base name.
+ * @param jarFile the jar file to write to.
+ * @param files the files to write to the jar.
+ * @param publicId the id to use.
+ * @throws BuildException if there is an error.
+ */
+ protected void writeJar(String baseName, File jarFile, Hashtable files, String publicId)
+ throws BuildException {
+ //build the home classes list.
+ Vector homes = new Vector();
+ Iterator it = files.keySet().iterator();
+ while (it.hasNext()) {
+ String clazz = (String) it.next();
+ if (clazz.endsWith("Home.class")) {
+ //remove .class extension
+ String home = toClass(clazz);
+ homes.add(home);
+ log(" Home " + home, Project.MSG_VERBOSE);
+ }
+ }
+
+ buildBorlandStubs(homes.iterator());
+
+ //add the gen files to the collection
+ files.putAll(genfiles);
+
+ super.writeJar(baseName, jarFile, files, publicId);
+
+ if (verify) {
+ verifyBorlandJar(jarFile);
+ }
+
+ if (generateclient) {
+ generateClient(jarFile);
+ }
+ genfiles.clear();
+ }
+
+ /**
+ * convert a class file name : A/B/C/toto.class
+ * into a class name: A.B.C.toto
+ */
+ private String toClass(String filename) {
+ //remove the .class
+ String classname = filename.substring(0, filename.lastIndexOf(".class"));
+ classname = classname.replace('\\', '.');
+ return classname;
+ }
+
+ /**
+ * convert a file name : A/B/C/toto.java
+ * into a class name: A/B/C/toto.class
+ */
+ private String toClassFile(String filename) {
+ //remove the .class
+ String classfile = filename.substring(0, filename.lastIndexOf(".java"));
+ classfile = classfile + ".class";
+ return classfile;
+ }
+
+ // implementation of org.apache.tools.ant.taskdefs.ExecuteStreamHandler interface
+
+ /** {@inheritDoc}. */
+ public void start() throws IOException { }
+ /** {@inheritDoc}. */
+ public void stop() { }
+ /** {@inheritDoc}. */
+ public void setProcessInputStream(OutputStream param1) throws IOException { }
+
+ /**
+ * Set the output stream of the process.
+ * @param is the input stream.
+ * @throws IOException if there is an error.
+ */
+ public void setProcessOutputStream(InputStream is) throws IOException {
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ String javafile;
+ while ((javafile = reader.readLine()) != null) {
+ if (javafile.endsWith(".java")) {
+ String classfile = toClassFile(javafile);
+ String key = classfile.substring(
+ getConfig().srcDir.getAbsolutePath().length() + 1);
+ genfiles.put(key, new File(classfile));
+ }
+ }
+ reader.close();
+ } catch (Exception e) {
+ String msg = "Exception while parsing java2iiop output. Details: " + e.toString();
+ throw new BuildException(msg, e);
+ }
+ }
+
+ /**
+ * Set the error stream of the process.
+ * @param is the input stream.
+ * @throws IOException if there is an error.
+ */
+ public void setProcessErrorStream(InputStream is) throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ String s = reader.readLine();
+ if (s != null) {
+ log("[java2iiop] " + s, Project.MSG_ERR);
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandGenerateClient.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandGenerateClient.java
new file mode 100644
index 00000000..dd46269d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandGenerateClient.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.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.ExecTask;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Generates a Borland Application Server 4.5 client JAR using as
+ * input the EJB JAR file.
+ *
+ * Two mode are available: java mode (default) and fork mode. With the fork mode,
+ * it is impossible to add classpath to the command line.
+ *
+ * @ant.task name="blgenclient" category="ejb"
+ */
+public class BorlandGenerateClient extends Task {
+ static final String JAVA_MODE = "java";
+ static final String FORK_MODE = "fork";
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ /** debug the generateclient task */
+ boolean debug = false;
+
+ /** hold the ejbjar file name */
+ File ejbjarfile = null;
+
+ /** hold the client jar file name */
+ File clientjarfile = null;
+
+ /** hold the classpath */
+ Path classpath;
+
+ /** hold the mode (java|fork) */
+ String mode = FORK_MODE;
+
+ /** hold the version */
+ int version = BorlandDeploymentTool.BAS;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Set the version attribute.
+ * @param version the value to use.
+ */
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ /**
+ * Command launching mode: java or fork.
+ * @param s the mode to use.
+ */
+ public void setMode(String s) {
+ mode = s;
+ }
+
+ /**
+ * If true, turn on the debug mode for each of the Borland tools launched.
+ * @param debug a <code>boolean</code> value.
+ */
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ }
+
+ /**
+ * EJB JAR file.
+ * @param ejbfile the file to use.
+ */
+ public void setEjbjar(File ejbfile) {
+ ejbjarfile = ejbfile;
+ }
+
+ /**
+ * Client JAR file name.
+ * @param clientjar the file to use.
+ */
+ public void setClientjar(File clientjar) {
+ clientjarfile = clientjar;
+ }
+
+ /**
+ * Path to use for classpath.
+ * @param classpath the path to use.
+ */
+ public void setClasspath(Path classpath) {
+ if (this.classpath == null) {
+ this.classpath = classpath;
+ } else {
+ this.classpath.append(classpath);
+ }
+ }
+
+ /**
+ * Adds path to the classpath.
+ * @return a path to be configured as a nested element.
+ */
+ public Path createClasspath() {
+ if (this.classpath == null) {
+ this.classpath = new Path(getProject());
+ }
+ return this.classpath.createPath();
+ }
+
+ /**
+ * Reference to existing path, to use as a classpath.
+ * @param r the reference to use.
+ */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+
+ /**
+ * Do the work.
+ *
+ * The work is actually done by creating a separate JVM to run a java task.
+ *
+ * @exception BuildException if something goes wrong with the build
+ */
+ public void execute() throws BuildException {
+ if (ejbjarfile == null || ejbjarfile.isDirectory()) {
+ throw new BuildException("invalid ejb jar file.");
+ }
+
+ if (clientjarfile == null || clientjarfile.isDirectory()) {
+ log("invalid or missing client jar file.", Project.MSG_VERBOSE);
+ String ejbjarname = ejbjarfile.getAbsolutePath();
+ //clientname = ejbjarfile+client.jar
+ String clientname = ejbjarname.substring(0, ejbjarname.lastIndexOf("."));
+ clientname = clientname + "client.jar";
+ clientjarfile = new File(clientname);
+ }
+
+ if (mode == null) {
+ log("mode is null default mode is java");
+ setMode(JAVA_MODE);
+ }
+
+ if (!(version == BorlandDeploymentTool.BES
+ || version == BorlandDeploymentTool.BAS)) {
+ throw new BuildException("version " + version
+ + " is not supported");
+ }
+
+ log("client jar file is " + clientjarfile);
+
+ if (mode.equalsIgnoreCase(FORK_MODE)) {
+ executeFork();
+ } else {
+ executeJava();
+ } // end of else
+ }
+
+ /**
+ * launch the generate client using java api.
+ * @throws BuildException if there is an error.
+ */
+ protected void executeJava() throws BuildException {
+ try {
+ if (version == BorlandDeploymentTool.BES) {
+ throw new BuildException("java mode is supported only for "
+ + "previous version <=" + BorlandDeploymentTool.BAS);
+ }
+
+ log("mode : java");
+
+ Java execTask = null;
+ execTask = new Java(this);
+
+ execTask.setDir(new File("."));
+ execTask.setClassname("com.inprise.server.commandline.EJBUtilities");
+ //classpath
+ //add at the end of the classpath
+ //the system classpath in order to find the tools.jar file
+ execTask.setClasspath(classpath.concatSystemClasspath());
+
+ execTask.setFork(true);
+ execTask.createArg().setValue("generateclient");
+ if (debug) {
+ execTask.createArg().setValue("-trace");
+ }
+
+ execTask.createArg().setValue("-short");
+ execTask.createArg().setValue("-jarfile");
+ // ejb jar file
+ execTask.createArg().setValue(ejbjarfile.getAbsolutePath());
+ //client jar file
+ execTask.createArg().setValue("-single");
+ execTask.createArg().setValue("-clientjarfile");
+ execTask.createArg().setValue(clientjarfile.getAbsolutePath());
+
+ log("Calling EJBUtilities", Project.MSG_VERBOSE);
+ execTask.execute();
+
+ } catch (Exception e) {
+ // Have to catch this because of the semantics of calling main()
+ String msg = "Exception while calling generateclient Details: " + e.toString();
+ throw new BuildException(msg, e);
+ }
+ }
+
+ /**
+ * launch the generate client using system api.
+ * @throws BuildException if there is an error.
+ */
+ protected void executeFork() throws BuildException {
+ if (version == BorlandDeploymentTool.BAS) {
+ executeForkV4();
+ }
+ if (version == BorlandDeploymentTool.BES) {
+ executeForkV5();
+ }
+ }
+
+ /**
+ * launch the generate client using system api.
+ * @throws BuildException if there is an error.
+ */
+ protected void executeForkV4() throws BuildException {
+ try {
+
+ log("mode : fork " + BorlandDeploymentTool.BAS, Project.MSG_DEBUG);
+
+ ExecTask execTask = new ExecTask(this);
+
+ execTask.setDir(new File("."));
+ execTask.setExecutable("iastool");
+ execTask.createArg().setValue("generateclient");
+ if (debug) {
+ execTask.createArg().setValue("-trace");
+ }
+
+ execTask.createArg().setValue("-short");
+ execTask.createArg().setValue("-jarfile");
+ // ejb jar file
+ execTask.createArg().setValue(ejbjarfile.getAbsolutePath());
+ //client jar file
+ execTask.createArg().setValue("-single");
+ execTask.createArg().setValue("-clientjarfile");
+ execTask.createArg().setValue(clientjarfile.getAbsolutePath());
+
+ log("Calling iastool", Project.MSG_VERBOSE);
+ execTask.execute();
+ } catch (Exception e) {
+ // Have to catch this because of the semantics of calling main()
+ String msg = "Exception while calling generateclient Details: "
+ + e.toString();
+ throw new BuildException(msg, e);
+ }
+
+ }
+
+ /**
+ * launch the generate client using system api.
+ * @throws BuildException if there is an error.
+ */
+ protected void executeForkV5() throws BuildException {
+ try {
+ log("mode : fork " + BorlandDeploymentTool.BES, Project.MSG_DEBUG);
+ ExecTask execTask = new ExecTask(this);
+
+ execTask.setDir(new File("."));
+
+ execTask.setExecutable("iastool");
+ if (debug) {
+ execTask.createArg().setValue("-debug");
+ }
+ execTask.createArg().setValue("-genclient");
+ execTask.createArg().setValue("-jars");
+ // ejb jar file
+ execTask.createArg().setValue(ejbjarfile.getAbsolutePath());
+ //client jar file
+ execTask.createArg().setValue("-target");
+ execTask.createArg().setValue(clientjarfile.getAbsolutePath());
+ //classpath
+ execTask.createArg().setValue("-cp");
+ execTask.createArg().setValue(classpath.toString());
+ log("Calling iastool", Project.MSG_VERBOSE);
+ execTask.execute();
+ } catch (Exception e) {
+ // Have to catch this because of the semantics of calling main()
+ String msg = "Exception while calling generateclient Details: "
+ + e.toString();
+ throw new BuildException(msg, e);
+ }
+
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DescriptorHandler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DescriptorHandler.java
new file mode 100644
index 00000000..158cba93
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DescriptorHandler.java
@@ -0,0 +1,390 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Hashtable;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.xml.sax.AttributeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * Inner class used by EjbJar to facilitate the parsing of deployment
+ * descriptors and the capture of appropriate information. Extends
+ * HandlerBase so it only implements the methods needed. During parsing
+ * creates a hashtable consisting of entries mapping the name it should be
+ * inserted into an EJB jar as to a File representing the file on disk. This
+ * list can then be accessed through the getFiles() method.
+ */
+public class DescriptorHandler extends org.xml.sax.HandlerBase {
+ private static final int DEFAULT_HASH_TABLE_SIZE = 10;
+ private static final int STATE_LOOKING_EJBJAR = 1;
+ private static final int STATE_IN_EJBJAR = 2;
+ private static final int STATE_IN_BEANS = 3;
+ private static final int STATE_IN_SESSION = 4;
+ private static final int STATE_IN_ENTITY = 5;
+ private static final int STATE_IN_MESSAGE = 6;
+
+ private Task owningTask;
+
+ private String publicId = null;
+
+ /**
+ * Bunch of constants used for storing entries in a hashtable, and for
+ * constructing the filenames of various parts of the ejb jar.
+ */
+ private static final String EJB_REF = "ejb-ref";
+ private static final String EJB_LOCAL_REF = "ejb-local-ref";
+ private static final String HOME_INTERFACE = "home";
+ private static final String REMOTE_INTERFACE = "remote";
+ private static final String LOCAL_HOME_INTERFACE = "local-home";
+ private static final String LOCAL_INTERFACE = "local";
+ private static final String BEAN_CLASS = "ejb-class";
+ private static final String PK_CLASS = "prim-key-class";
+ private static final String EJB_NAME = "ejb-name";
+ private static final String EJB_JAR = "ejb-jar";
+ private static final String ENTERPRISE_BEANS = "enterprise-beans";
+ private static final String ENTITY_BEAN = "entity";
+ private static final String SESSION_BEAN = "session";
+ private static final String MESSAGE_BEAN = "message-driven";
+
+ /**
+ * The state of the parsing
+ */
+ private int parseState = STATE_LOOKING_EJBJAR;
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ /**
+ * Instance variable used to store the name of the current element being
+ * processed by the SAX parser. Accessed by the SAX parser call-back methods
+ * startElement() and endElement().
+ */
+ protected String currentElement = null;
+
+ /**
+ * The text of the current element
+ */
+ protected String currentText = null;
+
+ /**
+ * Instance variable that stores the names of the files as they will be
+ * put into the jar file, mapped to File objects Accessed by the SAX
+ * parser call-back method characters().
+ */
+ protected Hashtable ejbFiles = null;
+
+ /**
+ * Instance variable that stores the value found in the &lt;ejb-name&gt; element
+ */
+ protected String ejbName = null;
+
+ private Hashtable fileDTDs = new Hashtable();
+
+ private Hashtable resourceDTDs = new Hashtable();
+
+ private boolean inEJBRef = false;
+
+ private Hashtable urlDTDs = new Hashtable();
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /**
+ * The directory containing the bean classes and interfaces. This is
+ * used for performing dependency file lookups.
+ */
+ private File srcDir;
+
+ /**
+ * Constructor for DescriptorHandler.
+ * @param task the task that owns this descriptor
+ * @param srcDir the source directory
+ */
+ public DescriptorHandler(Task task, File srcDir) {
+ this.owningTask = task;
+ this.srcDir = srcDir;
+ }
+
+ /**
+ * Register a dtd with a location.
+ * The location is one of a filename, a resource name in the classpath, or
+ * a URL.
+ * @param publicId the public identity of the dtd
+ * @param location the location of the dtd
+ */
+ public void registerDTD(String publicId, String location) {
+ if (location == null) {
+ return;
+ }
+
+ File fileDTD = new File(location);
+ if (!fileDTD.exists()) {
+ // resolve relative to project basedir
+ fileDTD = owningTask.getProject().resolveFile(location);
+ }
+
+ if (fileDTD.exists()) {
+ if (publicId != null) {
+ fileDTDs.put(publicId, fileDTD);
+ owningTask.log("Mapped publicId " + publicId + " to file "
+ + fileDTD, Project.MSG_VERBOSE);
+ }
+ return;
+ }
+
+ if (getClass().getResource(location) != null) {
+ if (publicId != null) {
+ resourceDTDs.put(publicId, location);
+ owningTask.log("Mapped publicId " + publicId + " to resource "
+ + location, Project.MSG_VERBOSE);
+ }
+ }
+
+ try {
+ if (publicId != null) {
+ URL urldtd = new URL(location);
+ urlDTDs.put(publicId, urldtd);
+ }
+ } catch (java.net.MalformedURLException e) {
+ //ignored
+ }
+
+ }
+
+ /**
+ * Resolve the entity.
+ * @see org.xml.sax.EntityResolver#resolveEntity(String, String).
+ * @param publicId The public identifier, or <code>null</code>
+ * if none is available.
+ * @param systemId The system identifier provided in the XML
+ * document. Will not be <code>null</code>.
+ * @return an inputsource for this identifier
+ * @throws SAXException if there is a problem.
+ */
+ public InputSource resolveEntity(String publicId, String systemId)
+ throws SAXException {
+ this.publicId = publicId;
+
+ File dtdFile = (File) fileDTDs.get(publicId);
+ if (dtdFile != null) {
+ try {
+ owningTask.log("Resolved " + publicId + " to local file "
+ + dtdFile, Project.MSG_VERBOSE);
+ return new InputSource(new FileInputStream(dtdFile));
+ } catch (FileNotFoundException ex) {
+ // ignore
+ }
+ }
+
+ String dtdResourceName = (String) resourceDTDs.get(publicId);
+ if (dtdResourceName != null) {
+ InputStream is = this.getClass().getResourceAsStream(dtdResourceName);
+ if (is != null) {
+ owningTask.log("Resolved " + publicId + " to local resource "
+ + dtdResourceName, Project.MSG_VERBOSE);
+ return new InputSource(is);
+ }
+ }
+
+ URL dtdUrl = (URL) urlDTDs.get(publicId);
+ if (dtdUrl != null) {
+ try {
+ InputStream is = dtdUrl.openStream();
+ owningTask.log("Resolved " + publicId + " to url "
+ + dtdUrl, Project.MSG_VERBOSE);
+ return new InputSource(is);
+ } catch (IOException ioe) {
+ //ignore
+ }
+ }
+
+ owningTask.log("Could not resolve ( publicId: " + publicId
+ + ", systemId: " + systemId + ") to a local entity", Project.MSG_INFO);
+
+ return null;
+ }
+
+ /**
+ * Getter method that returns the set of files to include in the EJB jar.
+ * @return the map of files
+ */
+ public Hashtable getFiles() {
+ return (ejbFiles == null) ? new Hashtable() : ejbFiles;
+ }
+
+ /**
+ * Get the publicId of the DTD
+ * @return the public id
+ */
+ public String getPublicId() {
+ return publicId;
+ }
+
+ /**
+ * Getter method that returns the value of the &lt;ejb-name&gt; element.
+ * @return the ejb name
+ */
+ public String getEjbName() {
+ return ejbName;
+ }
+
+ /**
+ * SAX parser call-back method that is used to initialize the values of some
+ * instance variables to ensure safe operation.
+ * @throws SAXException on error
+ */
+ public void startDocument() throws SAXException {
+ this.ejbFiles = new Hashtable(DEFAULT_HASH_TABLE_SIZE, 1);
+ this.currentElement = null;
+ inEJBRef = false;
+ }
+
+
+ /**
+ * SAX parser call-back method that is invoked when a new element is entered
+ * into. Used to store the context (attribute name) in the currentAttribute
+ * instance variable.
+ * @param name The name of the element being entered.
+ * @param attrs Attributes associated to the element.
+ * @throws SAXException on error
+ */
+ public void startElement(String name, AttributeList attrs)
+ throws SAXException {
+ this.currentElement = name;
+ currentText = "";
+ if (name.equals(EJB_REF) || name.equals(EJB_LOCAL_REF)) {
+ inEJBRef = true;
+ } else if (parseState == STATE_LOOKING_EJBJAR && name.equals(EJB_JAR)) {
+ parseState = STATE_IN_EJBJAR;
+ } else if (parseState == STATE_IN_EJBJAR && name.equals(ENTERPRISE_BEANS)) {
+ parseState = STATE_IN_BEANS;
+ } else if (parseState == STATE_IN_BEANS && name.equals(SESSION_BEAN)) {
+ parseState = STATE_IN_SESSION;
+ } else if (parseState == STATE_IN_BEANS && name.equals(ENTITY_BEAN)) {
+ parseState = STATE_IN_ENTITY;
+ } else if (parseState == STATE_IN_BEANS && name.equals(MESSAGE_BEAN)) {
+ parseState = STATE_IN_MESSAGE;
+ }
+ }
+
+
+ /**
+ * SAX parser call-back method that is invoked when an element is exited.
+ * Used to blank out (set to the empty string, not nullify) the name of
+ * the currentAttribute. A better method would be to use a stack as an
+ * instance variable, however since we are only interested in leaf-node
+ * data this is a simpler and workable solution.
+ * @param name The name of the attribute being exited. Ignored
+ * in this implementation.
+ * @throws SAXException on error
+ */
+ public void endElement(String name) throws SAXException {
+ processElement();
+ currentText = "";
+ this.currentElement = "";
+ if (name.equals(EJB_REF) || name.equals(EJB_LOCAL_REF)) {
+ inEJBRef = false;
+ } else if (parseState == STATE_IN_ENTITY && name.equals(ENTITY_BEAN)) {
+ parseState = STATE_IN_BEANS;
+ } else if (parseState == STATE_IN_SESSION && name.equals(SESSION_BEAN)) {
+ parseState = STATE_IN_BEANS;
+ } else if (parseState == STATE_IN_MESSAGE && name.equals(MESSAGE_BEAN)) {
+ parseState = STATE_IN_BEANS;
+ } else if (parseState == STATE_IN_BEANS && name.equals(ENTERPRISE_BEANS)) {
+ parseState = STATE_IN_EJBJAR;
+ } else if (parseState == STATE_IN_EJBJAR && name.equals(EJB_JAR)) {
+ parseState = STATE_LOOKING_EJBJAR;
+ }
+ }
+
+ /**
+ * SAX parser call-back method invoked whenever characters are located within
+ * an element. currentAttribute (modified by startElement and endElement)
+ * tells us whether we are in an interesting element (one of the up to four
+ * classes of an EJB). If so then converts the classname from the format
+ * org.apache.tools.ant.Parser to the convention for storing such a class,
+ * org/apache/tools/ant/Parser.class. This is then resolved into a file
+ * object under the srcdir which is stored in a Hashtable.
+ * @param ch A character array containing all the characters in
+ * the element, and maybe others that should be ignored.
+ * @param start An integer marking the position in the char
+ * array to start reading from.
+ * @param length An integer representing an offset into the
+ * char array where the current data terminates.
+ * @throws SAXException on error
+ */
+ public void characters(char[] ch, int start, int length)
+ throws SAXException {
+
+ currentText += new String(ch, start, length);
+ }
+
+
+ /**
+ * Called when an endelement is seen.
+ * This may be overridden in derived classes.
+ * This updates the ejbfiles if the element is an interface or a bean class.
+ * This updates the ejbname if the element is an ejb name.
+ */
+ protected void processElement() {
+ if (inEJBRef
+ || (parseState != STATE_IN_ENTITY
+ && parseState != STATE_IN_SESSION
+ && parseState != STATE_IN_MESSAGE)) {
+ return;
+ }
+
+ if (currentElement.equals(HOME_INTERFACE)
+ || currentElement.equals(REMOTE_INTERFACE)
+ || currentElement.equals(LOCAL_INTERFACE)
+ || currentElement.equals(LOCAL_HOME_INTERFACE)
+ || currentElement.equals(BEAN_CLASS)
+ || currentElement.equals(PK_CLASS)) {
+
+ // Get the filename into a String object
+ File classFile = null;
+ String className = currentText.trim();
+
+ // If it's a primitive wrapper then we shouldn't try and put
+ // it into the jar, so ignore it.
+ if (!className.startsWith("java.")
+ && !className.startsWith("javax.")) {
+ // Translate periods into path separators, add .class to the
+ // name, create the File object and add it to the Hashtable.
+ className = className.replace('.', File.separatorChar);
+ className += ".class";
+ classFile = new File(srcDir, className);
+ ejbFiles.put(className, classFile);
+ }
+ }
+
+ // Get the value of the <ejb-name> tag. Only the first occurrence.
+ if (currentElement.equals(EJB_NAME)) {
+ if (ejbName == null) {
+ ejbName = currentText.trim();
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java
new file mode 100644
index 00000000..6ed8e34a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java
@@ -0,0 +1,61 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ejb;
+
+
+
+import javax.xml.parsers.SAXParser;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+
+/**
+ * The interface to implement for deployment tools.
+ */
+public interface EJBDeploymentTool {
+ /**
+ * Process a deployment descriptor, generating the necessary vendor specific
+ * deployment files.
+ *
+ * @param descriptorFilename the name of the deployment descriptor
+ * @param saxParser a SAX parser which can be used to parse the deployment descriptor.
+ * @throws BuildException if there is an error.
+ */
+ void processDescriptor(String descriptorFilename, SAXParser saxParser)
+ throws BuildException;
+
+ /**
+ * Called to validate that the tool parameters have been configured.
+ * @throws BuildException if there is an error.
+ */
+ void validateConfigured() throws BuildException;
+
+ /**
+ * Set the task which owns this tool
+ * @param task the task.
+ */
+ void setTask(Task task);
+
+ /**
+ * Configure this tool for use in the ejbjar task.
+ * @param config contains configuration state.
+ */
+ void configure(EjbJar.Config config);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java
new file mode 100644
index 00000000..e9b7ed42
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java
@@ -0,0 +1,630 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ejb;
+
+// Standard java imports
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.xml.sax.SAXException;
+
+/**
+ * Provides automated EJB JAR file creation.
+ * <p>
+ * Extends the
+ * MatchingTask class provided in the default ant distribution to provide a
+ * directory scanning EJB jarfile generator.
+ * </p>
+ *
+ * <p>
+ * The task works by taking the deployment descriptors one at a time and
+ * parsing them to locate the names of the classes which should be placed in
+ * the jar. The classnames are translated to java.io.Files by replacing
+ * periods with File.separatorChar and resolving the generated filename as a
+ * relative path under the srcDir attribute. All necessary files are then
+ * assembled into a jarfile. One jarfile is constructed for each deployment
+ * descriptor found.
+ * </p>
+ *
+ * */
+public class EjbJar extends MatchingTask {
+
+ /**
+ * Inner class used to record information about the location of a local DTD
+ */
+ public static class DTDLocation
+ extends org.apache.tools.ant.types.DTDLocation {
+ }
+
+ /**
+ * A class which contains the configuration state of the ejbjar task.
+ * This state is passed to the deployment tools for configuration
+ */
+ static class Config {
+ // CheckStyle:VisibilityModifier OFF - bc
+ /**
+ * Stores a handle to the directory under which to search for class
+ * files
+ */
+ public File srcDir;
+
+ /**
+ * Stores a handle to the directory under which to search for
+ * deployment descriptors
+ */
+ public File descriptorDir;
+
+ /** Instance variable that marks the end of the 'basename' */
+ public String baseNameTerminator = "-";
+
+ /** Stores a handle to the destination EJB Jar file */
+ public String baseJarName;
+
+ /**
+ * Instance variable that determines whether to use a package structure
+ * of a flat directory as the destination for the jar files.
+ */
+ public boolean flatDestDir = false;
+
+ /**
+ * The classpath to use when loading classes
+ */
+ public Path classpath;
+
+ /**
+ * A Fileset of support classes
+ */
+ public List supportFileSets = new ArrayList();
+
+ /**
+ * The list of configured DTD locations
+ */
+ public ArrayList dtdLocations = new ArrayList();
+
+ /**
+ * The naming scheme used to determine the generated jar name
+ * from the descriptor information
+ */
+ public NamingScheme namingScheme;
+
+ /**
+ * The Manifest file
+ */
+ public File manifest;
+
+ /**
+ * The dependency analyzer to use to add additional classes to the jar
+ */
+ public String analyzer;
+ // CheckStyle:VisibilityModifier ON
+ }
+
+ /**
+ * An EnumeratedAttribute class for handling different EJB jar naming
+ * schemes
+ */
+ public static class NamingScheme extends EnumeratedAttribute {
+ /**
+ * Naming scheme where generated jar is determined from the ejb-name in
+ * the deployment descriptor
+ */
+ public static final String EJB_NAME = "ejb-name";
+
+ /**
+ * Naming scheme where the generated jar name is based on the
+ * name of the directory containing the deployment descriptor
+ */
+ public static final String DIRECTORY = "directory";
+
+ /**
+ * Naming scheme where the generated jar name is based on the name of
+ * the deployment descriptor file
+ */
+ public static final String DESCRIPTOR = "descriptor";
+
+ /**
+ * Naming scheme where the generated jar is named by the basejarname
+ * attribute
+ */
+ public static final String BASEJARNAME = "basejarname";
+
+ /**
+ * Gets the values of the NamingScheme
+ *
+ * @return an array of the values of this attribute class.
+ */
+ public String[] getValues() {
+ return new String[] {EJB_NAME, DIRECTORY, DESCRIPTOR, BASEJARNAME};
+ }
+ }
+
+ /**
+ * CMP versions supported
+ * valid CMP versions are 1.0 and 2.0
+ * @since ant 1.6
+ */
+ public static class CMPVersion extends EnumeratedAttribute {
+ /** 1.0 value */
+ public static final String CMP1_0 = "1.0";
+ /** 2.0 value */
+ public static final String CMP2_0 = "2.0";
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return new String[]{
+ CMP1_0,
+ CMP2_0,
+ };
+ }
+ }
+ /**
+ * The config which is built by this task and used by the various deployment
+ * tools to access the configuration of the ejbjar task
+ */
+ private Config config = new Config();
+
+
+ /**
+ * Stores a handle to the directory to put the Jar files in. This is
+ * only used by the generic deployment descriptor tool which is created
+ * if no other deployment descriptor tools are provided. Normally each
+ * deployment tool will specify the desitination dir itself.
+ */
+ private File destDir;
+
+ /** Instance variable that stores the suffix for the generated jarfile. */
+ private String genericJarSuffix = "-generic.jar";
+
+ /** Instance variable that stores the CMP version for the jboss jarfile. */
+ private String cmpVersion = CMPVersion.CMP1_0;
+
+ /** The list of deployment tools we are going to run. */
+ private ArrayList deploymentTools = new ArrayList();
+
+ /**
+ * Add a deployment tool to the list of deployment tools that will be
+ * processed
+ *
+ * @param deploymentTool a deployment tool instance to which descriptors
+ * will be passed for processing.
+ */
+ protected void addDeploymentTool(EJBDeploymentTool deploymentTool) {
+ deploymentTool.setTask(this);
+ deploymentTools.add(deploymentTool);
+ }
+
+ /**
+ * Adds a deployment tool for Weblogic server.
+ *
+ * @return the deployment tool instance to be configured.
+ */
+ public WeblogicDeploymentTool createWeblogic() {
+ WeblogicDeploymentTool tool = new WeblogicDeploymentTool();
+ addDeploymentTool(tool);
+ return tool;
+ }
+
+ /**
+ * Adds a deployment tool for Websphere 4.0 server.
+ *
+ * @return the deployment tool instance to be configured.
+ */
+ public WebsphereDeploymentTool createWebsphere() {
+ WebsphereDeploymentTool tool = new WebsphereDeploymentTool();
+ addDeploymentTool(tool);
+ return tool;
+ }
+
+ /**
+ * Adds a deployment tool for Borland server.
+ *
+ * @return the deployment tool instance to be configured.
+ */
+ public BorlandDeploymentTool createBorland() {
+ log("Borland deployment tools", Project.MSG_VERBOSE);
+
+ BorlandDeploymentTool tool = new BorlandDeploymentTool();
+ tool.setTask(this);
+ deploymentTools.add(tool);
+ return tool;
+ }
+
+ /**
+ * Adds a deployment tool for iPlanet Application Server.
+ *
+ * @return the deployment tool instance to be configured.
+ */
+ public IPlanetDeploymentTool createIplanet() {
+ log("iPlanet Application Server deployment tools", Project.MSG_VERBOSE);
+
+ IPlanetDeploymentTool tool = new IPlanetDeploymentTool();
+ addDeploymentTool(tool);
+ return tool;
+ }
+
+ /**
+ * Adds a deployment tool for JBoss server.
+ *
+ * @return the deployment tool instance to be configured.
+ */
+ public JbossDeploymentTool createJboss() {
+ JbossDeploymentTool tool = new JbossDeploymentTool();
+ addDeploymentTool(tool);
+ return tool;
+ }
+
+ /**
+ * Adds a deployment tool for JOnAS server.
+ *
+ * @return the deployment tool instance to be configured.
+ */
+ public JonasDeploymentTool createJonas() {
+ log("JOnAS deployment tools", Project.MSG_VERBOSE);
+
+ JonasDeploymentTool tool = new JonasDeploymentTool();
+ addDeploymentTool(tool);
+ return tool;
+ }
+
+ /**
+ * Adds a deployment tool for Weblogic when using the Toplink
+ * Object-Relational mapping.
+ *
+ * @return the deployment tool instance to be configured.
+ */
+ public WeblogicTOPLinkDeploymentTool createWeblogictoplink() {
+ log("The <weblogictoplink> element is no longer required. Please use "
+ + "the <weblogic> element and set newCMP=\"true\"",
+ Project.MSG_INFO);
+ WeblogicTOPLinkDeploymentTool tool
+ = new WeblogicTOPLinkDeploymentTool();
+ addDeploymentTool(tool);
+ return tool;
+ }
+
+ /**
+ * Adds to the classpath used to locate the super classes and
+ * interfaces of the classes that will make up the EJB JAR.
+ *
+ * @return the path to be configured.
+ */
+ public Path createClasspath() {
+ if (config.classpath == null) {
+ config.classpath = new Path(getProject());
+ }
+ return config.classpath.createPath();
+ }
+
+ /**
+ * Create a DTD location record. This stores the location of a DTD. The
+ * DTD is identified by its public Id. The location may either be a file
+ * location or a resource location.
+ *
+ * @return the DTD location object to be configured by Ant
+ */
+ public DTDLocation createDTD() {
+ DTDLocation dtdLocation = new DTDLocation();
+ config.dtdLocations.add(dtdLocation);
+
+ return dtdLocation;
+ }
+
+ /**
+ * Adds a fileset for support elements.
+ *
+ * @return a fileset which can be populated with support files.
+ */
+ public FileSet createSupport() {
+ FileSet supportFileSet = new FileSet();
+ config.supportFileSets.add(supportFileSet);
+ return supportFileSet;
+ }
+
+
+ /**
+ * Set the Manifest file to use when jarring. As of EJB 1.1, manifest
+ * files are no longer used to configure the EJB. However, they still
+ * have a vital importance if the EJB is intended to be packaged in an
+ * EAR file. By adding "Class-Path" settings to a Manifest file, the EJB
+ * can look for classes inside the EAR file itself, allowing for easier
+ * deployment. This is outlined in the J2EE specification, and all J2EE
+ * components are meant to support it.
+ *
+ * @param manifest the manifest to be used in the EJB jar
+ */
+ public void setManifest(File manifest) {
+ config.manifest = manifest;
+ }
+
+ /**
+ * Sets the source directory, which is the directory that
+ * contains the classes that will be added to the EJB jar. Typically
+ * this will include the home and remote interfaces and the bean class.
+ *
+ * @param inDir the source directory.
+ */
+ public void setSrcdir(File inDir) {
+ config.srcDir = inDir;
+ }
+
+ /**
+ * Set the descriptor directory. The descriptor directory contains the
+ * EJB deployment descriptors. These are XML files that declare the
+ * properties of a bean in a particular deployment scenario. Such
+ * properties include, for example, the transactional nature of the bean
+ * and the security access control to the bean's methods.
+ *
+ * @param inDir the directory containing the deployment descriptors.
+ */
+ public void setDescriptordir(File inDir) {
+ config.descriptorDir = inDir;
+ }
+
+ /**
+ * Set the analyzer to use when adding in dependencies to the JAR.
+ *
+ * @param analyzer the name of the dependency analyzer or a class.
+ */
+ public void setDependency(String analyzer) {
+ config.analyzer = analyzer;
+ }
+
+ /**
+ * Set the base name of the EJB JAR that is to be created if it is not
+ * to be determined from the name of the deployment descriptor files.
+ *
+ * @param inValue the basename that will be used when writing the jar
+ * file containing the EJB
+ */
+ public void setBasejarname(String inValue) {
+ config.baseJarName = inValue;
+ if (config.namingScheme == null) {
+ config.namingScheme = new NamingScheme();
+ config.namingScheme.setValue(NamingScheme.BASEJARNAME);
+ } else if (!config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME)) {
+ throw new BuildException("The basejarname attribute is not "
+ + "compatible with the "
+ + config.namingScheme.getValue() + " naming scheme");
+ }
+ }
+
+ /**
+ * Set the naming scheme used to determine the name of the generated jars
+ * from the deployment descriptor
+ *
+ * @param namingScheme the naming scheme to be used
+ */
+ public void setNaming(NamingScheme namingScheme) {
+ config.namingScheme = namingScheme;
+ if (!config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME)
+ && config.baseJarName != null) {
+ throw new BuildException("The basejarname attribute is not "
+ + "compatible with the "
+ + config.namingScheme.getValue() + " naming scheme");
+ }
+ }
+
+ /**
+ * Gets the destination directory.
+ *
+ * @return destination directory
+ * @since ant 1.6
+ */
+ public File getDestdir() {
+ return this.destDir;
+ }
+
+ /**
+ * Set the destination directory. The EJB jar files will be written into
+ * this directory. The jar files that exist in this directory are also
+ * used when determining if the contents of the jar file have changed.
+ * Note that this parameter is only used if no deployment tools are
+ * specified. Typically each deployment tool will specify its own
+ * destination directory.
+ *
+ * @param inDir the destination directory in which to generate jars
+ */
+ public void setDestdir(File inDir) {
+ this.destDir = inDir;
+ }
+
+ /**
+ * Gets the CMP version.
+ *
+ * @return CMP version
+ * @since ant 1.6
+ */
+ public String getCmpversion() {
+ return this.cmpVersion;
+ }
+
+ /**
+ * Sets the CMP version.
+ *
+ * @param version CMP version.
+ * Must be either <code>1.0</code> or <code>2.0</code>.<br/>
+ * Default is <code>1.0</code>.<br/>
+ * Initially, only the JBoss implementation does something specific for CMP 2.0.<br/>
+ * @since ant 1.6
+ */
+ public void setCmpversion(CMPVersion version) {
+ this.cmpVersion = version.getValue();
+ }
+
+ /**
+ * Set the classpath to use when resolving classes for inclusion in the jar.
+ *
+ * @param classpath the classpath to use.
+ */
+ public void setClasspath(Path classpath) {
+ config.classpath = classpath;
+ }
+
+ /**
+ * Controls whether the
+ * destination JARs are written out in the destination directory with
+ * the same hierarchical structure from which the deployment descriptors
+ * have been read. If this is set to true the generated EJB jars are
+ * written into the root of the destination directory, otherwise they
+ * are written out in the same relative position as the deployment
+ * descriptors in the descriptor directory.
+ *
+ * @param inValue the new value of the flatdestdir flag.
+ */
+ public void setFlatdestdir(boolean inValue) {
+ config.flatDestDir = inValue;
+ }
+
+ /**
+ * Set the suffix for the generated jar file. When generic jars are
+ * generated, they have a suffix which is appended to the the bean name
+ * to create the name of the jar file. Note that this suffix includes
+ * the extension fo te jar file and should therefore end with an
+ * appropriate extension such as .jar or .ear
+ *
+ * @param inString the string to use as the suffix.
+ */
+ public void setGenericjarsuffix(String inString) {
+ this.genericJarSuffix = inString;
+ }
+
+ /**
+ * The string which terminates the bean name.
+ * The convention used by this task is
+ * that bean descriptors are named as the BeanName with some suffix. The
+ * baseNameTerminator string separates the bean name and the suffix and
+ * is used to determine the bean name.
+ *
+ * @param inValue a string which marks the end of the basename.
+ */
+ public void setBasenameterminator(String inValue) {
+ config.baseNameTerminator = inValue;
+ }
+
+ /**
+ * Validate the config that has been configured from the build file
+ *
+ * @throws BuildException if the config is not valid
+ */
+ private void validateConfig() throws BuildException {
+ if (config.srcDir == null) {
+ throw new BuildException("The srcDir attribute must be specified");
+ }
+
+ if (config.descriptorDir == null) {
+ config.descriptorDir = config.srcDir;
+ }
+
+ if (config.namingScheme == null) {
+ config.namingScheme = new NamingScheme();
+ config.namingScheme.setValue(NamingScheme.DESCRIPTOR);
+ } else if (config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME)
+ && config.baseJarName == null) {
+ throw new BuildException("The basejarname attribute must "
+ + "be specified with the basejarname naming scheme");
+ }
+ }
+
+ /**
+ * Invoked by Ant after the task is prepared, when it is ready to execute
+ * this task.
+ *
+ * This will configure all of the nested deployment tools to allow them to
+ * process the jar. If no deployment tools have been configured a generic
+ * tool is created to handle the jar.
+ *
+ * A parser is configured and then each descriptor found is passed to all
+ * the deployment tool elements for processing.
+ *
+ * @exception BuildException thrown whenever a problem is
+ * encountered that cannot be recovered from, to signal to ant
+ * that a major problem occurred within this task.
+ */
+ public void execute() throws BuildException {
+ validateConfig();
+
+ if (deploymentTools.size() == 0) {
+ GenericDeploymentTool genericTool = new GenericDeploymentTool();
+ genericTool.setTask(this);
+ genericTool.setDestdir(destDir);
+ genericTool.setGenericJarSuffix(genericJarSuffix);
+ deploymentTools.add(genericTool);
+ }
+
+ for (Iterator i = deploymentTools.iterator(); i.hasNext();) {
+ EJBDeploymentTool tool = (EJBDeploymentTool) i.next();
+ tool.configure(config);
+ tool.validateConfigured();
+ }
+
+ try {
+ // Create the parser using whatever parser the system dictates
+ SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+ saxParserFactory.setValidating(true);
+ SAXParser saxParser = saxParserFactory.newSAXParser();
+
+
+ DirectoryScanner ds = getDirectoryScanner(config.descriptorDir);
+ ds.scan();
+ String[] files = ds.getIncludedFiles();
+
+ log(files.length + " deployment descriptors located.",
+ Project.MSG_VERBOSE);
+
+ // Loop through the files. Each file represents one deployment
+ // descriptor, and hence one bean in our model.
+ for (int index = 0; index < files.length; ++index) {
+ // process the deployment descriptor in each tool
+ for (Iterator i = deploymentTools.iterator(); i.hasNext();) {
+ EJBDeploymentTool tool = (EJBDeploymentTool) i.next();
+ tool.processDescriptor(files[index], saxParser);
+ }
+ }
+ } catch (SAXException se) {
+ String msg = "SAXException while creating parser."
+ + " Details: "
+ + se.getMessage();
+ throw new BuildException(msg, se);
+ } catch (ParserConfigurationException pce) {
+ String msg = "ParserConfigurationException while creating parser. "
+ + "Details: " + pce.getMessage();
+ throw new BuildException(msg, pce);
+ }
+ } // end of execute()
+
+}
+
+
+
+
+
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java
new file mode 100644
index 00000000..069bdfcf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java
@@ -0,0 +1,953 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+import javax.xml.parsers.SAXParser;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.depend.DependencyAnalyzer;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+/**
+ * A deployment tool which creates generic EJB jars. Generic jars contains
+ * only those classes and META-INF entries specified in the EJB 1.1 standard
+ *
+ * This class is also used as a framework for the creation of vendor specific
+ * deployment tools. A number of template methods are provided through which the
+ * vendor specific tool can hook into the EJB creation process.
+ *
+ */
+public class GenericDeploymentTool implements EJBDeploymentTool {
+ /** The default buffer byte size to use for IO */
+ public static final int DEFAULT_BUFFER_SIZE = 1024;
+ /** The level to use for compression */
+ public static final int JAR_COMPRESS_LEVEL = 9;
+
+ /** The standard META-INF directory in jar files */
+ protected static final String META_DIR = "META-INF/";
+
+ /** The standard MANIFEST file */
+ protected static final String MANIFEST = META_DIR + "MANIFEST.MF";
+
+ /** Name for EJB Deployment descriptor within EJB jars */
+ protected static final String EJB_DD = "ejb-jar.xml";
+
+ /** A dependency analyzer name to find ancestor classes */
+ public static final String ANALYZER_SUPER = "super";
+ /** A dependency analyzer name to find all related classes */
+ public static final String ANALYZER_FULL = "full";
+ /** A dependency analyzer name for no analyzer */
+ public static final String ANALYZER_NONE = "none";
+
+ /** The default analyzer */
+ public static final String DEFAULT_ANALYZER = ANALYZER_SUPER;
+
+ /** The analyzer class for the super analyzer */
+ public static final String ANALYZER_CLASS_SUPER
+ = "org.apache.tools.ant.util.depend.bcel.AncestorAnalyzer";
+ /** The analyzer class for the super analyzer */
+ public static final String ANALYZER_CLASS_FULL
+ = "org.apache.tools.ant.util.depend.bcel.FullAnalyzer";
+
+ /**
+ * The configuration from the containing task. This config combined
+ * with the settings of the individual attributes here constitues the
+ * complete config for this deployment tool.
+ */
+ private EjbJar.Config config;
+
+ /** Stores a handle to the directory to put the Jar files in */
+ private File destDir;
+
+ /** The classpath to use with this deployment tool. This is appended to
+ any paths from the ejbjar task itself.*/
+ private Path classpath;
+
+ /** Instance variable that stores the suffix for the generated jarfile. */
+ private String genericJarSuffix = "-generic.jar";
+
+ /**
+ * The task to which this tool belongs. This is used to access services
+ * provided by the ant core, such as logging.
+ */
+ private Task task;
+
+ /**
+ * The classloader generated from the given classpath to load
+ * the super classes and super interfaces.
+ */
+ private ClassLoader classpathLoader = null;
+
+ /**
+ * Set of files have been loaded into the EJB jar
+ */
+ private Set addedfiles;
+
+ /**
+ * Handler used to parse the EJB XML descriptor
+ */
+ private DescriptorHandler handler;
+
+ /**
+ * Dependency analyzer used to collect class dependencies
+ */
+ private DependencyAnalyzer dependencyAnalyzer;
+
+ /** No arg constructor */
+ public GenericDeploymentTool() {
+ }
+
+
+ /**
+ * Set the destination directory; required.
+ * @param inDir the destination directory.
+ */
+ public void setDestdir(File inDir) {
+ this.destDir = inDir;
+ }
+
+ /**
+ * Get the destination directory.
+ *
+ * @return the destination directory into which EJB jars are to be written
+ */
+ protected File getDestDir() {
+ return destDir;
+ }
+
+
+ /**
+ * Set the task which owns this tool
+ *
+ * @param task the Task to which this deployment tool is associated.
+ */
+ public void setTask(Task task) {
+ this.task = task;
+ }
+
+ /**
+ * Get the task for this tool.
+ *
+ * @return the Task instance this tool is associated with.
+ */
+ protected Task getTask() {
+ return task;
+ }
+
+ /**
+ * Get the basename terminator.
+ *
+ * @return an ejbjar task configuration
+ */
+ protected EjbJar.Config getConfig() {
+ return config;
+ }
+
+ /**
+ * Indicate if this build is using the base jar name.
+ *
+ * @return true if the name of the generated jar is coming from the
+ * basejarname attribute
+ */
+ protected boolean usingBaseJarName() {
+ return config.baseJarName != null;
+ }
+
+ /**
+ * Set the suffix for the generated jar file.
+ * @param inString the string to use as the suffix.
+ */
+ public void setGenericJarSuffix(String inString) {
+ this.genericJarSuffix = inString;
+ }
+
+ /**
+ * Add the classpath for the user classes
+ *
+ * @return a Path instance to be configured by Ant.
+ */
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(task.getProject());
+ }
+ return classpath.createPath();
+ }
+
+ /**
+ * Set the classpath to be used for this compilation.
+ *
+ * @param classpath the classpath to be used for this build.
+ */
+ public void setClasspath(Path classpath) {
+ this.classpath = classpath;
+ }
+
+ /**
+ * Get the classpath by combining the one from the surrounding task, if any
+ * and the one from this tool.
+ *
+ * @return the combined classpath
+ */
+ protected Path getCombinedClasspath() {
+ Path combinedPath = classpath;
+ if (config.classpath != null) {
+ if (combinedPath == null) {
+ combinedPath = config.classpath;
+ } else {
+ combinedPath.append(config.classpath);
+ }
+ }
+
+ return combinedPath;
+ }
+
+ /**
+ * Log a message to the Ant output.
+ *
+ * @param message the message to be logged.
+ * @param level the severity of this message.
+ */
+ protected void log(String message, int level) {
+ getTask().log(message, level);
+ }
+
+ /**
+ * Get the build file location associated with this element's task.
+ *
+ * @return the task's location instance.
+ */
+ protected Location getLocation() {
+ return getTask().getLocation();
+ }
+
+ private void createAnalyzer() {
+ String analyzer = config.analyzer;
+ if (analyzer == null) {
+ analyzer = DEFAULT_ANALYZER;
+ }
+
+ if (analyzer.equals(ANALYZER_NONE)) {
+ return;
+ }
+
+ String analyzerClassName = null;
+ if (analyzer.equals(ANALYZER_SUPER)) {
+ analyzerClassName = ANALYZER_CLASS_SUPER;
+ } else if (analyzer.equals(ANALYZER_FULL)) {
+ analyzerClassName = ANALYZER_CLASS_FULL;
+ } else {
+ analyzerClassName = analyzer;
+ }
+
+ try {
+ Class analyzerClass = Class.forName(analyzerClassName);
+ dependencyAnalyzer
+ = (DependencyAnalyzer) analyzerClass.newInstance();
+ dependencyAnalyzer.addClassPath(new Path(task.getProject(),
+ config.srcDir.getPath()));
+ dependencyAnalyzer.addClassPath(config.classpath);
+ } catch (NoClassDefFoundError e) {
+ dependencyAnalyzer = null;
+ task.log("Unable to load dependency analyzer: " + analyzerClassName
+ + " - dependent class not found: " + e.getMessage(),
+ Project.MSG_WARN);
+ } catch (Exception e) {
+ dependencyAnalyzer = null;
+ task.log("Unable to load dependency analyzer: " + analyzerClassName
+ + " - exception: " + e.getMessage(),
+ Project.MSG_WARN);
+ }
+ }
+
+
+ /**
+ * Configure this tool for use in the ejbjar task.
+ *
+ * @param config the configuration from the surrounding ejbjar task.
+ */
+ public void configure(EjbJar.Config config) {
+ this.config = config;
+
+ createAnalyzer();
+ classpathLoader = null;
+ }
+
+ /**
+ * Utility method that encapsulates the logic of adding a file entry to
+ * a .jar file. Used by execute() to add entries to the jar file as it is
+ * constructed.
+ * @param jStream A JarOutputStream into which to write the
+ * jar entry.
+ * @param inputFile A File from which to read the
+ * contents the file being added.
+ * @param logicalFilename A String representing the name, including
+ * all relevant path information, that should be stored for the entry
+ * being added.
+ * @throws BuildException if there is a problem.
+ */
+ protected void addFileToJar(JarOutputStream jStream,
+ File inputFile,
+ String logicalFilename)
+ throws BuildException {
+ FileInputStream iStream = null;
+ try {
+ if (!addedfiles.contains(logicalFilename)) {
+ iStream = new FileInputStream(inputFile);
+ // Create the zip entry and add it to the jar file
+ ZipEntry zipEntry = new ZipEntry(logicalFilename.replace('\\', '/'));
+ jStream.putNextEntry(zipEntry);
+
+ // Create the file input stream, and buffer everything over
+ // to the jar output stream
+ byte[] byteBuffer = new byte[2 * DEFAULT_BUFFER_SIZE];
+ int count = 0;
+ do {
+ jStream.write(byteBuffer, 0, count);
+ count = iStream.read(byteBuffer, 0, byteBuffer.length);
+ } while (count != -1);
+
+ //add it to list of files in jar
+ addedfiles.add(logicalFilename);
+ }
+ } catch (IOException ioe) {
+ log("WARNING: IOException while adding entry "
+ + logicalFilename + " to jarfile from "
+ + inputFile.getPath() + " " + ioe.getClass().getName()
+ + "-" + ioe.getMessage(), Project.MSG_WARN);
+ } finally {
+ // Close up the file input stream for the class file
+ if (iStream != null) {
+ try {
+ iStream.close();
+ } catch (IOException closeException) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Get a descriptionHandler.
+ * @param srcDir the source directory.
+ * @return a handler.
+ */
+ protected DescriptorHandler getDescriptorHandler(File srcDir) {
+ DescriptorHandler h = new DescriptorHandler(getTask(), srcDir);
+
+ registerKnownDTDs(h);
+
+ // register any DTDs supplied by the user
+ for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) {
+ EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
+ h.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
+ }
+ return h;
+ }
+
+ /**
+ * Register the locations of all known DTDs.
+ *
+ * vendor-specific subclasses should override this method to define
+ * the vendor-specific locations of the EJB DTDs
+ * @param handler no used in this class.
+ */
+ protected void registerKnownDTDs(DescriptorHandler handler) {
+ // none to register for generic
+ }
+
+ /** {@inheritDoc}. */
+ public void processDescriptor(String descriptorFileName, SAXParser saxParser) {
+
+ checkConfiguration(descriptorFileName, saxParser);
+
+ try {
+ handler = getDescriptorHandler(config.srcDir);
+
+ // Retrive the files to be added to JAR from EJB descriptor
+ Hashtable ejbFiles = parseEjbFiles(descriptorFileName, saxParser);
+
+ // Add any support classes specified in the build file
+ addSupportClasses(ejbFiles);
+
+ // Determine the JAR filename (without filename extension)
+ String baseName = getJarBaseName(descriptorFileName);
+
+ String ddPrefix = getVendorDDPrefix(baseName, descriptorFileName);
+
+ File manifestFile = getManifestFile(ddPrefix);
+ if (manifestFile != null) {
+ ejbFiles.put(MANIFEST, manifestFile);
+ }
+
+
+
+ // First the regular deployment descriptor
+ ejbFiles.put(META_DIR + EJB_DD,
+ new File(config.descriptorDir, descriptorFileName));
+
+ // now the vendor specific files, if any
+ addVendorFiles(ejbFiles, ddPrefix);
+
+ // add any dependent files
+ checkAndAddDependants(ejbFiles);
+
+ // Lastly create File object for the Jar files. If we are using
+ // a flat destination dir, then we need to redefine baseName!
+ if (config.flatDestDir && baseName.length() != 0) {
+ int startName = baseName.lastIndexOf(File.separator);
+ if (startName == -1) {
+ startName = 0;
+ }
+
+ int endName = baseName.length();
+ baseName = baseName.substring(startName, endName);
+ }
+
+ File jarFile = getVendorOutputJarFile(baseName);
+
+
+ // Check to see if we need a build and start doing the work!
+ if (needToRebuild(ejbFiles, jarFile)) {
+ // Log that we are going to build...
+ log("building "
+ + jarFile.getName()
+ + " with "
+ + String.valueOf(ejbFiles.size())
+ + " files",
+ Project.MSG_INFO);
+
+ // Use helper method to write the jarfile
+ String publicId = getPublicId();
+ writeJar(baseName, jarFile, ejbFiles, publicId);
+
+ } else {
+ // Log that the file is up to date...
+ log(jarFile.toString() + " is up to date.",
+ Project.MSG_VERBOSE);
+ }
+
+ } catch (SAXException se) {
+ String msg = "SAXException while parsing '"
+ + descriptorFileName
+ + "'. This probably indicates badly-formed XML."
+ + " Details: "
+ + se.getMessage();
+ throw new BuildException(msg, se);
+ } catch (IOException ioe) {
+ String msg = "IOException while parsing'"
+ + descriptorFileName
+ + "'. This probably indicates that the descriptor"
+ + " doesn't exist. Details: "
+ + ioe.getMessage();
+ throw new BuildException(msg, ioe);
+ }
+ }
+
+ /**
+ * This method is called as the first step in the processDescriptor method
+ * to allow vendor-specific subclasses to validate the task configuration
+ * prior to processing the descriptor. If the configuration is invalid,
+ * a BuildException should be thrown.
+ *
+ * @param descriptorFileName String representing the file name of an EJB
+ * descriptor to be processed
+ * @param saxParser SAXParser which may be used to parse the XML
+ * descriptor
+ * @throws BuildException if there is a problem.
+ */
+ protected void checkConfiguration(String descriptorFileName,
+ SAXParser saxParser) throws BuildException {
+
+ /*
+ * For the GenericDeploymentTool, do nothing. Vendor specific
+ * subclasses should throw a BuildException if the configuration is
+ * invalid for their server.
+ */
+ }
+
+ /**
+ * This method returns a list of EJB files found when the specified EJB
+ * descriptor is parsed and processed.
+ *
+ * @param descriptorFileName String representing the file name of an EJB
+ * descriptor to be processed
+ * @param saxParser SAXParser which may be used to parse the XML
+ * descriptor
+ * @return Hashtable of EJB class (and other) files to be
+ * added to the completed JAR file
+ * @throws SAXException Any SAX exception, possibly wrapping another
+ * exception
+ * @throws IOException An IOException from the parser, possibly from a
+ * the byte stream or character stream
+ */
+ protected Hashtable parseEjbFiles(String descriptorFileName, SAXParser saxParser)
+ throws IOException, SAXException {
+ FileInputStream descriptorStream = null;
+ Hashtable ejbFiles = null;
+
+ try {
+
+ /* Parse the ejb deployment descriptor. While it may not
+ * look like much, we use a SAXParser and an inner class to
+ * get hold of all the classfile names for the descriptor.
+ */
+ descriptorStream
+ = new FileInputStream(new File(config.descriptorDir, descriptorFileName));
+ saxParser.parse(new InputSource(descriptorStream), handler);
+
+ ejbFiles = handler.getFiles();
+
+ } finally {
+ if (descriptorStream != null) {
+ try {
+ descriptorStream.close();
+ } catch (IOException closeException) {
+ // ignore
+ }
+ }
+ }
+
+ return ejbFiles;
+ }
+
+ /**
+ * Adds any classes the user specifies using <i>support</i> nested elements
+ * to the <code>ejbFiles</code> Hashtable.
+ *
+ * @param ejbFiles Hashtable of EJB classes (and other) files that will be
+ * added to the completed JAR file
+ */
+ protected void addSupportClasses(Hashtable ejbFiles) {
+ // add in support classes if any
+ Project project = task.getProject();
+ for (Iterator i = config.supportFileSets.iterator(); i.hasNext();) {
+ FileSet supportFileSet = (FileSet) i.next();
+ File supportBaseDir = supportFileSet.getDir(project);
+ DirectoryScanner supportScanner = supportFileSet.getDirectoryScanner(project);
+ supportScanner.scan();
+ String[] supportFiles = supportScanner.getIncludedFiles();
+ for (int j = 0; j < supportFiles.length; ++j) {
+ ejbFiles.put(supportFiles[j], new File(supportBaseDir, supportFiles[j]));
+ }
+ }
+ }
+
+
+ /**
+ * Using the EJB descriptor file name passed from the <code>ejbjar</code>
+ * task, this method returns the "basename" which will be used to name the
+ * completed JAR file.
+ *
+ * @param descriptorFileName String representing the file name of an EJB
+ * descriptor to be processed
+ * @return The "basename" which will be used to name the
+ * completed JAR file
+ */
+ protected String getJarBaseName(String descriptorFileName) {
+
+ String baseName = "";
+
+ // Work out what the base name is
+ if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME)) {
+ String canonicalDescriptor = descriptorFileName.replace('\\', '/');
+ int index = canonicalDescriptor.lastIndexOf('/');
+ if (index != -1) {
+ baseName = descriptorFileName.substring(0, index + 1);
+ }
+ baseName += config.baseJarName;
+ } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
+ int lastSeparatorIndex = descriptorFileName.lastIndexOf(File.separator);
+ int endBaseName = -1;
+ if (lastSeparatorIndex != -1) {
+ endBaseName = descriptorFileName.indexOf(config.baseNameTerminator,
+ lastSeparatorIndex);
+ } else {
+ endBaseName = descriptorFileName.indexOf(config.baseNameTerminator);
+ }
+
+ if (endBaseName != -1) {
+ baseName = descriptorFileName.substring(0, endBaseName);
+ } else {
+ throw new BuildException("Unable to determine jar name "
+ + "from descriptor \"" + descriptorFileName + "\"");
+ }
+ } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
+ File descriptorFile = new File(config.descriptorDir, descriptorFileName);
+ String path = descriptorFile.getAbsolutePath();
+ int lastSeparatorIndex
+ = path.lastIndexOf(File.separator);
+ if (lastSeparatorIndex == -1) {
+ throw new BuildException("Unable to determine directory name holding descriptor");
+ }
+ String dirName = path.substring(0, lastSeparatorIndex);
+ int dirSeparatorIndex = dirName.lastIndexOf(File.separator);
+ if (dirSeparatorIndex != -1) {
+ dirName = dirName.substring(dirSeparatorIndex + 1);
+ }
+
+ baseName = dirName;
+ } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME)) {
+ baseName = handler.getEjbName();
+ }
+ return baseName;
+ }
+
+ /**
+ * Get the prefix for vendor deployment descriptors.
+ *
+ * This will contain the path and the start of the descriptor name,
+ * depending on the naming scheme
+ * @param baseName the base name to use.
+ * @param descriptorFileName the file name to use.
+ * @return the prefix.
+ */
+ public String getVendorDDPrefix(String baseName, String descriptorFileName) {
+ String ddPrefix = null;
+
+ if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
+ ddPrefix = baseName + config.baseNameTerminator;
+ } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME)
+ || config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME)
+ || config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
+ String canonicalDescriptor = descriptorFileName.replace('\\', '/');
+ int index = canonicalDescriptor.lastIndexOf('/');
+ if (index == -1) {
+ ddPrefix = "";
+ } else {
+ ddPrefix = descriptorFileName.substring(0, index + 1);
+ }
+ }
+ return ddPrefix;
+ }
+
+ /**
+ * Add any vendor specific files which should be included in the
+ * EJB Jar.
+ * @param ejbFiles a hashtable entryname -> file.
+ * @param ddPrefix a prefix to use.
+ */
+ protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+ // nothing to add for generic tool.
+ }
+
+
+ /**
+ * Get the vendor specific name of the Jar that will be output. The modification date
+ * of this jar will be checked against the dependent bean classes.
+ * @param baseName the basename to use.
+ */
+ File getVendorOutputJarFile(String baseName) {
+ return new File(destDir, baseName + genericJarSuffix);
+ }
+
+ /**
+ * This method checks the timestamp on each file listed in the <code>
+ * ejbFiles</code> and compares them to the timestamp on the <code>jarFile
+ * </code>. If the <code>jarFile</code>'s timestamp is more recent than
+ * each EJB file, <code>true</code> is returned. Otherwise, <code>false
+ * </code> is returned.
+ * TODO: find a way to check the manifest-file, that is found by naming convention
+ *
+ * @param ejbFiles Hashtable of EJB classes (and other) files that will be
+ * added to the completed JAR file
+ * @param jarFile JAR file which will contain all of the EJB classes (and
+ * other) files
+ * @return boolean indicating whether or not the <code>jarFile</code>
+ * is up to date
+ */
+ protected boolean needToRebuild(Hashtable ejbFiles, File jarFile) {
+ if (jarFile.exists()) {
+ long lastBuild = jarFile.lastModified();
+
+ Iterator fileIter = ejbFiles.values().iterator();
+
+ // Loop through the files seeing if any has been touched
+ // more recently than the destination jar.
+ while (fileIter.hasNext()) {
+ File currentFile = (File) fileIter.next();
+ if (lastBuild < currentFile.lastModified()) {
+ log("Build needed because " + currentFile.getPath() + " is out of date",
+ Project.MSG_VERBOSE);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the Public ID of the DTD specified in the EJB descriptor. Not
+ * every vendor-specific <code>DeploymentTool</code> will need to reference
+ * this value or may want to determine this value in a vendor-specific way.
+ *
+ * @return Public ID of the DTD specified in the EJB descriptor.
+ */
+ protected String getPublicId() {
+ return handler.getPublicId();
+ }
+
+ /**
+ * Get the manifest file to use for building the generic jar.
+ *
+ * If the file does not exist the global manifest from the config is used
+ * otherwise the default Ant manifest will be used.
+ *
+ * @param prefix the prefix where to llook for the manifest file based on
+ * the naming convention.
+ *
+ * @return the manifest file or null if the manifest file does not exist
+ */
+ protected File getManifestFile(String prefix) {
+ File manifestFile
+ = new File(getConfig().descriptorDir, prefix + "manifest.mf");
+ if (manifestFile.exists()) {
+ return manifestFile;
+ }
+
+ if (config.manifest != null) {
+ return config.manifest;
+ }
+ return null;
+ }
+
+ /**
+ * Method used to encapsulate the writing of the JAR file. Iterates over the
+ * filenames/java.io.Files in the Hashtable stored on the instance variable
+ * ejbFiles.
+ * @param baseName the base name to use.
+ * @param jarfile the jar file to write to.
+ * @param files the files to write to the jar.
+ * @param publicId the id to use.
+ * @throws BuildException if there is a problem.
+ */
+ protected void writeJar(String baseName, File jarfile, Hashtable files,
+ String publicId) throws BuildException {
+
+ JarOutputStream jarStream = null;
+ try {
+ // clean the addedfiles set
+ if (addedfiles == null) {
+ addedfiles = new HashSet();
+ } else {
+ addedfiles.clear();
+ }
+
+ /* If the jarfile already exists then whack it and recreate it.
+ * Should probably think of a more elegant way to handle this
+ * so that in case of errors we don't leave people worse off
+ * than when we started =)
+ */
+ if (jarfile.exists()) {
+ jarfile.delete();
+ }
+ jarfile.getParentFile().mkdirs();
+ jarfile.createNewFile();
+
+ InputStream in = null;
+ Manifest manifest = null;
+ try {
+ File manifestFile = (File) files.get(MANIFEST);
+ if (manifestFile != null && manifestFile.exists()) {
+ in = new FileInputStream(manifestFile);
+ } else {
+ String defaultManifest = "/org/apache/tools/ant/defaultManifest.mf";
+ in = this.getClass().getResourceAsStream(defaultManifest);
+ if (in == null) {
+ throw new BuildException("Could not find "
+ + "default manifest: " + defaultManifest);
+ }
+ }
+
+ manifest = new Manifest(in);
+ } catch (IOException e) {
+ throw new BuildException ("Unable to read manifest", e, getLocation());
+ } finally {
+ if (in != null) {
+ in.close();
+ }
+ }
+
+ // Create the streams necessary to write the jarfile
+
+ jarStream = new JarOutputStream(new FileOutputStream(jarfile), manifest);
+ jarStream.setMethod(JarOutputStream.DEFLATED);
+
+ // Loop through all the class files found and add them to the jar
+ for (Iterator entryIterator = files.keySet().iterator(); entryIterator.hasNext();) {
+ String entryName = (String) entryIterator.next();
+ if (entryName.equals(MANIFEST)) {
+ continue;
+ }
+
+ File entryFile = (File) files.get(entryName);
+
+ log("adding file '" + entryName + "'",
+ Project.MSG_VERBOSE);
+
+ addFileToJar(jarStream, entryFile, entryName);
+
+ // See if there are any inner classes for this class and add them in if there are
+ InnerClassFilenameFilter flt = new InnerClassFilenameFilter(entryFile.getName());
+ File entryDir = entryFile.getParentFile();
+ String[] innerfiles = entryDir.list(flt);
+ if (innerfiles != null) {
+ for (int i = 0, n = innerfiles.length; i < n; i++) {
+
+ //get and clean up innerclass name
+ int entryIndex = entryName.lastIndexOf(entryFile.getName()) - 1;
+ if (entryIndex < 0) {
+ entryName = innerfiles[i];
+ } else {
+ entryName = entryName.substring(0, entryIndex)
+ + File.separatorChar + innerfiles[i];
+ }
+ // link the file
+ entryFile = new File(config.srcDir, entryName);
+
+ log("adding innerclass file '" + entryName + "'",
+ Project.MSG_VERBOSE);
+
+ addFileToJar(jarStream, entryFile, entryName);
+
+ }
+ }
+ }
+ } catch (IOException ioe) {
+ String msg = "IOException while processing ejb-jar file '"
+ + jarfile.toString()
+ + "'. Details: "
+ + ioe.getMessage();
+ throw new BuildException(msg, ioe);
+ } finally {
+ if (jarStream != null) {
+ try {
+ jarStream.close();
+ } catch (IOException closeException) {
+ // ignore
+ }
+ }
+ }
+ } // end of writeJar
+
+
+ /**
+ * Add all available classes, that depend on Remote, Home, Bean, PK
+ * @param checkEntries files, that are extracted from the deployment descriptor
+ * @throws BuildException if there is a problem.
+ */
+ protected void checkAndAddDependants(Hashtable checkEntries)
+ throws BuildException {
+
+ if (dependencyAnalyzer == null) {
+ return;
+ }
+
+ dependencyAnalyzer.reset();
+
+ Iterator i = checkEntries.keySet().iterator();
+ while (i.hasNext()) {
+ String entryName = (String) i.next();
+ if (entryName.endsWith(".class")) {
+ String className = entryName.substring(0,
+ entryName.length() - ".class".length());
+ className = className.replace(File.separatorChar, '/');
+ className = className.replace('/', '.');
+
+ dependencyAnalyzer.addRootClass(className);
+ }
+ }
+
+ Enumeration e = dependencyAnalyzer.getClassDependencies();
+
+ while (e.hasMoreElements()) {
+ String classname = (String) e.nextElement();
+ String location
+ = classname.replace('.', File.separatorChar) + ".class";
+ File classFile = new File(config.srcDir, location);
+ if (classFile.exists()) {
+ checkEntries.put(location, classFile);
+ log("dependent class: " + classname + " - " + classFile,
+ Project.MSG_VERBOSE);
+ }
+ }
+ }
+
+
+ /**
+ * Returns a Classloader object which parses the passed in generic EjbJar classpath.
+ * The loader is used to dynamically load classes from javax.ejb.* and the classes
+ * being added to the jar.
+ * @return a classloader.
+ */
+ protected ClassLoader getClassLoaderForBuild() {
+ if (classpathLoader != null) {
+ return classpathLoader;
+ }
+
+ Path combinedClasspath = getCombinedClasspath();
+
+ // only generate a new ClassLoader if we have a classpath
+ if (combinedClasspath == null) {
+ classpathLoader = getClass().getClassLoader();
+ } else {
+ // Memory leak in line below
+ classpathLoader
+ = getTask().getProject().createClassLoader(combinedClasspath);
+ }
+
+ return classpathLoader;
+ }
+
+ /**
+ * Called to validate that the tool parameters have been configured.
+ *
+ * @throws BuildException If the Deployment Tool's configuration isn't
+ * valid
+ */
+ public void validateConfigured() throws BuildException {
+ if ((destDir == null) || (!destDir.isDirectory())) {
+ String msg = "A valid destination directory must be specified "
+ + "using the \"destdir\" attribute.";
+ throw new BuildException(msg, getLocation());
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetDeploymentTool.java
new file mode 100644
index 00000000..cbc8526a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetDeploymentTool.java
@@ -0,0 +1,403 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import javax.xml.parsers.SAXParser;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.xml.sax.SAXException;
+
+/**
+ * This class is used to generate iPlanet Application Server (iAS) 6.0 stubs and
+ * skeletons and build an EJB Jar file. It is designed to be used with the Ant
+ * <code>ejbjar</code> task. If only stubs and skeletons need to be generated
+ * (in other words, if no JAR file needs to be created), refer to the
+ * <code>iplanet-ejbc</code> task and the <code>IPlanetEjbcTask</code> class.
+ * <p>
+ * The following attributes may be specified by the user:
+ * <ul>
+ * <li><i>destdir</i> -- The base directory into which the generated JAR
+ * files will be written. Each JAR file is written
+ * in directories which correspond to their location
+ * within the "descriptordir" namespace. This is a
+ * required attribute.
+ * <li><i>classpath</i> -- The classpath used when generating EJB stubs and
+ * skeletons. This is an optional attribute (if
+ * omitted, the classpath specified in the "ejbjar"
+ * parent task will be used). If specified, the
+ * classpath elements will be prepended to the
+ * classpath specified in the parent "ejbjar" task.
+ * Note that nested "classpath" elements may also be
+ * used.
+ * <li><i>keepgenerated</i> -- Indicates whether or not the Java source
+ * files which are generated by ejbc will be
+ * saved or automatically deleted. If "yes",
+ * the source files will be retained. This is
+ * an optional attribute (if omitted, it
+ * defaults to "no").
+ * <li><i>debug</i> -- Indicates whether or not the ejbc utility should
+ * log additional debugging statements to the standard
+ * output. If "yes", the additional debugging statements
+ * will be generated (if omitted, it defaults to "no").
+ * <li><i>iashome</i> -- May be used to specify the "home" directory for
+ * this iPlanet Application server installation. This
+ * is used to find the ejbc utility if it isn't
+ * included in the user's system path. This is an
+ * optional attribute (if specified, it should refer
+ * to the <code>[install-location]/iplanet/ias6/ias
+ * </code> directory). If omitted, the ejbc utility
+ * must be on the user's system path.
+ * <li><i>suffix</i> -- String value appended to the JAR filename when
+ * creating each JAR. This attribute is not required
+ * (if omitted, it defaults to ".jar").
+ * </ul>
+ * <p>
+ * For each EJB descriptor found in the "ejbjar" parent task, this deployment
+ * tool will locate the three classes that comprise the EJB. If these class
+ * files cannot be located in the specified <code>srcdir</code> directory, the
+ * task will fail. The task will also attempt to locate the EJB stubs and
+ * skeletons in this directory. If found, the timestamps on the stubs and
+ * skeletons will be checked to ensure they are up to date. Only if these files
+ * cannot be found or if they are out of date will ejbc be called.
+ *
+ * @see IPlanetEjbc
+ */
+public class IPlanetDeploymentTool extends GenericDeploymentTool {
+
+ /* Attributes set by the Ant build file */
+ private File iashome;
+ private String jarSuffix = ".jar";
+ private boolean keepgenerated = false;
+ private boolean debug = false;
+
+ /*
+ * Filenames of the standard EJB descriptor (which is passed to this class
+ * from the parent "ejbjar" task) and the iAS-specific EJB descriptor
+ * (whose name is determined by this class). Both filenames are relative
+ * to the directory specified by the "srcdir" attribute in the ejbjar task.
+ */
+ private String descriptorName;
+ private String iasDescriptorName;
+
+ /*
+ * The displayName variable stores the value of the "display-name" element
+ * from the standard EJB descriptor. As a future enhancement to this task,
+ * we may determine the name of the EJB JAR file using this display-name,
+ * but this has not be implemented yet.
+ */
+ private String displayName;
+
+ /*
+ * Regardless of the name of the iAS-specific EJB descriptor file, it will
+ * written in the completed JAR file as "ias-ejb-jar.xml". This is the
+ * naming convention implemented by iAS.
+ */
+ private static final String IAS_DD = "ias-ejb-jar.xml";
+
+ /**
+ * Setter method used to store the "home" directory of the user's iAS
+ * installation. The directory specified should typically be
+ * <code>[install-location]/iplanet/ias6/ias</code>.
+ *
+ * @param iashome The home directory for the user's iAS installation.
+ */
+ public void setIashome(File iashome) {
+ this.iashome = iashome;
+ }
+
+ /**
+ * Setter method used to specify whether the Java source files generated by
+ * the ejbc utility should be saved or automatically deleted.
+ *
+ * @param keepgenerated boolean which, if <code>true</code>, indicates that
+ * Java source files generated by ejbc for the stubs
+ * and skeletons should be kept.
+ */
+ public void setKeepgenerated(boolean keepgenerated) {
+ this.keepgenerated = keepgenerated;
+ }
+
+ /**
+ * Sets whether or not debugging output will be generated when ejbc is
+ * executed.
+ *
+ * @param debug A boolean indicating if debugging output should be generated
+ */
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ }
+
+ /**
+ * Setter method used to specify the filename suffix (for example, ".jar")
+ * for the JAR files to be created.
+ *
+ * @param jarSuffix The string to use as the JAR filename suffix.
+ */
+ public void setSuffix(String jarSuffix) {
+ this.jarSuffix = jarSuffix;
+ }
+
+ /**
+ * Since iAS doesn't generate a "generic" JAR as part of its processing,
+ * this attribute is ignored and a warning message is displayed to the user.
+ *
+ * @param inString the string to use as the suffix. This parameter is
+ * ignored.
+ */
+ @Override
+ public void setGenericJarSuffix(String inString) {
+ log("Since a generic JAR file is not created during processing, the "
+ + "iPlanet Deployment Tool does not support the "
+ + "\"genericjarsuffix\" attribute. It will be ignored.",
+ Project.MSG_WARN);
+ }
+
+ /** {@inheritDoc}. */
+ @Override
+ public void processDescriptor(String descriptorName, SAXParser saxParser) {
+ this.descriptorName = descriptorName;
+ this.iasDescriptorName = null;
+
+ log("iPlanet Deployment Tool processing: " + descriptorName + " (and "
+ + getIasDescriptorName() + ")", Project.MSG_VERBOSE);
+
+ super.processDescriptor(descriptorName, saxParser);
+ }
+
+ /**
+ * Verifies that the user selections are valid.
+ *
+ * @param descriptorFileName String representing the file name of an EJB
+ * descriptor to be processed
+ * @param saxParser SAXParser which may be used to parse the XML
+ * descriptor
+ * @throws BuildException If the user selections are invalid.
+ */
+ @Override
+ protected void checkConfiguration(String descriptorFileName,
+ SAXParser saxParser) throws BuildException {
+
+ int startOfName = descriptorFileName.lastIndexOf(File.separatorChar) + 1;
+ String stdXml = descriptorFileName.substring(startOfName);
+ if (stdXml.equals(EJB_DD) && (getConfig().baseJarName == null)) {
+ String msg = "No name specified for the completed JAR file. The EJB"
+ + " descriptor should be prepended with the JAR "
+ + "name or it should be specified using the "
+ + "attribute \"basejarname\" in the \"ejbjar\" task.";
+ throw new BuildException(msg, getLocation());
+ }
+
+ File iasDescriptor = new File(getConfig().descriptorDir,
+ getIasDescriptorName());
+ if ((!iasDescriptor.exists()) || (!iasDescriptor.isFile())) {
+ String msg = "The iAS-specific EJB descriptor ("
+ + iasDescriptor + ") was not found.";
+ throw new BuildException(msg, getLocation());
+ }
+
+ if ((iashome != null) && (!iashome.isDirectory())) {
+ String msg = "If \"iashome\" is specified, it must be a valid "
+ + "directory (it was set to " + iashome + ").";
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+ /**
+ * This method returns a list of EJB files found when the specified EJB
+ * descriptor is parsed and processed.
+ *
+ * @param descriptorFileName String representing the file name of an EJB
+ * descriptor to be processed
+ * @param saxParser SAXParser which may be used to parse the XML
+ * descriptor
+ * @return Hashtable of EJB class (and other) files to be
+ * added to the completed JAR file
+ * @throws IOException An IOException from the parser, possibly from
+ * the byte stream or character stream
+ * @throws SAXException Any SAX exception, possibly wrapping another
+ * exception
+ */
+ @Override
+ protected Hashtable parseEjbFiles(String descriptorFileName,
+ SAXParser saxParser) throws IOException, SAXException {
+
+ Hashtable files;
+
+ /* Build and populate an instance of the ejbc utility */
+ IPlanetEjbc ejbc = new IPlanetEjbc(
+ new File(getConfig().descriptorDir,
+ descriptorFileName),
+ new File(getConfig().descriptorDir,
+ getIasDescriptorName()),
+ getConfig().srcDir,
+ getCombinedClasspath().toString(),
+ saxParser);
+ ejbc.setRetainSource(keepgenerated);
+ ejbc.setDebugOutput(debug);
+ if (iashome != null) {
+ ejbc.setIasHomeDir(iashome);
+ }
+ if (getConfig().dtdLocations != null) {
+ for (Iterator i = getConfig().dtdLocations.iterator();
+ i.hasNext();) {
+ EjbJar.DTDLocation dtdLocation =
+ (EjbJar.DTDLocation) i.next();
+ ejbc.registerDTD(dtdLocation.getPublicId(),
+ dtdLocation.getLocation());
+ }
+ }
+
+ /* Execute the ejbc utility -- stubs/skeletons are rebuilt, if needed */
+ try {
+ ejbc.execute();
+ } catch (IPlanetEjbc.EjbcException e) {
+ throw new BuildException("An error has occurred while trying to "
+ + "execute the iAS ejbc utility", e, getLocation());
+ }
+
+ displayName = ejbc.getDisplayName();
+ files = ejbc.getEjbFiles();
+
+ /* Add CMP descriptors to the list of EJB files */
+ String[] cmpDescriptors = ejbc.getCmpDescriptors();
+ if (cmpDescriptors.length > 0) {
+ File baseDir = getConfig().descriptorDir;
+
+ int endOfPath = descriptorFileName.lastIndexOf(File.separator);
+ String relativePath = descriptorFileName.substring(0, endOfPath + 1);
+
+ for (int i = 0; i < cmpDescriptors.length; i++) {
+ int endOfCmp = cmpDescriptors[i].lastIndexOf('/');
+ String cmpDescriptor = cmpDescriptors[i].substring(endOfCmp + 1);
+
+ File cmpFile = new File(baseDir, relativePath + cmpDescriptor);
+ if (!cmpFile.exists()) {
+ throw new BuildException("The CMP descriptor file ("
+ + cmpFile + ") could not be found.", getLocation());
+ }
+ files.put(cmpDescriptors[i], cmpFile);
+ }
+ }
+
+ return files;
+ }
+
+ /**
+ * Add the iAS-specific EJB descriptor to the list of files which will be
+ * written to the JAR file.
+ *
+ * @param ejbFiles Hashtable of EJB class (and other) files to be added to
+ * the completed JAR file.
+ * @param ddPrefix not used
+ */
+ @Override
+ protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+ ejbFiles.put(META_DIR + IAS_DD, new File(getConfig().descriptorDir,
+ getIasDescriptorName()));
+ }
+
+ /**
+ * Get the name of the Jar that will be written. The modification date
+ * of this jar will be checked against the dependent bean classes.
+ *
+ * @param baseName String name of the EJB JAR file to be written (without
+ * a filename extension).
+ *
+ * @return File representing the JAR file which will be written.
+ */
+ @Override
+ File getVendorOutputJarFile(String baseName) {
+ File jarFile = new File(getDestDir(), baseName + jarSuffix);
+ log("JAR file name: " + jarFile.toString(), Project.MSG_VERBOSE);
+ return jarFile;
+ }
+
+ /**
+ * The iAS ejbc utility doesn't require the Public ID of the descriptor's
+ * DTD for it to process correctly--this method always returns <code>null
+ * </code>.
+ *
+ * @return <code>null</code>.
+ */
+ @Override
+ protected String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Determines the name of the iAS-specific EJB descriptor using the
+ * specified standard EJB descriptor name. In general, the standard
+ * descriptor will be named "[basename]-ejb-jar.xml", and this method will
+ * return "[basename]-ias-ejb-jar.xml".
+ *
+ * @return The name of the iAS-specific EJB descriptor file.
+ */
+ private String getIasDescriptorName() {
+
+ /* Only calculate the descriptor name once */
+ if (iasDescriptorName != null) {
+ return iasDescriptorName;
+ }
+
+ String path = ""; // Directory path of the EJB descriptor
+ String basename; // Filename appearing before name terminator
+ String remainder; // Filename appearing after the name terminator
+
+ /* Find the end of the standard descriptor's relative path */
+ int startOfFileName = descriptorName.lastIndexOf(File.separatorChar);
+ if (startOfFileName != -1) {
+ path = descriptorName.substring(0, startOfFileName + 1);
+ }
+
+ /* Check to see if the standard name is used (there's no basename) */
+ if (descriptorName.substring(startOfFileName + 1).equals(EJB_DD)) {
+ basename = "";
+ remainder = EJB_DD;
+
+ } else {
+ int endOfBaseName = descriptorName.indexOf(
+ getConfig().baseNameTerminator,
+ startOfFileName);
+ /*
+ * Check for the odd case where the terminator and/or filename
+ * extension aren't found. These will ensure "ias-" appears at the
+ * end of the name and before the '.' (if present).
+ */
+ if (endOfBaseName < 0) {
+ endOfBaseName = descriptorName.lastIndexOf('.') - 1;
+ if (endOfBaseName < 0) {
+ endOfBaseName = descriptorName.length() - 1;
+ }
+ }
+
+ basename = descriptorName.substring(startOfFileName + 1,
+ endOfBaseName + 1);
+ remainder = descriptorName.substring(endOfBaseName + 1);
+ }
+
+ iasDescriptorName = path + basename + "ias-" + remainder;
+ return iasDescriptorName;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbc.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbc.java
new file mode 100644
index 00000000..ed799d33
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbc.java
@@ -0,0 +1,1495 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ejb;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.AttributeList;
+import org.xml.sax.HandlerBase;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * Compiles EJB stubs and skeletons for the iPlanet Application
+ * Server (iAS). The class will read a standard EJB descriptor (as well as an
+ * EJB descriptor specific to iPlanet Application Server) to identify one or
+ * more EJBs to process. It will search for EJB "source" classes (the remote
+; * interface, home interface, and EJB implementation class) and the EJB stubs
+ * and skeletons in the specified destination directory. Only if the stubs and
+ * skeletons cannot be found or if they're out of date will the iPlanet
+ * Application Server ejbc utility be run.
+ * <p>
+ * Because this class (and it's assorted inner classes) may be bundled into the
+ * iPlanet Application Server distribution at some point (and removed from the
+ * Ant distribution), the class has been written to be independent of all
+ * Ant-specific classes. It is also for this reason (and to avoid cluttering
+ * the Apache Ant source files) that this utility has been packaged into a
+ * single source file.
+ * <p>
+ * For more information on Ant Tasks for iPlanet Application Server, see the
+ * <code>IPlanetDeploymentTool</code> and <code>IPlanetEjbcTask</code> classes.
+ *
+ * @see IPlanetDeploymentTool
+ * @see IPlanetEjbcTask
+ * @ant.task ignore="true"
+ */
+public class IPlanetEjbc {
+
+ private static final int MIN_NUM_ARGS = 2;
+ private static final int MAX_NUM_ARGS = 8;
+ private static final int NUM_CLASSES_WITH_IIOP = 15;
+ private static final int NUM_CLASSES_WITHOUT_IIOP = 9;
+
+ /* Constants used for the "beantype" attribute */
+ private static final String ENTITY_BEAN = "entity";
+ private static final String STATELESS_SESSION = "stateless";
+ private static final String STATEFUL_SESSION = "stateful";
+
+ /* Filenames of the standard EJB descriptor and the iAS-specific descriptor */
+ private File stdDescriptor;
+ private File iasDescriptor;
+
+ /*
+ * Directory where "source" EJB files are stored and where stubs and
+ * skeletons will also be written.
+ */
+ private File destDirectory;
+
+ /* Classpath used when the iAS ejbc is called */
+ private String classpath;
+ private String[] classpathElements;
+
+ /* Options passed to the iAS ejbc */
+ private boolean retainSource = false;
+ private boolean debugOutput = false;
+
+ /* iAS installation directory (used if ejbc isn't on user's PATH) */
+ private File iasHomeDir;
+
+ /* Parser and handler used to process both EJB descriptor files */
+ private SAXParser parser;
+ private EjbcHandler handler = new EjbcHandler();
+
+ /*
+ * This Hashtable maintains a list of EJB class files processed by the ejbc
+ * utility (both "source" class files as well as stubs and skeletons). The
+ * key for the Hashtable is a String representing the path to the class file
+ * (relative to the destination directory). The value for the Hashtable is
+ * a File object which reference the actual class file.
+ */
+ private Hashtable ejbFiles = new Hashtable();
+
+ /* Value of the display-name element read from the standard EJB descriptor */
+ private String displayName;
+
+ /**
+ * Constructs an instance which may be used to process EJB descriptors and
+ * generate EJB stubs and skeletons, if needed.
+ *
+ * @param stdDescriptor File referencing a standard EJB descriptor.
+ * @param iasDescriptor File referencing an iAS-specific EJB descriptor.
+ * @param destDirectory File referencing the base directory where both
+ * EJB "source" files are found and where stubs and
+ * skeletons will be written.
+ * @param classpath String representation of the classpath to be used
+ * by the iAS ejbc utility.
+ * @param parser SAXParser to be used to process both of the EJB
+ * descriptors.
+ * @todo classpathElements is not needed here, its never used
+ * (at least IDEA tells me so! :)
+ */
+ public IPlanetEjbc(File stdDescriptor,
+ File iasDescriptor,
+ File destDirectory,
+ String classpath,
+ SAXParser parser) {
+ this.stdDescriptor = stdDescriptor;
+ this.iasDescriptor = iasDescriptor;
+ this.destDirectory = destDirectory;
+ this.classpath = classpath;
+ this.parser = parser;
+
+ /*
+ * Parse the classpath into it's individual elements and store the
+ * results in the "classpathElements" instance variable.
+ */
+ List elements = new ArrayList();
+ if (classpath != null) {
+ StringTokenizer st = new StringTokenizer(classpath,
+ File.pathSeparator);
+ while (st.hasMoreTokens()) {
+ elements.add(st.nextToken());
+ }
+ classpathElements
+ = (String[]) elements.toArray(new String[elements.size()]);
+ }
+ }
+
+ /**
+ * If true, the Java source files which are generated by the
+ * ejbc process are retained.
+ *
+ * @param retainSource A boolean indicating if the Java source files for
+ * the stubs and skeletons should be retained.
+ * @todo This is not documented in the HTML. On purpose?
+ */
+ public void setRetainSource(boolean retainSource) {
+ this.retainSource = retainSource;
+ }
+
+ /**
+ * If true, enables debugging output when ejbc is executed.
+ *
+ * @param debugOutput A boolean indicating if debugging output should be
+ * generated
+ */
+ public void setDebugOutput(boolean debugOutput) {
+ this.debugOutput = debugOutput;
+ }
+
+ /**
+ * Registers the location of a local DTD file or resource. By registering
+ * a local DTD, EJB descriptors can be parsed even when the remote servers
+ * which contain the "public" DTDs cannot be accessed.
+ *
+ * @param publicID The public DTD identifier found in an XML document.
+ * @param location The file or resource name for the appropriate DTD stored
+ * on the local machine.
+ */
+ public void registerDTD(String publicID, String location) {
+ handler.registerDTD(publicID, location);
+ }
+
+ /**
+ * May be used to specify the "home" directory for this iAS installation.
+ * The directory specified should typically be
+ * <code>[install-location]/iplanet/ias6/ias</code>.
+ *
+ * @param iasHomeDir The home directory for the user's iAS installation.
+ */
+ public void setIasHomeDir(File iasHomeDir) {
+ this.iasHomeDir = iasHomeDir;
+ }
+
+ /**
+ * Returns a Hashtable which contains a list of EJB class files processed by
+ * the ejbc utility (both "source" class files as well as stubs and
+ * skeletons). The key for the Hashtable is a String representing the path
+ * to the class file (relative to the destination directory). The value for
+ * the Hashtable is a File object which reference the actual class file.
+ *
+ * @return The list of EJB files processed by the ejbc utility.
+ */
+ public Hashtable getEjbFiles() {
+ return ejbFiles;
+ }
+
+ /**
+ * Returns the display-name element read from the standard EJB descriptor.
+ *
+ * @return The EJB-JAR display name.
+ */
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ /**
+ * Returns the list of CMP descriptors referenced in the EJB descriptors.
+ *
+ * @return An array of CMP descriptors.
+ */
+ public String[] getCmpDescriptors() {
+ List returnList = new ArrayList();
+
+ EjbInfo[] ejbs = handler.getEjbs();
+
+ for (int i = 0; i < ejbs.length; i++) {
+ List descriptors = (List) ejbs[i].getCmpDescriptors();
+ returnList.addAll(descriptors);
+ }
+
+ return (String[]) returnList.toArray(new String[returnList.size()]);
+ }
+
+ /**
+ * Main application method for the iPlanet Application Server ejbc utility.
+ * If the application is run with no commandline arguments, a usage
+ * statement is printed for the user.
+ *
+ * @param args The commandline arguments passed to the application.
+ */
+ public static void main(String[] args) {
+ File stdDescriptor;
+ File iasDescriptor;
+ File destDirectory = null;
+ String classpath = null;
+ SAXParser parser = null;
+ boolean debug = false;
+ boolean retainSource = false;
+ IPlanetEjbc ejbc;
+
+ if ((args.length < MIN_NUM_ARGS) || (args.length > MAX_NUM_ARGS)) {
+ usage();
+ return;
+ }
+
+ stdDescriptor = new File(args[args.length - 2]);
+ iasDescriptor = new File(args[args.length - 1]);
+
+ for (int i = 0; i < args.length - 2; i++) {
+ if (args[i].equals("-classpath")) {
+ classpath = args[++i];
+ } else if (args[i].equals("-d")) {
+ destDirectory = new File(args[++i]);
+ } else if (args[i].equals("-debug")) {
+ debug = true;
+ } else if (args[i].equals("-keepsource")) {
+ retainSource = true;
+ } else {
+ usage();
+ return;
+ }
+ }
+
+ /* If the -classpath flag isn't specified, use the system classpath */
+ if (classpath == null) {
+ Properties props = System.getProperties();
+ classpath = props.getProperty("java.class.path");
+ }
+
+ /*
+ * If the -d flag isn't specified, use the working directory as the
+ * destination directory
+ */
+ if (destDirectory == null) {
+ Properties props = System.getProperties();
+ destDirectory = new File(props.getProperty("user.dir"));
+ }
+
+ /* Construct a SAXParser used to process the descriptors */
+ SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+ parserFactory.setValidating(true);
+ try {
+ parser = parserFactory.newSAXParser();
+ } catch (Exception e) {
+ // SAXException or ParserConfigurationException may be thrown
+ System.out.println("An exception was generated while trying to ");
+ System.out.println("create a new SAXParser.");
+ e.printStackTrace();
+ return;
+ }
+
+ /* Build and populate an instance of the ejbc utility */
+ ejbc = new IPlanetEjbc(stdDescriptor, iasDescriptor, destDirectory,
+ classpath, parser);
+ ejbc.setDebugOutput(debug);
+ ejbc.setRetainSource(retainSource);
+
+ /* Execute the ejbc utility -- stubs/skeletons are rebuilt, if needed */
+ try {
+ ejbc.execute();
+ } catch (IOException e) {
+ System.out.println("An IOException has occurred while reading the "
+ + "XML descriptors (" + e.getMessage() + ").");
+ return;
+ } catch (SAXException e) {
+ System.out.println("A SAXException has occurred while reading the "
+ + "XML descriptors (" + e.getMessage() + ").");
+ return;
+ } catch (IPlanetEjbc.EjbcException e) {
+ System.out.println("An error has occurred while executing the ejbc "
+ + "utility (" + e.getMessage() + ").");
+ return;
+ }
+ }
+
+ /**
+ * Print a usage statement.
+ */
+ private static void usage() {
+ System.out.println("java org.apache.tools.ant.taskdefs.optional.ejb.IPlanetEjbc \\");
+ System.out.println(" [OPTIONS] [EJB 1.1 descriptor] [iAS EJB descriptor]");
+ System.out.println("");
+ System.out.println("Where OPTIONS are:");
+ System.out.println(" -debug -- for additional debugging output");
+ System.out.println(" -keepsource -- to retain Java source files generated");
+ System.out.println(" -classpath [classpath] -- classpath used for compilation");
+ System.out.println(" -d [destination directory] -- directory for compiled classes");
+ System.out.println("");
+ System.out.println("If a classpath is not specified, the system classpath");
+ System.out.println("will be used. If a destination directory is not specified,");
+ System.out.println("the current working directory will be used (classes will");
+ System.out.println("still be placed in subfolders which correspond to their");
+ System.out.println("package name).");
+ System.out.println("");
+ System.out.println("The EJB home interface, remote interface, and implementation");
+ System.out.println("class must be found in the destination directory. In");
+ System.out.println("addition, the destination will look for the stubs and skeletons");
+ System.out.println("in the destination directory to ensure they are up to date.");
+ }
+
+ /**
+ * Compiles the stub and skeletons for the specified EJBs, if they need to
+ * be updated.
+ *
+ * @throws EjbcException If the ejbc utility cannot be correctly configured
+ * or if one or more of the EJB "source" classes
+ * cannot be found in the destination directory
+ * @throws IOException If the parser encounters a problem reading the XML
+ * file
+ * @throws SAXException If the parser encounters a problem processing the
+ * XML descriptor (it may wrap another exception)
+ */
+ public void execute() throws EjbcException, IOException, SAXException {
+
+ checkConfiguration(); // Throws EjbcException if unsuccessful
+
+ EjbInfo[] ejbs = getEjbs(); // Returns list of EJBs for processing
+
+ for (int i = 0; i < ejbs.length; i++) {
+ log("EJBInfo...");
+ log(ejbs[i].toString());
+ }
+
+ for (int i = 0; i < ejbs.length; i++) {
+ EjbInfo ejb = ejbs[i];
+
+ ejb.checkConfiguration(destDirectory); // Throws EjbcException
+
+ if (ejb.mustBeRecompiled(destDirectory)) {
+ log(ejb.getName() + " must be recompiled using ejbc.");
+
+ String[] arguments = buildArgumentList(ejb);
+ callEjbc(arguments);
+
+ } else {
+ log(ejb.getName() + " is up to date.");
+ }
+ }
+ }
+
+ /**
+ * Executes the iPlanet Application Server ejbc command-line utility.
+ *
+ * @param arguments Command line arguments to be passed to the ejbc utility.
+ */
+ private void callEjbc(String[] arguments) {
+
+ /* Concatenate all of the command line arguments into a single String */
+ StringBuffer args = new StringBuffer();
+ for (int i = 0; i < arguments.length; i++) {
+ args.append(arguments[i]).append(" ");
+ }
+
+ /* If an iAS home directory is specified, prepend it to the commmand */
+ String command;
+ if (iasHomeDir == null) {
+ command = "";
+ } else {
+ command = iasHomeDir.toString() + File.separator + "bin"
+ + File.separator;
+ }
+ command += "ejbc ";
+
+ log(command + args);
+
+ /*
+ * Use the Runtime object to execute an external command. Use the
+ * RedirectOutput inner class to direct the standard and error output
+ * from the command to the JRE's standard output
+ */
+ try {
+ Process p = Runtime.getRuntime().exec(command + args);
+ RedirectOutput output = new RedirectOutput(p.getInputStream());
+ RedirectOutput error = new RedirectOutput(p.getErrorStream());
+ output.start();
+ error.start();
+ p.waitFor();
+ p.destroy();
+ } catch (IOException e) {
+ log("An IOException has occurred while trying to execute ejbc.");
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ // Do nothing
+ }
+ }
+
+ /**
+ * Verifies that the user selections are valid.
+ *
+ * @throws EjbcException If the user selections are invalid.
+ */
+ protected void checkConfiguration() throws EjbcException {
+
+ String msg = "";
+
+ if (stdDescriptor == null) {
+ msg += "A standard XML descriptor file must be specified. ";
+ }
+ if (iasDescriptor == null) {
+ msg += "An iAS-specific XML descriptor file must be specified. ";
+ }
+ if (classpath == null) {
+ msg += "A classpath must be specified. ";
+ }
+ if (parser == null) {
+ msg += "An XML parser must be specified. ";
+ }
+
+ if (destDirectory == null) {
+ msg += "A destination directory must be specified. ";
+ } else if (!destDirectory.exists()) {
+ msg += "The destination directory specified does not exist. ";
+ } else if (!destDirectory.isDirectory()) {
+ msg += "The destination specified is not a directory. ";
+ }
+
+ if (msg.length() > 0) {
+ throw new EjbcException(msg);
+ }
+ }
+
+ /**
+ * Parses the EJB descriptors and returns a list of EJBs which may need to
+ * be compiled.
+ *
+ * @return An array of objects which describe the EJBs to be
+ * processed.
+ * @throws IOException If the parser encounters a problem reading the XML
+ * files
+ * @throws SAXException If the parser encounters a problem processing the
+ * XML descriptor (it may wrap another exception)
+ */
+ private EjbInfo[] getEjbs() throws IOException, SAXException {
+ EjbInfo[] ejbs = null;
+
+ /*
+ * The EJB information is gathered from the standard XML EJB descriptor
+ * and the iAS-specific XML EJB descriptor using a SAX parser.
+ */
+
+ parser.parse(stdDescriptor, handler);
+ parser.parse(iasDescriptor, handler);
+ ejbs = handler.getEjbs();
+
+ return ejbs;
+ }
+
+ /**
+ * Based on this object's instance variables as well as the EJB to be
+ * processed, the correct flags and parameters are set for the ejbc
+ * command-line utility.
+ * @param ejb The EJB for which stubs and skeletons will be compiled.
+ * @return An array of Strings which are the command-line parameters for
+ * for the ejbc utility.
+ */
+ private String[] buildArgumentList(EjbInfo ejb) {
+
+ List arguments = new ArrayList();
+
+ /* OPTIONAL COMMAND LINE PARAMETERS */
+
+ if (debugOutput) {
+ arguments.add("-debug");
+ }
+
+ /* No beantype flag is needed for an entity bean */
+ if (ejb.getBeantype().equals(STATELESS_SESSION)) {
+ arguments.add("-sl");
+ } else if (ejb.getBeantype().equals(STATEFUL_SESSION)) {
+ arguments.add("-sf");
+ }
+
+ if (ejb.getIiop()) {
+ arguments.add("-iiop");
+ }
+
+ if (ejb.getCmp()) {
+ arguments.add("-cmp");
+ }
+
+ if (retainSource) {
+ arguments.add("-gs");
+ }
+
+ if (ejb.getHasession()) {
+ arguments.add("-fo");
+ }
+
+ /* REQUIRED COMMAND LINE PARAMETERS */
+
+ arguments.add("-classpath");
+ arguments.add(classpath);
+
+ arguments.add("-d");
+ arguments.add(destDirectory.toString());
+
+ arguments.add(ejb.getHome().getQualifiedClassName());
+ arguments.add(ejb.getRemote().getQualifiedClassName());
+ arguments.add(ejb.getImplementation().getQualifiedClassName());
+
+ /* Convert the List into an Array and return it */
+ return (String[]) arguments.toArray(new String[arguments.size()]);
+ }
+
+ /**
+ * Convenience method used to print messages to the user if debugging
+ * messages are enabled.
+ *
+ * @param msg The String to print to standard output.
+ */
+ private void log(String msg) {
+ if (debugOutput) {
+ System.out.println(msg);
+ }
+ }
+
+
+ /* Inner classes follow */
+
+
+ /**
+ * This inner class is used to signal any problems during the execution of
+ * the ejbc compiler.
+ *
+ */
+ public class EjbcException extends Exception {
+
+ /**
+ * Constructs an exception with the given descriptive message.
+ *
+ * @param msg Description of the exception which has occurred.
+ */
+ public EjbcException(String msg) {
+ super(msg);
+ }
+ } // End of EjbcException inner class
+
+
+ /**
+ * This inner class is an XML document handler that can be used to parse EJB
+ * descriptors (both the standard EJB descriptor as well as the iAS-specific
+ * descriptor that stores additional values for iAS). Once the descriptors
+ * have been processed, the list of EJBs found can be obtained by calling
+ * the <code>getEjbs()</code> method.
+ *
+ * @see IPlanetEjbc.EjbInfo
+ */
+ private class EjbcHandler extends HandlerBase {
+ /** EJB 1.1 ID */
+ private static final String PUBLICID_EJB11 =
+ "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN";
+ /** IPlanet ID */
+ private static final String PUBLICID_IPLANET_EJB_60 =
+ "-//Sun Microsystems, Inc.//DTD iAS Enterprise JavaBeans 1.0//EN";
+ /** EJB 1.1 location */
+ private static final String DEFAULT_IAS60_EJB11_DTD_LOCATION =
+ "ejb-jar_1_1.dtd";
+ /** IAS60 location */
+ private static final String DEFAULT_IAS60_DTD_LOCATION =
+ "IASEjb_jar_1_0.dtd";
+
+ /*
+ * Two Maps are used to track local DTDs that will be used in case the
+ * remote copies of these DTDs cannot be accessed. The key for the Map
+ * is the DTDs public ID and the value is the local location for the DTD
+ */
+ private Map resourceDtds = new HashMap();
+ private Map fileDtds = new HashMap();
+
+ private Map ejbs = new HashMap(); // List of EJBs found in XML
+ private EjbInfo currentEjb; // One item within the Map
+ private boolean iasDescriptor = false; // Is doc iAS or EJB descriptor
+
+ private String currentLoc = ""; // Tracks current element
+ private String currentText; // Tracks current text data
+ private String ejbType; // "session" or "entity"
+
+ /**
+ * Constructs a new instance of the handler and registers local copies
+ * of the standard EJB 1.1 descriptor DTD as well as iAS's EJB
+ * descriptor DTD.
+ */
+ public EjbcHandler() {
+ registerDTD(PUBLICID_EJB11, DEFAULT_IAS60_EJB11_DTD_LOCATION);
+ registerDTD(PUBLICID_IPLANET_EJB_60, DEFAULT_IAS60_DTD_LOCATION);
+ }
+
+ /**
+ * Returns the list of EJB objects found during the processing of the
+ * standard EJB 1.1 descriptor and iAS-specific EJB descriptor.
+ *
+ * @return An array of EJBs which were found during the descriptor
+ * parsing.
+ */
+ public EjbInfo[] getEjbs() {
+ return (EjbInfo[]) ejbs.values().toArray(new EjbInfo[ejbs.size()]);
+ }
+
+ /**
+ * Returns the value of the display-name element found in the standard
+ * EJB 1.1 descriptor.
+ *
+ * @return String display-name value.
+ */
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ /**
+ * Registers a local DTD that will be used when parsing an EJB
+ * descriptor. When the DTD's public identifier is found in an XML
+ * document, the parser will reference the local DTD rather than the
+ * remote DTD. This enables XML documents to be processed even when the
+ * public DTD isn't available.
+ *
+ * @param publicID The DTD's public identifier.
+ * @param location The location of the local DTD copy -- the location
+ * may either be a resource found on the classpath or a
+ * local file.
+ */
+ public void registerDTD(String publicID, String location) {
+ log("Registering: " + location);
+ if ((publicID == null) || (location == null)) {
+ return;
+ }
+
+ if (ClassLoader.getSystemResource(location) != null) {
+ log("Found resource: " + location);
+ resourceDtds.put(publicID, location);
+ } else {
+ File dtdFile = new File(location);
+ if (dtdFile.exists() && dtdFile.isFile()) {
+ log("Found file: " + location);
+ fileDtds.put(publicID, location);
+ }
+ }
+ }
+
+ /**
+ * Resolves an external entity found during XML processing. If a public
+ * ID is found that has been registered with the handler, an <code>
+ * InputSource</code> will be returned which refers to the local copy.
+ * If the public ID hasn't been registered or if an error occurs, the
+ * superclass implementation is used.
+ *
+ * @param publicId The DTD's public identifier.
+ * @param systemId The location of the DTD, as found in the XML document.
+ */
+ public InputSource resolveEntity(String publicId, String systemId)
+ throws SAXException {
+ InputStream inputStream = null;
+
+
+ try {
+
+ /* Search the resource Map and (if not found) file Map */
+
+ String location = (String) resourceDtds.get(publicId);
+ if (location != null) {
+ inputStream
+ = ClassLoader.getSystemResource(location).openStream();
+ } else {
+ location = (String) fileDtds.get(publicId);
+ if (location != null) {
+ inputStream = new FileInputStream(location);
+ }
+ }
+ } catch (IOException e) {
+ return super.resolveEntity(publicId, systemId);
+ }
+
+ if (inputStream == null) {
+ return super.resolveEntity(publicId, systemId);
+ } else {
+ return new InputSource(inputStream);
+ }
+ }
+
+ /**
+ * Receive notification that the start of an XML element has been found.
+ *
+ * @param name String name of the element found.
+ * @param atts AttributeList of the attributes included with the element
+ * (if any).
+ * @throws SAXException If the parser cannot process the document.
+ */
+ public void startElement(String name, AttributeList atts)
+ throws SAXException {
+
+ /*
+ * I need to "push" the element onto the String (currentLoc) which
+ * always represents the current location in the XML document.
+ */
+ currentLoc += "\\" + name;
+
+ /* A new element has started, so reset the text being captured */
+ currentText = "";
+
+ if (currentLoc.equals("\\ejb-jar")) {
+ iasDescriptor = false;
+ } else if (currentLoc.equals("\\ias-ejb-jar")) {
+ iasDescriptor = true;
+ }
+
+ if ((name.equals("session")) || (name.equals("entity"))) {
+ ejbType = name;
+ }
+ }
+
+ /**
+ * Receive notification that character data has been found in the XML
+ * document
+ *
+ * @param ch Array of characters which have been found in the document.
+ * @param start Starting index of the data found in the document.
+ * @param len The number of characters found in the document.
+ * @throws SAXException If the parser cannot process the document.
+ */
+ public void characters(char[] ch, int start, int len)
+ throws SAXException {
+
+ currentText += new String(ch).substring(start, start + len);
+ }
+
+ /**
+ * Receive notification that the end of an XML element has been found.
+ *
+ * @param name String name of the element.
+ * @throws SAXException If the parser cannot process the document.
+ */
+ public void endElement(String name) throws SAXException {
+
+ /*
+ * If this is a standard EJB 1.1 descriptor, we are looking for one
+ * set of data, while if this is an iAS-specific descriptor, we're
+ * looking for different set of data. Hand the processing off to
+ * the appropriate method.
+ */
+ if (iasDescriptor) {
+ iasCharacters(currentText);
+ } else {
+ stdCharacters(currentText);
+ }
+
+ /*
+ * I need to "pop" the element off the String (currentLoc) which
+ * always represents my current location in the XML document.
+ */
+
+ int nameLength = name.length() + 1; // Add one for the "\"
+ int locLength = currentLoc.length();
+
+ currentLoc = currentLoc.substring(0, locLength - nameLength);
+ }
+
+ /**
+ * Receive notification that character data has been found in a standard
+ * EJB 1.1 descriptor. We're interested in retrieving the home
+ * interface, remote interface, implementation class, the type of bean,
+ * and if the bean uses CMP.
+ *
+ * @param value String data found in the XML document.
+ */
+ private void stdCharacters(String value) {
+
+ if (currentLoc.equals("\\ejb-jar\\display-name")) {
+ displayName = value;
+ return;
+ }
+
+ String base = "\\ejb-jar\\enterprise-beans\\" + ejbType;
+
+ if (currentLoc.equals(base + "\\ejb-name")) {
+ currentEjb = (EjbInfo) ejbs.get(value);
+ if (currentEjb == null) {
+ currentEjb = new EjbInfo(value);
+ ejbs.put(value, currentEjb);
+ }
+ } else if (currentLoc.equals(base + "\\home")) {
+ currentEjb.setHome(value);
+ } else if (currentLoc.equals(base + "\\remote")) {
+ currentEjb.setRemote(value);
+ } else if (currentLoc.equals(base + "\\ejb-class")) {
+ currentEjb.setImplementation(value);
+ } else if (currentLoc.equals(base + "\\prim-key-class")) {
+ currentEjb.setPrimaryKey(value);
+ } else if (currentLoc.equals(base + "\\session-type")) {
+ currentEjb.setBeantype(value);
+ } else if (currentLoc.equals(base + "\\persistence-type")) {
+ currentEjb.setCmp(value);
+ }
+ }
+
+ /**
+ * Receive notification that character data has been found in an
+ * iAS-specific descriptor. We're interested in retrieving data
+ * indicating whether the bean must support RMI/IIOP access, whether
+ * the bean must provide highly available stubs and skeletons (in the
+ * case of stateful session beans), and if this bean uses additional
+ * CMP XML descriptors (in the case of entity beans with CMP).
+ *
+ * @param value String data found in the XML document.
+ */
+ private void iasCharacters(String value) {
+ String base = "\\ias-ejb-jar\\enterprise-beans\\" + ejbType;
+
+ if (currentLoc.equals(base + "\\ejb-name")) {
+ currentEjb = (EjbInfo) ejbs.get(value);
+ if (currentEjb == null) {
+ currentEjb = new EjbInfo(value);
+ ejbs.put(value, currentEjb);
+ }
+ } else if (currentLoc.equals(base + "\\iiop")) {
+ currentEjb.setIiop(value);
+ } else if (currentLoc.equals(base + "\\failover-required")) {
+ currentEjb.setHasession(value);
+ } else if (currentLoc.equals(base + "\\persistence-manager"
+ + "\\properties-file-location")) {
+ currentEjb.addCmpDescriptor(value);
+ }
+ }
+ } // End of EjbcHandler inner class
+
+
+ /**
+ * This inner class represents an EJB that will be compiled using ejbc.
+ *
+ */
+ private class EjbInfo {
+ private String name; // EJB's display name
+ private Classname home; // EJB's home interface name
+ private Classname remote; // EJB's remote interface name
+ private Classname implementation; // EJB's implementation class
+ private Classname primaryKey; // EJB's primary key class
+ private String beantype = "entity"; // or "stateful" or "stateless"
+ private boolean cmp = false; // Does this EJB support CMP?
+ private boolean iiop = false; // Does this EJB support IIOP?
+ private boolean hasession = false; // Does this EJB require failover?
+ private List cmpDescriptors = new ArrayList(); // CMP descriptor list
+
+ /**
+ * Construct a new EJBInfo object with the given name.
+ *
+ * @param name The display name for the EJB.
+ */
+ public EjbInfo(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the display name of the EJB. If a display name has not been
+ * set, it returns the EJB implementation classname (if the
+ * implementation class is not set, it returns "[unnamed]").
+ *
+ * @return The display name for the EJB.
+ */
+ public String getName() {
+ if (name == null) {
+ if (implementation == null) {
+ return "[unnamed]";
+ } else {
+ return implementation.getClassName();
+ }
+ }
+ return name;
+ }
+
+ /*
+ * Below are getter's and setter's for each of the instance variables.
+ * Note that (in addition to supporting setters with the same type as
+ * the instance variable) a setter is provided with takes a String
+ * argument -- this are provided so the XML document handler can set
+ * the EJB values using the Strings it parses.
+ */
+
+ public void setHome(String home) {
+ setHome(new Classname(home));
+ }
+
+ public void setHome(Classname home) {
+ this.home = home;
+ }
+
+ public Classname getHome() {
+ return home;
+ }
+
+ public void setRemote(String remote) {
+ setRemote(new Classname(remote));
+ }
+
+ public void setRemote(Classname remote) {
+ this.remote = remote;
+ }
+
+ public Classname getRemote() {
+ return remote;
+ }
+
+ public void setImplementation(String implementation) {
+ setImplementation(new Classname(implementation));
+ }
+
+ public void setImplementation(Classname implementation) {
+ this.implementation = implementation;
+ }
+
+ public Classname getImplementation() {
+ return implementation;
+ }
+
+ public void setPrimaryKey(String primaryKey) {
+ setPrimaryKey(new Classname(primaryKey));
+ }
+
+ public void setPrimaryKey(Classname primaryKey) {
+ this.primaryKey = primaryKey;
+ }
+
+ public Classname getPrimaryKey() {
+ return primaryKey;
+ }
+
+ public void setBeantype(String beantype) {
+ this.beantype = beantype.toLowerCase();
+ }
+
+ public String getBeantype() {
+ return beantype;
+ }
+
+ public void setCmp(boolean cmp) {
+ this.cmp = cmp;
+ }
+
+ public void setCmp(String cmp) {
+ setCmp(cmp.equals("Container"));
+ }
+
+ public boolean getCmp() {
+ return cmp;
+ }
+
+ public void setIiop(boolean iiop) {
+ this.iiop = iiop;
+ }
+
+ public void setIiop(String iiop) {
+ setIiop(iiop.equals("true"));
+ }
+
+ public boolean getIiop() {
+ return iiop;
+ }
+
+ public void setHasession(boolean hasession) {
+ this.hasession = hasession;
+ }
+
+ public void setHasession(String hasession) {
+ setHasession(hasession.equals("true"));
+ }
+
+ public boolean getHasession() {
+ return hasession;
+ }
+
+ public void addCmpDescriptor(String descriptor) {
+ cmpDescriptors.add(descriptor);
+ }
+
+ public List getCmpDescriptors() {
+ return cmpDescriptors;
+ }
+
+ /**
+ * Verifies that the EJB is valid--if it is invalid, an exception is
+ * thrown
+ *
+ *
+ * @param buildDir The directory where the EJB remote interface, home
+ * interface, and implementation class must be found.
+ * @throws EjbcException If the EJB is invalid.
+ */
+ private void checkConfiguration(File buildDir) throws EjbcException {
+
+ /* Check that the specified instance variables are valid */
+ if (home == null) {
+ throw new EjbcException("A home interface was not found "
+ + "for the " + name + " EJB.");
+ }
+ if (remote == null) {
+ throw new EjbcException("A remote interface was not found "
+ + "for the " + name + " EJB.");
+ }
+ if (implementation == null) {
+ throw new EjbcException("An EJB implementation class was not "
+ + "found for the " + name + " EJB.");
+ }
+
+ if ((!beantype.equals(ENTITY_BEAN))
+ && (!beantype.equals(STATELESS_SESSION))
+ && (!beantype.equals(STATEFUL_SESSION))) {
+ throw new EjbcException("The beantype found (" + beantype + ") "
+ + "isn't valid in the " + name + " EJB.");
+ }
+
+ if (cmp && (!beantype.equals(ENTITY_BEAN))) {
+ System.out.println("CMP stubs and skeletons may not be generated"
+ + " for a Session Bean -- the \"cmp\" attribute will be"
+ + " ignoredfor the " + name + " EJB.");
+ }
+
+ if (hasession && (!beantype.equals(STATEFUL_SESSION))) {
+ System.out.println("Highly available stubs and skeletons may "
+ + "only be generated for a Stateful Session Bean -- the "
+ + "\"hasession\" attribute will be ignored for the "
+ + name + " EJB.");
+ }
+
+ /* Check that the EJB "source" classes all exist */
+ if (!remote.getClassFile(buildDir).exists()) {
+ throw new EjbcException("The remote interface "
+ + remote.getQualifiedClassName() + " could not be "
+ + "found.");
+ }
+ if (!home.getClassFile(buildDir).exists()) {
+ throw new EjbcException("The home interface "
+ + home.getQualifiedClassName() + " could not be "
+ + "found.");
+ }
+ if (!implementation.getClassFile(buildDir).exists()) {
+ throw new EjbcException("The EJB implementation class "
+ + implementation.getQualifiedClassName() + " could "
+ + "not be found.");
+ }
+ }
+
+ /**
+ * Determines if the ejbc utility needs to be run or not. If the stubs
+ * and skeletons can all be found in the destination directory AND all
+ * of their timestamps are more recent than the EJB source classes
+ * (home, remote, and implementation classes), the method returns
+ * <code>false</code>. Otherwise, the method returns <code>true</code>.
+ *
+ * @param destDir The directory where the EJB source classes, stubs and
+ * skeletons are located.
+ * @return A boolean indicating whether or not the ejbc utility needs to
+ * be run to bring the stubs and skeletons up to date.
+ */
+ public boolean mustBeRecompiled(File destDir) {
+
+ long sourceModified = sourceClassesModified(destDir);
+
+ long destModified = destClassesModified(destDir);
+
+ return (destModified < sourceModified);
+ }
+
+ /**
+ * Examines each of the EJB source classes (home, remote, and
+ * implementation) and returns the modification timestamp for the
+ * "oldest" class.
+ *
+ * @param classpath The classpath to be used to find the source EJB
+ * classes. If <code>null</code>, the system classpath
+ * is used.
+ * @return The modification timestamp for the "oldest" EJB source class.
+ * @throws BuildException If one of the EJB source classes cannot be
+ * found on the classpath.
+ */
+ private long sourceClassesModified(File buildDir) {
+ long latestModified; // The timestamp of the "newest" class
+ long modified; // Timestamp for a given class
+ File remoteFile; // File for the remote interface class
+ File homeFile; // File for the home interface class
+ File implFile; // File for the EJB implementation class
+ File pkFile; // File for the EJB primary key class
+
+ /* Check the timestamp on the remote interface */
+ remoteFile = remote.getClassFile(buildDir);
+ modified = remoteFile.lastModified();
+ if (modified == -1) {
+ System.out.println("The class "
+ + remote.getQualifiedClassName() + " couldn't "
+ + "be found on the classpath");
+ return -1;
+ }
+ latestModified = modified;
+
+ /* Check the timestamp on the home interface */
+ homeFile = home.getClassFile(buildDir);
+ modified = homeFile.lastModified();
+ if (modified == -1) {
+ System.out.println("The class "
+ + home.getQualifiedClassName() + " couldn't be "
+ + "found on the classpath");
+ return -1;
+ }
+ latestModified = Math.max(latestModified, modified);
+
+ /* Check the timestamp of the primary key class */
+ if (primaryKey != null) {
+ pkFile = primaryKey.getClassFile(buildDir);
+ modified = pkFile.lastModified();
+ if (modified == -1) {
+ System.out.println("The class "
+ + primaryKey.getQualifiedClassName() + "couldn't be "
+ + "found on the classpath");
+ return -1;
+ }
+ latestModified = Math.max(latestModified, modified);
+ } else {
+ pkFile = null;
+ }
+
+ /* Check the timestamp on the EJB implementation class.
+ *
+ * Note that if ONLY the implementation class has changed, it's not
+ * necessary to rebuild the EJB stubs and skeletons. For this
+ * reason, we ensure the file exists (using lastModified above), but
+ * we DON'T compare it's timestamp with the timestamps of the home
+ * and remote interfaces (because it's irrelevant in determining if
+ * ejbc must be run)
+ */
+ implFile = implementation.getClassFile(buildDir);
+ modified = implFile.lastModified();
+ if (modified == -1) {
+ System.out.println("The class "
+ + implementation.getQualifiedClassName()
+ + " couldn't be found on the classpath");
+ return -1;
+ }
+
+ String pathToFile = remote.getQualifiedClassName();
+ pathToFile = pathToFile.replace('.', File.separatorChar) + ".class";
+ ejbFiles.put(pathToFile, remoteFile);
+
+ pathToFile = home.getQualifiedClassName();
+ pathToFile = pathToFile.replace('.', File.separatorChar) + ".class";
+ ejbFiles.put(pathToFile, homeFile);
+
+ pathToFile = implementation.getQualifiedClassName();
+ pathToFile = pathToFile.replace('.', File.separatorChar) + ".class";
+ ejbFiles.put(pathToFile, implFile);
+
+ if (pkFile != null) {
+ pathToFile = primaryKey.getQualifiedClassName();
+ pathToFile = pathToFile.replace('.', File.separatorChar) + ".class";
+ ejbFiles.put(pathToFile, pkFile);
+ }
+
+ return latestModified;
+ }
+
+ /**
+ * Examines each of the EJB stubs and skeletons in the destination
+ * directory and returns the modification timestamp for the "oldest"
+ * class. If one of the stubs or skeletons cannot be found, <code>-1
+ * </code> is returned.
+ *
+ * @param dest The directory in which the EJB stubs and skeletons are
+ * stored.
+ * @return The modification timestamp for the "oldest" EJB stub or
+ * skeleton. If one of the classes cannot be found, <code>-1
+ * </code> is returned.
+ * @throws BuildException If the canonical path of the destination
+ * directory cannot be found.
+ */
+ private long destClassesModified(File destDir) {
+ String[] classnames = classesToGenerate(); // List of all stubs & skels
+ long destClassesModified = new Date().getTime(); // Earliest mod time
+ boolean allClassesFound = true; // Has each been found?
+
+ /*
+ * Loop through each stub/skeleton class that must be generated, and
+ * determine (if all exist) which file has the most recent timestamp
+ */
+ for (int i = 0; i < classnames.length; i++) {
+
+ String pathToClass =
+ classnames[i].replace('.', File.separatorChar) + ".class";
+ File classFile = new File(destDir, pathToClass);
+
+ /*
+ * Add each stub/skeleton class to the list of EJB files. Note
+ * that each class is added even if it doesn't exist now.
+ */
+ ejbFiles.put(pathToClass, classFile);
+
+ allClassesFound = allClassesFound && classFile.exists();
+
+ if (allClassesFound) {
+ long fileMod = classFile.lastModified();
+
+ /* Keep track of the oldest modification timestamp */
+ destClassesModified = Math.min(destClassesModified, fileMod);
+ }
+ }
+
+ return (allClassesFound) ? destClassesModified : -1;
+ }
+
+ /**
+ * Builds an array of class names which represent the stubs and
+ * skeletons which need to be generated for a given EJB. The class
+ * names are fully qualified. Nine classes are generated for all EJBs
+ * while an additional six classes are generated for beans requiring
+ * RMI/IIOP access.
+ *
+ * @return An array of Strings representing the fully-qualified class
+ * names for the stubs and skeletons to be generated.
+ */
+ private String[] classesToGenerate() {
+ String[] classnames = (iiop)
+ ? new String[NUM_CLASSES_WITH_IIOP]
+ : new String[NUM_CLASSES_WITHOUT_IIOP];
+
+ final String remotePkg = remote.getPackageName() + ".";
+ final String remoteClass = remote.getClassName();
+ final String homePkg = home.getPackageName() + ".";
+ final String homeClass = home.getClassName();
+ final String implPkg = implementation.getPackageName() + ".";
+ final String implFullClass = implementation.getQualifiedWithUnderscores();
+ int index = 0;
+
+ classnames[index++] = implPkg + "ejb_fac_" + implFullClass;
+ classnames[index++] = implPkg + "ejb_home_" + implFullClass;
+ classnames[index++] = implPkg + "ejb_skel_" + implFullClass;
+ classnames[index++] = remotePkg + "ejb_kcp_skel_" + remoteClass;
+ classnames[index++] = homePkg + "ejb_kcp_skel_" + homeClass;
+ classnames[index++] = remotePkg + "ejb_kcp_stub_" + remoteClass;
+ classnames[index++] = homePkg + "ejb_kcp_stub_" + homeClass;
+ classnames[index++] = remotePkg + "ejb_stub_" + remoteClass;
+ classnames[index++] = homePkg + "ejb_stub_" + homeClass;
+
+ if (!iiop) {
+ return classnames;
+ }
+
+ classnames[index++] = "org.omg.stub." + remotePkg + "_"
+ + remoteClass + "_Stub";
+ classnames[index++] = "org.omg.stub." + homePkg + "_"
+ + homeClass + "_Stub";
+ classnames[index++] = "org.omg.stub." + remotePkg
+ + "_ejb_RmiCorbaBridge_"
+ + remoteClass + "_Tie";
+ classnames[index++] = "org.omg.stub." + homePkg
+ + "_ejb_RmiCorbaBridge_"
+ + homeClass + "_Tie";
+
+ classnames[index++] = remotePkg + "ejb_RmiCorbaBridge_"
+ + remoteClass;
+ classnames[index++] = homePkg + "ejb_RmiCorbaBridge_" + homeClass;
+
+ return classnames;
+ }
+
+ /**
+ * Convenience method which creates a String representation of all the
+ * instance variables of an EjbInfo object.
+ *
+ * @return A String representing the EjbInfo instance.
+ */
+ public String toString() {
+ String s = "EJB name: " + name
+ + "\n\r home: " + home
+ + "\n\r remote: " + remote
+ + "\n\r impl: " + implementation
+ + "\n\r primaryKey: " + primaryKey
+ + "\n\r beantype: " + beantype
+ + "\n\r cmp: " + cmp
+ + "\n\r iiop: " + iiop
+ + "\n\r hasession: " + hasession;
+
+ Iterator i = cmpDescriptors.iterator();
+ while (i.hasNext()) {
+ s += "\n\r CMP Descriptor: " + i.next();
+ }
+
+ return s;
+ }
+
+ } // End of EjbInfo inner class
+
+ /**
+ * Convenience class used to represent the fully qualified name of a Java
+ * class. It provides an easy way to retrieve components of the class name
+ * in a format that is convenient for building iAS stubs and skeletons.
+ *
+ */
+ private static class Classname {
+ private String qualifiedName; // Fully qualified name of the Java class
+ private String packageName; // Name of the package for this class
+ private String className; // Name of the class without the package
+
+ /**
+ * This constructor builds an object which represents the name of a Java
+ * class.
+ *
+ * @param qualifiedName String representing the fully qualified class
+ * name of the Java class.
+ */
+ public Classname(String qualifiedName) {
+ if (qualifiedName == null) {
+ return;
+ }
+
+ this.qualifiedName = qualifiedName;
+
+ int index = qualifiedName.lastIndexOf('.');
+ if (index == -1) {
+ className = qualifiedName;
+ packageName = "";
+ } else {
+ packageName = qualifiedName.substring(0, index);
+ className = qualifiedName.substring(index + 1);
+ }
+ }
+
+ /**
+ * Gets the fully qualified name of the Java class.
+ *
+ * @return String representing the fully qualified class name.
+ */
+ public String getQualifiedClassName() {
+ return qualifiedName;
+ }
+
+ /**
+ * Gets the package name for the Java class.
+ *
+ * @return String representing the package name for the class.
+ */
+ public String getPackageName() {
+ return packageName;
+ }
+
+ /**
+ * Gets the Java class name without the package structure.
+ *
+ * @return String representing the name for the class.
+ */
+ public String getClassName() {
+ return className;
+ }
+
+ /**
+ * Gets the fully qualified name of the Java class with underscores
+ * separating the components of the class name rather than periods.
+ * This format is used in naming some of the stub and skeleton classes
+ * for the iPlanet Application Server.
+ *
+ * @return String representing the fully qualified class name using
+ * underscores instead of periods.
+ */
+ public String getQualifiedWithUnderscores() {
+ return qualifiedName.replace('.', '_');
+ }
+
+ /**
+ * Returns a File which references the class relative to the specified
+ * directory. Note that the class file may or may not exist.
+ *
+ * @param directory A File referencing the base directory containing
+ * class files.
+ * @return File referencing this class.
+ */
+ public File getClassFile(File directory) {
+ String pathToFile = qualifiedName.replace('.', File.separatorChar)
+ + ".class";
+ return new File(directory, pathToFile);
+ }
+
+ /**
+ * String representation of this class name. It returns the fully
+ * qualified class name.
+ *
+ * @return String representing the fully qualified class name.
+ */
+ public String toString() {
+ return getQualifiedClassName();
+ }
+ } // End of Classname inner class
+
+
+ /**
+ * Thread class used to redirect output from an <code>InputStream</code> to
+ * the JRE standard output. This class may be used to redirect output from
+ * an external process to the standard output.
+ *
+ */
+ private static class RedirectOutput extends Thread {
+
+ private InputStream stream; // Stream to read and redirect to standard output
+
+ /**
+ * Constructs a new instance that will redirect output from the
+ * specified stream to the standard output.
+ *
+ * @param stream InputStream which will be read and redirected to the
+ * standard output.
+ */
+ public RedirectOutput(InputStream stream) {
+ this.stream = stream;
+ }
+
+ /**
+ * Reads text from the input stream and redirects it to standard output
+ * using a separate thread.
+ */
+ public void run() {
+ BufferedReader reader = new BufferedReader(
+ new InputStreamReader(stream));
+ String text;
+ try {
+ while ((text = reader.readLine()) != null) {
+ System.out.println(text);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ // Do nothing
+ }
+ }
+ }
+ } // End of RedirectOutput inner class
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbcTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbcTask.java
new file mode 100644
index 00000000..ee5dc854
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbcTask.java
@@ -0,0 +1,321 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.xml.sax.SAXException;
+
+/**
+ * Compiles EJB stubs and skeletons for the iPlanet Application Server.
+ * The EJBs to be processed are specified by the EJB 1.1 standard XML
+ * descriptor, and additional attributes are obtained from the iPlanet Application
+ * Server-specific XML descriptor. Since the XML descriptors can include
+ * multiple EJBs, this is a convenient way of specifying many EJBs in a single
+ * Ant task. The following attributes are allowed:
+ * <ul>
+ * <li><i>ejbdescriptor</i> -- Standard EJB 1.1 XML descriptor (typically
+ * titled "ejb-jar.xml"). This attribute is
+ * required.
+ * <li><i>iasdescriptor</i> -- EJB XML descriptor for iPlanet Application
+ * Server (typically titled "ias-ejb-jar.xml).
+ * This attribute is required.
+ * <li><i>dest</i> -- The is the base directory where the RMI stubs and
+ * skeletons are written. In addition, the class files
+ * for each bean (home interface, remote interface, and
+ * EJB implementation) must be found in this directory.
+ * This attribute is required.
+ * <li><i>classpath</i> -- The classpath used when generating EJB stubs and
+ * skeletons. This is an optional attribute (if
+ * omitted, the classpath specified when Ant was
+ * started will be used). Nested "classpath"
+ * elements may also be used.
+ * <li><i>keepgenerated</i> -- Indicates whether or not the Java source
+ * files which are generated by ejbc will be
+ * saved or automatically deleted. If "yes",
+ * the source files will be retained. This is
+ * an optional attribute (if omitted, it
+ * defaults to "no").
+ * <li><i>debug</i> -- Indicates whether or not the ejbc utility should
+ * log additional debugging statements to the standard
+ * output. If "yes", the additional debugging statements
+ * will be generated (if omitted, it defaults to "no").
+ * <li><i>iashome</i> -- May be used to specify the "home" directory for
+ * this iPlanet Application Server installation. This
+ * is used to find the ejbc utility if it isn't
+ * included in the user's system path. This is an
+ * optional attribute (if specified, it should refer
+ * to the <code>[install-location]/iplanet/ias6/ias
+ * </code> directory). If omitted, the ejbc utility
+ * must be on the user's system path.
+ * </ul>
+ * <p>
+ * For each EJB specified, this task will locate the three classes that comprise
+ * the EJB. If these class files cannot be located in the <code>dest</code>
+ * directory, the task will fail. The task will also attempt to locate the EJB
+ * stubs and skeletons in this directory. If found, the timestamps on the
+ * stubs and skeletons will be checked to ensure they are up to date. Only if
+ * these files cannot be found or if they are out of date will ejbc be called
+ * to generate new stubs and skeletons.
+ *
+ * @see IPlanetEjbc
+ *
+ * @ant.task name="iplanet-ejbc" category="ejb"
+ */
+public class IPlanetEjbcTask extends Task {
+
+ /* Attributes set by the Ant build file */
+ private File ejbdescriptor;
+ private File iasdescriptor;
+ private File dest;
+ private Path classpath;
+ private boolean keepgenerated = false;
+ private boolean debug = false;
+ private File iashome;
+
+ /**
+ * Sets the location of the standard XML EJB descriptor. Typically, this
+ * file is named "ejb-jar.xml".
+ *
+ * @param ejbdescriptor The name and location of the EJB descriptor.
+ */
+ public void setEjbdescriptor(File ejbdescriptor) {
+ this.ejbdescriptor = ejbdescriptor;
+ }
+
+ /**
+ * Sets the location of the iAS-specific XML EJB descriptor. Typically,
+ * this file is named "ias-ejb-jar.xml".
+ *
+ * @param iasdescriptor The name and location of the iAS-specific EJB
+ * descriptor.
+ */
+ public void setIasdescriptor (File iasdescriptor) {
+ this.iasdescriptor = iasdescriptor;
+ }
+
+ /**
+ * Sets the destination directory where the EJB source classes must exist
+ * and where the stubs and skeletons will be written. The destination
+ * directory must exist before this task is executed.
+ *
+ * @param dest The directory where the compiled classes will be written.
+ */
+ public void setDest(File dest) {
+ this.dest = dest;
+ }
+
+ /**
+ * Sets the classpath to be used when compiling the EJB stubs and skeletons.
+ *
+ * @param classpath The classpath to be used.
+ */
+ public void setClasspath(Path classpath) {
+ if (this.classpath == null) {
+ this.classpath = classpath;
+ } else {
+ this.classpath.append(classpath);
+ }
+ }
+
+ /**
+ * Adds to the classpath used when compiling the EJB stubs and skeletons.
+ * @return the class path.
+ */
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(getProject());
+ }
+ return classpath.createPath();
+ }
+
+ /**
+ * If true, the Java source files which are generated by ejbc will be saved .
+ *
+ * @param keepgenerated A boolean indicating if the Java source files for
+ * the stubs and skeletons should be retained.
+ */
+ public void setKeepgenerated(boolean keepgenerated) {
+ this.keepgenerated = keepgenerated;
+ }
+
+ /**
+ * If true, debugging output will be generated when ejbc is
+ * executed.
+ *
+ * @param debug A boolean indicating if debugging output should be generated
+ */
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ }
+
+ /**
+ * May be used to specify the "home" directory for this iAS installation.
+ * The directory specified should typically be
+ * <code>[install-location]/iplanet/ias6/ias</code>.
+ *
+ * @param iashome The home directory for the user's iAS installation.
+ */
+ public void setIashome(File iashome) {
+ this.iashome = iashome;
+ }
+
+ /**
+ * Does the work.
+ * @throws BuildException if there is a problem.
+ */
+ public void execute() throws BuildException {
+ checkConfiguration();
+
+ executeEjbc(getParser());
+ }
+
+ /**
+ * Verifies that the user selections are valid.
+ *
+ * @throws BuildException If the user selections are invalid.
+ */
+ private void checkConfiguration() throws BuildException {
+
+ if (ejbdescriptor == null) {
+ String msg = "The standard EJB descriptor must be specified using "
+ + "the \"ejbdescriptor\" attribute.";
+ throw new BuildException(msg, getLocation());
+ }
+ if ((!ejbdescriptor.exists()) || (!ejbdescriptor.isFile())) {
+ String msg = "The standard EJB descriptor (" + ejbdescriptor
+ + ") was not found or isn't a file.";
+ throw new BuildException(msg, getLocation());
+ }
+
+ if (iasdescriptor == null) {
+ String msg = "The iAS-speific XML descriptor must be specified using"
+ + " the \"iasdescriptor\" attribute.";
+ throw new BuildException(msg, getLocation());
+ }
+ if ((!iasdescriptor.exists()) || (!iasdescriptor.isFile())) {
+ String msg = "The iAS-specific XML descriptor (" + iasdescriptor
+ + ") was not found or isn't a file.";
+ throw new BuildException(msg, getLocation());
+ }
+
+ if (dest == null) {
+ String msg = "The destination directory must be specified using "
+ + "the \"dest\" attribute.";
+ throw new BuildException(msg, getLocation());
+ }
+ if ((!dest.exists()) || (!dest.isDirectory())) {
+ String msg = "The destination directory (" + dest + ") was not "
+ + "found or isn't a directory.";
+ throw new BuildException(msg, getLocation());
+ }
+
+ if ((iashome != null) && (!iashome.isDirectory())) {
+ String msg = "If \"iashome\" is specified, it must be a valid "
+ + "directory (it was set to " + iashome + ").";
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+ /**
+ * Returns a SAXParser that may be used to process the XML descriptors.
+ *
+ * @return Parser which may be used to process the EJB descriptors.
+ * @throws BuildException If the parser cannot be created or configured.
+ */
+ private SAXParser getParser() throws BuildException {
+
+ SAXParser saxParser = null;
+ try {
+ SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+ saxParserFactory.setValidating(true);
+ saxParser = saxParserFactory.newSAXParser();
+ } catch (SAXException e) {
+ String msg = "Unable to create a SAXParser: " + e.getMessage();
+ throw new BuildException(msg, e, getLocation());
+ } catch (ParserConfigurationException e) {
+ String msg = "Unable to create a SAXParser: " + e.getMessage();
+ throw new BuildException(msg, e, getLocation());
+ }
+
+ return saxParser;
+ }
+
+ /**
+ * Executes the EJBc utility using the SAXParser provided.
+ *
+ * @param saxParser SAXParser that may be used to process the EJB
+ * descriptors
+ * @throws BuildException If there is an error reading or parsing the XML
+ * descriptors
+ */
+ private void executeEjbc(SAXParser saxParser) throws BuildException {
+ IPlanetEjbc ejbc = new IPlanetEjbc(ejbdescriptor,
+ iasdescriptor,
+ dest,
+ getClasspath().toString(),
+ saxParser);
+ ejbc.setRetainSource(keepgenerated);
+ ejbc.setDebugOutput(debug);
+ if (iashome != null) {
+ ejbc.setIasHomeDir(iashome);
+ }
+
+ try {
+ ejbc.execute();
+ } catch (IOException e) {
+ String msg = "An IOException occurred while trying to read the XML "
+ + "descriptor file: " + e.getMessage();
+ throw new BuildException(msg, e, getLocation());
+ } catch (SAXException e) {
+ String msg = "A SAXException occurred while trying to read the XML "
+ + "descriptor file: " + e.getMessage();
+ throw new BuildException(msg, e, getLocation());
+ } catch (IPlanetEjbc.EjbcException e) {
+ String msg = "An exception occurred while trying to run the ejbc "
+ + "utility: " + e.getMessage();
+ throw new BuildException(msg, e, getLocation());
+ }
+ }
+
+ /**
+ * Returns the CLASSPATH to be used when calling EJBc. If no user CLASSPATH
+ * is specified, the System classpath is returned instead.
+ *
+ * @return Path The classpath to be used for EJBc.
+ */
+ private Path getClasspath() {
+ Path cp = null;
+ if (classpath == null) {
+ cp = (new Path(getProject())).concatSystemClasspath("last");
+ } else {
+ cp = classpath.concatSystemClasspath("ignore");
+ }
+
+ return cp;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/InnerClassFilenameFilter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/InnerClassFilenameFilter.java
new file mode 100644
index 00000000..e92d9879
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/InnerClassFilenameFilter.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.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.FilenameFilter;
+
+/**
+ * A filename filter for inner class files of a particular class.
+ */
+public class InnerClassFilenameFilter implements FilenameFilter {
+ private String baseClassName;
+
+ /**
+ * Constructor of filter.
+ * @param baseclass the class to filter inner classes on.
+ */
+ InnerClassFilenameFilter(String baseclass) {
+ int extidx = baseclass.lastIndexOf(".class");
+ if (extidx == -1) {
+ extidx = baseclass.length() - 1;
+ }
+ baseClassName = baseclass.substring(0, extidx);
+ }
+
+ /**
+ * Check if the file name passes the filter.
+ * @param dir not used.
+ * @param filename the filename to filter on.
+ * @return true if the filename is an inner class of the base class.
+ */
+ public boolean accept(File dir, String filename) {
+ if ((filename.lastIndexOf(".") != filename.lastIndexOf(".class"))
+ || (filename.indexOf(baseClassName + "$") != 0)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JbossDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JbossDeploymentTool.java
new file mode 100644
index 00000000..79f4574e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JbossDeploymentTool.java
@@ -0,0 +1,111 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.util.Hashtable;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * The deployment tool to add the jboss specific deployment descriptor to the ejb jar file.
+ * Jboss only requires one additional file jboss.xml and does not require any additional
+ * compilation.
+ *
+ * @version 1.0
+ * @see EjbJar#createJboss
+ */
+public class JbossDeploymentTool extends GenericDeploymentTool {
+ protected static final String JBOSS_DD = "jboss.xml";
+ protected static final String JBOSS_CMP10D = "jaws.xml";
+ protected static final String JBOSS_CMP20D = "jbosscmp-jdbc.xml";
+
+ /** Instance variable that stores the suffix for the jboss jarfile. */
+ private String jarSuffix = ".jar";
+
+ /**
+ * Setter used to store the suffix for the generated JBoss jar file.
+ * @param inString the string to use as the suffix.
+ */
+ public void setSuffix(String inString) {
+ jarSuffix = inString;
+ }
+
+ /**
+ * Add any vendor specific files which should be included in the
+ * EJB Jar.
+ * @param ejbFiles the hashtable of files to populate.
+ * @param ddPrefix the prefix to use.
+ */
+ protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+ File jbossDD = new File(getConfig().descriptorDir, ddPrefix + JBOSS_DD);
+ if (jbossDD.exists()) {
+ ejbFiles.put(META_DIR + JBOSS_DD, jbossDD);
+ } else {
+ log("Unable to locate jboss deployment descriptor. "
+ + "It was expected to be in " + jbossDD.getPath(),
+ Project.MSG_WARN);
+ return;
+ }
+ String descriptorFileName = JBOSS_CMP10D;
+ if (EjbJar.CMPVersion.CMP2_0.equals(getParent().getCmpversion())) {
+ descriptorFileName = JBOSS_CMP20D;
+ }
+ File jbossCMPD
+ = new File(getConfig().descriptorDir, ddPrefix + descriptorFileName);
+
+ if (jbossCMPD.exists()) {
+ ejbFiles.put(META_DIR + descriptorFileName, jbossCMPD);
+ } else {
+ log("Unable to locate jboss cmp descriptor. "
+ + "It was expected to be in "
+ + jbossCMPD.getPath(), Project.MSG_VERBOSE);
+ return;
+ }
+ }
+
+ /**
+ * Get the vendor specific name of the Jar that will be output. The modification date
+ * of this jar will be checked against the dependent bean classes.
+ */
+ File getVendorOutputJarFile(String baseName) {
+ if (getDestDir() == null && getParent().getDestdir() == null) {
+ throw new BuildException("DestDir not specified");
+ }
+ if (getDestDir() == null) {
+ return new File(getParent().getDestdir(), baseName + jarSuffix);
+ } else {
+ return new File(getDestDir(), baseName + jarSuffix);
+ }
+ }
+
+ /**
+ * Called to validate that the tool parameters have been configured.
+ *
+ * @throws BuildException If the Deployment Tool's configuration isn't
+ * valid
+ * @since ant 1.6
+ */
+ public void validateConfigured() throws BuildException {
+ }
+
+ private EjbJar getParent() {
+ return (EjbJar) this.getTask();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JonasDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JonasDeploymentTool.java
new file mode 100644
index 00000000..81fe8059
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JonasDeploymentTool.java
@@ -0,0 +1,835 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.xml.parsers.SAXParser;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * The deployment tool to add the jonas specific deployment descriptors to the
+ * ejb JAR file. JONAS only requires one additional file jonas-ejb-jar.xml.
+ *
+ * @version 1.0
+ * @see EjbJar#createJonas
+ */
+public class JonasDeploymentTool extends GenericDeploymentTool {
+
+ /** Public Id of the standard deployment descriptor DTD. */
+ protected static final String EJB_JAR_1_1_PUBLIC_ID
+ = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN";
+ protected static final String EJB_JAR_2_0_PUBLIC_ID
+ = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN";
+
+ /** Public Id of the JOnAS-specific deployment descriptor DTD. */
+ protected static final String JONAS_EJB_JAR_2_4_PUBLIC_ID
+ = "-//ObjectWeb//DTD JOnAS 2.4//EN";
+ protected static final String JONAS_EJB_JAR_2_5_PUBLIC_ID
+ = "-//ObjectWeb//DTD JOnAS 2.5//EN";
+
+ /** RMI ORB. */
+ protected static final String RMI_ORB = "RMI";
+
+ /** JEREMIE ORB. */
+ protected static final String JEREMIE_ORB = "JEREMIE";
+
+ /** DAVID ORB. */
+ protected static final String DAVID_ORB = "DAVID";
+
+ /**
+ * Name of the standard deployment descriptor DTD (these files are stored in
+ * the ${JONAS_ROOT}/xml directory).
+ */
+ protected static final String EJB_JAR_1_1_DTD = "ejb-jar_1_1.dtd";
+ protected static final String EJB_JAR_2_0_DTD = "ejb-jar_2_0.dtd";
+
+ /**
+ * Name of the JOnAS-specific deployment descriptor DTD (these files are
+ * stored in the ${JONAS_ROOT}/xml directory).
+ */
+ protected static final String JONAS_EJB_JAR_2_4_DTD
+ = "jonas-ejb-jar_2_4.dtd";
+ protected static final String JONAS_EJB_JAR_2_5_DTD
+ = "jonas-ejb-jar_2_5.dtd";
+
+ /** Default JOnAS deployment descriptor name. */
+ protected static final String JONAS_DD = "jonas-ejb-jar.xml";
+
+ /** GenIC class name (JOnAS 2.5) */
+ protected static final String GENIC_CLASS =
+ "org.objectweb.jonas_ejb.genic.GenIC";
+
+ /** Old GenIC class name (JOnAS 2.4.x). */
+ protected static final String OLD_GENIC_CLASS_1 =
+ "org.objectweb.jonas_ejb.tools.GenWholeIC";
+
+ /** Old GenIC class name. */
+ protected static final String OLD_GENIC_CLASS_2 =
+ "org.objectweb.jonas_ejb.tools.GenIC";
+
+ /**
+ * Filename of the standard EJB descriptor (which is passed to this class
+ * from the parent "ejbjar" task). This file is relative to the directory
+ * specified by the "srcdir" attribute in the ejbjar task.
+ */
+ private String descriptorName;
+
+ /**
+ * Filename of the JOnAS-specific EJB descriptor (which is passed to this
+ * class from the parent "ejbjar" task). This file is relative to the
+ * directory specified by the "srcdir" attribute in the ejbjar task.
+ */
+ private String jonasDescriptorName;
+
+ /* ------------- */
+ /* GenIC options */
+ /* ------------- */
+
+ /**
+ * Temporary output directory used by GenIC.
+ */
+ private File outputdir;
+
+ /**
+ * <code>true</code> if the intermediate Java source files generated by
+ * GenIC must be deleted or not. The default is <code>false</code>
+ */
+ private boolean keepgenerated = false;
+
+ /**
+ * <code>true</code> if the generated source files must not be compiled via
+ * the java and rmi compilers. The default is <code>false</code>.
+ */
+ private boolean nocompil = false;
+
+ /**
+ * <code>true</code> if the XML deployment descriptors must be parsed
+ * without validation. The default is <code>false</code>.
+ */
+ private boolean novalidation = false;
+
+ /**
+ * Java compiler to use. The default is the value of
+ * <code>build.compiler</code> property.
+ */
+ private String javac;
+
+ /** Options to pass to the java compiler. */
+ private String javacopts;
+
+ /** Options to pass to the rmi compiler. */
+ private String rmicopts;
+
+ /**
+ * Whether or not the RMI skeleton and stub must be modified to
+ * implement the implicit propagation of the security context (the
+ * transactional context is always provided). The default is
+ * <code>false</code>.
+ */
+ private boolean secpropag = false;
+
+ /**
+ * <code>true</code> if the GenIC call must be verbose. The default
+ * is <code>false</code>.
+ */
+ private boolean verbose = false;
+
+ /** Additional args to send to GenIC. */
+ private String additionalargs;
+
+ /* ------------- */
+ /* other options */
+ /* ------------- */
+
+ /** JOnAS root directory. */
+ private File jonasroot;
+
+ /**
+ * <code>true</code> if the generic JAR file used as input to GenIC must be
+ * retained. The default is <code>false</code>.
+ */
+ private boolean keepgeneric = false;
+
+ /** Stores the suffix for the JOnAS JAR file. The default is '.jar'. */
+ private String suffix = ".jar";
+
+ /**
+ * ORB to use (RMI, JEREMIE or DAVID). If omitted, it defaults to the one
+ * present in classpath. If specified, the corresponding JOnAS JAR is
+ * automatically added to the classpath.
+ */
+ private String orb;
+
+ /**
+ * <code>true</code> if GenIC must not be run on the EJB JAR.
+ * The default is <code>false</code>.
+ */
+ private boolean nogenic = false;
+
+ /* -------------------- */
+ /* GenIC options setter */
+ /* -------------------- */
+
+ /**
+ * Sets the <code>keepgenerated</code> flag.
+ *
+ * @param aBoolean <code>true</code> if the flag must be set.
+ */
+ public void setKeepgenerated(boolean aBoolean) {
+ keepgenerated = aBoolean;
+ }
+
+ /**
+ * Sets the additional arguments.
+ *
+ * @param aString additional args.
+ */
+ public void setAdditionalargs(String aString) {
+ additionalargs = aString;
+ }
+
+ /**
+ * Sets the <code>nocompil</code> flag.
+ *
+ * @param aBoolean <code>true</code> if the flag must be set.
+ */
+ public void setNocompil(boolean aBoolean) {
+ nocompil = aBoolean;
+ }
+
+ /**
+ * Sets the <code>novalidation</code> flag.
+ *
+ * @param aBoolean <code>true</code> if the flag must be set.
+ */
+ public void setNovalidation(boolean aBoolean) {
+ novalidation = aBoolean;
+ }
+
+ /**
+ * Sets the java compiler to use.
+ *
+ * @param aString the java compiler.
+ */
+ public void setJavac(String aString) {
+ javac = aString;
+ }
+
+ /**
+ * Set the options to pass to the java compiler.
+ *
+ * @param aString the options.
+ */
+ public void setJavacopts(String aString) {
+ javacopts = aString;
+ }
+
+ /**
+ * Set the options to pass to the rmi compiler.
+ *
+ * @param aString the options.
+ */
+ public void setRmicopts(String aString) {
+ rmicopts = aString;
+ }
+
+ /**
+ * Sets the <code>secpropag</code> flag.
+ *
+ * @param aBoolean <code>true</code> if the flag must be set.
+ */
+ public void setSecpropag(boolean aBoolean) {
+ secpropag = aBoolean;
+ }
+
+ /**
+ * Sets the <code>verbose</code> flag.
+ *
+ * @param aBoolean <code>true</code> if the flag must be set.
+ */
+ public void setVerbose(boolean aBoolean) {
+ verbose = aBoolean;
+ }
+
+ /* -------------------- */
+ /* other options setter */
+ /* -------------------- */
+
+ /**
+ * Set the JOnAS root directory.
+ *
+ * @param aFile the JOnAS root directory.
+ */
+ public void setJonasroot(File aFile) {
+ jonasroot = aFile;
+ }
+
+ /**
+ * Sets the <code>keepgeneric</code> flag.
+ *
+ * @param aBoolean <code>true</code> if the flag must be set.
+ */
+ public void setKeepgeneric(boolean aBoolean) {
+ keepgeneric = aBoolean;
+ }
+
+ /**
+ * Sets the jar suffix.
+ *
+ * @param aString the string to use as the suffix.
+ */
+ public void setJarsuffix(String aString) {
+ suffix = aString;
+ }
+
+ /**
+ * Sets the <code>orb</code> to construct classpath.
+ *
+ * @param aString 'RMI', 'JEREMIE', or 'DAVID'.
+ */
+ public void setOrb(String aString) {
+ orb = aString;
+ }
+
+ /**
+ * Sets the <code>nogenic</code> flag.
+ *
+ * @param aBoolean <code>true</code> if the flag must be set.
+ */
+ public void setNogenic(boolean aBoolean) {
+ nogenic = aBoolean;
+ }
+
+ /* ------------- */
+ /* other methods */
+ /* ------------- */
+
+ /** {@inheritDoc}. */
+ public void processDescriptor(String aDescriptorName, SAXParser saxParser) {
+
+ descriptorName = aDescriptorName;
+
+ log("JOnAS Deployment Tool processing: " + descriptorName,
+ Project.MSG_VERBOSE);
+
+ super.processDescriptor(descriptorName, saxParser);
+
+ if (outputdir != null) {
+ // the method deleteOnExit() do not work because the directory is not empty
+ log("Deleting temp output directory '" + outputdir + "'.", Project.MSG_VERBOSE);
+ deleteAllFiles(outputdir);
+ }
+ }
+
+ /** {@inheritDoc}. */
+ protected void writeJar(String baseName, File jarfile, Hashtable ejbFiles, String publicId)
+ throws BuildException {
+
+ // create the generic jar first
+ File genericJarFile = super.getVendorOutputJarFile(baseName);
+ super.writeJar(baseName, genericJarFile, ejbFiles, publicId);
+
+ // GenIC call on generic jar
+ addGenICGeneratedFiles(genericJarFile, ejbFiles);
+
+ // create the real jar
+ super.writeJar(baseName, getVendorOutputJarFile(baseName), ejbFiles, publicId);
+
+ if (!keepgeneric) {
+ log("Deleting generic JAR " + genericJarFile.toString(), Project.MSG_VERBOSE);
+ genericJarFile.delete();
+ }
+ }
+
+ /** {@inheritDoc}. */
+ protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+
+ // JOnAS-specific descriptor deployment
+ jonasDescriptorName = getJonasDescriptorName();
+ File jonasDD = new File(getConfig().descriptorDir, jonasDescriptorName);
+
+ if (jonasDD.exists()) {
+ ejbFiles.put(META_DIR + JONAS_DD, jonasDD);
+ } else {
+ log("Unable to locate the JOnAS deployment descriptor. It was expected to be in: "
+ + jonasDD.getPath() + ".", Project.MSG_WARN);
+ }
+ }
+
+ /** {@inheritDoc}. */
+ protected File getVendorOutputJarFile(String baseName) {
+ return new File(getDestDir(), baseName + suffix);
+ }
+
+ /**
+ * Determines the name of the JOnAS-specific EJB descriptor using the
+ * specified standard EJB descriptor name. In general, the standard
+ * descriptor will be named "[basename]-ejb-jar.xml", and this method will
+ * return "[basename]-jonas-ejb-jar.xml" or "jonas-[basename].xml"
+ *
+ * @return The name of the JOnAS-specific EJB descriptor file.
+ */
+ private String getJonasDescriptorName() {
+
+ // descriptorName = <path><basename><basenameterminator><remainder>
+ // examples = /org/objectweb/fooAppli/foo/Foo-ejb-jar.xml
+ // examples = /org/objectweb/fooAppli/foo/Foo.xml (JOnAS convention)
+
+ String jonasDN; // JOnAS-specific DD
+ boolean jonasConvention = false; // true if the JOnAS convention is used for the DD
+ String path; // Directory path of the EJB descriptor
+ String fileName; // EJB descriptor file name
+ String baseName; // Filename appearing before name terminator
+ String remainder; // Filename appearing after the name terminator
+
+ int startOfFileName = descriptorName.lastIndexOf(File.separatorChar);
+ if (startOfFileName != -1) {
+ // extract path info
+ path = descriptorName.substring(0, startOfFileName + 1);
+ fileName = descriptorName.substring(startOfFileName + 1);
+ } else {
+ // descriptorName is just a file without path
+ path = "";
+ fileName = descriptorName;
+ }
+
+ if (fileName.startsWith(EJB_DD)) {
+ return path + JONAS_DD;
+ }
+
+ int endOfBaseName = descriptorName.indexOf(getConfig().baseNameTerminator, startOfFileName);
+
+ /*
+ * Check for the odd case where the terminator and/or filename
+ * extension aren't found. These will ensure "jonas-" appears at the
+ * end of the name and before the '.' (if present).
+ */
+ if (endOfBaseName < 0) {
+ // baseNameTerminator not found: the descriptor use the
+ // JOnAS naming convention, ie [Foo.xml,jonas-Foo.xml] and
+ // not [Foo<baseNameTerminator>-ejb-jar.xml,
+ // Foo<baseNameTerminator>-jonas-ejb-jar.xml].
+ endOfBaseName = descriptorName.lastIndexOf('.') - 1;
+ if (endOfBaseName < 0) {
+ // no . found
+ endOfBaseName = descriptorName.length() - 1;
+ }
+
+ jonasConvention = true;
+ }
+
+ baseName = descriptorName.substring(startOfFileName + 1, endOfBaseName + 1);
+ remainder = descriptorName.substring(endOfBaseName + 1);
+
+ if (jonasConvention) {
+ jonasDN = path + "jonas-" + baseName + ".xml";
+ } else {
+ jonasDN = path + baseName + "jonas-" + remainder;
+ }
+
+ log("Standard EJB descriptor name: " + descriptorName, Project.MSG_VERBOSE);
+ log("JOnAS-specific descriptor name: " + jonasDN, Project.MSG_VERBOSE);
+
+ return jonasDN;
+ }
+
+ /** {@inheritDoc}. */
+ protected String getJarBaseName(String descriptorFileName) {
+
+ String baseName = null;
+
+ if (getConfig().namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
+
+ // try to find JOnAS specific convention name
+ if (descriptorFileName.indexOf(getConfig().baseNameTerminator) == -1) {
+
+ // baseNameTerminator not found: the descriptor use the
+ // JOnAS naming convention, ie [Foo.xml,jonas-Foo.xml] and
+ // not [Foo<baseNameTerminator>-ejb-jar.xml,
+ // Foo<baseNameTerminator>-jonas-ejb-jar.xml].
+
+ String aCanonicalDescriptor = descriptorFileName.replace('\\', '/');
+ int lastSeparatorIndex = aCanonicalDescriptor.lastIndexOf('/');
+ int endOfBaseName;
+
+ if (lastSeparatorIndex != -1) {
+ endOfBaseName = descriptorFileName.indexOf(".xml", lastSeparatorIndex);
+ } else {
+ endOfBaseName = descriptorFileName.indexOf(".xml");
+ }
+
+ if (endOfBaseName != -1) {
+ baseName = descriptorFileName.substring(0, endOfBaseName);
+ }
+ }
+ }
+
+ if (baseName == null) {
+ // else get standard baseName
+ baseName = super.getJarBaseName(descriptorFileName);
+ }
+
+ log("JAR base name: " + baseName, Project.MSG_VERBOSE);
+
+ return baseName;
+ }
+
+ /** {@inheritDoc}. */
+ protected void registerKnownDTDs(DescriptorHandler handler) {
+ handler.registerDTD(EJB_JAR_1_1_PUBLIC_ID,
+ jonasroot + File.separator + "xml" + File.separator + EJB_JAR_1_1_DTD);
+ handler.registerDTD(EJB_JAR_2_0_PUBLIC_ID,
+ jonasroot + File.separator + "xml" + File.separator + EJB_JAR_2_0_DTD);
+
+ handler.registerDTD(JONAS_EJB_JAR_2_4_PUBLIC_ID,
+ jonasroot + File.separator + "xml" + File.separator + JONAS_EJB_JAR_2_4_DTD);
+ handler.registerDTD(JONAS_EJB_JAR_2_5_PUBLIC_ID,
+ jonasroot + File.separator + "xml" + File.separator + JONAS_EJB_JAR_2_5_DTD);
+ }
+
+ /**
+ * Add to the given hashtable all the file generated by GenIC.
+ *
+ * @param genericJarFile jar file.
+ * @param ejbFiles the hashtable.
+ */
+ private void addGenICGeneratedFiles(
+ File genericJarFile, Hashtable ejbFiles) {
+ Java genicTask = null; // GenIC task
+ String genicClass = null; // GenIC class (3 are supported for various
+ // versions
+ if (nogenic) {
+ return;
+ }
+
+ genicTask = new Java(getTask());
+ genicTask.setTaskName("genic");
+ genicTask.setFork(true);
+
+ // jonasroot
+ genicTask.createJvmarg().setValue("-Dinstall.root=" + jonasroot);
+
+ // java policy file
+ String jonasConfigDir = jonasroot + File.separator + "config";
+ File javaPolicyFile = new File(jonasConfigDir, "java.policy");
+ if (javaPolicyFile.exists()) {
+ genicTask.createJvmarg().setValue("-Djava.security.policy="
+ + javaPolicyFile.toString());
+ }
+
+ // outputdir
+ try {
+ outputdir = createTempDir();
+ } catch (IOException aIOException) {
+ String msg = "Cannot create temp dir: " + aIOException.getMessage();
+ throw new BuildException(msg, aIOException);
+ }
+ log("Using temporary output directory: " + outputdir, Project.MSG_VERBOSE);
+
+ genicTask.createArg().setValue("-d");
+ genicTask.createArg().setFile(outputdir);
+
+ // work around a bug of GenIC 2.5
+ String key;
+ File f;
+ Enumeration keys = ejbFiles.keys();
+ while (keys.hasMoreElements()) {
+ key = (String) keys.nextElement();
+ f = new File(outputdir + File.separator + key);
+ f.getParentFile().mkdirs();
+ }
+ log("Worked around a bug of GenIC 2.5.", Project.MSG_VERBOSE);
+
+ // classpath
+ Path classpath = getCombinedClasspath();
+ if (classpath == null) {
+ classpath = new Path(getTask().getProject());
+ }
+ classpath.append(new Path(classpath.getProject(), jonasConfigDir));
+ classpath.append(new Path(classpath.getProject(), outputdir.toString()));
+
+ // try to create the classpath for the correct ORB
+ if (orb != null) {
+ String orbJar = jonasroot + File.separator + "lib"
+ + File.separator + orb + "_jonas.jar";
+ classpath.append(new Path(classpath.getProject(), orbJar));
+ }
+ log("Using classpath: " + classpath.toString(), Project.MSG_VERBOSE);
+ genicTask.setClasspath(classpath);
+
+ // class name (search in the classpath provided for the ejbjar element)
+ genicClass = getGenicClassName(classpath);
+ if (genicClass == null) {
+ log("Cannot find GenIC class in classpath.", Project.MSG_ERR);
+ throw new BuildException("GenIC class not found, please check the classpath.");
+ } else {
+ log("Using '" + genicClass + "' GenIC class." , Project.MSG_VERBOSE);
+ genicTask.setClassname(genicClass);
+ }
+
+ // keepgenerated
+ if (keepgenerated) {
+ genicTask.createArg().setValue("-keepgenerated");
+ }
+
+ // nocompil
+ if (nocompil) {
+ genicTask.createArg().setValue("-nocompil");
+ }
+
+ // novalidation
+ if (novalidation) {
+ genicTask.createArg().setValue("-novalidation");
+ }
+
+ // javac
+ if (javac != null) {
+ genicTask.createArg().setValue("-javac");
+ genicTask.createArg().setLine(javac);
+ }
+
+ // javacopts
+ if (javacopts != null && !javacopts.equals("")) {
+ genicTask.createArg().setValue("-javacopts");
+ genicTask.createArg().setLine(javacopts);
+ }
+
+ // rmicopts
+ if (rmicopts != null && !rmicopts.equals("")) {
+ genicTask.createArg().setValue("-rmicopts");
+ genicTask.createArg().setLine(rmicopts);
+ }
+
+ // secpropag
+ if (secpropag) {
+ genicTask.createArg().setValue("-secpropag");
+ }
+
+ // verbose
+ if (verbose) {
+ genicTask.createArg().setValue("-verbose");
+ }
+
+ // additionalargs
+ if (additionalargs != null) {
+ genicTask.createArg().setValue(additionalargs);
+ }
+
+ // the generated classes must not be added in the generic JAR!
+ // is that buggy on old JOnAS (2.4) ??
+ genicTask.createArg().setValue("-noaddinjar");
+
+ // input file to process by GenIC
+ genicTask.createArg().setValue(genericJarFile.getPath());
+
+ // calling GenIC task
+ log("Calling " + genicClass + " for " + getConfig().descriptorDir
+ + File.separator + descriptorName + ".", Project.MSG_VERBOSE);
+
+ if (genicTask.executeJava() != 0) {
+
+ // the method deleteOnExit() do not work because the directory is not empty
+ log("Deleting temp output directory '" + outputdir + "'.", Project.MSG_VERBOSE);
+ deleteAllFiles(outputdir);
+
+ if (!keepgeneric) {
+ log("Deleting generic JAR " + genericJarFile.toString(),
+ Project.MSG_VERBOSE);
+ genericJarFile.delete();
+ }
+
+ throw new BuildException("GenIC reported an error.");
+ }
+
+ // add the generated files to the ejbFiles
+ addAllFiles(outputdir, "", ejbFiles);
+ }
+
+ /**
+ * Get the GenIC class name to use in the given classpath.
+ *
+ * @param classpath classpath where the GenIC class must be searched.
+ * @return the GenIC class name. Return <code>null</code> if the class name
+ * cannot be found.
+ */
+ String getGenicClassName(Path classpath) {
+
+ log("Looking for GenIC class in classpath: "
+ + classpath.toString(), Project.MSG_VERBOSE);
+
+ AntClassLoader cl = null;
+
+ try {
+ cl = classpath.getProject().createClassLoader(classpath);
+
+ try {
+ cl.loadClass(JonasDeploymentTool.GENIC_CLASS);
+ log("Found GenIC class '" + JonasDeploymentTool.GENIC_CLASS
+ + "' in classpath.", Project.MSG_VERBOSE);
+ return JonasDeploymentTool.GENIC_CLASS;
+
+ } catch (ClassNotFoundException cnf1) {
+ log("GenIC class '" + JonasDeploymentTool.GENIC_CLASS
+ + "' not found in classpath.",
+ Project.MSG_VERBOSE);
+ }
+
+ try {
+ cl.loadClass(JonasDeploymentTool.OLD_GENIC_CLASS_1);
+ log("Found GenIC class '"
+ + JonasDeploymentTool.OLD_GENIC_CLASS_1
+ + "' in classpath.", Project.MSG_VERBOSE);
+ return JonasDeploymentTool.OLD_GENIC_CLASS_1;
+
+ } catch (ClassNotFoundException cnf2) {
+ log("GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_1
+ + "' not found in classpath.",
+ Project.MSG_VERBOSE);
+ }
+
+ try {
+ cl.loadClass(JonasDeploymentTool.OLD_GENIC_CLASS_2);
+ log("Found GenIC class '"
+ + JonasDeploymentTool.OLD_GENIC_CLASS_2
+ + "' in classpath.", Project.MSG_VERBOSE);
+ return JonasDeploymentTool.OLD_GENIC_CLASS_2;
+
+ } catch (ClassNotFoundException cnf3) {
+ log("GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_2
+ + "' not found in classpath.",
+ Project.MSG_VERBOSE);
+ }
+ } finally {
+ if (cl != null) {
+ cl.cleanup();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Verify the configuration.
+ * @param descriptorFileName the name of the descriptor file.
+ * @param saxParser not used.
+ * @throws BuildException if there is an error.
+ */
+ protected void checkConfiguration(String descriptorFileName,
+ SAXParser saxParser) throws BuildException {
+
+ // jonasroot
+ if (jonasroot == null) {
+ throw new BuildException("The jonasroot attribut is not set.");
+ } else if (!jonasroot.isDirectory()) {
+ throw new BuildException("The jonasroot attribut '" + jonasroot
+ + "' is not a valid directory.");
+ }
+
+ // orb
+ if (orb != null && !orb.equals(RMI_ORB) && !orb.equals(JEREMIE_ORB)
+ && !orb.equals(DAVID_ORB)) {
+ throw new BuildException("The orb attribut '" + orb
+ + "' is not valid (must be either "
+ + RMI_ORB + ", " + JEREMIE_ORB + " or " + DAVID_ORB + ").");
+ }
+
+ // additionalargs
+ if (additionalargs != null && additionalargs.equals("")) {
+ throw new BuildException("Empty additionalargs attribut.");
+ }
+
+ // javac
+ if (javac != null && javac.equals("")) {
+ throw new BuildException("Empty javac attribut.");
+ }
+ }
+
+ /* ----------------------------------------------------------------------------------- */
+ /* utilitary methods */
+ /* ----------------------------------------------------------------------------------- */
+
+ /**
+ * Create a temporary directory for GenIC output.
+ *
+ * @return the temp directory.
+ * @throws BuildException if a temp directory cannot be created.
+ */
+ private File createTempDir() throws IOException {
+ File tmpDir = File.createTempFile("genic", null, null);
+ tmpDir.delete();
+ if (!tmpDir.mkdir()) {
+ throw new IOException("Cannot create the temporary directory '" + tmpDir + "'.");
+ }
+ return tmpDir;
+ }
+
+ /**
+ * Delete a file. If the file is a directory, delete recursivly all the
+ * files inside.
+ *
+ * @param aFile file to delete.
+ */
+ private void deleteAllFiles(File aFile) {
+ if (aFile.isDirectory()) {
+ File[] someFiles = aFile.listFiles();
+
+ for (int i = 0; i < someFiles.length; i++) {
+ deleteAllFiles(someFiles[i]);
+ }
+ }
+ aFile.delete();
+ }
+
+ /**
+ * Add a file to the a given hashtable. If the file is a directory, add
+ * recursivly all the files inside to the hashtable.
+ *
+ * @param file the file to add.
+ * @param rootDir the current sub-directory to scan.
+ * @param hashtable the hashtable where to add the files.
+ */
+ private void addAllFiles(File file, String rootDir, Hashtable hashtable) {
+
+ if (!file.exists()) {
+ throw new IllegalArgumentException();
+ }
+
+ String newRootDir;
+ if (file.isDirectory()) {
+ File[] files = file.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ if (rootDir.length() > 0) {
+ newRootDir = rootDir + File.separator + files[i].getName();
+ } else {
+ newRootDir = files[i].getName();
+ }
+ addAllFiles(files[i], newRootDir, hashtable);
+ }
+ } else {
+ hashtable.put(rootDir, file);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicDeploymentTool.java
new file mode 100644
index 00000000..550f59ce
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicDeploymentTool.java
@@ -0,0 +1,932 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+import org.xml.sax.InputSource;
+
+/**
+ The weblogic element is used to control the weblogic.ejbc compiler for
+ generating weblogic EJB jars. Prior to Ant 1.3, the method of locating CMP
+ descriptors was to use the ejbjar naming convention. So if your ejb-jar was
+ called, Customer-ejb-jar.xml, your weblogic descriptor was called Customer-
+ weblogic-ejb-jar.xml and your CMP descriptor had to be Customer-weblogic-cmp-
+ rdbms-jar.xml. In addition, the &lt;type-storage&gt; element in the weblogic
+ descriptor had to be set to the standard name META-INF/weblogic-cmp-rdbms-
+ jar.xml, as that is where the CMP descriptor was mapped to in the generated
+ jar.
+*/
+public class WeblogicDeploymentTool extends GenericDeploymentTool {
+ /** EJB11 id */
+ public static final String PUBLICID_EJB11
+ = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN";
+ /** EJB20 id */
+ public static final String PUBLICID_EJB20
+ = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN";
+ /** Weblogic 5.1.0 id */
+ public static final String PUBLICID_WEBLOGIC_EJB510
+ = "-//BEA Systems, Inc.//DTD WebLogic 5.1.0 EJB//EN";
+ /** Weblogic 6.0.0 id */
+ public static final String PUBLICID_WEBLOGIC_EJB600
+ = "-//BEA Systems, Inc.//DTD WebLogic 6.0.0 EJB//EN";
+ /** Weblogic 7.0.0 id */
+ public static final String PUBLICID_WEBLOGIC_EJB700
+ = "-//BEA Systems, Inc.//DTD WebLogic 7.0.0 EJB//EN";
+
+ /** Weblogic 5.1 dtd location */
+ protected static final String DEFAULT_WL51_EJB11_DTD_LOCATION
+ = "/weblogic/ejb/deployment/xml/ejb-jar.dtd";
+ /** Weblogic 6.0 ejb 1.1 dtd location */
+ protected static final String DEFAULT_WL60_EJB11_DTD_LOCATION
+ = "/weblogic/ejb20/dd/xml/ejb11-jar.dtd";
+ /** Weblogic 6.0 ejb 2.0 dtd location */
+ protected static final String DEFAULT_WL60_EJB20_DTD_LOCATION
+ = "/weblogic/ejb20/dd/xml/ejb20-jar.dtd";
+
+ protected static final String DEFAULT_WL51_DTD_LOCATION
+ = "/weblogic/ejb/deployment/xml/weblogic-ejb-jar.dtd";
+ protected static final String DEFAULT_WL60_51_DTD_LOCATION
+ = "/weblogic/ejb20/dd/xml/weblogic510-ejb-jar.dtd";
+ protected static final String DEFAULT_WL60_DTD_LOCATION
+ = "/weblogic/ejb20/dd/xml/weblogic600-ejb-jar.dtd";
+ protected static final String DEFAULT_WL70_DTD_LOCATION
+ = "/weblogic/ejb20/dd/xml/weblogic700-ejb-jar.dtd";
+
+ protected static final String DEFAULT_COMPILER = "default";
+
+ protected static final String WL_DD = "weblogic-ejb-jar.xml";
+ protected static final String WL_CMP_DD = "weblogic-cmp-rdbms-jar.xml";
+
+ protected static final String COMPILER_EJB11 = "weblogic.ejbc";
+ protected static final String COMPILER_EJB20 = "weblogic.ejbc20";
+
+ /** File utilities instance for copying jars */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** Instance variable that stores the suffix for the weblogic jarfile. */
+ private String jarSuffix = ".jar";
+
+ /** Instance variable that stores the location of the weblogic DTD file. */
+ private String weblogicDTD;
+
+ /** Instance variable that stores the location of the ejb 1.1 DTD file. */
+ private String ejb11DTD;
+
+ /** Instance variable that determines whether generic ejb jars are kept. */
+ private boolean keepgenerated = false;
+
+ /**
+ * Instance variable that stores the fully qualified classname of the
+ * weblogic EJBC compiler
+ */
+ private String ejbcClass = null;
+
+ private String additionalArgs = "";
+
+ /**
+ * additional args to pass to the spawned jvm
+ */
+ private String additionalJvmArgs = "";
+
+ private boolean keepGeneric = false;
+
+ private String compiler = null;
+
+ private boolean alwaysRebuild = true;
+
+ /** controls whether ejbc is run on the generated jar */
+ private boolean noEJBC = false;
+
+ /** Indicates if the old CMP location convention is to be used. */
+ private boolean newCMP = false;
+
+ /** The classpath to the weblogic classes. */
+ private Path wlClasspath = null;
+
+ /** System properties for the JVM. */
+ private Vector sysprops = new Vector();
+
+ /**
+ * The weblogic.StdoutSeverityLevel to use when running the JVM that
+ * executes ejbc. Set to 16 to avoid the warnings about EJB Home and
+ * Remotes being in the classpath
+ */
+ private Integer jvmDebugLevel = null;
+
+ private File outputDir;
+
+ /**
+ * Add a nested sysproperty element.
+ * @param sysp the element to add.
+ */
+ public void addSysproperty(Environment.Variable sysp) {
+ sysprops.add(sysp);
+ }
+
+
+ /**
+ * Get the classpath to the weblogic classpaths.
+ * @return the classpath to configure.
+ */
+ public Path createWLClasspath() {
+ if (wlClasspath == null) {
+ wlClasspath = new Path(getTask().getProject());
+ }
+ return wlClasspath.createPath();
+ }
+
+ /**
+ * If set ejbc will use this directory as the output
+ * destination rather than a jar file. This allows for the
+ * generation of &quot;exploded&quot; jars.
+ * @param outputDir the directory to be used.
+ */
+ public void setOutputDir(File outputDir) {
+ this.outputDir = outputDir;
+ }
+
+
+ /**
+ * Optional classpath to WL6.0.
+ * Weblogic 6.0 will give a warning if the home and remote interfaces
+ * of a bean are on the system classpath used to run weblogic.ejbc.
+ * In that case, the standard weblogic classes should be set with
+ * this attribute (or equivalent nested element) and the
+ * home and remote interfaces located with the standard classpath
+ * attribute.
+ * @param wlClasspath the path to be used.
+ */
+ public void setWLClasspath(Path wlClasspath) {
+ this.wlClasspath = wlClasspath;
+ }
+
+
+ /**
+ * The compiler (switch <code>-compiler</code>) to use; optional.
+ * This allows for the selection of a different compiler
+ * to be used for the compilation of the generated Java
+ * files. This could be set, for example, to Jikes to
+ * compile with the Jikes compiler. If this is not set
+ * and the <code>build.compiler</code> property is set
+ * to jikes, the Jikes compiler will be used. If this
+ * is not desired, the value &quot;<code>default</code>&quot;
+ * may be given to use the default compiler.
+ * @param compiler the compiler to be used.
+ */
+ public void setCompiler(String compiler) {
+ this.compiler = compiler;
+ }
+
+
+ /**
+ * Set the rebuild flag to false to only update changes in the jar rather
+ * than rerunning ejbc; optional, default true.
+ * This flag controls whether weblogic.ejbc is always
+ * invoked to build the jar file. In certain circumstances,
+ * such as when only a bean class has been changed, the jar
+ * can be generated by merely replacing the changed classes
+ * and not rerunning ejbc. Setting this to false will reduce
+ * the time to run ejbjar.
+ * @param rebuild a <code>boolean</code> value.
+ */
+ public void setRebuild(boolean rebuild) {
+ this.alwaysRebuild = rebuild;
+ }
+
+
+ /**
+ * Sets the weblogic.StdoutSeverityLevel to use when running the JVM that
+ * executes ejbc; optional. Set to 16 to avoid the warnings about EJB Home and
+ * Remotes being in the classpath
+ * @param jvmDebugLevel the value to use.
+ */
+ public void setJvmDebugLevel(Integer jvmDebugLevel) {
+ this.jvmDebugLevel = jvmDebugLevel;
+ }
+
+
+ /**
+ * Get the debug level.
+ * @return the jvm debug level (may be null).
+ */
+ public Integer getJvmDebugLevel() {
+ return jvmDebugLevel;
+ }
+
+
+
+ /**
+ * Setter used to store the suffix for the generated weblogic jar file.
+ *
+ * @param inString the string to use as the suffix.
+ */
+ public void setSuffix(String inString) {
+ this.jarSuffix = inString;
+ }
+
+
+ /**
+ * controls whether the generic file used as input to
+ * ejbc is retained; defaults to false
+ *
+ * @param inValue true for keep generic
+ */
+ public void setKeepgeneric(boolean inValue) {
+ this.keepGeneric = inValue;
+ }
+
+
+ /**
+ * Controls whether weblogic will keep the generated Java
+ * files used to build the class files added to the
+ * jar. This can be useful when debugging; default is false.
+ *
+ * @param inValue either 'true' or 'false'
+ */
+ public void setKeepgenerated(String inValue) {
+ this.keepgenerated = Boolean.valueOf(inValue).booleanValue();
+ }
+
+
+ /**
+ * Any optional extra arguments pass to the weblogic.ejbc
+ * tool.
+ * @param args extra arguments to pass to the ejbc tool.
+ */
+ public void setArgs(String args) {
+ this.additionalArgs = args;
+ }
+
+
+ /**
+ * Set any additional arguments to pass to the weblogic JVM; optional.
+ * @param args the arguments to be passed to the JVM
+ */
+ public void setJvmargs(String args) {
+ this.additionalJvmArgs = args;
+ }
+
+ /**
+ * Set the classname of the ejbc compiler; optional
+ * Normally ejbjar determines
+ * the appropriate class based on the DTD used for the EJB. The EJB 2.0 compiler
+ * featured in weblogic 6 has, however, been deprecated in version 7. When
+ * using with version 7 this attribute should be set to
+ * &quot;weblogic.ejbc&quot; to avoid the deprecation warning.
+ * @param ejbcClass the name of the class to use.
+ */
+ public void setEjbcClass(String ejbcClass) {
+ this.ejbcClass = ejbcClass;
+ }
+
+
+ /**
+ * Get the ejbc compiler class.
+ * @return the name of the ejbc compiler class.
+ */
+ public String getEjbcClass() {
+ return ejbcClass;
+ }
+
+
+ /**
+ * <b>Deprecated</b>. Defines the location of the ejb-jar DTD in
+ * the weblogic class hierarchy. Should not be needed, and the
+ * nested &lt;dtd&gt; element is recommended when it is.
+ *
+ * @param inString the string to use as the DTD location.
+ */
+ public void setWeblogicdtd(String inString) {
+ setEJBdtd(inString);
+ }
+
+
+ /**
+ * <b>Deprecated</b>. Defines the location of weblogic DTD in
+ * the weblogic class hierarchy. Should not be needed, and the
+ * nested &lt;dtd&gt; element is recommended when it is.
+ *
+ * @param inString the string to use as the DTD location.
+ */
+ public void setWLdtd(String inString) {
+ this.weblogicDTD = inString;
+ }
+
+
+ /**
+ * <b>Deprecated</b>. Defines the location of Sun's EJB DTD in
+ * the weblogic class hierarchy. Should not be needed, and the
+ * nested &lt;dtd&gt; element is recommended when it is.
+ *
+ * @param inString the string to use as the DTD location.
+ */
+ public void setEJBdtd(String inString) {
+ this.ejb11DTD = inString;
+ }
+
+
+ /**
+ * Set the value of the oldCMP scheme. This is an antonym for newCMP
+ * @ant.attribute ignore="true'
+ * @param oldCMP a <code>boolean</code> value.
+ */
+ public void setOldCMP(boolean oldCMP) {
+ this.newCMP = !oldCMP;
+ }
+
+
+ /**
+ * If this is set to true, the new method for locating
+ * CMP descriptors will be used; optional, default false.
+ * <P>
+ * The old CMP scheme locates the
+ * weblogic CMP descriptor based on the naming convention where the
+ * weblogic CMP file is expected to be named with the bean name as the
+ * prefix. Under this scheme the name of the CMP descriptor does not match
+ * the name actually used in the main weblogic EJB descriptor. Also,
+ * descriptors which contain multiple CMP references could not be used.
+ * @param newCMP a <code>boolean</code> value.
+ */
+ public void setNewCMP(boolean newCMP) {
+ this.newCMP = newCMP;
+ }
+
+
+ /**
+ * Do not EJBC the jar after it has been put together;
+ * optional, default false
+ * @param noEJBC a <code>boolean</code> value.
+ */
+ public void setNoEJBC(boolean noEJBC) {
+ this.noEJBC = noEJBC;
+ }
+
+
+ /**
+ * Register the DTDs.
+ * @param handler the handler to use.
+ */
+ protected void registerKnownDTDs(DescriptorHandler handler) {
+ // register all the known DTDs
+ handler.registerDTD(PUBLICID_EJB11, DEFAULT_WL51_EJB11_DTD_LOCATION);
+ handler.registerDTD(PUBLICID_EJB11, DEFAULT_WL60_EJB11_DTD_LOCATION);
+ handler.registerDTD(PUBLICID_EJB11, ejb11DTD);
+ handler.registerDTD(PUBLICID_EJB20, DEFAULT_WL60_EJB20_DTD_LOCATION);
+ }
+
+
+ /**
+ * Get the weblogic descriptor handler.
+ * @param srcDir the source directory.
+ * @return the descriptor.
+ */
+ protected DescriptorHandler getWeblogicDescriptorHandler(final File srcDir) {
+ DescriptorHandler handler =
+ new DescriptorHandler(getTask(), srcDir) {
+ protected void processElement() {
+ if (currentElement.equals("type-storage")) {
+ // Get the filename of vendor specific descriptor
+ String fileNameWithMETA = currentText;
+ //trim the META_INF\ off of the file name
+ String fileName
+ = fileNameWithMETA.substring(META_DIR.length(),
+ fileNameWithMETA.length());
+ File descriptorFile = new File(srcDir, fileName);
+
+ ejbFiles.put(fileNameWithMETA, descriptorFile);
+ }
+ }
+ };
+
+ handler.registerDTD(PUBLICID_WEBLOGIC_EJB510, DEFAULT_WL51_DTD_LOCATION);
+ handler.registerDTD(PUBLICID_WEBLOGIC_EJB510, DEFAULT_WL60_51_DTD_LOCATION);
+ handler.registerDTD(PUBLICID_WEBLOGIC_EJB600, DEFAULT_WL60_DTD_LOCATION);
+ handler.registerDTD(PUBLICID_WEBLOGIC_EJB700, DEFAULT_WL70_DTD_LOCATION);
+ handler.registerDTD(PUBLICID_WEBLOGIC_EJB510, weblogicDTD);
+ handler.registerDTD(PUBLICID_WEBLOGIC_EJB600, weblogicDTD);
+
+ for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) {
+ EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
+
+ handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
+ }
+ return handler;
+ }
+
+
+ /**
+ * Add any vendor specific files which should be included in the EJB Jar.
+ * @param ejbFiles the hash table to be populated.
+ * @param ddPrefix the prefix to use.
+ */
+ protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+ File weblogicDD = new File(getConfig().descriptorDir, ddPrefix + WL_DD);
+
+ if (weblogicDD.exists()) {
+ ejbFiles.put(META_DIR + WL_DD,
+ weblogicDD);
+ } else {
+ log("Unable to locate weblogic deployment descriptor. "
+ + "It was expected to be in "
+ + weblogicDD.getPath(), Project.MSG_WARN);
+ return;
+ }
+
+ if (!newCMP) {
+ log("The old method for locating CMP files has been DEPRECATED.", Project.MSG_VERBOSE);
+ log("Please adjust your weblogic descriptor and set "
+ + "newCMP=\"true\" to use the new CMP descriptor "
+ + "inclusion mechanism. ", Project.MSG_VERBOSE);
+ // The the weblogic cmp deployment descriptor
+ File weblogicCMPDD = new File(getConfig().descriptorDir, ddPrefix + WL_CMP_DD);
+
+ if (weblogicCMPDD.exists()) {
+ ejbFiles.put(META_DIR + WL_CMP_DD,
+ weblogicCMPDD);
+ }
+ } else {
+ // now that we have the weblogic descriptor, we parse the file
+ // to find other descriptors needed to deploy the bean.
+ // this could be the weblogic-cmp-rdbms.xml or any other O/R
+ // mapping tool descriptors.
+ try {
+ File ejbDescriptor = (File) ejbFiles.get(META_DIR + EJB_DD);
+ SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+
+ saxParserFactory.setValidating(true);
+
+ SAXParser saxParser = saxParserFactory.newSAXParser();
+ DescriptorHandler handler
+ = getWeblogicDescriptorHandler(ejbDescriptor.getParentFile());
+
+ saxParser.parse(new InputSource
+ (new FileInputStream(weblogicDD)),
+ handler);
+
+ Hashtable ht = handler.getFiles();
+ Enumeration e = ht.keys();
+
+ while (e.hasMoreElements()) {
+ String key = (String) e.nextElement();
+
+ ejbFiles.put(key, ht.get(key));
+ }
+ } catch (Exception e) {
+ String msg = "Exception while adding Vendor specific files: " + e.toString();
+
+ throw new BuildException(msg, e);
+ }
+ }
+ }
+
+
+ /**
+ * Get the vendor specific name of the Jar that will be output. The
+ * modification date of this jar will be checked against the dependent
+ * bean classes.
+ */
+ File getVendorOutputJarFile(String baseName) {
+ return new File(getDestDir(), baseName + jarSuffix);
+ }
+
+
+ /**
+ * Helper method invoked by execute() for each WebLogic jar to be built.
+ * Encapsulates the logic of constructing a java task for calling
+ * weblogic.ejbc and executing it.
+ *
+ * @param sourceJar java.io.File representing the source (EJB1.1) jarfile.
+ * @param destJar java.io.File representing the destination, WebLogic
+ * jarfile.
+ */
+ private void buildWeblogicJar(File sourceJar, File destJar, String publicId) {
+ Java javaTask = null;
+
+ if (noEJBC) {
+ try {
+ FILE_UTILS.copyFile(sourceJar, destJar);
+ if (!keepgenerated) {
+ sourceJar.delete();
+ }
+ return;
+ } catch (IOException e) {
+ throw new BuildException("Unable to write EJB jar", e);
+ }
+ }
+
+ String ejbcClassName = ejbcClass;
+
+ try {
+ javaTask = new Java(getTask());
+ javaTask.setTaskName("ejbc");
+
+ javaTask.createJvmarg().setLine(additionalJvmArgs);
+ if (!(sysprops.isEmpty())) {
+ for (Enumeration en = sysprops.elements(); en.hasMoreElements();) {
+ Environment.Variable entry
+ = (Environment.Variable) en.nextElement();
+ javaTask.addSysproperty(entry);
+ }
+ }
+
+ if (getJvmDebugLevel() != null) {
+ javaTask.createJvmarg().setLine(" -Dweblogic.StdoutSeverityLevel=" + jvmDebugLevel);
+ }
+
+ if (ejbcClassName == null) {
+ // try to determine it from publicId
+ if (PUBLICID_EJB11.equals(publicId)) {
+ ejbcClassName = COMPILER_EJB11;
+ } else if (PUBLICID_EJB20.equals(publicId)) {
+ ejbcClassName = COMPILER_EJB20;
+ } else {
+ log("Unrecognized publicId " + publicId
+ + " - using EJB 1.1 compiler", Project.MSG_WARN);
+ ejbcClassName = COMPILER_EJB11;
+ }
+ }
+
+ javaTask.setClassname(ejbcClassName);
+ javaTask.createArg().setLine(additionalArgs);
+ if (keepgenerated) {
+ javaTask.createArg().setValue("-keepgenerated");
+ }
+ if (compiler == null) {
+ // try to use the compiler specified by build.compiler.
+ // Right now we are just going to allow Jikes
+ String buildCompiler
+ = getTask().getProject().getProperty("build.compiler");
+
+ if (buildCompiler != null && buildCompiler.equals("jikes")) {
+ javaTask.createArg().setValue("-compiler");
+ javaTask.createArg().setValue("jikes");
+ }
+ } else {
+ if (!compiler.equals(DEFAULT_COMPILER)) {
+ javaTask.createArg().setValue("-compiler");
+ javaTask.createArg().setLine(compiler);
+ }
+ }
+
+ Path combinedClasspath = getCombinedClasspath();
+ if (wlClasspath != null && combinedClasspath != null
+ && combinedClasspath.toString().trim().length() > 0) {
+ javaTask.createArg().setValue("-classpath");
+ javaTask.createArg().setPath(combinedClasspath);
+ }
+
+ javaTask.createArg().setValue(sourceJar.getPath());
+ if (outputDir == null) {
+ javaTask.createArg().setValue(destJar.getPath());
+ } else {
+ javaTask.createArg().setValue(outputDir.getPath());
+ }
+
+ Path classpath = wlClasspath;
+
+ if (classpath == null) {
+ classpath = getCombinedClasspath();
+ }
+
+ javaTask.setFork(true);
+ if (classpath != null) {
+ javaTask.setClasspath(classpath);
+ }
+
+ log("Calling " + ejbcClassName + " for " + sourceJar.toString(),
+ Project.MSG_VERBOSE);
+
+ if (javaTask.executeJava() != 0) {
+ throw new BuildException("Ejbc reported an error");
+ }
+ } catch (Exception e) {
+ // Have to catch this because of the semantics of calling main()
+ String msg = "Exception while calling " + ejbcClassName
+ + ". Details: " + e.toString();
+
+ throw new BuildException(msg, e);
+ }
+ }
+
+
+ /**
+ * Method used to encapsulate the writing of the JAR file. Iterates over
+ * the filenames/java.io.Files in the Hashtable stored on the instance
+ * variable ejbFiles.
+ * @param baseName the base name.
+ * @param jarFile the jar file to populate.
+ * @param files the hash table of files to write.
+ * @param publicId the id to use.
+ * @throws BuildException if there is a problem.
+ */
+ protected void writeJar(String baseName, File jarFile, Hashtable files,
+ String publicId) throws BuildException {
+ // need to create a generic jar first.
+ File genericJarFile = super.getVendorOutputJarFile(baseName);
+
+ super.writeJar(baseName, genericJarFile, files, publicId);
+
+ if (alwaysRebuild || isRebuildRequired(genericJarFile, jarFile)) {
+ buildWeblogicJar(genericJarFile, jarFile, publicId);
+ }
+ if (!keepGeneric) {
+ log("deleting generic jar " + genericJarFile.toString(),
+ Project.MSG_VERBOSE);
+ genericJarFile.delete();
+ }
+ }
+
+
+ /**
+ * Called to validate that the tool parameters have been configured.
+ * @throws BuildException if there is an error.
+ */
+ public void validateConfigured() throws BuildException {
+ super.validateConfigured();
+ }
+
+
+ /**
+ * Helper method to check to see if a weblogic EBJ1.1 jar needs to be
+ * rebuilt using ejbc. Called from writeJar it sees if the "Bean" classes
+ * are the only thing that needs to be updated and either updates the Jar
+ * with the Bean classfile or returns true, saying that the whole weblogic
+ * jar needs to be regened with ejbc. This allows faster build times for
+ * working developers. <p>
+ *
+ * The way weblogic ejbc works is it creates wrappers for the publicly
+ * defined methods as they are exposed in the remote interface. If the
+ * actual bean changes without changing the the method signatures then
+ * only the bean classfile needs to be updated and the rest of the
+ * weblogic jar file can remain the same. If the Interfaces, ie. the
+ * method signatures change or if the xml deployment descriptors changed,
+ * the whole jar needs to be rebuilt with ejbc. This is not strictly true
+ * for the xml files. If the JNDI name changes then the jar doesn't have to
+ * be rebuild, but if the resources references change then it does. At
+ * this point the weblogic jar gets rebuilt if the xml files change at
+ * all.
+ *
+ * @param genericJarFile java.io.File The generic jar file.
+ * @param weblogicJarFile java.io.File The weblogic jar file to check to
+ * see if it needs to be rebuilt.
+ * @return true if the jar needs to be rebuilt.
+ */
+ // CheckStyle:MethodLength OFF - this will no be fixed
+ protected boolean isRebuildRequired(File genericJarFile, File weblogicJarFile) {
+ boolean rebuild = false;
+
+ JarFile genericJar = null;
+ JarFile wlJar = null;
+ File newWLJarFile = null;
+ JarOutputStream newJarStream = null;
+ ClassLoader genericLoader = null;
+
+ try {
+ log("Checking if weblogic Jar needs to be rebuilt for jar " + weblogicJarFile.getName(),
+ Project.MSG_VERBOSE);
+ // Only go forward if the generic and the weblogic file both exist
+ if (genericJarFile.exists() && genericJarFile.isFile()
+ && weblogicJarFile.exists() && weblogicJarFile.isFile()) {
+ //open jar files
+ genericJar = new JarFile(genericJarFile);
+ wlJar = new JarFile(weblogicJarFile);
+
+ Hashtable genericEntries = new Hashtable();
+ Hashtable wlEntries = new Hashtable();
+ Hashtable replaceEntries = new Hashtable();
+
+ //get the list of generic jar entries
+ for (Enumeration e = genericJar.entries(); e.hasMoreElements();) {
+ JarEntry je = (JarEntry) e.nextElement();
+
+ genericEntries.put(je.getName().replace('\\', '/'), je);
+ }
+ //get the list of weblogic jar entries
+ for (Enumeration e = wlJar.entries(); e.hasMoreElements();) {
+ JarEntry je = (JarEntry) e.nextElement();
+
+ wlEntries.put(je.getName(), je);
+ }
+
+ //Cycle Through generic and make sure its in weblogic
+ genericLoader = getClassLoaderFromJar(genericJarFile);
+
+ for (Enumeration e = genericEntries.keys(); e.hasMoreElements();) {
+ String filepath = (String) e.nextElement();
+
+ if (wlEntries.containsKey(filepath)) {
+ // File name/path match
+
+ // Check files see if same
+ JarEntry genericEntry = (JarEntry) genericEntries.get(filepath);
+ JarEntry wlEntry = (JarEntry) wlEntries.get(filepath);
+
+ if ((genericEntry.getCrc() != wlEntry.getCrc())
+ || (genericEntry.getSize() != wlEntry.getSize())) {
+
+ if (genericEntry.getName().endsWith(".class")) {
+ //File are different see if its an object or an interface
+ String classname
+ = genericEntry.getName()
+ .replace(File.separatorChar, '.')
+ .replace('/', '.');
+
+ classname = classname.substring(0, classname.lastIndexOf(".class"));
+
+ Class genclass = genericLoader.loadClass(classname);
+
+ if (genclass.isInterface()) {
+ //Interface changed rebuild jar.
+ log("Interface " + genclass.getName()
+ + " has changed", Project.MSG_VERBOSE);
+ rebuild = true;
+ break;
+ } else {
+ //Object class Changed update it.
+ replaceEntries.put(filepath, genericEntry);
+ }
+ } else {
+ // is it the manifest. If so ignore it
+ if (!genericEntry.getName().equals("META-INF/MANIFEST.MF")) {
+ //File other then class changed rebuild
+ log("Non class file " + genericEntry.getName()
+ + " has changed", Project.MSG_VERBOSE);
+ rebuild = true;
+ break;
+ }
+ }
+ }
+ } else {
+ // a file doesn't exist rebuild
+
+ log("File " + filepath + " not present in weblogic jar",
+ Project.MSG_VERBOSE);
+ rebuild = true;
+ break;
+ }
+ }
+
+ if (!rebuild) {
+ log("No rebuild needed - updating jar", Project.MSG_VERBOSE);
+ newWLJarFile = new File(weblogicJarFile.getAbsolutePath() + ".temp");
+ if (newWLJarFile.exists()) {
+ newWLJarFile.delete();
+ }
+
+ newJarStream = new JarOutputStream(new FileOutputStream(newWLJarFile));
+ newJarStream.setLevel(0);
+
+ //Copy files from old weblogic jar
+ for (Enumeration e = wlEntries.elements(); e.hasMoreElements();) {
+ byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+ int bytesRead;
+ InputStream is;
+ JarEntry je = (JarEntry) e.nextElement();
+
+ if (je.getCompressedSize() == -1
+ || je.getCompressedSize() == je.getSize()) {
+ newJarStream.setLevel(0);
+ } else {
+ newJarStream.setLevel(JAR_COMPRESS_LEVEL);
+ }
+
+ // Update with changed Bean class
+ if (replaceEntries.containsKey(je.getName())) {
+ log("Updating Bean class from generic Jar "
+ + je.getName(), Project.MSG_VERBOSE);
+ // Use the entry from the generic jar
+ je = (JarEntry) replaceEntries.get(je.getName());
+ is = genericJar.getInputStream(je);
+ } else {
+ //use fle from original weblogic jar
+
+ is = wlJar.getInputStream(je);
+ }
+ newJarStream.putNextEntry(new JarEntry(je.getName()));
+
+ while ((bytesRead = is.read(buffer)) != -1) {
+ newJarStream.write(buffer, 0, bytesRead);
+ }
+ is.close();
+ }
+ } else {
+ log("Weblogic Jar rebuild needed due to changed "
+ + "interface or XML", Project.MSG_VERBOSE);
+ }
+ } else {
+ rebuild = true;
+ }
+ } catch (ClassNotFoundException cnfe) {
+ String cnfmsg = "ClassNotFoundException while processing ejb-jar file"
+ + ". Details: "
+ + cnfe.getMessage();
+
+ throw new BuildException(cnfmsg, cnfe);
+ } catch (IOException ioe) {
+ String msg = "IOException while processing ejb-jar file "
+ + ". Details: "
+ + ioe.getMessage();
+
+ throw new BuildException(msg, ioe);
+ } finally {
+ // need to close files and perhaps rename output
+ if (genericJar != null) {
+ try {
+ genericJar.close();
+ } catch (IOException closeException) {
+ // empty
+ }
+ }
+
+ if (wlJar != null) {
+ try {
+ wlJar.close();
+ } catch (IOException closeException) {
+ // empty
+ }
+ }
+
+ if (newJarStream != null) {
+ try {
+ newJarStream.close();
+ } catch (IOException closeException) {
+ // empty
+ }
+
+ try {
+ FILE_UTILS.rename(newWLJarFile, weblogicJarFile);
+ } catch (IOException renameException) {
+ log(renameException.getMessage(), Project.MSG_WARN);
+ rebuild = true;
+ }
+ }
+ if (genericLoader != null
+ && genericLoader instanceof AntClassLoader) {
+ AntClassLoader loader = (AntClassLoader) genericLoader;
+ loader.cleanup();
+ }
+ }
+
+ return rebuild;
+ }
+
+
+ /**
+ * Helper method invoked by isRebuildRequired to get a ClassLoader for a
+ * Jar File passed to it.
+ *
+ * @param classjar java.io.File representing jar file to get classes from.
+ * @return the classloader for the jarfile.
+ * @throws IOException if there is a problem.
+ */
+ protected ClassLoader getClassLoaderFromJar(File classjar) throws IOException {
+ Path lookupPath = new Path(getTask().getProject());
+
+ lookupPath.setLocation(classjar);
+
+ Path classpath = getCombinedClasspath();
+
+ if (classpath != null) {
+ lookupPath.append(classpath);
+ }
+
+ return getTask().getProject().createClassLoader(lookupPath);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicTOPLinkDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicTOPLinkDeploymentTool.java
new file mode 100644
index 00000000..0752bbec
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicTOPLinkDeploymentTool.java
@@ -0,0 +1,113 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.util.Hashtable;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Deployment tool for Weblogic TOPLink.
+ */
+public class WeblogicTOPLinkDeploymentTool extends WeblogicDeploymentTool {
+
+ private static final String TL_DTD_LOC
+ = "http://www.objectpeople.com/tlwl/dtd/toplink-cmp_2_5_1.dtd";
+
+ private String toplinkDescriptor;
+ private String toplinkDTD;
+
+ /**
+ * Setter used to store the name of the toplink descriptor.
+ * @param inString the string to use as the descriptor name.
+ */
+ public void setToplinkdescriptor(String inString) {
+ this.toplinkDescriptor = inString;
+ }
+
+ /**
+ * Setter used to store the location of the toplink DTD file.
+ * This is expected to be an URL (file or otherwise). If running
+ * this on NT using a file URL, the safest thing would be to not use a
+ * drive spec in the URL and make sure the file resides on the drive that
+ * ANT is running from. This will keep the setting in the build XML
+ * platform independent.
+ *
+ * @param inString the string to use as the DTD location.
+ */
+ public void setToplinkdtd(String inString) {
+ this.toplinkDTD = inString;
+ }
+
+ /**
+ * Get the descriptor handler.
+ * @param srcDir the source file.
+ * @return the descriptor handler.
+ */
+ protected DescriptorHandler getDescriptorHandler(File srcDir) {
+ DescriptorHandler handler = super.getDescriptorHandler(srcDir);
+ if (toplinkDTD != null) {
+ handler.registerDTD("-//The Object People, Inc.//"
+ + "DTD TOPLink for WebLogic CMP 2.5.1//EN", toplinkDTD);
+ } else {
+ handler.registerDTD("-//The Object People, Inc.//"
+ + "DTD TOPLink for WebLogic CMP 2.5.1//EN", TL_DTD_LOC);
+ }
+ return handler;
+ }
+
+ /**
+ * Add any vendor specific files which should be included in the
+ * EJB Jar.
+ * @param ejbFiles the hashtable to add files to.
+ * @param ddPrefix the prefix to use.
+ */
+ protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+ super.addVendorFiles(ejbFiles, ddPrefix);
+ // Then the toplink deployment descriptor
+
+ // Setup a naming standard here?.
+
+
+ File toplinkDD = new File(getConfig().descriptorDir, ddPrefix + toplinkDescriptor);
+
+ if (toplinkDD.exists()) {
+ ejbFiles.put(META_DIR + toplinkDescriptor,
+ toplinkDD);
+ } else {
+ log("Unable to locate toplink deployment descriptor. "
+ + "It was expected to be in "
+ + toplinkDD.getPath(), Project.MSG_WARN);
+ }
+ }
+
+ /**
+ * Called to validate that the tool parameters have been configured.
+ * @throws BuildException if there is an error.
+ */
+ public void validateConfigured() throws BuildException {
+ super.validateConfigured();
+ if (toplinkDescriptor == null) {
+ throw new BuildException("The toplinkdescriptor attribute must "
+ + "be specified");
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WebsphereDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WebsphereDeploymentTool.java
new file mode 100644
index 00000000..d15f9f52
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WebsphereDeploymentTool.java
@@ -0,0 +1,897 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Websphere deployment tool that augments the ejbjar task.
+ * Searches for the websphere specific deployment descriptors and
+ * adds them to the final ejb jar file. Websphere has two specific descriptors for session
+ * beans:
+ * <ul>
+ * <li>ibm-ejb-jar-bnd.xmi</li>
+ * <li>ibm-ejb-jar-ext.xmi</li>
+ * </ul>
+ * and another two for container managed entity beans:
+ * <ul>
+ * <li>Map.mapxmi</li>
+ * <li>Schema.dbxmi</li>
+ * </ul>
+ * In terms of WebSphere, the generation of container code and stubs is
+ * called <code>deployment</code>. This step can be performed by the websphere
+ * element as part of the jar generation process. If the switch
+ * <code>ejbdeploy</code> is on, the ejbdeploy tool from the websphere toolset
+ * is called for every ejb-jar. Unfortunately, this step only works, if you
+ * use the ibm jdk. Otherwise, the rmic (called by ejbdeploy) throws a
+ * ClassFormatError. Be sure to switch ejbdeploy off, if run ant with
+ * sun jdk.
+ *
+ */
+public class WebsphereDeploymentTool extends GenericDeploymentTool {
+
+ /** ID for ejb 1.1 */
+ public static final String PUBLICID_EJB11
+ = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN";
+ /** ID for ejb 2.0 */
+ public static final String PUBLICID_EJB20
+ = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN";
+ /** Schema directory */
+ protected static final String SCHEMA_DIR = "Schema/";
+
+ protected static final String WAS_EXT = "ibm-ejb-jar-ext.xmi";
+ protected static final String WAS_BND = "ibm-ejb-jar-bnd.xmi";
+ protected static final String WAS_CMP_MAP = "Map.mapxmi";
+ protected static final String WAS_CMP_SCHEMA = "Schema.dbxmi";
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** Instance variable that stores the suffix for the websphere jarfile. */
+ private String jarSuffix = ".jar";
+
+ /** Instance variable that stores the location of the ejb 1.1 DTD file. */
+ private String ejb11DTD;
+
+ /** Instance variable that determines whether generic ejb jars are kept. */
+
+ private boolean keepGeneric = false;
+
+ private boolean alwaysRebuild = true;
+
+ private boolean ejbdeploy = true;
+
+ /** Indicates if the old CMP location convention is to be used. */
+ private boolean newCMP = false;
+
+ /** The classpath to the websphere classes. */
+ private Path wasClasspath = null;
+
+ /** The DB Vendor name, the EJB is persisted against */
+ private String dbVendor;
+
+ /** The name of the database to create. (For top-down mapping only) */
+ private String dbName;
+
+ /** The name of the schema to create. (For top-down mappings only) */
+ private String dbSchema;
+
+ /** true - Only generate the deployment code, do not run RMIC or Javac */
+ private boolean codegen;
+
+ /** true - Only output error messages, suppress informational messages */
+ private boolean quiet = true;
+
+ /** true - Disable the validation steps */
+ private boolean novalidate;
+
+ /** true - Disable warning and informational messages */
+ private boolean nowarn;
+
+ /** true - Disable informational messages */
+ private boolean noinform;
+
+ /** true - Enable internal tracing */
+ private boolean trace;
+
+ /** Additional options for RMIC */
+ private String rmicOptions;
+
+ /** true- Use the WebSphere 3.5 compatible mapping rules */
+ private boolean use35MappingRules;
+
+ /** the scratchdir for the ejbdeploy operation */
+ private String tempdir = "_ejbdeploy_temp";
+
+ /** the home directory for websphere */
+ private File websphereHome;
+
+ /**
+ * Get the classpath to the websphere classpaths.
+ * @return the websphere classpath.
+ */
+ public Path createWASClasspath() {
+ if (wasClasspath == null) {
+ wasClasspath = new Path(getTask().getProject());
+ }
+ return wasClasspath.createPath();
+ }
+
+
+ /**
+ * Set the websphere classpath.
+ * @param wasClasspath the websphere classpath.
+ */
+ public void setWASClasspath(Path wasClasspath) {
+ this.wasClasspath = wasClasspath;
+ }
+
+
+ /** Sets the DB Vendor for the Entity Bean mapping ; optional.
+ * <p>
+ * Valid options can be obtained by running the following command:
+ * <code>
+ * &lt;WAS_HOME&gt;/bin/EJBDeploy.[sh/bat] -help
+ * </code>
+ * </p>
+ * <p>
+ * This is also used to determine the name of the Map.mapxmi and
+ * Schema.dbxmi files, for example Account-DB2UDB_V81-Map.mapxmi
+ * and Account-DB2UDB_V81-Schema.dbxmi.
+ * </p>
+ *
+ * @param dbvendor database vendor type
+ */
+ public void setDbvendor(String dbvendor) {
+ this.dbVendor = dbvendor;
+ }
+
+
+ /**
+ * Sets the name of the Database to create; optional.
+ *
+ * @param dbName name of the database
+ */
+ public void setDbname(String dbName) {
+ this.dbName = dbName;
+ }
+
+
+ /**
+ * Sets the name of the schema to create; optional.
+ *
+ * @param dbSchema name of the schema
+ */
+ public void setDbschema(String dbSchema) {
+ this.dbSchema = dbSchema;
+ }
+
+
+ /**
+ * Flag, default false, to only generate the deployment
+ * code, do not run RMIC or Javac
+ *
+ * @param codegen option
+ */
+ public void setCodegen(boolean codegen) {
+ this.codegen = codegen;
+ }
+
+
+ /**
+ * Flag, default true, to only output error messages.
+ *
+ * @param quiet option
+ */
+ public void setQuiet(boolean quiet) {
+ this.quiet = quiet;
+ }
+
+
+ /**
+ * Flag to disable the validation steps; optional, default false.
+ *
+ * @param novalidate option
+ */
+ public void setNovalidate(boolean novalidate) {
+ this.novalidate = novalidate;
+ }
+
+
+ /**
+ * Flag to disable warning and informational messages; optional, default false.
+ *
+ * @param nowarn option
+ */
+ public void setNowarn(boolean nowarn) {
+ this.nowarn = nowarn;
+ }
+
+
+ /**
+ * Flag to disable informational messages; optional, default false.
+ *
+ * @param noinform if true disables informational messages
+ */
+ public void setNoinform(boolean noinform) {
+ this.noinform = noinform;
+ }
+
+
+ /**
+ * Flag to enable internal tracing when set, optional, default false.
+ *
+ * @param trace a <code>boolean</code> value.
+ */
+ public void setTrace(boolean trace) {
+ this.trace = trace;
+ }
+
+ /**
+ * Set the rmic options.
+ *
+ * @param options the options to use.
+ */
+ public void setRmicoptions(String options) {
+ this.rmicOptions = options;
+ }
+
+ /**
+ * Flag to use the WebSphere 3.5 compatible mapping rules ; optional, default false.
+ *
+ * @param attr a <code>boolean</code> value.
+ */
+ public void setUse35(boolean attr) {
+ use35MappingRules = attr;
+ }
+
+
+ /**
+ * Set the rebuild flag to false to only update changes in the jar rather
+ * than rerunning ejbdeploy; optional, default true.
+ * @param rebuild a <code>boolean</code> value.
+ */
+ public void setRebuild(boolean rebuild) {
+ this.alwaysRebuild = rebuild;
+ }
+
+
+ /**
+ * String value appended to the basename of the deployment
+ * descriptor to create the filename of the WebLogic EJB
+ * jar file. Optional, default '.jar'.
+ * @param inString the string to use as the suffix.
+ */
+ public void setSuffix(String inString) {
+ this.jarSuffix = inString;
+ }
+
+
+ /**
+ * This controls whether the generic file used as input to
+ * ejbdeploy is retained; optional, default false.
+ * @param inValue either 'true' or 'false'.
+ */
+ public void setKeepgeneric(boolean inValue) {
+ this.keepGeneric = inValue;
+ }
+
+
+ /**
+ * Decide, whether ejbdeploy should be called or not;
+ * optional, default true.
+ *
+ * @param ejbdeploy a <code>boolean</code> value.
+ */
+ public void setEjbdeploy(boolean ejbdeploy) {
+ this.ejbdeploy = ejbdeploy;
+ }
+
+
+ /**
+ * Setter used to store the location of the Sun's Generic EJB DTD. This
+ * can be a file on the system or a resource on the classpath.
+ *
+ * @param inString the string to use as the DTD location.
+ */
+ public void setEJBdtd(String inString) {
+ this.ejb11DTD = inString;
+ }
+
+
+ /**
+ * Set the value of the oldCMP scheme. This is an antonym for newCMP
+ * @ant.attribute ignore="true"
+ * @param oldCMP a <code>boolean</code> value.
+ */
+ public void setOldCMP(boolean oldCMP) {
+ this.newCMP = !oldCMP;
+ }
+
+
+ /**
+ * Set the value of the newCMP scheme. The old CMP scheme locates the
+ * websphere CMP descriptor based on the naming convention where the
+ * websphere CMP file is expected to be named with the bean name as the
+ * prefix. Under this scheme the name of the CMP descriptor does not match
+ * the name actually used in the main websphere EJB descriptor. Also,
+ * descriptors which contain multiple CMP references could not be used.
+ * @param newCMP a <code>boolean</code> value.
+ */
+ public void setNewCMP(boolean newCMP) {
+ this.newCMP = newCMP;
+ }
+
+
+ /**
+ * The directory, where ejbdeploy will write temporary files;
+ * optional, defaults to '_ejbdeploy_temp'.
+ * @param tempdir the directory name to use.
+ */
+ public void setTempdir(String tempdir) {
+ this.tempdir = tempdir;
+ }
+
+
+ /** {@inheritDoc}. */
+ protected DescriptorHandler getDescriptorHandler(File srcDir) {
+ DescriptorHandler handler = new DescriptorHandler(getTask(), srcDir);
+ // register all the DTDs, both the ones that are known and
+ // any supplied by the user
+ handler.registerDTD(PUBLICID_EJB11, ejb11DTD);
+
+ for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) {
+ EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
+
+ handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
+ }
+
+ return handler;
+ }
+
+
+ /**
+ * Get a description handler.
+ * @param srcDir the source directory.
+ * @return the handler.
+ */
+ protected DescriptorHandler getWebsphereDescriptorHandler(final File srcDir) {
+ DescriptorHandler handler =
+ new DescriptorHandler(getTask(), srcDir) {
+ protected void processElement() {
+ }
+ };
+
+ for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) {
+ EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
+
+ handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
+ }
+ return handler;
+ }
+
+
+ /**
+ * Add any vendor specific files which should be included in the EJB Jar.
+ * @param ejbFiles a hashtable entryname -> file.
+ * @param baseName a prefix to use.
+ */
+ protected void addVendorFiles(Hashtable ejbFiles, String baseName) {
+
+ String ddPrefix = (usingBaseJarName() ? "" : baseName);
+ String dbPrefix = (dbVendor == null) ? "" : dbVendor + "-";
+
+ // Get the Extensions document
+ File websphereEXT = new File(getConfig().descriptorDir, ddPrefix + WAS_EXT);
+
+ if (websphereEXT.exists()) {
+ ejbFiles.put(META_DIR + WAS_EXT,
+ websphereEXT);
+ } else {
+ log("Unable to locate websphere extensions. "
+ + "It was expected to be in "
+ + websphereEXT.getPath(), Project.MSG_VERBOSE);
+ }
+
+ File websphereBND = new File(getConfig().descriptorDir, ddPrefix + WAS_BND);
+
+ if (websphereBND.exists()) {
+ ejbFiles.put(META_DIR + WAS_BND,
+ websphereBND);
+ } else {
+ log("Unable to locate websphere bindings. "
+ + "It was expected to be in "
+ + websphereBND.getPath(), Project.MSG_VERBOSE);
+ }
+
+ if (!newCMP) {
+ log("The old method for locating CMP files has been DEPRECATED.",
+ Project.MSG_VERBOSE);
+ log("Please adjust your websphere descriptor and set "
+ + "newCMP=\"true\" to use the new CMP descriptor "
+ + "inclusion mechanism. ", Project.MSG_VERBOSE);
+ } else {
+ // We attempt to put in the MAP and Schema files of CMP beans
+ try {
+ // Add the Map file
+ File websphereMAP = new File(getConfig().descriptorDir,
+ ddPrefix + dbPrefix + WAS_CMP_MAP);
+
+ if (websphereMAP.exists()) {
+ ejbFiles.put(META_DIR + WAS_CMP_MAP,
+ websphereMAP);
+ } else {
+ log("Unable to locate the websphere Map: "
+ + websphereMAP.getPath(), Project.MSG_VERBOSE);
+ }
+
+ File websphereSchema = new File(getConfig().descriptorDir,
+ ddPrefix + dbPrefix + WAS_CMP_SCHEMA);
+
+ if (websphereSchema.exists()) {
+ ejbFiles.put(META_DIR + SCHEMA_DIR + WAS_CMP_SCHEMA,
+ websphereSchema);
+ } else {
+ log("Unable to locate the websphere Schema: "
+ + websphereSchema.getPath(), Project.MSG_VERBOSE);
+ }
+ // Theres nothing else to see here...keep moving sonny
+ } catch (Exception e) {
+ String msg = "Exception while adding Vendor specific files: "
+ + e.toString();
+
+ throw new BuildException(msg, e);
+ }
+ }
+ }
+
+
+ /**
+ * Get the vendor specific name of the Jar that will be output. The
+ * modification date of this jar will be checked against the dependent
+ * bean classes.
+ */
+ File getVendorOutputJarFile(String baseName) {
+ return new File(getDestDir(), baseName + jarSuffix);
+ }
+
+
+ /**
+ * Gets the options for the EJB Deploy operation
+ *
+ * @return String
+ */
+ protected String getOptions() {
+ // Set the options
+ StringBuffer options = new StringBuffer();
+
+ if (dbVendor != null) {
+ options.append(" -dbvendor ").append(dbVendor);
+ }
+ if (dbName != null) {
+ options.append(" -dbname \"").append(dbName).append("\"");
+ }
+
+ if (dbSchema != null) {
+ options.append(" -dbschema \"").append(dbSchema).append("\"");
+ }
+
+ if (codegen) {
+ options.append(" -codegen");
+ }
+
+ if (quiet) {
+ options.append(" -quiet");
+ }
+
+ if (novalidate) {
+ options.append(" -novalidate");
+ }
+
+ if (nowarn) {
+ options.append(" -nowarn");
+ }
+
+ if (noinform) {
+ options.append(" -noinform");
+ }
+
+ if (trace) {
+ options.append(" -trace");
+ }
+
+ if (use35MappingRules) {
+ options.append(" -35");
+ }
+
+ if (rmicOptions != null) {
+ options.append(" -rmic \"").append(rmicOptions).append("\"");
+ }
+
+ return options.toString();
+ }
+
+
+ /**
+ * Helper method invoked by execute() for each websphere jar to be built.
+ * Encapsulates the logic of constructing a java task for calling
+ * websphere.ejbdeploy and executing it.
+ *
+ * @param sourceJar java.io.File representing the source (EJB1.1) jarfile.
+ * @param destJar java.io.File representing the destination, websphere
+ * jarfile.
+ */
+ private void buildWebsphereJar(File sourceJar, File destJar) {
+ try {
+ if (ejbdeploy) {
+ Java javaTask = new Java(getTask());
+ // Set the JvmArgs
+ javaTask.createJvmarg().setValue("-Xms64m");
+ javaTask.createJvmarg().setValue("-Xmx128m");
+
+ // Set the Environment variable
+ Environment.Variable var = new Environment.Variable();
+
+ var.setKey("websphere.lib.dir");
+ File libdir = new File(websphereHome, "lib");
+ var.setValue(libdir.getAbsolutePath());
+ javaTask.addSysproperty(var);
+
+ // Set the working directory
+ javaTask.setDir(websphereHome);
+
+ // Set the Java class name
+ javaTask.setTaskName("ejbdeploy");
+ javaTask.setClassname("com.ibm.etools.ejbdeploy.EJBDeploy");
+
+ javaTask.createArg().setValue(sourceJar.getPath());
+ javaTask.createArg().setValue(tempdir);
+ javaTask.createArg().setValue(destJar.getPath());
+ javaTask.createArg().setLine(getOptions());
+ if (getCombinedClasspath() != null
+ && getCombinedClasspath().toString().length() > 0) {
+ javaTask.createArg().setValue("-cp");
+ javaTask.createArg().setValue(getCombinedClasspath().toString());
+ }
+
+ Path classpath = wasClasspath;
+
+ if (classpath == null) {
+ classpath = getCombinedClasspath();
+ }
+
+ javaTask.setFork(true);
+ if (classpath != null) {
+ javaTask.setClasspath(classpath);
+ }
+
+ log("Calling websphere.ejbdeploy for " + sourceJar.toString(),
+ Project.MSG_VERBOSE);
+
+ javaTask.execute();
+ }
+ } catch (Exception e) {
+ // Have to catch this because of the semantics of calling main()
+ String msg = "Exception while calling ejbdeploy. Details: " + e.toString();
+
+ throw new BuildException(msg, e);
+ }
+ }
+
+ /** {@inheritDoc}. */
+ protected void writeJar(String baseName, File jarFile, Hashtable files, String publicId)
+ throws BuildException {
+ if (ejbdeploy) {
+ // create the -generic.jar, if required
+ File genericJarFile = super.getVendorOutputJarFile(baseName);
+
+ super.writeJar(baseName, genericJarFile, files, publicId);
+
+ // create the output .jar, if required
+ if (alwaysRebuild || isRebuildRequired(genericJarFile, jarFile)) {
+ buildWebsphereJar(genericJarFile, jarFile);
+ }
+ if (!keepGeneric) {
+ log("deleting generic jar " + genericJarFile.toString(),
+ Project.MSG_VERBOSE);
+ genericJarFile.delete();
+ }
+ } else {
+ // create the "undeployed" output .jar, if required
+ super.writeJar(baseName, jarFile, files, publicId);
+ }
+ }
+
+
+ /**
+ * Called to validate that the tool parameters have been configured.
+ * @throws BuildException if there is an error.
+ */
+ public void validateConfigured() throws BuildException {
+ super.validateConfigured();
+ if (ejbdeploy) {
+ String home = getTask().getProject().getProperty("websphere.home");
+ if (home == null) {
+ throw new BuildException("The 'websphere.home' property must "
+ + "be set when 'ejbdeploy=true'");
+ }
+ websphereHome = getTask().getProject().resolveFile(home);
+ }
+ }
+
+
+ /**
+ * Helper method to check to see if a websphere EBJ1.1 jar needs to be
+ * rebuilt using ejbdeploy. Called from writeJar it sees if the "Bean"
+ * classes are the only thing that needs to be updated and either updates
+ * the Jar with the Bean classfile or returns true, saying that the whole
+ * websphere jar needs to be regened with ejbdeploy. This allows faster
+ * build times for working developers. <p>
+ *
+ * The way websphere ejbdeploy works is it creates wrappers for the
+ * publicly defined methods as they are exposed in the remote interface.
+ * If the actual bean changes without changing the the method signatures
+ * then only the bean classfile needs to be updated and the rest of the
+ * websphere jar file can remain the same. If the Interfaces, ie. the
+ * method signatures change or if the xml deployment descriptors changed,
+ * the whole jar needs to be rebuilt with ejbdeploy. This is not strictly
+ * true for the xml files. If the JNDI name changes then the jar doesn't
+ * have to be rebuild, but if the resources references change then it
+ * does. At this point the websphere jar gets rebuilt if the xml files
+ * change at all.
+ *
+ * @param genericJarFile java.io.File The generic jar file.
+ * @param websphereJarFile java.io.File The websphere jar file to check to
+ * see if it needs to be rebuilt.
+ * @return true if a rebuild is required.
+ */
+ // CheckStyle:MethodLength OFF - this will no be fixed
+ protected boolean isRebuildRequired(File genericJarFile, File websphereJarFile) {
+ boolean rebuild = false;
+
+ JarFile genericJar = null;
+ JarFile wasJar = null;
+ File newwasJarFile = null;
+ JarOutputStream newJarStream = null;
+ ClassLoader genericLoader = null;
+
+ try {
+ log("Checking if websphere Jar needs to be rebuilt for jar "
+ + websphereJarFile.getName(), Project.MSG_VERBOSE);
+ // Only go forward if the generic and the websphere file both exist
+ if (genericJarFile.exists() && genericJarFile.isFile()
+ && websphereJarFile.exists() && websphereJarFile.isFile()) {
+ //open jar files
+ genericJar = new JarFile(genericJarFile);
+ wasJar = new JarFile(websphereJarFile);
+
+ Hashtable genericEntries = new Hashtable();
+ Hashtable wasEntries = new Hashtable();
+ Hashtable replaceEntries = new Hashtable();
+
+ //get the list of generic jar entries
+ for (Enumeration e = genericJar.entries(); e.hasMoreElements();) {
+ JarEntry je = (JarEntry) e.nextElement();
+
+ genericEntries.put(je.getName().replace('\\', '/'), je);
+ }
+ //get the list of websphere jar entries
+ for (Enumeration e = wasJar.entries(); e.hasMoreElements();) {
+ JarEntry je = (JarEntry) e.nextElement();
+
+ wasEntries.put(je.getName(), je);
+ }
+
+ //Cycle Through generic and make sure its in websphere
+ genericLoader = getClassLoaderFromJar(genericJarFile);
+
+ for (Enumeration e = genericEntries.keys(); e.hasMoreElements();) {
+ String filepath = (String) e.nextElement();
+
+ if (wasEntries.containsKey(filepath)) {
+ // File name/path match
+ // Check files see if same
+ JarEntry genericEntry = (JarEntry) genericEntries.get(filepath);
+ JarEntry wasEntry = (JarEntry) wasEntries.get(filepath);
+
+ if ((genericEntry.getCrc() != wasEntry.getCrc())
+ || (genericEntry.getSize() != wasEntry.getSize())) {
+
+ if (genericEntry.getName().endsWith(".class")) {
+ //File are different see if its an object or an interface
+ String classname
+ = genericEntry.getName().replace(File.separatorChar, '.');
+
+ classname = classname.substring(0, classname.lastIndexOf(".class"));
+
+ Class genclass = genericLoader.loadClass(classname);
+
+ if (genclass.isInterface()) {
+ //Interface changed rebuild jar.
+ log("Interface " + genclass.getName()
+ + " has changed", Project.MSG_VERBOSE);
+ rebuild = true;
+ break;
+ } else {
+ //Object class Changed update it.
+ replaceEntries.put(filepath, genericEntry);
+ }
+ } else {
+ // is it the manifest. If so ignore it
+ if (!genericEntry.getName().equals("META-INF/MANIFEST.MF")) {
+ //File other then class changed rebuild
+ log("Non class file " + genericEntry.getName()
+ + " has changed", Project.MSG_VERBOSE);
+ rebuild = true;
+ }
+ break;
+ }
+ }
+ } else {
+ // a file doesn't exist rebuild
+
+ log("File " + filepath + " not present in websphere jar",
+ Project.MSG_VERBOSE);
+ rebuild = true;
+ break;
+ }
+ }
+
+ if (!rebuild) {
+ log("No rebuild needed - updating jar", Project.MSG_VERBOSE);
+ newwasJarFile = new File(websphereJarFile.getAbsolutePath() + ".temp");
+ if (newwasJarFile.exists()) {
+ newwasJarFile.delete();
+ }
+
+ newJarStream = new JarOutputStream(new FileOutputStream(newwasJarFile));
+ newJarStream.setLevel(0);
+
+ //Copy files from old websphere jar
+ for (Enumeration e = wasEntries.elements(); e.hasMoreElements();) {
+ byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+ int bytesRead;
+ InputStream is;
+ JarEntry je = (JarEntry) e.nextElement();
+
+ if (je.getCompressedSize() == -1
+ || je.getCompressedSize() == je.getSize()) {
+ newJarStream.setLevel(0);
+ } else {
+ newJarStream.setLevel(JAR_COMPRESS_LEVEL);
+ }
+
+ // Update with changed Bean class
+ if (replaceEntries.containsKey(je.getName())) {
+ log("Updating Bean class from generic Jar " + je.getName(),
+ Project.MSG_VERBOSE);
+ // Use the entry from the generic jar
+ je = (JarEntry) replaceEntries.get(je.getName());
+ is = genericJar.getInputStream(je);
+ } else {
+ //use fle from original websphere jar
+
+ is = wasJar.getInputStream(je);
+ }
+ newJarStream.putNextEntry(new JarEntry(je.getName()));
+
+ while ((bytesRead = is.read(buffer)) != -1) {
+ newJarStream.write(buffer, 0, bytesRead);
+ }
+ is.close();
+ }
+ } else {
+ log("websphere Jar rebuild needed due to changed "
+ + "interface or XML", Project.MSG_VERBOSE);
+ }
+ } else {
+ rebuild = true;
+ }
+ } catch (ClassNotFoundException cnfe) {
+ String cnfmsg = "ClassNotFoundException while processing ejb-jar file"
+ + ". Details: "
+ + cnfe.getMessage();
+
+ throw new BuildException(cnfmsg, cnfe);
+ } catch (IOException ioe) {
+ String msg = "IOException while processing ejb-jar file "
+ + ". Details: "
+ + ioe.getMessage();
+
+ throw new BuildException(msg, ioe);
+ } finally {
+ // need to close files and perhaps rename output
+ if (genericJar != null) {
+ try {
+ genericJar.close();
+ } catch (IOException closeException) {
+ // Ignore
+ }
+ }
+
+ if (wasJar != null) {
+ try {
+ wasJar.close();
+ } catch (IOException closeException) {
+ // Ignore
+ }
+ }
+
+ if (newJarStream != null) {
+ try {
+ newJarStream.close();
+ } catch (IOException closeException) {
+ // Ignore
+ }
+
+ try {
+ FILE_UTILS.rename(newwasJarFile, websphereJarFile);
+ } catch (IOException renameException) {
+ log(renameException.getMessage(), Project.MSG_WARN);
+ rebuild = true;
+ }
+ }
+ if (genericLoader != null
+ && genericLoader instanceof AntClassLoader) {
+ AntClassLoader loader = (AntClassLoader) genericLoader;
+ loader.cleanup();
+ }
+ }
+
+ return rebuild;
+ }
+
+
+ /**
+ * Helper method invoked by isRebuildRequired to get a ClassLoader for a
+ * Jar File passed to it.
+ *
+ * @param classjar java.io.File representing jar file to get classes from.
+ * @return a classloader for the jar file.
+ * @throws IOException if there is an error.
+ */
+ protected ClassLoader getClassLoaderFromJar(File classjar) throws IOException {
+ Path lookupPath = new Path(getTask().getProject());
+
+ lookupPath.setLocation(classjar);
+
+ Path classpath = getCombinedClasspath();
+
+ if (classpath != null) {
+ lookupPath.append(classpath);
+ }
+
+ return getTask().getProject().createClassLoader(lookupPath);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatability.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatability.java
new file mode 100644
index 00000000..2c06daf1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatability.java
@@ -0,0 +1,57 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension;
+
+/**
+ * Enum used in (@link Extension) to indicate the compatibility
+ * of one extension to another. See (@link Extension) for instances
+ * of object.
+ *
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ * This file is from excalibur.extension package. Dont edit this file
+ * directly as there is no unit tests to make sure it is operational
+ * in ant. Edit file in excalibur and run tests there before changing
+ * ants file.
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ *
+ * @see Extension
+ */
+public final class Compatability {
+ /**
+ * A string representation of compatibility level.
+ */
+ private final String name;
+
+ /**
+ * Create a compatibility enum with specified name.
+ *
+ * @param name the name of compatibility level
+ */
+ Compatability(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Return name of compatibility level.
+ *
+ * @return the name of compatibility level
+ */
+ public String toString() {
+ return name;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatibility.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatibility.java
new file mode 100644
index 00000000..bb28cd6b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatibility.java
@@ -0,0 +1,57 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension;
+
+/**
+ * Enum used in (@link Extension) to indicate the compatibility
+ * of one extension to another. See (@link Extension) for instances
+ * of object.
+ *
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ * This file is from excalibur.extension package. Dont edit this file
+ * directly as there is no unit tests to make sure it is operational
+ * in ant. Edit file in excalibur and run tests there before changing
+ * ants file.
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ *
+ * @see Extension
+ */
+public final class Compatibility {
+ /**
+ * A string representation of compatibility level.
+ */
+ private final String name;
+
+ /**
+ * Create a compatibility enum with specified name.
+ *
+ * @param name the name of compatibility level
+ */
+ Compatibility(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Return name of compatibility level.
+ *
+ * @return the name of compatibility level
+ */
+ public String toString() {
+ return name;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/DeweyDecimal.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/DeweyDecimal.java
new file mode 100644
index 00000000..2edc2a78
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/DeweyDecimal.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.tools.ant.taskdefs.optional.extension;
+
+
+/**
+ * Utility class to contain version numbers in "Dewey Decimal"
+ * syntax. Numbers in the "Dewey Decimal" syntax consist of positive
+ * decimal integers separated by periods ".". For example, "2.0" or
+ * "1.2.3.4.5.6.7". This allows an extensible number to be used to
+ * represent major, minor, micro, etc versions. The version number
+ * must begin with a number.
+ *
+ * Original Implementation moved to org.apache.tools.ant.util.DeweyDecimal
+ * @deprecated use org.apache.tools.ant.util.DeweyDecimal instead.
+ * Deprecated since ant 1.8
+ */
+public final class DeweyDecimal extends org.apache.tools.ant.util.DeweyDecimal {
+
+ /**
+ * Construct a DeweyDecimal from an array of integer components.
+ *
+ * @param components an array of integer components.
+ */
+ public DeweyDecimal(final int[] components) {
+ super(components);
+ }
+
+ /**
+ * Construct a DeweyDecimal from string in DeweyDecimal format.
+ *
+ * @param string the string in dewey decimal format
+ * @exception NumberFormatException if string is malformed
+ */
+ public DeweyDecimal(final String string)
+ throws NumberFormatException {
+ super(string);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Extension.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Extension.java
new file mode 100644
index 00000000..d13d2f4e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Extension.java
@@ -0,0 +1,690 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import org.apache.tools.ant.util.DeweyDecimal;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * <p>Utility class that represents either an available "Optional Package"
+ * (formerly known as "Standard Extension") as described in the manifest
+ * of a JAR file, or the requirement for such an optional package.</p>
+ *
+ * <p>For more information about optional packages, see the document
+ * <em>Optional Package Versioning</em> in the documentation bundle for your
+ * Java2 Standard Edition package, in file
+ * <code>guide/extensions/versioning.html</code>.</p>
+ *
+ */
+public final class Extension {
+ /**
+ * Manifest Attribute Name object for EXTENSION_LIST.
+ */
+ public static final Attributes.Name EXTENSION_LIST
+ = new Attributes.Name("Extension-List");
+
+ /**
+ * <code>Name</code> object for <code>Optional-Extension-List</code>
+ * manifest attribute used for declaring optional dependencies on
+ * installed extensions. Note that the dependencies declared by this method
+ * are not required for the library to operate but if present will be used.
+ * It is NOT part of the official "Optional Package" specification.
+ *
+ * @see <a href="http://java.sun.com/j2se/1.3/docs/guide/extensions/spec.html#dependency">
+ * Installed extension dependency</a>
+ */
+ public static final Attributes.Name OPTIONAL_EXTENSION_LIST
+ = new Attributes.Name("Optional-Extension-List");
+
+ /**
+ * Manifest Attribute Name object for EXTENSION_NAME.
+ */
+ public static final Attributes.Name EXTENSION_NAME =
+ new Attributes.Name("Extension-Name");
+ /**
+ * Manifest Attribute Name object for SPECIFICATION_VERSION.
+ */
+ public static final Attributes.Name SPECIFICATION_VERSION
+ = Attributes.Name.SPECIFICATION_VERSION;
+
+ /**
+ * Manifest Attribute Name object for SPECIFICATION_VENDOR.
+ */
+ public static final Attributes.Name SPECIFICATION_VENDOR
+ = Attributes.Name.SPECIFICATION_VENDOR;
+
+ /**
+ * Manifest Attribute Name object for IMPLEMENTATION_VERSION.
+ */
+ public static final Attributes.Name IMPLEMENTATION_VERSION
+ = Attributes.Name.IMPLEMENTATION_VERSION;
+
+ /**
+ * Manifest Attribute Name object for IMPLEMENTATION_VENDOR.
+ */
+ public static final Attributes.Name IMPLEMENTATION_VENDOR
+ = Attributes.Name.IMPLEMENTATION_VENDOR;
+
+ /**
+ * Manifest Attribute Name object for IMPLEMENTATION_URL.
+ */
+ public static final Attributes.Name IMPLEMENTATION_URL
+ = new Attributes.Name("Implementation-URL");
+
+ /**
+ * Manifest Attribute Name object for IMPLEMENTATION_VENDOR_ID.
+ */
+ public static final Attributes.Name IMPLEMENTATION_VENDOR_ID
+ = new Attributes.Name("Implementation-Vendor-Id");
+
+ /**
+ * Enum indicating that extension is compatible with other extension.
+ */
+ public static final Compatibility COMPATIBLE
+ = new Compatibility("COMPATIBLE");
+
+ /**
+ * Enum indicating that extension requires an upgrade
+ * of specification to be compatible with other extension.
+ */
+ public static final Compatibility REQUIRE_SPECIFICATION_UPGRADE
+ = new Compatibility("REQUIRE_SPECIFICATION_UPGRADE");
+
+ /**
+ * Enum indicating that extension requires a vendor
+ * switch to be compatible with other extension.
+ */
+ public static final Compatibility REQUIRE_VENDOR_SWITCH
+ = new Compatibility("REQUIRE_VENDOR_SWITCH");
+
+ /**
+ * Enum indicating that extension requires an upgrade
+ * of implementation to be compatible with other extension.
+ */
+ public static final Compatibility REQUIRE_IMPLEMENTATION_UPGRADE
+ = new Compatibility("REQUIRE_IMPLEMENTATION_UPGRADE");
+
+ /**
+ * Enum indicating that extension is incompatible with
+ * other extension in ways other than other enums
+ * indicate). For example the other extension may have
+ * a different ID.
+ */
+ public static final Compatibility INCOMPATIBLE
+ = new Compatibility("INCOMPATIBLE");
+
+ /**
+ * The name of the optional package being made available, or required.
+ */
+ private String extensionName;
+
+ /**
+ * The version number (dotted decimal notation) of the specification
+ * to which this optional package conforms.
+ */
+ private DeweyDecimal specificationVersion;
+
+ /**
+ * The name of the company or organization that originated the
+ * specification to which this optional package conforms.
+ */
+ private String specificationVendor;
+
+ /**
+ * The unique identifier of the company that produced the optional
+ * package contained in this JAR file.
+ */
+ private String implementationVendorID;
+
+ /**
+ * The name of the company or organization that produced this
+ * implementation of this optional package.
+ */
+ private String implementationVendor;
+
+ /**
+ * The version number (dotted decimal notation) for this implementation
+ * of the optional package.
+ */
+ private DeweyDecimal implementationVersion;
+
+ /**
+ * The URL from which the most recent version of this optional package
+ * can be obtained if it is not already installed.
+ */
+ private String implementationURL;
+
+ /**
+ * Return an array of <code>Extension</code> objects representing optional
+ * packages that are available in the JAR file associated with the
+ * specified <code>Manifest</code>. If there are no such optional
+ * packages, a zero-length array is returned.
+ *
+ * @param manifest Manifest to be parsed
+ * @return the "available" extensions in specified manifest
+ */
+ public static Extension[] getAvailable(final Manifest manifest) {
+ if (null == manifest) {
+ return new Extension[ 0 ];
+ }
+
+ final ArrayList results = new ArrayList();
+
+ final Attributes mainAttributes = manifest.getMainAttributes();
+ if (null != mainAttributes) {
+ final Extension extension = getExtension("", mainAttributes);
+ if (null != extension) {
+ results.add(extension);
+ }
+ }
+
+ final Map entries = manifest.getEntries();
+ final Iterator keys = entries.keySet().iterator();
+ while (keys.hasNext()) {
+ final String key = (String) keys.next();
+ final Attributes attributes = (Attributes) entries.get(key);
+ final Extension extension = getExtension("", attributes);
+ if (null != extension) {
+ results.add(extension);
+ }
+ }
+
+ return (Extension[]) results.toArray(new Extension[results.size()]);
+ }
+
+ /**
+ * Return the set of <code>Extension</code> objects representing optional
+ * packages that are required by the application contained in the JAR
+ * file associated with the specified <code>Manifest</code>. If there
+ * are no such optional packages, a zero-length list is returned.
+ *
+ * @param manifest Manifest to be parsed
+ * @return the dependencies that are specified in manifest
+ */
+ public static Extension[] getRequired(final Manifest manifest) {
+ return getListed(manifest, Attributes.Name.EXTENSION_LIST);
+ }
+
+ /**
+ * Return the set of <code>Extension</code> objects representing "Optional
+ * Packages" that the application declares they will use if present. If
+ * there are no such optional packages, a zero-length list is returned.
+ *
+ * @param manifest Manifest to be parsed
+ * @return the optional dependencies that are specified in manifest
+ */
+ public static Extension[] getOptions(final Manifest manifest) {
+ return getListed(manifest, OPTIONAL_EXTENSION_LIST);
+ }
+
+ /**
+ * Add Extension to the specified manifest Attributes.
+ *
+ * @param attributes the attributes of manifest to add to
+ * @param extension the extension
+ */
+ public static void addExtension(final Extension extension,
+ final Attributes attributes) {
+ addExtension(extension, "", attributes);
+ }
+
+ /**
+ * Add Extension to the specified manifest Attributes.
+ * Use the specified prefix so that dependencies can added
+ * with a prefix such as "java3d-" etc.
+ *
+ * @param attributes the attributes of manifest to add to
+ * @param extension the extension
+ * @param prefix the name to prefix to extension
+ */
+ public static void addExtension(final Extension extension,
+ final String prefix,
+ final Attributes attributes) {
+ attributes.putValue(prefix + EXTENSION_NAME,
+ extension.getExtensionName());
+
+ final String specificationVendor = extension.getSpecificationVendor();
+ if (null != specificationVendor) {
+ attributes.putValue(prefix + SPECIFICATION_VENDOR,
+ specificationVendor);
+ }
+
+ final DeweyDecimal specificationVersion
+ = extension.getSpecificationVersion();
+ if (null != specificationVersion) {
+ attributes.putValue(prefix + SPECIFICATION_VERSION,
+ specificationVersion.toString());
+ }
+
+ final String implementationVendorID
+ = extension.getImplementationVendorID();
+ if (null != implementationVendorID) {
+ attributes.putValue(prefix + IMPLEMENTATION_VENDOR_ID,
+ implementationVendorID);
+ }
+
+ final String implementationVendor = extension.getImplementationVendor();
+ if (null != implementationVendor) {
+ attributes.putValue(prefix + IMPLEMENTATION_VENDOR,
+ implementationVendor);
+ }
+
+ final DeweyDecimal implementationVersion
+ = extension.getImplementationVersion();
+ if (null != implementationVersion) {
+ attributes.putValue(prefix + IMPLEMENTATION_VERSION,
+ implementationVersion.toString());
+ }
+
+ final String implementationURL = extension.getImplementationURL();
+ if (null != implementationURL) {
+ attributes.putValue(prefix + IMPLEMENTATION_URL,
+ implementationURL);
+ }
+ }
+
+ /**
+ * The constructor to create Extension object.
+ * Note that every component is allowed to be specified
+ * but only the extensionName is mandatory.
+ *
+ * @param extensionName the name of extension.
+ * @param specificationVersion the specification Version of extension.
+ * @param specificationVendor the specification Vendor of extension.
+ * @param implementationVersion the implementation Version of extension.
+ * @param implementationVendor the implementation Vendor of extension.
+ * @param implementationVendorId the implementation VendorId of extension.
+ * @param implementationURL the implementation URL of extension.
+ */
+ public Extension(final String extensionName,
+ final String specificationVersion,
+ final String specificationVendor,
+ final String implementationVersion,
+ final String implementationVendor,
+ final String implementationVendorId,
+ final String implementationURL) {
+ this.extensionName = extensionName;
+ this.specificationVendor = specificationVendor;
+
+ if (null != specificationVersion) {
+ try {
+ this.specificationVersion
+ = new DeweyDecimal(specificationVersion);
+ } catch (final NumberFormatException nfe) {
+ final String error = "Bad specification version format '"
+ + specificationVersion + "' in '" + extensionName
+ + "'. (Reason: " + nfe + ")";
+ throw new IllegalArgumentException(error);
+ }
+ }
+
+ this.implementationURL = implementationURL;
+ this.implementationVendor = implementationVendor;
+ this.implementationVendorID = implementationVendorId;
+
+ if (null != implementationVersion) {
+ try {
+ this.implementationVersion
+ = new DeweyDecimal(implementationVersion);
+ } catch (final NumberFormatException nfe) {
+ final String error = "Bad implementation version format '"
+ + implementationVersion + "' in '" + extensionName
+ + "'. (Reason: " + nfe + ")";
+ throw new IllegalArgumentException(error);
+ }
+ }
+
+ if (null == this.extensionName) {
+ throw new NullPointerException("extensionName property is null");
+ }
+ }
+
+ /**
+ * Get the name of the extension.
+ *
+ * @return the name of the extension
+ */
+ public String getExtensionName() {
+ return extensionName;
+ }
+
+ /**
+ * Get the vendor of the extensions specification.
+ *
+ * @return the vendor of the extensions specification.
+ */
+ public String getSpecificationVendor() {
+ return specificationVendor;
+ }
+
+ /**
+ * Get the version of the extensions specification.
+ *
+ * @return the version of the extensions specification.
+ */
+ public DeweyDecimal getSpecificationVersion() {
+ return specificationVersion;
+ }
+
+ /**
+ * Get the url of the extensions implementation.
+ *
+ * @return the url of the extensions implementation.
+ */
+ public String getImplementationURL() {
+ return implementationURL;
+ }
+
+ /**
+ * Get the vendor of the extensions implementation.
+ *
+ * @return the vendor of the extensions implementation.
+ */
+ public String getImplementationVendor() {
+ return implementationVendor;
+ }
+
+ /**
+ * Get the vendorID of the extensions implementation.
+ *
+ * @return the vendorID of the extensions implementation.
+ */
+ public String getImplementationVendorID() {
+ return implementationVendorID;
+ }
+
+ /**
+ * Get the version of the extensions implementation.
+ *
+ * @return the version of the extensions implementation.
+ */
+ public DeweyDecimal getImplementationVersion() {
+ return implementationVersion;
+ }
+
+ /**
+ * Return a Compatibility enum indicating the relationship of this
+ * <code>Extension</code> with the specified <code>Extension</code>.
+ *
+ * @param required Description of the required optional package
+ * @return the enum indicating the compatibility (or lack thereof)
+ * of specified extension
+ */
+ public Compatibility getCompatibilityWith(final Extension required) {
+ // Extension Name must match
+ if (!extensionName.equals(required.getExtensionName())) {
+ return INCOMPATIBLE;
+ }
+
+ // Available specification version must be >= required
+ final DeweyDecimal requiredSpecificationVersion
+ = required.getSpecificationVersion();
+ if (null != requiredSpecificationVersion) {
+ if (null == specificationVersion
+ || !isCompatible(specificationVersion, requiredSpecificationVersion)) {
+ return REQUIRE_SPECIFICATION_UPGRADE;
+ }
+ }
+
+ // Implementation Vendor ID must match
+ final String requiredImplementationVendorID
+ = required.getImplementationVendorID();
+ if (null != requiredImplementationVendorID) {
+ if (null == implementationVendorID
+ || !implementationVendorID.equals(requiredImplementationVendorID)) {
+ return REQUIRE_VENDOR_SWITCH;
+ }
+ }
+
+ // Implementation version must be >= required
+ final DeweyDecimal requiredImplementationVersion
+ = required.getImplementationVersion();
+ if (null != requiredImplementationVersion) {
+ if (null == implementationVersion
+ || !isCompatible(implementationVersion, requiredImplementationVersion)) {
+ return REQUIRE_IMPLEMENTATION_UPGRADE;
+ }
+ }
+
+ // This available optional package satisfies the requirements
+ return COMPATIBLE;
+ }
+
+ /**
+ * Return <code>true</code> if the specified <code>Extension</code>
+ * (which represents an optional package required by an application)
+ * is satisfied by this <code>Extension</code> (which represents an
+ * optional package that is already installed. Otherwise, return
+ * <code>false</code>.
+ *
+ * @param required Description of the required optional package
+ * @return true if the specified extension is compatible with this extension
+ */
+ public boolean isCompatibleWith(final Extension required) {
+ return (COMPATIBLE == getCompatibilityWith(required));
+ }
+
+ /**
+ * Return a String representation of this object.
+ *
+ * @return string representation of object.
+ */
+ public String toString() {
+ final String brace = ": ";
+
+ final StringBuffer sb = new StringBuffer(EXTENSION_NAME.toString());
+ sb.append(brace);
+ sb.append(extensionName);
+ sb.append(StringUtils.LINE_SEP);
+
+ if (null != specificationVersion) {
+ sb.append(SPECIFICATION_VERSION);
+ sb.append(brace);
+ sb.append(specificationVersion);
+ sb.append(StringUtils.LINE_SEP);
+ }
+
+ if (null != specificationVendor) {
+ sb.append(SPECIFICATION_VENDOR);
+ sb.append(brace);
+ sb.append(specificationVendor);
+ sb.append(StringUtils.LINE_SEP);
+ }
+
+ if (null != implementationVersion) {
+ sb.append(IMPLEMENTATION_VERSION);
+ sb.append(brace);
+ sb.append(implementationVersion);
+ sb.append(StringUtils.LINE_SEP);
+ }
+
+ if (null != implementationVendorID) {
+ sb.append(IMPLEMENTATION_VENDOR_ID);
+ sb.append(brace);
+ sb.append(implementationVendorID);
+ sb.append(StringUtils.LINE_SEP);
+ }
+
+ if (null != implementationVendor) {
+ sb.append(IMPLEMENTATION_VENDOR);
+ sb.append(brace);
+ sb.append(implementationVendor);
+ sb.append(StringUtils.LINE_SEP);
+ }
+
+ if (null != implementationURL) {
+ sb.append(IMPLEMENTATION_URL);
+ sb.append(brace);
+ sb.append(implementationURL);
+ sb.append(StringUtils.LINE_SEP);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Return <code>true</code> if the first version number is greater than
+ * or equal to the second; otherwise return <code>false</code>.
+ *
+ * @param first First version number (dotted decimal)
+ * @param second Second version number (dotted decimal)
+ */
+ private boolean isCompatible(final DeweyDecimal first,
+ final DeweyDecimal second) {
+ return first.isGreaterThanOrEqual(second);
+ }
+
+ /**
+ * Retrieve all the extensions listed under a particular key
+ * (Usually EXTENSION_LIST or OPTIONAL_EXTENSION_LIST).
+ *
+ * @param manifest the manifest to extract extensions from
+ * @param listKey the key used to get list (Usually
+ * EXTENSION_LIST or OPTIONAL_EXTENSION_LIST)
+ * @return the list of listed extensions
+ */
+ private static Extension[] getListed(final Manifest manifest,
+ final Attributes.Name listKey) {
+ final ArrayList results = new ArrayList();
+ final Attributes mainAttributes = manifest.getMainAttributes();
+
+ if (null != mainAttributes) {
+ getExtension(mainAttributes, results, listKey);
+ }
+
+ final Map entries = manifest.getEntries();
+ final Iterator keys = entries.keySet().iterator();
+ while (keys.hasNext()) {
+ final String key = (String) keys.next();
+ final Attributes attributes = (Attributes) entries.get(key);
+ getExtension(attributes, results, listKey);
+ }
+
+ return (Extension[]) results.toArray(new Extension[results.size()]);
+ }
+
+ /**
+ * Add required optional packages defined in the specified
+ * attributes entry, if any.
+ *
+ * @param attributes Attributes to be parsed
+ * @param required list to add required optional packages to
+ * @param listKey the key to use to lookup list, usually EXTENSION_LIST
+ * or OPTIONAL_EXTENSION_LIST
+ */
+ private static void getExtension(final Attributes attributes,
+ final ArrayList required,
+ final Attributes.Name listKey) {
+ final String names = attributes.getValue(listKey);
+ if (null == names) {
+ return;
+ }
+
+ final String[] extensions = split(names, " ");
+ for (int i = 0; i < extensions.length; i++) {
+ final String prefix = extensions[ i ] + "-";
+ final Extension extension = getExtension(prefix, attributes);
+
+ if (null != extension) {
+ required.add(extension);
+ }
+ }
+ }
+
+ /**
+ * Splits the string on every token into an array of strings.
+ *
+ * @param string the string
+ * @param onToken the token
+ * @return the resultant array
+ */
+ private static String[] split(final String string,
+ final String onToken) {
+ final StringTokenizer tokenizer = new StringTokenizer(string, onToken);
+ final String[] result = new String[ tokenizer.countTokens() ];
+
+ for (int i = 0; i < result.length; i++) {
+ result[ i ] = tokenizer.nextToken();
+ }
+
+ return result;
+ }
+
+ /**
+ * Extract an Extension from Attributes.
+ * Prefix indicates the prefix checked for each string.
+ * Usually the prefix is <em>"&lt;extension&gt;-"</em> if looking for a
+ * <b>Required</b> extension. If you are looking for an
+ * <b>Available</b> extension
+ * then the prefix is <em>""</em>.
+ *
+ * @param prefix the prefix for each attribute name
+ * @param attributes Attributes to searched
+ * @return the new Extension object, or null
+ */
+ private static Extension getExtension(final String prefix,
+ final Attributes attributes) {
+ //WARNING: We trim the values of all the attributes because
+ //Some extension declarations are badly defined (ie have spaces
+ //after version or vendorID)
+ final String nameKey = prefix + EXTENSION_NAME;
+ final String name = getTrimmedString(attributes.getValue(nameKey));
+ if (null == name) {
+ return null;
+ }
+
+ final String specVendorKey = prefix + SPECIFICATION_VENDOR;
+ final String specVendor
+ = getTrimmedString(attributes.getValue(specVendorKey));
+ final String specVersionKey = prefix + SPECIFICATION_VERSION;
+ final String specVersion
+ = getTrimmedString(attributes.getValue(specVersionKey));
+
+ final String impVersionKey = prefix + IMPLEMENTATION_VERSION;
+ final String impVersion
+ = getTrimmedString(attributes.getValue(impVersionKey));
+ final String impVendorKey = prefix + IMPLEMENTATION_VENDOR;
+ final String impVendor
+ = getTrimmedString(attributes.getValue(impVendorKey));
+ final String impVendorIDKey = prefix + IMPLEMENTATION_VENDOR_ID;
+ final String impVendorId
+ = getTrimmedString(attributes.getValue(impVendorIDKey));
+ final String impURLKey = prefix + IMPLEMENTATION_URL;
+ final String impURL = getTrimmedString(attributes.getValue(impURLKey));
+
+ return new Extension(name, specVersion, specVendor, impVersion,
+ impVendor, impVendorId, impURL);
+ }
+
+ /**
+ * Trim the supplied string if the string is non-null
+ *
+ * @param value the string to trim or null
+ * @return the trimmed string or null
+ */
+ private static String getTrimmedString(final String value) {
+ return null == value ? null : value.trim();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionAdapter.java
new file mode 100644
index 00000000..b3cfddc2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionAdapter.java
@@ -0,0 +1,215 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.DeweyDecimal;
+
+/**
+ * Simple class that represents an Extension and conforms to Ants
+ * patterns.
+ *
+ * @ant.datatype name="extension"
+ */
+public class ExtensionAdapter extends DataType {
+ /**
+ * The name of the optional package being made available, or required.
+ */
+ private String extensionName;
+
+ /**
+ * The version number (dotted decimal notation) of the specification
+ * to which this optional package conforms.
+ */
+ private DeweyDecimal specificationVersion;
+
+ /**
+ * The name of the company or organization that originated the
+ * specification to which this optional package conforms.
+ */
+ private String specificationVendor;
+
+ /**
+ * The unique identifier of the company that produced the optional
+ * package contained in this JAR file.
+ */
+ private String implementationVendorID;
+
+ /**
+ * The name of the company or organization that produced this
+ * implementation of this optional package.
+ */
+ private String implementationVendor;
+
+ /**
+ * The version number (dotted decimal notation) for this implementation
+ * of the optional package.
+ */
+ private DeweyDecimal implementationVersion;
+
+ /**
+ * The URL from which the most recent version of this optional package
+ * can be obtained if it is not already installed.
+ */
+ private String implementationURL;
+
+ /**
+ * Set the name of extension.
+ *
+ * @param extensionName the name of extension
+ */
+ public void setExtensionName(final String extensionName) {
+ verifyNotAReference();
+ this.extensionName = extensionName;
+ }
+
+ /**
+ * Set the specificationVersion of extension.
+ *
+ * @param specificationVersion the specificationVersion of extension
+ */
+ public void setSpecificationVersion(final String specificationVersion) {
+ verifyNotAReference();
+ this.specificationVersion = new DeweyDecimal(specificationVersion);
+ }
+
+ /**
+ * Set the specificationVendor of extension.
+ *
+ * @param specificationVendor the specificationVendor of extension
+ */
+ public void setSpecificationVendor(final String specificationVendor) {
+ verifyNotAReference();
+ this.specificationVendor = specificationVendor;
+ }
+
+ /**
+ * Set the implementationVendorID of extension.
+ *
+ * @param implementationVendorID the implementationVendorID of extension
+ */
+ public void setImplementationVendorId(final String implementationVendorID) {
+ verifyNotAReference();
+ this.implementationVendorID = implementationVendorID;
+ }
+
+ /**
+ * Set the implementationVendor of extension.
+ *
+ * @param implementationVendor the implementationVendor of extension
+ */
+ public void setImplementationVendor(final String implementationVendor) {
+ verifyNotAReference();
+ this.implementationVendor = implementationVendor;
+ }
+
+ /**
+ * Set the implementationVersion of extension.
+ *
+ * @param implementationVersion the implementationVersion of extension
+ */
+ public void setImplementationVersion(final String implementationVersion) {
+ verifyNotAReference();
+ this.implementationVersion = new DeweyDecimal(implementationVersion);
+ }
+
+ /**
+ * Set the implementationURL of extension.
+ *
+ * @param implementationURL the implementationURL of extension
+ */
+ public void setImplementationUrl(final String implementationURL) {
+ verifyNotAReference();
+ this.implementationURL = implementationURL;
+ }
+
+ /**
+ * Makes this instance in effect a reference to another ExtensionAdapter
+ * instance.
+ *
+ * <p>You must not set another attribute or nest elements inside
+ * this element if you make it a reference.</p>
+ *
+ * @param reference the reference to which this instance is associated
+ * @exception BuildException if this instance already has been configured.
+ */
+ public void setRefid(final Reference reference)
+ throws BuildException {
+ if (null != extensionName
+ || null != specificationVersion
+ || null != specificationVendor
+ || null != implementationVersion
+ || null != implementationVendorID
+ || null != implementationVendor
+ || null != implementationURL) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(reference);
+ }
+
+ private void verifyNotAReference()
+ throws BuildException {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ }
+
+ /**
+ * Convert this adpater object into an extension object.
+ *
+ * @return the extension object
+ */
+ Extension toExtension()
+ throws BuildException {
+ if (isReference()) {
+ return ((ExtensionAdapter) getCheckedRef()).toExtension();
+ }
+ dieOnCircularReference();
+ if (null == extensionName) {
+ final String message = "Extension is missing name.";
+ throw new BuildException(message);
+ }
+
+ String specificationVersionString = null;
+ if (null != specificationVersion) {
+ specificationVersionString = specificationVersion.toString();
+ }
+ String implementationVersionString = null;
+ if (null != implementationVersion) {
+ implementationVersionString = implementationVersion.toString();
+ }
+ return new Extension(extensionName,
+ specificationVersionString,
+ specificationVendor,
+ implementationVersionString,
+ implementationVendor,
+ implementationVendorID,
+ implementationURL);
+ }
+
+ /**
+ * a debug toString method.
+ * @return the extension in a string.
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return "{" + toExtension().toString() + "}";
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionResolver.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionResolver.java
new file mode 100644
index 00000000..a73282ec
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionResolver.java
@@ -0,0 +1,43 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Interface to locate a File that satisfies extension.
+ *
+ */
+public interface ExtensionResolver {
+ /**
+ * Attempt to locate File that satisfies
+ * extension via resolver.
+ *
+ * @param extension the extension
+ * @param project the Ant project instance
+ * @return the File satisfying extension, null
+ * if can not resolve extension
+ * @throws BuildException if error occurs attempting to
+ * resolve extension
+ */
+ File resolve(Extension extension, Project project)
+ throws BuildException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionSet.java
new file mode 100644
index 00000000..5aba37c9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionSet.java
@@ -0,0 +1,153 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * The Extension set lists a set of "Optional Packages" /
+ * "Extensions".
+ *
+ * @ant.datatype name="extension-set"
+ */
+public class ExtensionSet
+ extends DataType {
+ /**
+ * ExtensionAdapter objects representing extensions.
+ */
+ private final ArrayList extensions = new ArrayList();
+
+ /**
+ * Filesets specifying all the extensions wanted.
+ */
+ private final ArrayList extensionsFilesets = new ArrayList();
+
+ /**
+ * Adds an extension that this library requires.
+ *
+ * @param extensionAdapter an extension that this library requires.
+ */
+ public void addExtension(final ExtensionAdapter extensionAdapter) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ extensions.add(extensionAdapter);
+ }
+
+ /**
+ * Adds a set of files about which extensions data will be extracted.
+ *
+ * @param fileSet a set of files about which extensions data will be extracted.
+ */
+ public void addLibfileset(final LibFileSet fileSet) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ extensionsFilesets.add(fileSet);
+ }
+
+ /**
+ * Adds a set of files about which extensions data will be extracted.
+ *
+ * @param fileSet a set of files about which extensions data will be extracted.
+ */
+ public void addFileset(final FileSet fileSet) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ extensionsFilesets.add(fileSet);
+ }
+
+ /**
+ * Extract a set of Extension objects from the ExtensionSet.
+ *
+ * @param proj the project instance.
+ * @return an array containing the Extensions from this set
+ * @throws BuildException if an error occurs
+ */
+ public Extension[] toExtensions(final Project proj)
+ throws BuildException {
+ if (isReference()) {
+ return ((ExtensionSet) getCheckedRef()).toExtensions(proj);
+ }
+ dieOnCircularReference();
+ final ArrayList extensionsList = ExtensionUtil.toExtensions(extensions);
+ ExtensionUtil.extractExtensions(proj, extensionsList, extensionsFilesets);
+ return (Extension[]) extensionsList.toArray(new Extension[extensionsList.size()]);
+ }
+
+ /**
+ * Makes this instance in effect a reference to another ExtensionSet
+ * instance.
+ *
+ * <p>You must not set another attribute or nest elements inside
+ * this element if you make it a reference.</p>
+ *
+ * @param reference the reference to which this instance is associated
+ * @exception BuildException if this instance already has been configured.
+ */
+ @Override
+ public void setRefid(final Reference reference)
+ throws BuildException {
+ if (!extensions.isEmpty() || !extensionsFilesets.isEmpty()) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(reference);
+ }
+
+ @Override
+ protected synchronized void dieOnCircularReference(Stack stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ for (Iterator i = extensions.iterator(); i.hasNext();) {
+ pushAndInvokeCircularReferenceCheck((ExtensionAdapter) i.next(),
+ stk, p);
+ }
+ for (Iterator i = extensionsFilesets.iterator(); i.hasNext();) {
+ pushAndInvokeCircularReferenceCheck((FileSet) i.next(), stk, p);
+ }
+ setChecked(true);
+ }
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ * @return the extensions in a string.
+ */
+ @Override
+ public String toString() {
+ return "ExtensionSet" + Arrays.asList(toExtensions(getProject()));
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionUtil.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionUtil.java
new file mode 100644
index 00000000..089c7894
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionUtil.java
@@ -0,0 +1,215 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * A set of useful methods relating to extensions.
+ *
+ */
+public final class ExtensionUtil {
+ /**
+ * Class is not meant to be instantiated.
+ */
+ private ExtensionUtil() {
+ //all methods static
+ }
+
+ /**
+ * Convert a list of extensionAdapter objects to extensions.
+ *
+ * @param adapters the list of ExtensionAdapterss to add to convert
+ * @throws BuildException if an error occurs
+ */
+ static ArrayList toExtensions(final List adapters)
+ throws BuildException {
+ final ArrayList results = new ArrayList();
+
+ final int size = adapters.size();
+ for (int i = 0; i < size; i++) {
+ final ExtensionAdapter adapter =
+ (ExtensionAdapter) adapters.get(i);
+ final Extension extension = adapter.toExtension();
+ results.add(extension);
+ }
+
+ return results;
+ }
+
+ /**
+ * Generate a list of extensions from a specified fileset.
+ *
+ * @param libraries the list to add extensions to
+ * @param fileset the filesets containing librarys
+ * @throws BuildException if an error occurs
+ */
+ static void extractExtensions(final Project project,
+ final List libraries,
+ final List fileset)
+ throws BuildException {
+ if (!fileset.isEmpty()) {
+ final Extension[] extensions = getExtensions(project,
+ fileset);
+ for (int i = 0; i < extensions.length; i++) {
+ libraries.add(extensions[ i ]);
+ }
+ }
+ }
+
+ /**
+ * Retrieve extensions from the specified libraries.
+ *
+ * @param libraries the filesets for libraries
+ * @return the extensions contained in libraries
+ * @throws BuildException if failing to scan libraries
+ */
+ private static Extension[] getExtensions(final Project project,
+ final List libraries)
+ throws BuildException {
+ final ArrayList extensions = new ArrayList();
+ final Iterator iterator = libraries.iterator();
+ while (iterator.hasNext()) {
+ final FileSet fileSet = (FileSet) iterator.next();
+
+ boolean includeImpl = true;
+ boolean includeURL = true;
+
+ if (fileSet instanceof LibFileSet) {
+ LibFileSet libFileSet = (LibFileSet) fileSet;
+ includeImpl = libFileSet.isIncludeImpl();
+ includeURL = libFileSet.isIncludeURL();
+ }
+
+ final DirectoryScanner scanner = fileSet.getDirectoryScanner(project);
+ final File basedir = scanner.getBasedir();
+ final String[] files = scanner.getIncludedFiles();
+ for (int i = 0; i < files.length; i++) {
+ final File file = new File(basedir, files[ i ]);
+ loadExtensions(file, extensions, includeImpl, includeURL);
+ }
+ }
+ return (Extension[]) extensions.toArray(new Extension[extensions.size()]);
+ }
+
+ /**
+ * Load list of available extensions from specified file.
+ *
+ * @param file the file
+ * @param extensionList the list to add available extensions to
+ * @throws BuildException if there is an error
+ */
+ private static void loadExtensions(final File file,
+ final List extensionList,
+ final boolean includeImpl,
+ final boolean includeURL)
+ throws BuildException {
+ try {
+ final JarFile jarFile = new JarFile(file);
+ final Extension[] extensions =
+ Extension.getAvailable(jarFile.getManifest());
+ for (int i = 0; i < extensions.length; i++) {
+ final Extension extension = extensions[ i ];
+ addExtension(extensionList, extension, includeImpl, includeURL);
+ }
+ } catch (final Exception e) {
+ throw new BuildException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Add extension to list.
+ * If extension should not have implementation details but
+ * does strip them. If extension should not have url but does
+ * then strip it.
+ *
+ * @param extensionList the list of extensions to add to
+ * @param originalExtension the extension
+ * @param includeImpl false to exclude implementation details
+ * @param includeURL false to exclude implementation URL
+ */
+ private static void addExtension(final List extensionList,
+ final Extension originalExtension,
+ final boolean includeImpl,
+ final boolean includeURL) {
+ Extension extension = originalExtension;
+ if (!includeURL
+ && null != extension.getImplementationURL()) {
+ extension =
+ new Extension(extension.getExtensionName(),
+ extension.getSpecificationVersion().toString(),
+ extension.getSpecificationVendor(),
+ extension.getImplementationVersion().toString(),
+ extension.getImplementationVendor(),
+ extension.getImplementationVendorID(),
+ null);
+ }
+
+ final boolean hasImplAttributes =
+ null != extension.getImplementationURL()
+ || null != extension.getImplementationVersion()
+ || null != extension.getImplementationVendorID()
+ || null != extension.getImplementationVendor();
+
+ if (!includeImpl && hasImplAttributes) {
+ extension =
+ new Extension(extension.getExtensionName(),
+ extension.getSpecificationVersion().toString(),
+ extension.getSpecificationVendor(),
+ null,
+ null,
+ null,
+ extension.getImplementationURL());
+ }
+
+ extensionList.add(extension);
+ }
+
+ /**
+ * Retrieve manifest for specified file.
+ *
+ * @param file the file
+ * @return the manifest
+ * @throws BuildException if errror occurs (file doesn't exist,
+ * file not a jar, manifest doesn't exist in file)
+ */
+ static Manifest getManifest(final File file)
+ throws BuildException {
+ try {
+ final JarFile jarFile = new JarFile(file);
+ Manifest m = jarFile.getManifest();
+ if (m == null) {
+ throw new BuildException(file + " doesn't have a MANIFEST");
+ }
+ return m;
+ } catch (final IOException ioe) {
+ throw new BuildException(ioe.getMessage(), ioe);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtraAttribute.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtraAttribute.java
new file mode 100644
index 00000000..d52bec41
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtraAttribute.java
@@ -0,0 +1,83 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Simple holder for extra attributes in main section of manifest.
+ *
+ * @todo Refactor this and all the other parameter, sysproperty,
+ * property etc into a single class in framework
+ */
+public class ExtraAttribute {
+ private String name;
+ private String value;
+
+ /**
+ * Set the name of the parameter.
+ *
+ * @param name the name of parameter
+ */
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Set the value of the parameter.
+ *
+ * @param value the parameter value
+ */
+ public void setValue(final String value) {
+ this.value = value;
+ }
+
+ /**
+ * Retrieve name of parameter.
+ *
+ * @return the name of parameter.
+ */
+ String getName() {
+ return name;
+ }
+
+ /**
+ * Retrieve the value of parameter.
+ *
+ * @return the value of parameter.
+ */
+ String getValue() {
+ return value;
+ }
+
+ /**
+ * Make sure that neither the name or the value
+ * is null.
+ *
+ * @throws BuildException if the attribute is invalid.
+ */
+ public void validate() throws BuildException {
+ if (null == name) {
+ final String message = "Missing name from parameter.";
+ throw new BuildException(message);
+ } else if (null == value) {
+ final String message = "Missing value from parameter " + name + ".";
+ throw new BuildException(message);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibAvailableTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibAvailableTask.java
new file mode 100644
index 00000000..cebcf0d5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibAvailableTask.java
@@ -0,0 +1,157 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.jar.Manifest;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Checks whether an extension is present in a fileset or an extensionSet.
+ *
+ * @ant.task name="jarlib-available"
+ */
+public class JarLibAvailableTask extends Task {
+ /**
+ * The library to display information about.
+ */
+ private File libraryFile;
+
+ /**
+ * Filesets specifying all the librarys
+ * to display information about.
+ */
+ private final Vector extensionFileSets = new Vector();
+
+ /**
+ * The name of the property to set if extension is available.
+ */
+ private String propertyName;
+
+ /**
+ * The extension that is required.
+ */
+ private ExtensionAdapter requiredExtension;
+
+ /**
+ * The name of property to set if extensions are available.
+ *
+ * @param property The name of property to set if extensions is available.
+ */
+ public void setProperty(final String property) {
+ this.propertyName = property;
+ }
+
+ /**
+ * The JAR library to check.
+ *
+ * @param file The jar library to check.
+ */
+ public void setFile(final File file) {
+ this.libraryFile = file;
+ }
+
+ /**
+ * Set the Extension looking for.
+ *
+ * @param extension Set the Extension looking for.
+ */
+ public void addConfiguredExtension(final ExtensionAdapter extension) {
+ if (null != requiredExtension) {
+ final String message = "Can not specify extension to "
+ + "search for multiple times.";
+ throw new BuildException(message);
+ }
+ requiredExtension = extension;
+ }
+
+ /**
+ * Adds a set of extensions to search in.
+ *
+ * @param extensionSet a set of extensions to search in.
+ */
+ public void addConfiguredExtensionSet(final ExtensionSet extensionSet) {
+ extensionFileSets.addElement(extensionSet);
+ }
+
+ /**
+ * Execute the task.
+ *
+ * @throws BuildException if something goes wrong.
+ */
+ public void execute() throws BuildException {
+ validate();
+
+ final Extension test = requiredExtension.toExtension();
+
+ // Check if list of files to check has been specified
+ if (!extensionFileSets.isEmpty()) {
+ final Iterator iterator = extensionFileSets.iterator();
+ while (iterator.hasNext()) {
+ final ExtensionSet extensionSet
+ = (ExtensionSet) iterator.next();
+ final Extension[] extensions =
+ extensionSet.toExtensions(getProject());
+ for (int i = 0; i < extensions.length; i++) {
+ final Extension extension = extensions[ i ];
+ if (extension.isCompatibleWith(test)) {
+ getProject().setNewProperty(propertyName, "true");
+ }
+ }
+ }
+ } else {
+ final Manifest manifest = ExtensionUtil.getManifest(libraryFile);
+ final Extension[] extensions = Extension.getAvailable(manifest);
+ for (int i = 0; i < extensions.length; i++) {
+ final Extension extension = extensions[ i ];
+ if (extension.isCompatibleWith(test)) {
+ getProject().setNewProperty(propertyName, "true");
+ }
+ }
+ }
+ }
+
+ /**
+ * Validate the tasks parameters.
+ *
+ * @throws BuildException if invalid parameters found
+ */
+ private void validate() throws BuildException {
+ if (null == requiredExtension) {
+ final String message = "Extension element must be specified.";
+ throw new BuildException(message);
+ }
+
+ if (null == libraryFile && extensionFileSets.isEmpty()) {
+ final String message = "File attribute not specified.";
+ throw new BuildException(message);
+ }
+ if (null != libraryFile && !libraryFile.exists()) {
+ final String message = "File '" + libraryFile + "' does not exist.";
+ throw new BuildException(message);
+ }
+ if (null != libraryFile && !libraryFile.isFile()) {
+ final String message = "\'" + libraryFile + "\' is not a file.";
+ throw new BuildException(message);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibDisplayTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibDisplayTask.java
new file mode 100644
index 00000000..da12cd02
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibDisplayTask.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.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * Displays the "Optional Package" and "Package Specification" information
+ * contained within the specified JARs.
+ *
+ * <p>Prior to JDK1.3, an "Optional Package" was known as an Extension.
+ * The specification for this mechanism is available in the JDK1.3
+ * documentation in the directory
+ * $JDK_HOME/docs/guide/extensions/versioning.html. Alternatively it is
+ * available online at <a href="http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html">
+ * http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html</a>.</p>
+ *
+ * @ant.task name="jarlib-display"
+ */
+public class JarLibDisplayTask extends Task {
+ /**
+ * The library to display information about.
+ */
+ private File libraryFile;
+
+ /**
+ * Filesets specifying all the librarys
+ * to display information about.
+ */
+ private final Vector libraryFileSets = new Vector();
+
+ /**
+ * The JAR library to display information for.
+ *
+ * @param file The jar library to display information for.
+ */
+ public void setFile(final File file) {
+ this.libraryFile = file;
+ }
+
+ /**
+ * Adds a set of files about which library data will be displayed.
+ *
+ * @param fileSet a set of files about which library data will be displayed.
+ */
+ public void addFileset(final FileSet fileSet) {
+ libraryFileSets.addElement(fileSet);
+ }
+
+ /**
+ * Execute the task.
+ *
+ * @throws BuildException if the task fails.
+ */
+ public void execute() throws BuildException {
+ validate();
+
+ final LibraryDisplayer displayer = new LibraryDisplayer();
+ // Check if list of files to check has been specified
+ if (!libraryFileSets.isEmpty()) {
+ final Iterator iterator = libraryFileSets.iterator();
+ while (iterator.hasNext()) {
+ final FileSet fileSet = (FileSet) iterator.next();
+ final DirectoryScanner scanner
+ = fileSet.getDirectoryScanner(getProject());
+ final File basedir = scanner.getBasedir();
+ final String[] files = scanner.getIncludedFiles();
+ for (int i = 0; i < files.length; i++) {
+ final File file = new File(basedir, files[ i ]);
+ displayer.displayLibrary(file);
+ }
+ }
+ } else {
+ displayer.displayLibrary(libraryFile);
+ }
+ }
+
+ /**
+ * Validate the tasks parameters.
+ *
+ * @throws BuildException if invalid parameters found
+ */
+ private void validate() throws BuildException {
+ if (null == libraryFile && libraryFileSets.isEmpty()) {
+ final String message = "File attribute not specified.";
+ throw new BuildException(message);
+ }
+ if (null != libraryFile && !libraryFile.exists()) {
+ final String message = "File '" + libraryFile + "' does not exist.";
+ throw new BuildException(message);
+ }
+ if (null != libraryFile && !libraryFile.isFile()) {
+ final String message = "\'" + libraryFile + "\' is not a file.";
+ throw new BuildException(message);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibManifestTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibManifestTask.java
new file mode 100644
index 00000000..5afc57f1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibManifestTask.java
@@ -0,0 +1,296 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Generates a manifest that declares all the dependencies.
+ * The dependencies are determined by looking in the
+ * specified path and searching for Extension / "Optional Package"
+ * specifications in the manifests of the jars.
+ *
+ * <p>Prior to JDK1.3, an "Optional Package" was known as an Extension.
+ * The specification for this mechanism is available in the JDK1.3
+ * documentation in the directory
+ * $JDK_HOME/docs/guide/extensions/versioning.html. Alternatively it is
+ * available online at <a href="http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html">
+ * http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html</a>.</p>
+ *
+ * @ant.task name="jarlib-manifest"
+ */
+public final class JarLibManifestTask extends Task {
+ /**
+ * Version of manifest spec that task generates.
+ */
+ private static final String MANIFEST_VERSION = "1.0";
+
+ /**
+ * "Created-By" string used when creating manifest.
+ */
+ private static final String CREATED_BY = "Created-By";
+
+ /**
+ * The library to display information about.
+ */
+ private File destFile;
+
+ /**
+ * The extension supported by this library (if any).
+ */
+ private Extension extension;
+
+ /**
+ * ExtensionAdapter objects representing
+ * dependencies required by library.
+ */
+ private final ArrayList dependencies = new ArrayList();
+
+ /**
+ * ExtensionAdapter objects representing optional
+ * dependencies required by library.
+ */
+ private final ArrayList optionals = new ArrayList();
+
+ /**
+ * Extra attributes the user specifies for main section
+ * in manifest.
+ */
+ private final ArrayList extraAttributes = new ArrayList();
+
+ /**
+ * The location where generated manifest is placed.
+ *
+ * @param destFile The location where generated manifest is placed.
+ */
+ public void setDestfile(final File destFile) {
+ this.destFile = destFile;
+ }
+
+ /**
+ * Adds an extension that this library implements.
+ *
+ * @param extensionAdapter an extension that this library implements.
+ *
+ * @throws BuildException if there is multiple extensions detected
+ * in the library.
+ */
+ public void addConfiguredExtension(final ExtensionAdapter extensionAdapter)
+ throws BuildException {
+ if (null != extension) {
+ throw new BuildException("Can not have multiple extensions defined in one library.");
+ }
+ extension = extensionAdapter.toExtension();
+ }
+
+ /**
+ * Adds a set of extensions that this library requires.
+ *
+ * @param extensionSet a set of extensions that this library requires.
+ */
+ public void addConfiguredDepends(final ExtensionSet extensionSet) {
+ dependencies.add(extensionSet);
+ }
+
+ /**
+ * Adds a set of extensions that this library optionally requires.
+ *
+ * @param extensionSet a set of extensions that this library optionally requires.
+ */
+ public void addConfiguredOptions(final ExtensionSet extensionSet) {
+ optionals.add(extensionSet);
+ }
+
+ /**
+ * Adds an attribute that is to be put in main section of manifest.
+ *
+ * @param attribute an attribute that is to be put in main section of manifest.
+ */
+ public void addConfiguredAttribute(final ExtraAttribute attribute) {
+ extraAttributes.add(attribute);
+ }
+
+ /**
+ * Execute the task.
+ *
+ * @throws BuildException if the task fails.
+ */
+ public void execute() throws BuildException {
+ validate();
+
+ final Manifest manifest = new Manifest();
+ final Attributes attributes = manifest.getMainAttributes();
+
+ attributes.put(Attributes.Name.MANIFEST_VERSION, MANIFEST_VERSION);
+ attributes.putValue(CREATED_BY, "Apache Ant "
+ + getProject().getProperty(MagicNames.ANT_VERSION));
+
+ appendExtraAttributes(attributes);
+
+ if (null != extension) {
+ Extension.addExtension(extension, attributes);
+ }
+
+ //Add all the dependency data to manifest for dependencies
+ final ArrayList depends = toExtensions(dependencies);
+ appendExtensionList(attributes, Extension.EXTENSION_LIST, "lib", depends.size());
+ appendLibraryList(attributes, "lib", depends);
+
+ // Add all the dependency data to manifest for "optional"
+ //dependencies
+ final ArrayList option = toExtensions(optionals);
+ appendExtensionList(attributes, Extension.OPTIONAL_EXTENSION_LIST, "opt", option.size());
+ appendLibraryList(attributes, "opt", option);
+
+ try {
+ log("Generating manifest " + destFile.getAbsoluteFile(), Project.MSG_INFO);
+ writeManifest(manifest);
+ } catch (final IOException ioe) {
+ throw new BuildException(ioe.getMessage(), ioe);
+ }
+ }
+
+ /**
+ * Validate the tasks parameters.
+ *
+ * @throws BuildException if invalid parameters found
+ */
+ private void validate() throws BuildException {
+ if (null == destFile) {
+ throw new BuildException("Destfile attribute not specified.");
+ }
+ if (destFile.exists() && !destFile.isFile()) {
+ throw new BuildException(destFile + " is not a file.");
+ }
+ }
+
+ /**
+ * Add any extra attributes to the manifest.
+ *
+ * @param attributes the manifest section to write
+ * attributes to
+ */
+ private void appendExtraAttributes(final Attributes attributes) {
+ final Iterator iterator = extraAttributes.iterator();
+ while (iterator.hasNext()) {
+ final ExtraAttribute attribute =
+ (ExtraAttribute) iterator.next();
+ attributes.putValue(attribute.getName(),
+ attribute.getValue());
+ }
+ }
+
+ /**
+ * Write out manifest to destfile.
+ *
+ * @param manifest the manifest
+ * @throws IOException if error writing file
+ */
+ private void writeManifest(final Manifest manifest) throws IOException {
+ FileOutputStream output = null;
+ try {
+ output = new FileOutputStream(destFile);
+ manifest.write(output);
+ output.flush();
+ } finally {
+ if (null != output) {
+ try {
+ output.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Append specified extensions to specified attributes.
+ * Use the extensionKey to list the extensions, usually "Extension-List:"
+ * for required dependencies and "Optional-Extension-List:" for optional
+ * dependencies. NOTE: "Optional" dependencies are not part of the
+ * specification.
+ *
+ * @param attributes the attributes to add extensions to
+ * @param extensions the list of extensions
+ * @throws BuildException if an error occurs
+ */
+ private void appendLibraryList(final Attributes attributes, final String listPrefix,
+ final ArrayList extensions) throws BuildException {
+ final int size = extensions.size();
+ for (int i = 0; i < size; i++) {
+ final Extension ext = (Extension) extensions.get(i);
+ final String prefix = listPrefix + i + "-";
+ Extension.addExtension(ext, prefix, attributes);
+ }
+ }
+
+ /**
+ * Append an attribute such as "Extension-List: lib0 lib1 lib2"
+ * using specified prefix and counting up to specified size.
+ * Also use specified extensionKey so that can generate list of
+ * optional dependencies as well.
+ *
+ * @param size the number of librarys to list
+ * @param listPrefix the prefix for all librarys
+ * @param attributes the attributes to add key-value to
+ * @param extensionKey the key to use
+ */
+ private void appendExtensionList(final Attributes attributes,
+ final Attributes.Name extensionKey, final String listPrefix, final int size) {
+ final StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < size; i++) {
+ sb.append(listPrefix);
+ sb.append(i);
+ sb.append(' ');
+ }
+ //add in something like
+ //"Extension-List: javahelp java3d"
+ attributes.put(extensionKey, sb.toString());
+ }
+
+ /**
+ * Convert a list of ExtensionSet objects to extensions.
+ *
+ * @param extensionSets the list of ExtensionSets to add to list
+ * @throws BuildException if an error occurs
+ */
+ private ArrayList toExtensions(final ArrayList extensionSets) throws BuildException {
+ final ArrayList results = new ArrayList();
+
+ final int size = extensionSets.size();
+ for (int i = 0; i < size; i++) {
+ final ExtensionSet set = (ExtensionSet) extensionSets.get(i);
+ final Extension[] extensions = set.toExtensions(getProject());
+ for (int j = 0; j < extensions.length; j++) {
+ results.add(extensions[ j ]);
+ }
+ }
+ return results;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibResolveTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibResolveTask.java
new file mode 100644
index 00000000..c13194fa
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibResolveTask.java
@@ -0,0 +1,268 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.jar.Manifest;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.optional.extension.resolvers.AntResolver;
+import org.apache.tools.ant.taskdefs.optional.extension.resolvers.LocationResolver;
+import org.apache.tools.ant.taskdefs.optional.extension.resolvers.URLResolver;
+
+/**
+ * Tries to locate a JAR to satisfy an extension and place
+ * location of JAR into property.
+ *
+ * @ant.task name="jarlib-resolve"
+ */
+public class JarLibResolveTask extends Task {
+ /**
+ * The name of the property in which the location of
+ * library is stored.
+ */
+ private String propertyName;
+
+ /**
+ * The extension that is required.
+ */
+ private Extension requiredExtension;
+
+ /**
+ * The set of resolvers to use to attempt to locate library.
+ */
+ private final ArrayList resolvers = new ArrayList();
+
+ /**
+ * Flag to indicate that you should check that
+ * the librarys resolved actually contain
+ * extension and if they don't then raise
+ * an exception.
+ */
+ private boolean checkExtension = true;
+
+ /**
+ * Flag indicating whether or not you should
+ * throw a BuildException if you cannot resolve
+ * library.
+ */
+ private boolean failOnError = true;
+
+ /**
+ * The name of the property in which the location of
+ * library is stored.
+ *
+ * @param property The name of the property in which the location of
+ * library is stored.
+ */
+ public void setProperty(final String property) {
+ this.propertyName = property;
+ }
+
+ /**
+ * Check nested libraries for extensions
+ *
+ * @param checkExtension if true, libraries returned by nested
+ * resolvers should be checked to see if they supply extension.
+ */
+ public void setCheckExtension(final boolean checkExtension) {
+ this.checkExtension = checkExtension;
+ }
+
+ /**
+ * Set whether to fail if error.
+ *
+ * @param failOnError if true, failure to locate library should fail build.
+ */
+ public void setFailOnError(final boolean failOnError) {
+ this.failOnError = failOnError;
+ }
+
+ /**
+ * Adds location resolver to look for a library in a location
+ * relative to project directory.
+ *
+ * @param loc the resolver location to search.
+ */
+ public void addConfiguredLocation(final LocationResolver loc) {
+ resolvers.add(loc);
+ }
+
+ /**
+ * Adds a URL resolver to download a library from a URL
+ * to a local file.
+ *
+ * @param url the URL resolver from which to download the library
+ */
+ public void addConfiguredUrl(final URLResolver url) {
+ resolvers.add(url);
+ }
+
+ /**
+ * Adds Ant resolver to run an Ant build file to generate a library.
+ *
+ * @param ant the AntResolver to generate the library.
+ */
+ public void addConfiguredAnt(final AntResolver ant) {
+ resolvers.add(ant);
+ }
+
+ /**
+ * Set the Extension looking for.
+ *
+ * @param extension Set the Extension looking for.
+ */
+ public void addConfiguredExtension(final ExtensionAdapter extension) {
+ if (null != requiredExtension) {
+ final String message = "Can not specify extension to "
+ + "resolve multiple times.";
+ throw new BuildException(message);
+ }
+ requiredExtension = extension.toExtension();
+ }
+
+ /**
+ * Execute the task.
+ *
+ * @throws BuildException if the task fails.
+ */
+ public void execute() throws BuildException {
+ validate();
+
+ getProject().log("Resolving extension: " + requiredExtension, Project.MSG_VERBOSE);
+
+ String candidate = getProject().getProperty(propertyName);
+
+ if (null != candidate) {
+ final String message = "Property Already set to: " + candidate;
+ if (failOnError) {
+ throw new BuildException(message);
+ }
+ getProject().log(message, Project.MSG_ERR);
+ return;
+ }
+
+ final int size = resolvers.size();
+ for (int i = 0; i < size; i++) {
+ final ExtensionResolver resolver =
+ (ExtensionResolver) resolvers.get(i);
+
+ getProject().log("Searching for extension using Resolver:" + resolver,
+ Project.MSG_VERBOSE);
+
+ try {
+ final File file = resolver.resolve(requiredExtension, getProject());
+ try {
+ checkExtension(file);
+ return;
+ } catch (final BuildException be) {
+ final String message = "File " + file + " returned by "
+ + "resolver failed to satisfy extension due to: " + be.getMessage();
+ getProject().log(message, Project.MSG_WARN);
+ }
+ } catch (final BuildException be) {
+ final String message = "Failed to resolve extension to file " + "using resolver "
+ + resolver + " due to: " + be;
+ getProject().log(message, Project.MSG_WARN);
+ }
+ }
+ missingExtension();
+ }
+
+ /**
+ * Utility method that will throw a {@link BuildException}
+ * if {@link #failOnError} is true else it just displays
+ * a warning.
+ */
+ private void missingExtension() {
+ final String message = "Unable to resolve extension to a file";
+ if (failOnError) {
+ throw new BuildException(message);
+ }
+ getProject().log(message, Project.MSG_ERR);
+ }
+
+ /**
+ * Check if specified file satisfies extension.
+ * If it does then set the relevant property
+ * else throw a BuildException.
+ *
+ * @param file the candidate library
+ * @throws BuildException if library does not satisfy extension
+ */
+ private void checkExtension(final File file) {
+ if (!file.exists()) {
+ throw new BuildException("File " + file + " does not exist");
+ }
+ if (!file.isFile()) {
+ throw new BuildException("File " + file + " is not a file");
+ }
+ if (!checkExtension) {
+ getProject().log("Setting property to " + file
+ + " without verifying library satisfies extension", Project.MSG_VERBOSE);
+ setLibraryProperty(file);
+ } else {
+ getProject().log("Checking file " + file + " to see if it satisfies extension",
+ Project.MSG_VERBOSE);
+ final Manifest manifest = ExtensionUtil.getManifest(file);
+ final Extension[] extensions = Extension.getAvailable(manifest);
+ for (int i = 0; i < extensions.length; i++) {
+ final Extension extension = extensions[ i ];
+ if (extension.isCompatibleWith(requiredExtension)) {
+ setLibraryProperty(file);
+ return;
+ }
+ }
+ final String message = "File " + file + " skipped as it "
+ + "does not satisfy extension";
+ getProject().log(message, Project.MSG_VERBOSE);
+ throw new BuildException(message);
+ }
+ }
+
+ /**
+ * Utility method to set the appropriate property
+ * to indicate that specified file satisfies library
+ * requirements.
+ *
+ * @param file the library
+ */
+ private void setLibraryProperty(final File file) {
+ getProject().setNewProperty(propertyName, file.getAbsolutePath());
+ }
+
+ /**
+ * Validate the tasks parameters.
+ *
+ * @throws BuildException if invalid parameters found
+ */
+ private void validate() throws BuildException {
+ if (null == propertyName) {
+ final String message = "Property attribute must be specified.";
+ throw new BuildException(message);
+ }
+
+ if (null == requiredExtension) {
+ final String message = "Extension element must be specified.";
+ throw new BuildException(message);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibFileSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibFileSet.java
new file mode 100644
index 00000000..b21719e5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibFileSet.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.tools.ant.taskdefs.optional.extension;
+
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * LibFileSet represents a fileset containing libraries.
+ * Associated with the libraries is data pertaining to
+ * how they are to be handled when building manifests.
+ *
+ */
+public class LibFileSet
+ extends FileSet {
+ /**
+ * Flag indicating whether should include the
+ * "Implementation-URL" attribute in manifest.
+ * Defaults to false.
+ */
+ private boolean includeURL;
+
+ /**
+ * Flag indicating whether should include the
+ * "Implementation-*" attributes in manifest.
+ * Defaults to false.
+ */
+ private boolean includeImpl;
+
+ /**
+ * String that is the base URL for the librarys
+ * when constructing the "Implementation-URL"
+ * attribute. For instance setting the base to
+ * "http://jakarta.apache.org/avalon/libs/" and then
+ * including the library "excalibur-cli-1.0.jar" in the
+ * fileset will result in the "Implementation-URL" attribute
+ * being set to "http://jakarta.apache.org/avalon/libs/excalibur-cli-1.0.jar"
+ *
+ * Note this is only used if the library does not define
+ * "Implementation-URL" itself.
+ *
+ * Note that this also implies includeURL=true
+ */
+ private String urlBase;
+
+ /**
+ * Flag indicating whether should include the
+ * "Implementation-URL" attribute in manifest.
+ * Defaults to false.
+ *
+ * @param includeURL the flag
+ */
+ public void setIncludeUrl(boolean includeURL) {
+ this.includeURL = includeURL;
+ }
+
+ /**
+ * Flag indicating whether should include the
+ * "Implementation-*" attributes in manifest.
+ * Defaults to false.
+ *
+ * @param includeImpl the flag
+ */
+ public void setIncludeImpl(boolean includeImpl) {
+ this.includeImpl = includeImpl;
+ }
+
+ /**
+ * Set the url base for fileset.
+ *
+ * @param urlBase the base url
+ */
+ public void setUrlBase(String urlBase) {
+ this.urlBase = urlBase;
+ }
+
+ /**
+ * Get the includeURL flag.
+ *
+ * @return the includeURL flag.
+ */
+ boolean isIncludeURL() {
+ return includeURL;
+ }
+
+ /**
+ * Get the includeImpl flag.
+ *
+ * @return the includeImpl flag.
+ */
+ boolean isIncludeImpl() {
+ return includeImpl;
+ }
+
+ /**
+ * Get the urlbase.
+ *
+ * @return the urlbase.
+ */
+ String getUrlBase() {
+ return urlBase;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibraryDisplayer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibraryDisplayer.java
new file mode 100644
index 00000000..b0ee4f81
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibraryDisplayer.java
@@ -0,0 +1,150 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+import java.text.ParseException;
+import java.util.jar.Manifest;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Utility class to output the information in a jar relating
+ * to "Optional Packages" (formely known as "Extensions")
+ * and Package Specifications.
+ *
+ */
+class LibraryDisplayer {
+ /**
+ * Display the extensions and specifications contained
+ * within specified file.
+ *
+ * @param file the file
+ * @throws BuildException if fail to read file
+ */
+ void displayLibrary(final File file)
+ throws BuildException {
+ final Manifest manifest = ExtensionUtil.getManifest(file);
+ displayLibrary(file, manifest);
+ }
+
+ /**
+ * Display the extensions and specifications contained
+ * within specified file.
+ *
+ * @param file the file to use while reporting
+ * @param manifest the manifest of file
+ * @throws BuildException if fail to read file
+ */
+ void displayLibrary(final File file,
+ final Manifest manifest)
+ throws BuildException {
+ final Extension[] available = Extension.getAvailable(manifest);
+ final Extension[] required = Extension.getRequired(manifest);
+ final Extension[] options = Extension.getOptions(manifest);
+ final Specification[] specifications = getSpecifications(manifest);
+
+ if (0 == available.length && 0 == required.length && 0 == options.length
+ && 0 == specifications.length) {
+ return;
+ }
+
+ final String message = "File: " + file;
+ final int size = message.length();
+ printLine(size);
+ System.out.println(message);
+ printLine(size);
+ if (0 != available.length) {
+ System.out.println("Extensions Supported By Library:");
+ for (int i = 0; i < available.length; i++) {
+ final Extension extension = available[ i ];
+ System.out.println(extension.toString());
+ }
+ }
+
+ if (0 != required.length) {
+ System.out.println("Extensions Required By Library:");
+ for (int i = 0; i < required.length; i++) {
+ final Extension extension = required[ i ];
+ System.out.println(extension.toString());
+ }
+ }
+
+ if (0 != options.length) {
+ System.out.println("Extensions that will be used by Library if present:");
+ for (int i = 0; i < options.length; i++) {
+ final Extension extension = options[ i ];
+ System.out.println(extension.toString());
+ }
+ }
+
+ if (0 != specifications.length) {
+ System.out.println("Specifications Supported By Library:");
+ for (int i = 0; i < specifications.length; i++) {
+ final Specification specification = specifications[ i ];
+ displaySpecification(specification);
+ }
+ }
+ }
+
+ /**
+ * Print out a line of '-'s equal to specified size.
+ *
+ * @param size the number of dashes to printout
+ */
+ private void printLine(final int size) {
+ for (int i = 0; i < size; i++) {
+ System.out.print("-");
+ }
+ System.out.println();
+ }
+
+ /**
+ * Get specifications from manifest.
+ *
+ * @param manifest the manifest
+ * @return the specifications or null if none
+ * @throws BuildException if malformed specification sections
+ */
+ private Specification[] getSpecifications(final Manifest manifest)
+ throws BuildException {
+ try {
+ return Specification.getSpecifications(manifest);
+ } catch (final ParseException pe) {
+ throw new BuildException(pe.getMessage(), pe);
+ }
+ }
+
+ /**
+ * Print out specification details.
+ *
+ * @param specification the specification
+ */
+ private void displaySpecification(final Specification specification) {
+ final String[] sections = specification.getSections();
+ if (null != sections) {
+ final StringBuffer sb = new StringBuffer("Sections: ");
+ for (int i = 0; i < sections.length; i++) {
+ sb.append(" ");
+ sb.append(sections[ i ]);
+ }
+ System.out.println(sb);
+ }
+ System.out.println(specification.toString());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Specification.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Specification.java
new file mode 100644
index 00000000..1e4bb7b3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/Specification.java
@@ -0,0 +1,605 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import org.apache.tools.ant.util.DeweyDecimal;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * <p>Utility class that represents either an available "Optional Package"
+ * (formerly known as "Standard Extension") as described in the manifest
+ * of a JAR file, or the requirement for such an optional package.</p>
+ *
+ * <p>For more information about optional packages, see the document
+ * <em>Optional Package Versioning</em> in the documentation bundle for your
+ * Java2 Standard Edition package, in file
+ * <code>guide/extensions/versioning.html</code>.</p>
+ *
+ */
+public final class Specification {
+
+ private static final String MISSING = "Missing ";
+
+ /**
+ * Manifest Attribute Name object for SPECIFICATION_TITLE.
+ */
+ public static final Attributes.Name SPECIFICATION_TITLE
+ = Attributes.Name.SPECIFICATION_TITLE;
+
+ /**
+ * Manifest Attribute Name object for SPECIFICATION_VERSION.
+ */
+ public static final Attributes.Name SPECIFICATION_VERSION
+ = Attributes.Name.SPECIFICATION_VERSION;
+
+ /**
+ * Manifest Attribute Name object for SPECIFICATION_VENDOR.
+ */
+ public static final Attributes.Name SPECIFICATION_VENDOR
+ = Attributes.Name.SPECIFICATION_VENDOR;
+
+ /**
+ * Manifest Attribute Name object for IMPLEMENTATION_TITLE.
+ */
+ public static final Attributes.Name IMPLEMENTATION_TITLE
+ = Attributes.Name.IMPLEMENTATION_TITLE;
+
+ /**
+ * Manifest Attribute Name object for IMPLEMENTATION_VERSION.
+ */
+ public static final Attributes.Name IMPLEMENTATION_VERSION
+ = Attributes.Name.IMPLEMENTATION_VERSION;
+
+ /**
+ * Manifest Attribute Name object for IMPLEMENTATION_VENDOR.
+ */
+ public static final Attributes.Name IMPLEMENTATION_VENDOR
+ = Attributes.Name.IMPLEMENTATION_VENDOR;
+
+ /**
+ * Enum indicating that extension is compatible with other Package
+ * Specification.
+ */
+ public static final Compatibility COMPATIBLE =
+ new Compatibility("COMPATIBLE");
+
+ /**
+ * Enum indicating that extension requires an upgrade
+ * of specification to be compatible with other Package Specification.
+ */
+ public static final Compatibility REQUIRE_SPECIFICATION_UPGRADE =
+ new Compatibility("REQUIRE_SPECIFICATION_UPGRADE");
+
+ /**
+ * Enum indicating that extension requires a vendor
+ * switch to be compatible with other Package Specification.
+ */
+ public static final Compatibility REQUIRE_VENDOR_SWITCH =
+ new Compatibility("REQUIRE_VENDOR_SWITCH");
+
+ /**
+ * Enum indicating that extension requires an upgrade
+ * of implementation to be compatible with other Package Specification.
+ */
+ public static final Compatibility REQUIRE_IMPLEMENTATION_CHANGE =
+ new Compatibility("REQUIRE_IMPLEMENTATION_CHANGE");
+
+ /**
+ * This enum indicates that an extension is incompatible with
+ * other Package Specification in ways other than other enums
+ * indicate. For example, the other Package Specification
+ * may have a different ID.
+ */
+ public static final Compatibility INCOMPATIBLE =
+ new Compatibility("INCOMPATIBLE");
+
+ /**
+ * The name of the Package Specification.
+ */
+ private String specificationTitle;
+
+ /**
+ * The version number (dotted decimal notation) of the specification
+ * to which this optional package conforms.
+ */
+ private DeweyDecimal specificationVersion;
+
+ /**
+ * The name of the company or organization that originated the
+ * specification to which this specification conforms.
+ */
+ private String specificationVendor;
+
+ /**
+ * The title of implementation.
+ */
+ private String implementationTitle;
+
+ /**
+ * The name of the company or organization that produced this
+ * implementation of this specification.
+ */
+ private String implementationVendor;
+
+ /**
+ * The version string for implementation. The version string is
+ * opaque.
+ */
+ private String implementationVersion;
+
+ /**
+ * The sections of jar that the specification applies to.
+ */
+ private String[] sections;
+
+ /**
+ * Return an array of <code>Package Specification</code> objects.
+ * If there are no such optional packages, a zero-length array is returned.
+ *
+ * @param manifest Manifest to be parsed
+ * @return the Package Specifications extensions in specified manifest
+ * @throws ParseException if the attributes of the specifications cannot
+ * be parsed according to their expected formats.
+ */
+ public static Specification[] getSpecifications(final Manifest manifest)
+ throws ParseException {
+ if (null == manifest) {
+ return new Specification[ 0 ];
+ }
+
+ final ArrayList results = new ArrayList();
+
+ final Map entries = manifest.getEntries();
+ final Iterator keys = entries.keySet().iterator();
+ while (keys.hasNext()) {
+ final String key = (String) keys.next();
+ final Attributes attributes = (Attributes) entries.get(key);
+ final Specification specification
+ = getSpecification(key, attributes);
+ if (null != specification) {
+ results.add(specification);
+ }
+ }
+
+ final ArrayList trimmedResults = removeDuplicates(results);
+ return (Specification[]) trimmedResults.toArray(new Specification[trimmedResults.size()]);
+ }
+
+ /**
+ * The constructor to create Package Specification object.
+ * Note that every component is allowed to be specified
+ * but only the specificationTitle is mandatory.
+ *
+ * @param specificationTitle the name of specification.
+ * @param specificationVersion the specification Version.
+ * @param specificationVendor the specification Vendor.
+ * @param implementationTitle the title of implementation.
+ * @param implementationVersion the implementation Version.
+ * @param implementationVendor the implementation Vendor.
+ */
+ public Specification(final String specificationTitle,
+ final String specificationVersion,
+ final String specificationVendor,
+ final String implementationTitle,
+ final String implementationVersion,
+ final String implementationVendor) {
+ this(specificationTitle, specificationVersion, specificationVendor,
+ implementationTitle, implementationVersion, implementationVendor,
+ null);
+ }
+
+ /**
+ * The constructor to create Package Specification object.
+ * Note that every component is allowed to be specified
+ * but only the specificationTitle is mandatory.
+ *
+ * @param specificationTitle the name of specification.
+ * @param specificationVersion the specification Version.
+ * @param specificationVendor the specification Vendor.
+ * @param implementationTitle the title of implementation.
+ * @param implementationVersion the implementation Version.
+ * @param implementationVendor the implementation Vendor.
+ * @param sections the sections/packages that Specification applies to.
+ */
+ public Specification(final String specificationTitle,
+ final String specificationVersion,
+ final String specificationVendor,
+ final String implementationTitle,
+ final String implementationVersion,
+ final String implementationVendor,
+ final String[] sections) {
+ this.specificationTitle = specificationTitle;
+ this.specificationVendor = specificationVendor;
+
+ if (null != specificationVersion) {
+ try {
+ this.specificationVersion
+ = new DeweyDecimal(specificationVersion);
+ } catch (final NumberFormatException nfe) {
+ final String error = "Bad specification version format '"
+ + specificationVersion + "' in '" + specificationTitle
+ + "'. (Reason: " + nfe + ")";
+ throw new IllegalArgumentException(error);
+ }
+ }
+
+ this.implementationTitle = implementationTitle;
+ this.implementationVendor = implementationVendor;
+ this.implementationVersion = implementationVersion;
+
+ if (null == this.specificationTitle) {
+ throw new NullPointerException("specificationTitle");
+ }
+
+ String[] copy = null;
+ if (null != sections) {
+ copy = new String[ sections.length ];
+ System.arraycopy(sections, 0, copy, 0, sections.length);
+ }
+ this.sections = copy;
+ }
+
+ /**
+ * Get the title of the specification.
+ *
+ * @return the title of specification
+ */
+ public String getSpecificationTitle() {
+ return specificationTitle;
+ }
+
+ /**
+ * Get the vendor of the specification.
+ *
+ * @return the vendor of the specification.
+ */
+ public String getSpecificationVendor() {
+ return specificationVendor;
+ }
+
+ /**
+ * Get the title of the specification.
+ *
+ * @return the title of the specification.
+ */
+ public String getImplementationTitle() {
+ return implementationTitle;
+ }
+
+ /**
+ * Get the version of the specification.
+ *
+ * @return the version of the specification.
+ */
+ public DeweyDecimal getSpecificationVersion() {
+ return specificationVersion;
+ }
+
+ /**
+ * Get the vendor of the extensions implementation.
+ *
+ * @return the vendor of the extensions implementation.
+ */
+ public String getImplementationVendor() {
+ return implementationVendor;
+ }
+
+ /**
+ * Get the version of the implementation.
+ *
+ * @return the version of the implementation.
+ */
+ public String getImplementationVersion() {
+ return implementationVersion;
+ }
+
+ /**
+ * Return an array containing sections to which specification applies
+ * or null if relevant to no sections.
+ *
+ * @return an array containing sections to which specification applies
+ * or null if relevant to no sections.
+ */
+ public String[] getSections() {
+ if (null == sections) {
+ return null;
+ }
+ final String[] newSections = new String[ sections.length ];
+ System.arraycopy(sections, 0, newSections, 0, sections.length);
+ return newSections;
+ }
+
+ /**
+ * Return a Compatibility enum indicating the relationship of this
+ * <code>Package Specification</code> with the specified
+ * <code>Extension</code>.
+ *
+ * @param other the other specification
+ * @return the enum indicating the compatibility (or lack thereof)
+ * of specified Package Specification
+ */
+ public Compatibility getCompatibilityWith(final Specification other) {
+ // Specification Name must match
+ if (!specificationTitle.equals(other.getSpecificationTitle())) {
+ return INCOMPATIBLE;
+ }
+
+ // Available specification version must be >= required
+ final DeweyDecimal otherSpecificationVersion
+ = other.getSpecificationVersion();
+ if (null != specificationVersion) {
+ if (null == otherSpecificationVersion
+ || !isCompatible(specificationVersion, otherSpecificationVersion)) {
+ return REQUIRE_SPECIFICATION_UPGRADE;
+ }
+ }
+
+ // Implementation Vendor ID must match
+ final String otherImplementationVendor
+ = other.getImplementationVendor();
+ if (null != implementationVendor) {
+ if (null == otherImplementationVendor
+ || !implementationVendor.equals(otherImplementationVendor)) {
+ return REQUIRE_VENDOR_SWITCH;
+ }
+ }
+
+ // Implementation version must be >= required
+ final String otherImplementationVersion
+ = other.getImplementationVersion();
+ if (null != implementationVersion) {
+ if (null == otherImplementationVersion
+ || !implementationVersion.equals(otherImplementationVersion)) {
+ return REQUIRE_IMPLEMENTATION_CHANGE;
+ }
+ }
+
+ // This available optional package satisfies the requirements
+ return COMPATIBLE;
+ }
+
+ /**
+ * Return <code>true</code> if the specified <code>package</code>
+ * is satisfied by this <code>Specification</code>. Otherwise, return
+ * <code>false</code>.
+ *
+ * @param other the specification
+ * @return true if the specification is compatible with this specification
+ */
+ public boolean isCompatibleWith(final Specification other) {
+ return (COMPATIBLE == getCompatibilityWith(other));
+ }
+
+ /**
+ * Return a String representation of this object.
+ *
+ * @return string representation of object.
+ */
+ public String toString() {
+ final String brace = ": ";
+
+ final StringBuffer sb
+ = new StringBuffer(SPECIFICATION_TITLE.toString());
+ sb.append(brace);
+ sb.append(specificationTitle);
+ sb.append(StringUtils.LINE_SEP);
+
+ if (null != specificationVersion) {
+ sb.append(SPECIFICATION_VERSION);
+ sb.append(brace);
+ sb.append(specificationVersion);
+ sb.append(StringUtils.LINE_SEP);
+ }
+
+ if (null != specificationVendor) {
+ sb.append(SPECIFICATION_VENDOR);
+ sb.append(brace);
+ sb.append(specificationVendor);
+ sb.append(StringUtils.LINE_SEP);
+ }
+
+ if (null != implementationTitle) {
+ sb.append(IMPLEMENTATION_TITLE);
+ sb.append(brace);
+ sb.append(implementationTitle);
+ sb.append(StringUtils.LINE_SEP);
+ }
+
+ if (null != implementationVersion) {
+ sb.append(IMPLEMENTATION_VERSION);
+ sb.append(brace);
+ sb.append(implementationVersion);
+ sb.append(StringUtils.LINE_SEP);
+ }
+
+ if (null != implementationVendor) {
+ sb.append(IMPLEMENTATION_VENDOR);
+ sb.append(brace);
+ sb.append(implementationVendor);
+ sb.append(StringUtils.LINE_SEP);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Return <code>true</code> if the first version number is greater than
+ * or equal to the second; otherwise return <code>false</code>.
+ *
+ * @param first First version number (dotted decimal)
+ * @param second Second version number (dotted decimal)
+ */
+ private boolean isCompatible(final DeweyDecimal first,
+ final DeweyDecimal second) {
+ return first.isGreaterThanOrEqual(second);
+ }
+
+ /**
+ * Combine all specifications objects that are identical except
+ * for the sections.
+ *
+ * <p>Note this is very inefficent and should probably be fixed
+ * in the future.</p>
+ *
+ * @param list the array of results to trim
+ * @return an array list with all duplicates removed
+ */
+ private static ArrayList removeDuplicates(final ArrayList list) {
+ final ArrayList results = new ArrayList();
+ final ArrayList sections = new ArrayList();
+ while (list.size() > 0) {
+ final Specification specification = (Specification) list.remove(0);
+ final Iterator iterator = list.iterator();
+ while (iterator.hasNext()) {
+ final Specification other = (Specification) iterator.next();
+ if (isEqual(specification, other)) {
+ final String[] otherSections = other.getSections();
+ if (null != otherSections) {
+ sections.addAll(Arrays.asList(otherSections));
+ }
+ iterator.remove();
+ }
+ }
+
+ final Specification merged =
+ mergeInSections(specification, sections);
+ results.add(merged);
+ //Reset list of sections
+ sections.clear();
+ }
+
+ return results;
+ }
+
+ /**
+ * Test if two specifications are equal except for their sections.
+ *
+ * @param specification one specificaiton
+ * @param other the ohter specification
+ * @return true if two specifications are equal except for their
+ * sections, else false
+ */
+ private static boolean isEqual(final Specification specification,
+ final Specification other) {
+ return
+ specification.getSpecificationTitle().equals(other.getSpecificationTitle())
+ && specification.getSpecificationVersion().isEqual(other.getSpecificationVersion())
+ && specification.getSpecificationVendor().equals(other.getSpecificationVendor())
+ && specification.getImplementationTitle().equals(other.getImplementationTitle())
+ && specification.getImplementationVersion().equals(other.getImplementationVersion())
+ && specification.getImplementationVendor().equals(other.getImplementationVendor());
+ }
+
+ /**
+ * Merge the specified sections into specified section and return result.
+ * If no sections to be added then just return original specification.
+ *
+ * @param specification the specification
+ * @param sectionsToAdd the list of sections to merge
+ * @return the merged specification
+ */
+ private static Specification mergeInSections(final Specification specification,
+ final ArrayList sectionsToAdd) {
+ if (0 == sectionsToAdd.size()) {
+ return specification;
+ }
+ sectionsToAdd.addAll(Arrays.asList(specification.getSections()));
+
+ final String[] sections =
+ (String[]) sectionsToAdd.toArray(new String[sectionsToAdd.size()]);
+
+ return new Specification(specification.getSpecificationTitle(),
+ specification.getSpecificationVersion().toString(),
+ specification.getSpecificationVendor(),
+ specification.getImplementationTitle(),
+ specification.getImplementationVersion(),
+ specification.getImplementationVendor(),
+ sections);
+ }
+
+ /**
+ * Trim the supplied string if the string is non-null
+ *
+ * @param value the string to trim or null
+ * @return the trimmed string or null
+ */
+ private static String getTrimmedString(final String value) {
+ return value == null ? null : value.trim();
+ }
+
+ /**
+ * Extract an Package Specification from Attributes.
+ *
+ * @param attributes Attributes to searched
+ * @return the new Specification object, or null
+ */
+ private static Specification getSpecification(final String section,
+ final Attributes attributes)
+ throws ParseException {
+ //WARNING: We trim the values of all the attributes because
+ //Some extension declarations are badly defined (ie have spaces
+ //after version or vendor)
+ final String name
+ = getTrimmedString(attributes.getValue(SPECIFICATION_TITLE));
+ if (null == name) {
+ return null;
+ }
+
+ final String specVendor
+ = getTrimmedString(attributes.getValue(SPECIFICATION_VENDOR));
+ if (null == specVendor) {
+ throw new ParseException(MISSING + SPECIFICATION_VENDOR, 0);
+ }
+
+ final String specVersion
+ = getTrimmedString(attributes.getValue(SPECIFICATION_VERSION));
+ if (null == specVersion) {
+ throw new ParseException(MISSING + SPECIFICATION_VERSION, 0);
+ }
+
+ final String impTitle
+ = getTrimmedString(attributes.getValue(IMPLEMENTATION_TITLE));
+ if (null == impTitle) {
+ throw new ParseException(MISSING + IMPLEMENTATION_TITLE, 0);
+ }
+
+ final String impVersion
+ = getTrimmedString(attributes.getValue(IMPLEMENTATION_VERSION));
+ if (null == impVersion) {
+ throw new ParseException(MISSING + IMPLEMENTATION_VERSION, 0);
+ }
+
+ final String impVendor
+ = getTrimmedString(attributes.getValue(IMPLEMENTATION_VENDOR));
+ if (null == impVendor) {
+ throw new ParseException(MISSING + IMPLEMENTATION_VENDOR, 0);
+ }
+
+ return new Specification(name, specVersion, specVendor,
+ impTitle, impVersion, impVendor,
+ new String[]{section});
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/AntResolver.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/AntResolver.java
new file mode 100644
index 00000000..6284679f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/AntResolver.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.tools.ant.taskdefs.optional.extension.resolvers;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Ant;
+import org.apache.tools.ant.taskdefs.optional.extension.Extension;
+import org.apache.tools.ant.taskdefs.optional.extension.ExtensionResolver;
+
+/**
+ * Resolver that just returns s specified location.
+ *
+ */
+public class AntResolver implements ExtensionResolver {
+ private File antfile;
+ private File destfile;
+ private String target;
+
+ /**
+ * Sets the ant file
+ * @param antfile the ant file to set
+ */
+ public void setAntfile(final File antfile) {
+ this.antfile = antfile;
+ }
+
+ /**
+ * Sets the destination file
+ * @param destfile the destination file
+ */
+ public void setDestfile(final File destfile) {
+ this.destfile = destfile;
+ }
+
+ /**
+ * Sets the target
+ * @param target the target
+ */
+ public void setTarget(final String target) {
+ this.target = target;
+ }
+
+ /**
+ * Returns the resolved file
+ * @param extension the extension
+ * @param project the project
+ * @return the file resolved
+ * @throws BuildException if the file cannot be resolved
+ */
+ public File resolve(final Extension extension,
+ final Project project) throws BuildException {
+ validate();
+
+ final Ant ant = new Ant();
+ ant.setProject(project);
+ ant.setInheritAll(false);
+ ant.setAntfile(antfile.getName());
+
+ try {
+ final File dir =
+ antfile.getParentFile().getCanonicalFile();
+ ant.setDir(dir);
+ } catch (final IOException ioe) {
+ throw new BuildException(ioe.getMessage(), ioe);
+ }
+
+ if (null != target) {
+ ant.setTarget(target);
+ }
+
+ ant.execute();
+
+ return destfile;
+ }
+
+ /*
+ * Validates URL
+ */
+ private void validate() {
+ if (null == antfile) {
+ final String message = "Must specify Buildfile";
+ throw new BuildException(message);
+ }
+
+ if (null == destfile) {
+ final String message = "Must specify destination file";
+ throw new BuildException(message);
+ }
+ }
+
+ /**
+ * Returns a string representation
+ * @return the string representation
+ */
+ public String toString() {
+ return "Ant[" + antfile + "==>" + destfile + "]";
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/LocationResolver.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/LocationResolver.java
new file mode 100644
index 00000000..e2fec022
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/LocationResolver.java
@@ -0,0 +1,65 @@
+/*
+ * 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.tools.ant.taskdefs.optional.extension.resolvers;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.optional.extension.Extension;
+import org.apache.tools.ant.taskdefs.optional.extension.ExtensionResolver;
+
+/**
+ * Resolver that just returns s specified location.
+ *
+ */
+public class LocationResolver implements ExtensionResolver {
+ private String location;
+
+ /**
+ * Sets the location for this resolver
+ * @param location the location
+ */
+ public void setLocation(final String location) {
+ this.location = location;
+ }
+
+ /**
+ * Returns the resolved file
+ * @param extension the extension
+ * @param project the project
+ * @return the file resolved
+ * @throws BuildException if no location is set
+ */
+ public File resolve(final Extension extension,
+ final Project project) throws BuildException {
+ if (null == location) {
+ final String message = "No location specified for resolver";
+ throw new BuildException(message);
+ }
+
+ return project.resolveFile(location);
+ }
+ /**
+ * Returns a string representation of the Location
+ * @return the string representation
+ */
+ public String toString() {
+ return "Location[" + location + "]";
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/URLResolver.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/URLResolver.java
new file mode 100644
index 00000000..d693b899
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/URLResolver.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.tools.ant.taskdefs.optional.extension.resolvers;
+
+import java.io.File;
+import java.net.URL;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Get;
+import org.apache.tools.ant.taskdefs.optional.extension.Extension;
+import org.apache.tools.ant.taskdefs.optional.extension.ExtensionResolver;
+
+/**
+ * Resolver that just returns s specified location.
+ *
+ */
+public class URLResolver implements ExtensionResolver {
+ private File destfile;
+ private File destdir;
+ private URL url;
+
+ /**
+ * Sets the URL
+ * @param url the url
+ */
+ public void setUrl(final URL url) {
+ this.url = url;
+ }
+
+ /**
+ * Sets the destination file
+ * @param destfile the destination file
+ */
+ public void setDestfile(final File destfile) {
+ this.destfile = destfile;
+ }
+
+ /**
+ * Sets the destination directory
+ * @param destdir the destination directory
+ */
+ public void setDestdir(final File destdir) {
+ this.destdir = destdir;
+ }
+
+ /**
+ * Returns the file resolved from URL and directory
+ * @param extension the extension
+ * @param project the project
+ * @return file the file resolved
+ * @throws BuildException if the URL is invalid
+ */
+ public File resolve(final Extension extension,
+ final Project project) throws BuildException {
+ validate();
+
+ final File file = getDest();
+
+ final Get get = new Get();
+ get.setProject(project);
+ get.setDest(file);
+ get.setSrc(url);
+ get.execute();
+
+ return file;
+ }
+
+ /*
+ * Gets the destination file
+ */
+ private File getDest() {
+ File result;
+ if (null != destfile) {
+ result = destfile;
+ } else {
+ final String file = url.getFile();
+ String filename;
+ if (null == file || file.length() <= 1) {
+ filename = "default.file";
+ } else {
+ int index = file.lastIndexOf('/');
+ if (-1 == index) {
+ index = 0;
+ }
+ filename = file.substring(index);
+ }
+ result = new File(destdir, filename);
+ }
+ return result;
+ }
+
+ /*
+ * Validates URL
+ */
+ private void validate() {
+ if (null == url) {
+ final String message = "Must specify URL";
+ throw new BuildException(message);
+ }
+
+ if (null == destdir && null == destfile) {
+ final String message = "Must specify destination file or directory";
+ throw new BuildException(message);
+ } else if (null != destdir && null != destfile) {
+ final String message = "Must not specify both destination file or directory";
+ throw new BuildException(message);
+ }
+ }
+
+ /**
+ * Returns a string representation of the URL
+ * @return the string representation
+ */
+ public String toString() {
+ return "URL[" + url + "]";
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java
new file mode 100644
index 00000000..82731fe9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java
@@ -0,0 +1,632 @@
+/*
+ * 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.tools.ant.taskdefs.optional.i18n;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.LineTokenizer;
+
+/**
+ * Translates text embedded in files using Resource Bundle files.
+ * Since ant 1.6 preserves line endings
+ *
+ */
+public class Translate extends MatchingTask {
+ /**
+ * search a bundle matching the specified language, the country and the variant
+ */
+ private static final int BUNDLE_SPECIFIED_LANGUAGE_COUNTRY_VARIANT = 0;
+ /**
+ * search a bundle matching the specified language, and the country
+ */
+ private static final int BUNDLE_SPECIFIED_LANGUAGE_COUNTRY = 1;
+ /**
+ * search a bundle matching the specified language only
+ */
+ private static final int BUNDLE_SPECIFIED_LANGUAGE = 2;
+ /**
+ * search a bundle matching nothing special
+ */
+ private static final int BUNDLE_NOMATCH = 3;
+ /**
+ * search a bundle matching the language, the country and the variant
+ * of the current locale of the computer
+ */
+ private static final int BUNDLE_DEFAULT_LANGUAGE_COUNTRY_VARIANT = 4;
+ /**
+ * search a bundle matching the language, and the country
+ * of the current locale of the computer
+ */
+ private static final int BUNDLE_DEFAULT_LANGUAGE_COUNTRY = 5;
+ /**
+ * search a bundle matching the language only
+ * of the current locale of the computer
+ */
+ private static final int BUNDLE_DEFAULT_LANGUAGE = 6;
+ /**
+ * number of possibilities for the search
+ */
+ private static final int BUNDLE_MAX_ALTERNATIVES = BUNDLE_DEFAULT_LANGUAGE + 1;
+ /**
+ * Family name of resource bundle
+ */
+ private String bundle;
+
+ /**
+ * Locale specific language of the resource bundle
+ */
+ private String bundleLanguage;
+
+ /**
+ * Locale specific country of the resource bundle
+ */
+ private String bundleCountry;
+
+ /**
+ * Locale specific variant of the resource bundle
+ */
+ private String bundleVariant;
+
+ /**
+ * Destination directory
+ */
+ private File toDir;
+
+ /**
+ * Source file encoding scheme
+ */
+ private String srcEncoding;
+
+ /**
+ * Destination file encoding scheme
+ */
+ private String destEncoding;
+
+ /**
+ * Resource Bundle file encoding scheme, defaults to srcEncoding
+ */
+ private String bundleEncoding;
+
+ /**
+ * Starting token to identify keys
+ */
+ private String startToken;
+
+ /**
+ * Ending token to identify keys
+ */
+ private String endToken;
+
+ /**
+ * Whether or not to create a new destination file.
+ * Defaults to <code>false</code>.
+ */
+ private boolean forceOverwrite;
+
+ /**
+ * Vector to hold source file sets.
+ */
+ private Vector filesets = new Vector();
+
+ /**
+ * Holds key value pairs loaded from resource bundle file
+ */
+ private Hashtable resourceMap = new Hashtable();
+ /**
+
+ * Used to resolve file names.
+ */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Last Modified Timestamp of resource bundle file being used.
+ */
+ private long[] bundleLastModified = new long[BUNDLE_MAX_ALTERNATIVES];
+
+ /**
+ * Last Modified Timestamp of source file being used.
+ */
+ private long srcLastModified;
+
+ /**
+ * Last Modified Timestamp of destination file being used.
+ */
+ private long destLastModified;
+
+ /**
+ * Has at least one file from the bundle been loaded?
+ */
+ private boolean loaded = false;
+
+ /**
+ * Sets Family name of resource bundle; required.
+ * @param bundle family name of resource bundle
+ */
+ public void setBundle(String bundle) {
+ this.bundle = bundle;
+ }
+
+ /**
+ * Sets locale specific language of resource bundle; optional.
+ * @param bundleLanguage language of the bundle
+ */
+ public void setBundleLanguage(String bundleLanguage) {
+ this.bundleLanguage = bundleLanguage;
+ }
+
+ /**
+ * Sets locale specific country of resource bundle; optional.
+ * @param bundleCountry country of the bundle
+ */
+ public void setBundleCountry(String bundleCountry) {
+ this.bundleCountry = bundleCountry;
+ }
+
+ /**
+ * Sets locale specific variant of resource bundle; optional.
+ * @param bundleVariant locale variant of resource bundle
+ */
+ public void setBundleVariant(String bundleVariant) {
+ this.bundleVariant = bundleVariant;
+ }
+
+ /**
+ * Sets Destination directory; required.
+ * @param toDir destination directory
+ */
+ public void setToDir(File toDir) {
+ this.toDir = toDir;
+ }
+
+ /**
+ * Sets starting token to identify keys; required.
+ * @param startToken starting token to identify keys
+ */
+ public void setStartToken(String startToken) {
+ this.startToken = startToken;
+ }
+
+ /**
+ * Sets ending token to identify keys; required.
+ * @param endToken ending token to identify keys
+ */
+ public void setEndToken(String endToken) {
+ this.endToken = endToken;
+ }
+
+ /**
+ * Sets source file encoding scheme; optional,
+ * defaults to encoding of local system.
+ * @param srcEncoding source file encoding
+ */
+ public void setSrcEncoding(String srcEncoding) {
+ this.srcEncoding = srcEncoding;
+ }
+
+ /**
+ * Sets destination file encoding scheme; optional. Defaults to source file
+ * encoding
+ * @param destEncoding destination file encoding scheme
+ */
+ public void setDestEncoding(String destEncoding) {
+ this.destEncoding = destEncoding;
+ }
+
+ /**
+ * Sets Resource Bundle file encoding scheme; optional. Defaults to source file
+ * encoding
+ * @param bundleEncoding bundle file encoding scheme
+ */
+ public void setBundleEncoding(String bundleEncoding) {
+ this.bundleEncoding = bundleEncoding;
+ }
+
+ /**
+ * Whether or not to overwrite existing file irrespective of
+ * whether it is newer than the source file as well as the
+ * resource bundle file.
+ * Defaults to false.
+ * @param forceOverwrite whether or not to overwrite existing files
+ */
+ public void setForceOverwrite(boolean forceOverwrite) {
+ this.forceOverwrite = forceOverwrite;
+ }
+
+ /**
+ * Adds a set of files to translate as a nested fileset element.
+ * @param set the fileset to be added
+ */
+ public void addFileset(FileSet set) {
+ filesets.addElement(set);
+ }
+
+ /**
+ * Check attributes values, load resource map and translate
+ * @throws BuildException if the required attributes are not set
+ * Required : <ul>
+ * <li>bundle</li>
+ * <li>starttoken</li>
+ * <li>endtoken</li>
+ * </ul>
+ */
+ public void execute() throws BuildException {
+ if (bundle == null) {
+ throw new BuildException("The bundle attribute must be set.",
+ getLocation());
+ }
+
+ if (startToken == null) {
+ throw new BuildException("The starttoken attribute must be set.",
+ getLocation());
+ }
+
+ if (endToken == null) {
+ throw new BuildException("The endtoken attribute must be set.",
+ getLocation());
+ }
+
+ if (bundleLanguage == null) {
+ Locale l = Locale.getDefault();
+ bundleLanguage = l.getLanguage();
+ }
+
+ if (bundleCountry == null) {
+ bundleCountry = Locale.getDefault().getCountry();
+ }
+
+ if (bundleVariant == null) {
+ Locale l = new Locale(bundleLanguage, bundleCountry);
+ bundleVariant = l.getVariant();
+ }
+
+ if (toDir == null) {
+ throw new BuildException("The todir attribute must be set.",
+ getLocation());
+ }
+
+ if (!toDir.exists()) {
+ toDir.mkdirs();
+ } else if (toDir.isFile()) {
+ throw new BuildException(toDir + " is not a directory");
+ }
+
+ if (srcEncoding == null) {
+ srcEncoding = System.getProperty("file.encoding");
+ }
+
+ if (destEncoding == null) {
+ destEncoding = srcEncoding;
+ }
+
+ if (bundleEncoding == null) {
+ bundleEncoding = srcEncoding;
+ }
+
+ loadResourceMaps();
+
+ translate();
+ }
+
+ /**
+ * Load resource maps based on resource bundle encoding scheme.
+ * The resource bundle lookup searches for resource files with various
+ * suffixes on the basis of (1) the desired locale and (2) the default
+ * locale (basebundlename), in the following order from lower-level
+ * (more specific) to parent-level (less specific):
+ *
+ * basebundlename + "_" + language1 + "_" + country1 + "_" + variant1
+ * basebundlename + "_" + language1 + "_" + country1
+ * basebundlename + "_" + language1
+ * basebundlename
+ * basebundlename + "_" + language2 + "_" + country2 + "_" + variant2
+ * basebundlename + "_" + language2 + "_" + country2
+ * basebundlename + "_" + language2
+ *
+ * To the generated name, a ".properties" string is appeneded and
+ * once this file is located, it is treated just like a properties file
+ * but with bundle encoding also considered while loading.
+ */
+ private void loadResourceMaps() throws BuildException {
+ Locale locale = new Locale(bundleLanguage,
+ bundleCountry,
+ bundleVariant);
+ String language = locale.getLanguage().length() > 0
+ ? "_" + locale.getLanguage() : "";
+ String country = locale.getCountry().length() > 0
+ ? "_" + locale.getCountry() : "";
+ String variant = locale.getVariant().length() > 0
+ ? "_" + locale.getVariant() : "";
+ String bundleFile = bundle + language + country + variant;
+ processBundle(bundleFile, BUNDLE_SPECIFIED_LANGUAGE_COUNTRY_VARIANT, false);
+
+ bundleFile = bundle + language + country;
+ processBundle(bundleFile, BUNDLE_SPECIFIED_LANGUAGE_COUNTRY, false);
+
+ bundleFile = bundle + language;
+ processBundle(bundleFile, BUNDLE_SPECIFIED_LANGUAGE, false);
+
+ bundleFile = bundle;
+ processBundle(bundleFile, BUNDLE_NOMATCH, false);
+
+ //Load default locale bundle files
+ //using default file encoding scheme.
+ locale = Locale.getDefault();
+
+ language = locale.getLanguage().length() > 0
+ ? "_" + locale.getLanguage() : "";
+ country = locale.getCountry().length() > 0
+ ? "_" + locale.getCountry() : "";
+ variant = locale.getVariant().length() > 0
+ ? "_" + locale.getVariant() : "";
+ bundleEncoding = System.getProperty("file.encoding");
+
+ bundleFile = bundle + language + country + variant;
+ processBundle(bundleFile, BUNDLE_DEFAULT_LANGUAGE_COUNTRY_VARIANT, false);
+
+ bundleFile = bundle + language + country;
+ processBundle(bundleFile, BUNDLE_DEFAULT_LANGUAGE_COUNTRY, false);
+
+ bundleFile = bundle + language;
+ processBundle(bundleFile, BUNDLE_DEFAULT_LANGUAGE, true);
+ }
+
+ /**
+ * Process each file that makes up this bundle.
+ */
+ private void processBundle(final String bundleFile, final int i,
+ final boolean checkLoaded) throws BuildException {
+ final File propsFile = getProject().resolveFile(bundleFile + ".properties");
+ FileInputStream ins = null;
+ try {
+ ins = new FileInputStream(propsFile);
+ loaded = true;
+ bundleLastModified[i] = propsFile.lastModified();
+ log("Using " + propsFile, Project.MSG_DEBUG);
+ loadResourceMap(ins);
+ } catch (IOException ioe) {
+ log(propsFile + " not found.", Project.MSG_DEBUG);
+ //if all resource files associated with this bundle
+ //have been scanned for and still not able to
+ //find a single resrouce file, throw exception
+ if (!loaded && checkLoaded) {
+ throw new BuildException(ioe.getMessage(), getLocation());
+ }
+ }
+ }
+
+ /**
+ * Load resourceMap with key value pairs. Values of existing keys
+ * are not overwritten. Bundle's encoding scheme is used.
+ */
+ private void loadResourceMap(FileInputStream ins) throws BuildException {
+ try {
+ BufferedReader in = null;
+ InputStreamReader isr = new InputStreamReader(ins, bundleEncoding);
+ in = new BufferedReader(isr);
+ String line = null;
+ while ((line = in.readLine()) != null) {
+ //So long as the line isn't empty and isn't a comment...
+ if (line.trim().length() > 1 && '#' != line.charAt(0) && '!' != line.charAt(0)) {
+ //Legal Key-Value separators are :, = and white space.
+ int sepIndex = line.indexOf('=');
+ if (-1 == sepIndex) {
+ sepIndex = line.indexOf(':');
+ }
+ if (-1 == sepIndex) {
+ for (int k = 0; k < line.length(); k++) {
+ if (Character.isSpaceChar(line.charAt(k))) {
+ sepIndex = k;
+ break;
+ }
+ }
+ }
+ //Only if we do have a key is there going to be a value
+ if (-1 != sepIndex) {
+ String key = line.substring(0, sepIndex).trim();
+ String value = line.substring(sepIndex + 1).trim();
+ //Handle line continuations, if any
+ while (value.endsWith("\\")) {
+ value = value.substring(0, value.length() - 1);
+ line = in.readLine();
+ if (line != null) {
+ value = value + line.trim();
+ } else {
+ break;
+ }
+ }
+ if (key.length() > 0) {
+ //Has key already been loaded into resourceMap?
+ if (resourceMap.get(key) == null) {
+ resourceMap.put(key, value);
+ }
+ }
+ }
+ }
+ }
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException ioe) {
+ throw new BuildException(ioe.getMessage(), getLocation());
+ }
+ }
+
+ /**
+ * Reads source file line by line using the source encoding and
+ * searches for keys that are sandwiched between the startToken
+ * and endToken. The values for these keys are looked up from
+ * the hashtable and substituted. If the hashtable doesn't
+ * contain the key, they key itself is used as the value.
+ * Detination files and directories are created as needed.
+ * The destination file is overwritten only if
+ * the forceoverwritten attribute is set to true if
+ * the source file or any associated bundle resource file is
+ * newer than the destination file.
+ */
+ private void translate() throws BuildException {
+ int filesProcessed = 0;
+ final int size = filesets.size();
+ for (int i = 0; i < size; i++) {
+ FileSet fs = (FileSet) filesets.elementAt(i);
+ DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+ String[] srcFiles = ds.getIncludedFiles();
+ for (int j = 0; j < srcFiles.length; j++) {
+ try {
+ File dest = FILE_UTILS.resolveFile(toDir, srcFiles[j]);
+ //Make sure parent dirs exist, else, create them.
+ try {
+ File destDir = new File(dest.getParent());
+ if (!destDir.exists()) {
+ destDir.mkdirs();
+ }
+ } catch (Exception e) {
+ log("Exception occurred while trying to check/create "
+ + " parent directory. " + e.getMessage(),
+ Project.MSG_DEBUG);
+ }
+ destLastModified = dest.lastModified();
+ File src = FILE_UTILS.resolveFile(ds.getBasedir(), srcFiles[j]);
+ srcLastModified = src.lastModified();
+ //Check to see if dest file has to be recreated
+ boolean needsWork = forceOverwrite
+ || destLastModified < srcLastModified;
+ if (!needsWork) {
+ for (int icounter = 0; icounter < BUNDLE_MAX_ALTERNATIVES; icounter++) {
+ needsWork = (destLastModified < bundleLastModified[icounter]);
+ if (needsWork) {
+ break;
+ }
+ }
+ }
+ if (needsWork) {
+ log("Processing " + srcFiles[j],
+ Project.MSG_DEBUG);
+ translateOneFile(src, dest);
+ ++filesProcessed;
+ } else {
+ log("Skipping " + srcFiles[j]
+ + " as destination file is up to date",
+ Project.MSG_VERBOSE);
+ }
+ } catch (IOException ioe) {
+ throw new BuildException(ioe.getMessage(), getLocation());
+ }
+ }
+ }
+ log("Translation performed on " + filesProcessed + " file(s).", Project.MSG_DEBUG);
+ }
+
+ private void translateOneFile(File src, File dest) throws IOException {
+ BufferedWriter out = null;
+ BufferedReader in = null;
+ try {
+ FileOutputStream fos = new FileOutputStream(dest);
+ out = new BufferedWriter(new OutputStreamWriter(fos, destEncoding));
+ FileInputStream fis = new FileInputStream(src);
+ in = new BufferedReader(new InputStreamReader(fis, srcEncoding));
+ String line;
+ LineTokenizer lineTokenizer = new LineTokenizer();
+ lineTokenizer.setIncludeDelims(true);
+ line = lineTokenizer.getToken(in);
+ while ((line) != null) {
+ // 2003-02-21 new replace algorithm by tbee (tbee@tbee.org)
+ // because it wasn't able to replace something like "@aaa;@bbb;"
+
+ // is there a startToken
+ // and there is still stuff following the startToken
+ int startIndex = line.indexOf(startToken);
+ while (startIndex >= 0
+ && (startIndex + startToken.length()) <= line.length()) {
+ // the new value, this needs to be here
+ // because it is required to calculate the next position to
+ // search from at the end of the loop
+ String replace = null;
+
+ // we found a starttoken, is there an endtoken following?
+ // start at token+tokenlength because start and end
+ // token may be identical
+ int endIndex = line.indexOf(endToken, startIndex
+ + startToken.length());
+ if (endIndex < 0) {
+ startIndex += 1;
+ } else {
+ // grab the token
+ String token = line.substring(startIndex
+ + startToken.length(),
+ endIndex);
+
+ // If there is a white space or = or :, then
+ // it isn't to be treated as a valid key.
+ boolean validToken = true;
+ for (int k = 0; k < token.length() && validToken; k++) {
+ char c = token.charAt(k);
+ if (c == ':' || c == '='
+ || Character.isSpaceChar(c)) {
+ validToken = false;
+ }
+ }
+ if (!validToken) {
+ startIndex += 1;
+ } else {
+ // find the replace string
+ if (resourceMap.containsKey(token)) {
+ replace = (String) resourceMap.get(token);
+ } else {
+ log("Replacement string missing for: " + token,
+ Project.MSG_VERBOSE);
+ replace = startToken + token + endToken;
+ }
+
+
+ // generate the new line
+ line = line.substring(0, startIndex) + replace
+ + line.substring(endIndex + endToken.length());
+
+ // set start position for next search
+ startIndex += replace.length();
+ }
+ }
+
+ // find next starttoken
+ startIndex = line.indexOf(startToken, startIndex);
+ }
+ out.write(line);
+ line = lineTokenizer.getToken(in);
+ }
+ } finally {
+ FileUtils.close(in);
+ FileUtils.close(out);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/image/Image.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/image/Image.java
new file mode 100644
index 00000000..162e3756
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/image/Image.java
@@ -0,0 +1,421 @@
+/*
+ * 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.tools.ant.taskdefs.optional.image;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.media.jai.JAI;
+import javax.media.jai.PlanarImage;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.optional.image.Draw;
+import org.apache.tools.ant.types.optional.image.ImageOperation;
+import org.apache.tools.ant.types.optional.image.Rotate;
+import org.apache.tools.ant.types.optional.image.Scale;
+import org.apache.tools.ant.types.optional.image.TransformOperation;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.IdentityMapper;
+
+import com.sun.media.jai.codec.FileSeekableStream;
+
+/**
+ * A MatchingTask which relies on <a
+ * href="http://java.sun.com/products/java-media/jai">JAI (Java
+ * Advanced Imaging)</a> to perform image manipulation operations on
+ * existing images. The operations are represented as ImageOperation
+ * DataType objects. The operations are arranged to conform to the
+ * Chaining Model of JAI. Check out the <a
+ * href="http://java.sun.com/products/java-media/jai/forDevelopers/jai1_0_1guide-unc/">
+ * JAI Programming Guide</a>.
+ *
+ * @see org.apache.tools.ant.types.optional.image.ImageOperation
+ * @see org.apache.tools.ant.types.DataType
+ */
+public class Image extends MatchingTask {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected Vector instructions = new Vector();
+ protected boolean overwrite = false;
+ protected Vector filesets = new Vector();
+ protected File srcDir = null;
+ protected File destDir = null;
+
+ // CheckStyle:MemberNameCheck OFF - bc
+
+ //cannot remove underscores due to protected visibility >:(
+ protected String str_encoding = "JPEG";
+ protected boolean garbage_collect = false;
+
+ private boolean failonerror = true;
+
+ // CheckStyle:MemberNameCheck ON
+
+ // CheckStyle:VisibilityModifier ON
+
+ private Mapper mapperElement = null;
+
+ /**
+ * Add a set of files to be deleted.
+ * @param set the FileSet to add.
+ */
+ public void addFileset(FileSet set) {
+ filesets.addElement(set);
+ }
+
+ /**
+ * Set whether to fail on error.
+ * If false, note errors to the output but keep going.
+ * @param failonerror true or false.
+ */
+ public void setFailOnError(boolean failonerror) {
+ this.failonerror = failonerror;
+ }
+
+ /**
+ * Set the source dir to find the image files.
+ * @param srcDir the directory in which the image files reside.
+ */
+ public void setSrcdir(File srcDir) {
+ this.srcDir = srcDir;
+ }
+
+ /**
+ * Set the image encoding type. <a
+ * href="http://java.sun.com/products/java-media/jai/forDevelopers/jai1_0_1guide-unc/Encode.doc.html#56610">
+ * See this table in the JAI Programming Guide</a>.
+ * @param encoding the String image encoding.
+ */
+ public void setEncoding(String encoding) {
+ str_encoding = encoding;
+ }
+
+ /**
+ * Set whether to overwrite a file if there is a naming conflict.
+ * @param overwrite whether to overwrite.
+ */
+ public void setOverwrite(boolean overwrite) {
+ this.overwrite = overwrite;
+ }
+
+ /**
+ * Set whether to invoke Garbage Collection after each image processed.
+ * Defaults to false.
+ * @param gc whether to invoke the garbage collector.
+ */
+ public void setGc(boolean gc) {
+ garbage_collect = gc;
+ }
+
+ /**
+ * Set the destination directory for manipulated images.
+ * @param destDir The destination directory.
+ */
+ public void setDestDir(File destDir) {
+ this.destDir = destDir;
+ }
+
+ /**
+ * Add an ImageOperation to chain.
+ * @param instr The ImageOperation to append to the chain.
+ */
+ public void addImageOperation(ImageOperation instr) {
+ instructions.add(instr);
+ }
+
+ /**
+ * Add a Rotate ImageOperation to the chain.
+ * @param instr The Rotate operation to add to the chain.
+ * @see org.apache.tools.ant.types.optional.image.Rotate
+ */
+ public void addRotate(Rotate instr) {
+ instructions.add(instr);
+ }
+
+ /**
+ * Add a Scale ImageOperation to the chain.
+ * @param instr The Scale operation to add to the chain.
+ * @see org.apache.tools.ant.types.optional.image.Scale
+ */
+ public void addScale(Scale instr) {
+ instructions.add(instr);
+ }
+
+ /**
+ * Add a Draw ImageOperation to the chain. DrawOperation
+ * DataType objects can be nested inside the Draw object.
+ * @param instr The Draw operation to add to the chain.
+ * @see org.apache.tools.ant.types.optional.image.Draw
+ * @see org.apache.tools.ant.types.optional.image.DrawOperation
+ */
+ public void addDraw(Draw instr) {
+ instructions.add(instr);
+ }
+
+ /**
+ * Add an ImageOperation to chain.
+ * @param instr The ImageOperation to append to the chain.
+ * @since Ant 1.7
+ */
+ public void add(ImageOperation instr) {
+ addImageOperation(instr);
+ }
+
+ /**
+ * Defines the mapper to map source to destination files.
+ * @return a mapper to be configured
+ * @exception BuildException if more than one mapper is defined
+ * @since Ant 1.8.0
+ */
+ public Mapper createMapper() throws BuildException {
+ if (mapperElement != null) {
+ throw new BuildException("Cannot define more than one mapper",
+ getLocation());
+ }
+ mapperElement = new Mapper(getProject());
+ return mapperElement;
+ }
+
+ /**
+ * Add a nested filenamemapper.
+ * @param fileNameMapper the mapper to add.
+ * @since Ant 1.8.0
+ */
+ public void add(FileNameMapper fileNameMapper) {
+ createMapper().add(fileNameMapper);
+ }
+
+ /**
+ * Executes all the chained ImageOperations on the files inside
+ * the directory.
+ * @since Ant 1.8.0
+ */
+ public int processDir(final File srcDir, final String[] srcNames,
+ final File dstDir, final FileNameMapper mapper) {
+ int writeCount = 0;
+
+ for (int i = 0; i < srcNames.length; ++i) {
+ final String srcName = srcNames[i];
+ final File srcFile = new File(srcDir, srcName).getAbsoluteFile();
+
+ final String[] dstNames = mapper.mapFileName(srcName);
+ if (dstNames == null) {
+ log(srcFile + " skipped, don't know how to handle it",
+ Project.MSG_VERBOSE);
+ continue;
+ }
+
+ for (int j = 0; j < dstNames.length; ++j){
+
+ final String dstName = dstNames[j];
+ final File dstFile = new File(dstDir, dstName).getAbsoluteFile();
+
+ if (dstFile.exists()){
+ // avoid overwriting unless necessary
+ if(!overwrite
+ && srcFile.lastModified() <= dstFile.lastModified()) {
+
+ log(srcFile + " omitted as " + dstFile
+ + " is up to date.", Project.MSG_VERBOSE);
+
+ // don't overwrite the file
+ continue;
+ }
+
+ // avoid extra work while overwriting
+ if (!srcFile.equals(dstFile)){
+ dstFile.delete();
+ }
+ }
+ processFile(srcFile, dstFile);
+ ++writeCount;
+ }
+ }
+
+ // run the garbage collector if wanted
+ if (garbage_collect) {
+ System.gc();
+ }
+
+ return writeCount;
+ }
+
+ /**
+ * Executes all the chained ImageOperations on the file
+ * specified.
+ * @param file The file to be processed.
+ * @deprecated this method isn't used anymore
+ */
+ public void processFile(File file) {
+ processFile(file, new File(destDir == null
+ ? srcDir : destDir, file.getName()));
+ }
+
+ /**
+ * Executes all the chained ImageOperations on the file
+ * specified.
+ * @param file The file to be processed.
+ * @param newFile The file to write to.
+ * @since Ant 1.8.0
+ */
+ public void processFile(File file, File newFile) {
+ try {
+ log("Processing File: " + file.getAbsolutePath());
+
+ FileSeekableStream input = null;
+ PlanarImage image = null;
+ try {
+ input = new FileSeekableStream(file);
+ image = JAI.create("stream", input);
+ final int size = instructions.size();
+ for (int i = 0; i < size; i++) {
+ Object instr = instructions.elementAt(i);
+ if (instr instanceof TransformOperation) {
+ image = ((TransformOperation) instr)
+ .executeTransformOperation(image);
+ } else {
+ log("Not a TransformOperation: " + instr);
+ }
+ }
+ } finally {
+ FileUtils.close(input);
+ }
+
+ File dstParent = newFile.getParentFile();
+ if (!dstParent.isDirectory()
+ && !(dstParent.mkdirs() || dstParent.isDirectory())) {
+ throw new BuildException("Failed to create parent directory "
+ + dstParent);
+ }
+
+ if ((overwrite && newFile.exists()) && (!newFile.equals(file))) {
+ newFile.delete();
+ }
+
+ FileOutputStream stream = null;
+ try {
+ stream = new FileOutputStream(newFile);
+
+ JAI.create("encode", image, stream,
+ str_encoding.toUpperCase(Locale.ENGLISH),
+ null);
+ stream.flush();
+ } finally {
+ FileUtils.close(stream);
+ }
+ } catch (IOException err) {
+ if (!file.equals(newFile)){
+ newFile.delete();
+ }
+ if (!failonerror) {
+ log("Error processing file: " + err);
+ } else {
+ throw new BuildException(err);
+ }
+ } catch (java.lang.RuntimeException rerr) {
+ if (!file.equals(newFile)){
+ newFile.delete();
+ }
+ if (!failonerror) {
+ log("Error processing file: " + rerr);
+ } else {
+ throw new BuildException(rerr);
+ }
+ }
+ }
+
+ /**
+ * Executes the Task.
+ * @throws BuildException on error.
+ */
+ public void execute() throws BuildException {
+
+ validateAttributes();
+
+ try {
+ File dest = destDir != null ? destDir : srcDir;
+
+ int writeCount = 0;
+
+ // build mapper
+ final FileNameMapper mapper;
+ if (mapperElement==null){
+ mapper = new IdentityMapper();
+ } else {
+ mapper = mapperElement.getImplementation();
+ }
+
+ // deal with specified srcDir
+ if (srcDir != null) {
+ final DirectoryScanner ds = super.getDirectoryScanner(srcDir);
+
+ final String[] files = ds.getIncludedFiles();
+ writeCount += processDir(srcDir, files, dest, mapper);
+ }
+ // deal with the filesets
+ final int size = filesets.size();
+ for (int i = 0; i < size; i++) {
+ final FileSet fs = (FileSet) filesets.elementAt(i);
+ final DirectoryScanner ds =
+ fs.getDirectoryScanner(getProject());
+ final String[] files = ds.getIncludedFiles();
+ final File fromDir = fs.getDir(getProject());
+ writeCount += processDir(fromDir, files, dest, mapper);
+ }
+
+ if (writeCount>0){
+ log("Processed " + writeCount +
+ (writeCount == 1 ? " image." : " images."));
+ }
+
+ } catch (Exception err) {
+ err.printStackTrace();
+ throw new BuildException(err.getMessage());
+ }
+ }
+
+ /**
+ * Ensure we have a consistent and legal set of attributes, and set
+ * any internal flags necessary based on different combinations
+ * of attributes.
+ * @throws BuildException on error.
+ */
+ protected void validateAttributes() throws BuildException {
+ if (srcDir == null && filesets.size() == 0) {
+ throw new BuildException("Specify at least one source"
+ + "--a srcDir or a fileset.");
+ }
+ if (srcDir == null && destDir == null) {
+ throw new BuildException("Specify the destDir, or the srcDir.");
+ }
+ if (str_encoding.equalsIgnoreCase("jpg")) {
+ str_encoding = "JPEG";
+ } else if (str_encoding.equalsIgnoreCase("tif")) {
+ str_encoding = "TIFF";
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/AbstractHotDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/AbstractHotDeploymentTool.java
new file mode 100644
index 00000000..fdbde749
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/AbstractHotDeploymentTool.java
@@ -0,0 +1,193 @@
+/*
+ * 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.tools.ant.taskdefs.optional.j2ee;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Abstract class to support vendor-specific hot deployment tools.
+ * This class will validate boilerplate attributes.
+ *
+ * Subclassing this class for a vendor specific tool involves the
+ * following.
+ * <ol><li>Implement the <code>isActionValid()<code> method to insure the
+ * action supplied as the "action" attribute of ServerDeploy is valid.
+ * <li>Implement the <code>validateAttributes()</code> method to insure
+ * all required attributes are supplied, and are in the correct format.
+ * <li>Add a <code>add&lt;TOOL&gt;</code> method to the ServerDeploy
+ * class. This method will be called when Ant encounters a
+ * <code>add&lt;TOOL&gt;</code> task nested in the
+ * <code>serverdeploy</code> task.
+ * <li>Define the <code>deploy</code> method. This method should perform
+ * whatever task it takes to hot-deploy the component. IE: spawn a JVM and
+ * run class, exec a native executable, run Java code...
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy
+ */
+public abstract class AbstractHotDeploymentTool implements HotDeploymentTool {
+ /** The parent task **/
+ private ServerDeploy task;
+
+ /** The classpath passed to the JVM on execution. **/
+ private Path classpath;
+
+ /** The username for the deployment server. **/
+ private String userName;
+
+ /** The password for the deployment server. **/
+ private String password;
+
+ /** The address of the deployment server **/
+ private String server;
+
+ /**
+ * Add a classpath as a nested element.
+ * @return A Path object representing the classpath to be used.
+ */
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(task.getProject());
+ }
+ return classpath.createPath();
+ }
+
+ /**
+ * Determines if the "action" attribute defines a valid action.
+ * <p>Subclasses should determine if the action passed in is
+ * supported by the vendor's deployment tool.
+ * <p>Actions may by "deploy", "delete", etc... It all depends
+ * on the tool.
+ * @return true if the "action" attribute is valid, false if not.
+ */
+ protected abstract boolean isActionValid();
+
+ /**
+ * Validates the passed in attributes.
+ * Subclasses should chain to this super-method to insure
+ * validation of boilerplate attributes.
+ * <p>Only the "action" attribute is required in the
+ * base class. Subclasses should check attributes accordingly.
+ * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+ */
+ public void validateAttributes() throws BuildException {
+ if (task.getAction() == null) {
+ throw new BuildException("The \"action\" attribute must be set");
+ }
+
+ if (!isActionValid()) {
+ throw new BuildException("Invalid action \"" + task.getAction() + "\" passed");
+ }
+
+ if (classpath == null) {
+ throw new BuildException("The classpath attribute must be set");
+ }
+ }
+
+ /**
+ * Perform the actual deployment.
+ * It's up to the subclasses to implement the actual behavior.
+ * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+ */
+ public abstract void deploy() throws BuildException;
+
+ /**
+ * Sets the parent task.
+ * @param task a ServerDeploy object representing the parent task.
+ * @ant.attribute ignore="true"
+ */
+ public void setTask(ServerDeploy task) {
+ this.task = task;
+ }
+
+ /**
+ * Returns the task field, a ServerDeploy object.
+ * @return An ServerDeploy representing the parent task.
+ */
+ protected ServerDeploy getTask() {
+ return task;
+ }
+
+ /**
+ * gets the classpath field.
+ * @return A Path representing the "classpath" attribute.
+ */
+ public Path getClasspath() {
+ return classpath;
+ }
+
+ /**
+ * The classpath to be passed to the JVM running the tool;
+ * optional depending upon the tool.
+ * The classpath may also be supplied as a nested element.
+ * @param classpath A Path object representing the "classpath" attribute.
+ */
+ public void setClasspath(Path classpath) {
+ this.classpath = classpath;
+ }
+
+ /**
+ * Returns the userName field.
+ * @return A String representing the "userName" attribute.
+ */
+ public String getUserName() {
+ return userName;
+ }
+
+ /**
+ * The user with privileges to deploy applications to the server; optional.
+ * @param userName A String representing the "userName" attribute.
+ */
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ /**
+ * Returns the password field.
+ * @return A String representing the "password" attribute.
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * The password of the user; optional.
+ * @param password A String representing the "password" attribute.
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ /**
+ * Returns the server field.
+ * @return A String representing the "server" attribute.
+ */
+ public String getServer() {
+ return server;
+ }
+
+ /**
+ * The address or URL for the server where the component will be deployed.
+ * @param server A String representing the "server" attribute.
+ */
+ public void setServer(String server) {
+ this.server = server;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/GenericHotDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/GenericHotDeploymentTool.java
new file mode 100644
index 00000000..5a5abbab
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/GenericHotDeploymentTool.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.tools.ant.taskdefs.optional.j2ee;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * A generic tool for J2EE server hot deployment.
+ * <p>The simple implementation spawns a JVM with the supplied
+ * class name, jvm args, and arguments.
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy
+ */
+public class GenericHotDeploymentTool extends AbstractHotDeploymentTool {
+ /** A Java task used to run the deployment tool **/
+ private Java java;
+
+ /** The fully qualified class name of the deployment tool **/
+ private String className;
+
+ /** List of valid actions **/
+ private static final String[] VALID_ACTIONS = {ACTION_DEPLOY};
+
+ /**
+ * Add a nested argument element to hand to the deployment tool; optional.
+ * @return A Commandline.Argument object representing the
+ * command line argument being passed when the deployment
+ * tool is run. IE: "-user=mark", "-password=venture"...
+ */
+ public Commandline.Argument createArg() {
+ return java.createArg();
+ }
+
+ /**
+ * Add a nested argument element to hand to the JVM running the
+ * deployment tool.
+ * Creates a nested arg element.
+ * @return A Commandline.Argument object representing the
+ * JVM command line argument being passed when the deployment
+ * tool is run. IE: "-ms64m", "-mx128m"...
+ */
+ public Commandline.Argument createJvmarg() {
+ return java.createJvmarg();
+ }
+
+ /**
+ * Determines if the "action" attribute defines a valid action.
+ * <p>Subclasses should determine if the action passed in is
+ * supported by the vendor's deployment tool.
+ * For this generic implementation, the only valid action is "deploy"
+ * @return true if the "action" attribute is valid, false if not.
+ */
+ protected boolean isActionValid() {
+ return (getTask().getAction().equals(VALID_ACTIONS[0]));
+ }
+
+ /**
+ * Sets the parent task.
+ * @param task An ServerDeploy object representing the parent task.
+ * @ant.attribute ignored="true"
+ */
+ public void setTask(ServerDeploy task) {
+ super.setTask(task);
+ java = new Java(task);
+ }
+
+ /**
+ * Perform the actual deployment.
+ * For this generic implementation, a JVM is spawned using the
+ * supplied classpath, classname, JVM args, and command line arguments.
+ * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+ */
+ public void deploy() throws BuildException {
+ java.setClassname(className);
+ java.setClasspath(getClasspath());
+ java.setFork(true);
+ java.setFailonerror(true);
+ java.execute();
+ }
+
+ /**
+ * Validates the passed in attributes.
+ * Ensures the className and arguments attribute have been set.
+ * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+ */
+ public void validateAttributes() throws BuildException {
+ super.validateAttributes();
+
+ if (className == null) {
+ throw new BuildException("The classname attribute must be set");
+ }
+ }
+
+ /**
+ * The name of the class to execute to perform
+ * deployment; required.
+ * Example: "com.foobar.tools.deploy.DeployTool"
+ * @param className The fully qualified class name of the class
+ * to perform deployment.
+ */
+ public void setClassName(String className) {
+ this.className = className;
+ }
+
+ /**
+ * get the java attribute.
+ * @return the java attribute.
+ */
+ public Java getJava() {
+ return java;
+ }
+
+ /**
+ * Get the classname attribute.
+ * @return the classname value.
+ */
+ public String getClassName() {
+ return className;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/HotDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/HotDeploymentTool.java
new file mode 100644
index 00000000..16c55d8e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/HotDeploymentTool.java
@@ -0,0 +1,61 @@
+/*
+ * 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.tools.ant.taskdefs.optional.j2ee;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * An interface for vendor-specific "hot" deployment tools.
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy
+ */
+public interface HotDeploymentTool {
+ /** The delete action String **/
+ String ACTION_DELETE = "delete";
+
+ /** The deploy action String **/
+ String ACTION_DEPLOY = "deploy";
+
+ /** The list action String **/
+ String ACTION_LIST = "list";
+
+ /** The undeploy action String **/
+ String ACTION_UNDEPLOY = "undeploy";
+
+ /** The update action String **/
+ String ACTION_UPDATE = "update";
+
+ /**
+ * Validates the passed in attributes.
+ * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+ */
+ void validateAttributes() throws BuildException;
+
+ /**
+ * Perform the actual deployment.
+ * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+ */
+ void deploy() throws BuildException;
+
+ /**
+ * Sets the parent task.
+ * @param task A ServerDeploy object representing the parent task.
+ */
+ void setTask(ServerDeploy task);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/JonasHotDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/JonasHotDeploymentTool.java
new file mode 100644
index 00000000..c7a33a19
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/JonasHotDeploymentTool.java
@@ -0,0 +1,252 @@
+/*
+ * 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.tools.ant.taskdefs.optional.j2ee;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * An Ant wrapper task for the weblogic.deploy tool. This is used
+ * to hot-deploy J2EE applications to a running WebLogic server.
+ * This is <b>not</b> the same as creating the application
+ * archive. This task assumes the archive (EAR, JAR, or WAR) file
+ * has been assembled and is supplied as the "source" attribute.
+ * <p>
+ *
+ * In the end, this task assembles the commandline parameters and
+ * runs the weblogic.deploy tool in a separate JVM.
+ *
+ *@see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool
+ *@see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool
+ *@see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy
+ */
+public class JonasHotDeploymentTool extends GenericHotDeploymentTool implements HotDeploymentTool {
+
+ /**
+ * Description of the Field
+ */
+ protected static final String DEFAULT_ORB = "RMI";
+
+ /**
+ * The classname of the tool to run *
+ */
+ private static final String JONAS_DEPLOY_CLASS_NAME = "org.objectweb.jonas.adm.JonasAdmin";
+
+ /**
+ * All the valid actions that weblogic.deploy permits *
+ */
+ private static final String[] VALID_ACTIONS
+ = {ACTION_DELETE, ACTION_DEPLOY, ACTION_LIST, ACTION_UNDEPLOY, ACTION_UPDATE};
+
+ /**
+ * Description of the Field
+ */
+ private File jonasroot;
+
+ /**
+ * Description of the Field
+ */
+ private String orb = null;
+
+ /**
+ * Description of the Field
+ */
+ private String davidHost;
+
+ /**
+ * Description of the Field
+ */
+ private int davidPort;
+
+
+ /**
+ * Set the host for the David ORB; required if
+ * ORB==david.
+ *
+ *@param inValue The new davidhost value
+ */
+ public void setDavidhost(final String inValue) {
+ davidHost = inValue;
+ }
+
+
+ /**
+ * Set the port for the David ORB; required if
+ * ORB==david.
+ *
+ *@param inValue The new davidport value
+ */
+ public void setDavidport(final int inValue) {
+ davidPort = inValue;
+ }
+
+
+ /**
+ * set the jonas root directory (-Dinstall.root=). This
+ * element is required.
+ *
+ *@param inValue The new jonasroot value
+ */
+ public void setJonasroot(final File inValue) {
+ jonasroot = inValue;
+ }
+
+
+ /**
+ *
+ * Choose your ORB : RMI, JEREMIE, DAVID, ...; optional.
+ * If omitted, it defaults
+ * to the one present in classpath. The corresponding JOnAS JAR is
+ * automatically added to the classpath. If your orb is DAVID (RMI/IIOP) you must
+ * specify davidhost and davidport properties.
+ *
+ *@param inValue RMI, JEREMIE, DAVID,...
+ */
+ public void setOrb(final String inValue) {
+ orb = inValue;
+ }
+
+
+ /**
+ * gets the classpath field.
+ *
+ *@return A Path representing the "classpath" attribute.
+ */
+ public Path getClasspath() {
+
+ Path aClassPath = super.getClasspath();
+
+ if (aClassPath == null) {
+ aClassPath = new Path(getTask().getProject());
+ }
+ if (orb != null) {
+ String aOrbJar = new File(jonasroot, "lib/" + orb + "_jonas.jar").toString();
+ String aConfigDir = new File(jonasroot, "config/").toString();
+ Path aJOnASOrbPath = new Path(aClassPath.getProject(),
+ aOrbJar + File.pathSeparator + aConfigDir);
+ aClassPath.append(aJOnASOrbPath);
+ }
+ return aClassPath;
+ }
+
+
+ /**
+ * Validates the passed in attributes. <p>
+ *
+ * The rules are:
+ * <ol>
+ * <li> If action is "deploy" or "update" the "application"
+ * and "source" attributes must be supplied.
+ * <li> If action is "delete" or "undeploy" the
+ * "application" attribute must be supplied.
+ *
+ *@exception BuildException Description
+ * of Exception
+ */
+ public void validateAttributes() throws BuildException {
+ // super.validateAttributes(); // don't want to call this method
+
+ Java java = getJava();
+
+ String action = getTask().getAction();
+ if (action == null) {
+ throw new BuildException("The \"action\" attribute must be set");
+ }
+
+ if (!isActionValid()) {
+ throw new BuildException("Invalid action \"" + action + "\" passed");
+ }
+
+ if (getClassName() == null) {
+ setClassName(JONAS_DEPLOY_CLASS_NAME);
+ }
+
+ if (jonasroot == null || jonasroot.isDirectory()) {
+ java.createJvmarg().setValue("-Dinstall.root=" + jonasroot);
+ java.createJvmarg().setValue("-Djava.security.policy=" + jonasroot
+ + "/config/java.policy");
+
+ if ("DAVID".equals(orb)) {
+ java.createJvmarg().setValue("-Dorg.omg.CORBA.ORBClass"
+ + "=org.objectweb.david.libs.binding.orbs.iiop.IIOPORB");
+ java.createJvmarg().setValue("-Dorg.omg.CORBA.ORBSingletonClass="
+ + "org.objectweb.david.libs.binding.orbs.ORBSingletonClass");
+ java.createJvmarg().setValue("-Djavax.rmi.CORBA.StubClass="
+ + "org.objectweb.david.libs.stub_factories.rmi.StubDelegate");
+ java.createJvmarg().setValue("-Djavax.rmi.CORBA.PortableRemoteObjectClass="
+ + "org.objectweb.david.libs.binding.rmi.ORBPortableRemoteObjectDelegate");
+ java.createJvmarg().setValue("-Djavax.rmi.CORBA.UtilClass="
+ + "org.objectweb.david.libs.helpers.RMIUtilDelegate");
+ java.createJvmarg().setValue("-Ddavid.CosNaming.default_method=0");
+ java.createJvmarg().setValue("-Ddavid.rmi.ValueHandlerClass="
+ + "com.sun.corba.se.internal.io.ValueHandlerImpl");
+ if (davidHost != null) {
+ java.createJvmarg().setValue("-Ddavid.CosNaming.default_host="
+ + davidHost);
+ }
+ if (davidPort != 0) {
+ java.createJvmarg().setValue("-Ddavid.CosNaming.default_port="
+ + davidPort);
+ }
+ }
+ }
+
+ if (getServer() != null) {
+ java.createArg().setLine("-n " + getServer());
+ }
+
+ if (action.equals(ACTION_DEPLOY)
+ || action.equals(ACTION_UPDATE)
+ || action.equals("redeploy")) {
+ java.createArg().setLine("-a " + getTask().getSource());
+ } else if (action.equals(ACTION_DELETE) || action.equals(ACTION_UNDEPLOY)) {
+ java.createArg().setLine("-r " + getTask().getSource());
+ } else if (action.equals(ACTION_LIST)) {
+ java.createArg().setValue("-l");
+ }
+ }
+
+
+ /**
+ * Determines if the action supplied is valid. <p>
+ *
+ * Valid actions are contained in the static array
+ * VALID_ACTIONS
+ *
+ *@return true if the action attribute is valid, false if
+ * not.
+ */
+ protected boolean isActionValid() {
+ boolean valid = false;
+
+ String action = getTask().getAction();
+
+ for (int i = 0; i < VALID_ACTIONS.length; i++) {
+ if (action.equals(VALID_ACTIONS[i])) {
+ valid = true;
+ break;
+ }
+ }
+
+ return valid;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/ServerDeploy.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/ServerDeploy.java
new file mode 100644
index 00000000..8965b8e8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/ServerDeploy.java
@@ -0,0 +1,153 @@
+/*
+ * 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.tools.ant.taskdefs.optional.j2ee;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Controls hot deployment tools for J2EE servers.
+ *
+ * This class is used as a framework for the creation of vendor specific
+ * hot deployment tools.
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.GenericHotDeploymentTool
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.WebLogicHotDeploymentTool
+ */
+public class ServerDeploy extends Task {
+ /** The action to be performed. IE: "deploy", "delete", etc... **/
+ private String action;
+
+ /** The source (fully-qualified path) to the component being deployed **/
+ private File source;
+
+ /** The vendor specific tool for deploying the component **/
+ private Vector vendorTools = new Vector();
+
+ ///////////////////////////////////////////////////////////////////////////
+ //
+ // Place vendor specific tool creations here.
+ //
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Creates a generic deployment tool.
+ * <p>Ant calls this method on creation to handle embedded "generic" elements
+ * in the ServerDeploy task.
+ * @param tool An instance of GenericHotDeployment tool, passed in by Ant.
+ */
+ public void addGeneric(GenericHotDeploymentTool tool) {
+ tool.setTask(this);
+ vendorTools.addElement(tool);
+ }
+
+ /**
+ * Creates a WebLogic deployment tool, for deployment to WebLogic servers.
+ * <p>Ant calls this method on creation to handle embedded "weblogic" elements
+ * in the ServerDeploy task.
+ * @param tool An instance of WebLogicHotDeployment tool, passed in by Ant.
+ */
+ public void addWeblogic(WebLogicHotDeploymentTool tool) {
+ tool.setTask(this);
+ vendorTools.addElement(tool);
+ }
+
+ /**
+ * Creates a JOnAS deployment tool, for deployment to JOnAS servers.
+ * <p>Ant calls this method on creation to handle embedded "jonas" elements
+ * in the ServerDeploy task.
+ * @param tool An instance of JonasHotDeployment tool, passed in by Ant.
+ */
+ public void addJonas(JonasHotDeploymentTool tool) {
+ tool.setTask(this);
+ vendorTools.addElement(tool);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ //
+ // Execute method
+ //
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Execute the task.
+ * <p>This method calls the deploy() method on each of the vendor-specific tools
+ * in the <code>vendorTools</code> collection. This performs the actual
+ * process of deployment on each tool.
+ * @exception org.apache.tools.ant.BuildException if the attributes
+ * are invalid or incomplete, or a failure occurs in the deployment process.
+ */
+ public void execute() throws BuildException {
+ for (Enumeration e = vendorTools.elements();
+ e.hasMoreElements();) {
+ HotDeploymentTool tool = (HotDeploymentTool) e.nextElement();
+ tool.validateAttributes();
+ tool.deploy();
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ //
+ // Set/get methods
+ //
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns the action field.
+ * @return A string representing the "action" attribute.
+ */
+ public String getAction() {
+ return action;
+ }
+
+ /**
+ * The action to be performed, usually "deploy"; required.
+ * Some tools support additional actions, such as "delete", "list", "undeploy", "update"...
+ * @param action A String representing the "action" attribute.
+ */
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ /**
+ * Returns the source field (the path/filename of the component to be
+ * deployed.
+ * @return A File object representing the "source" attribute.
+ */
+ public File getSource() {
+ return source;
+ }
+
+ /**
+ * The filename of the component to be deployed; optional
+ * depending upon the tool and the action.
+ * @param source String representing the "source" attribute.
+ */
+ public void setSource(File source) {
+ this.source = source;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/WebLogicHotDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/WebLogicHotDeploymentTool.java
new file mode 100644
index 00000000..da875092
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/WebLogicHotDeploymentTool.java
@@ -0,0 +1,247 @@
+/*
+ * 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.tools.ant.taskdefs.optional.j2ee;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Java;
+
+/**
+ * An Ant wrapper task for the weblogic.deploy tool. This is used to
+ * hot-deploy J2EE applications to a running WebLogic server.
+ * This is <b>not</b> the same as creating the application archive.
+ * This task assumes the archive (EAR, JAR, or WAR) file has been
+ * assembled and is supplied as the "source" attribute.
+ * <p>In the end, this task assembles the commandline parameters
+ * and runs the weblogic.deploy tool in a separate JVM.
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool
+ * @see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy
+ */
+public class WebLogicHotDeploymentTool extends AbstractHotDeploymentTool
+ implements HotDeploymentTool {
+ private static final int STRING_BUFFER_SIZE = 1024;
+ /** The classname of the tool to run **/
+ private static final String WEBLOGIC_DEPLOY_CLASS_NAME = "weblogic.deploy";
+
+ /** All the valid actions that weblogic.deploy permits **/
+ private static final String[] VALID_ACTIONS
+ = {ACTION_DELETE, ACTION_DEPLOY, ACTION_LIST, ACTION_UNDEPLOY, ACTION_UPDATE};
+
+ /** Represents the "-debug" flag from weblogic.deploy **/
+ private boolean debug;
+
+ /** The application name that is being deployed **/
+ private String application;
+
+ /** The component name:target(s) for the "-component" argument of weblogic.deploy **/
+ private String component;
+
+ /**
+ * Perform the actual deployment.
+ * For this implementation, a JVM is spawned and the weblogic.deploy
+ * tools is executed.
+ * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+ */
+ public void deploy() {
+ Java java = new Java(getTask());
+ java.setFork(true);
+ java.setFailonerror(true);
+ java.setClasspath(getClasspath());
+
+ java.setClassname(WEBLOGIC_DEPLOY_CLASS_NAME);
+ java.createArg().setLine(getArguments());
+ java.execute();
+ }
+
+ /**
+ * Validates the passed in attributes.
+ * <p>The rules are:
+ * <ol><li>If action is "deploy" or "update" the "application" and "source"
+ * attributes must be supplied.
+ * <li>If action is "delete" or "undeploy" the "application" attribute must
+ * be supplied.
+ * @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete
+ */
+ public void validateAttributes() throws BuildException {
+ super.validateAttributes();
+
+ String action = getTask().getAction();
+
+ // check that the password has been set
+ if ((getPassword() == null)) {
+ throw new BuildException("The password attribute must be set.");
+ }
+
+ // check for missing application on deploy & update
+ if ((action.equals(ACTION_DEPLOY) || action.equals(ACTION_UPDATE))
+ && application == null) {
+ throw new BuildException("The application attribute must be set "
+ + "if action = " + action);
+ }
+
+ // check for missing source on deploy & update
+ if ((action.equals(ACTION_DEPLOY) || action.equals(ACTION_UPDATE))
+ && getTask().getSource() == null) {
+ throw new BuildException("The source attribute must be set if "
+ + "action = " + action);
+ }
+
+ // check for missing application on delete & undeploy
+ if ((action.equals(ACTION_DELETE) || action.equals(ACTION_UNDEPLOY))
+ && application == null) {
+ throw new BuildException("The application attribute must be set if "
+ + "action = " + action);
+ }
+ }
+
+ /**
+ * Builds the arguments to pass to weblogic.deploy according to the
+ * supplied action.
+ * @return A String containing the arguments for the weblogic.deploy tool.
+ * @throws BuildException if there is an error.
+ */
+ public String getArguments() throws BuildException {
+ String action = getTask().getAction();
+ String args = null;
+
+ if (action.equals(ACTION_DEPLOY) || action.equals(ACTION_UPDATE)) {
+ args = buildDeployArgs();
+ } else if (action.equals(ACTION_DELETE) || action.equals(ACTION_UNDEPLOY)) {
+ args = buildUndeployArgs();
+ } else if (action.equals(ACTION_LIST)) {
+ args = buildListArgs();
+ }
+
+ return args;
+ }
+
+ /**
+ * Determines if the action supplied is valid.
+ * <p>Valid actions are contained in the static array VALID_ACTIONS
+ * @return true if the action attribute is valid, false if not.
+ */
+ protected boolean isActionValid() {
+ boolean valid = false;
+
+ String action = getTask().getAction();
+
+ for (int i = 0; i < VALID_ACTIONS.length; i++) {
+ if (action.equals(VALID_ACTIONS[i])) {
+ valid = true;
+ break;
+ }
+ }
+
+ return valid;
+ }
+
+ /**
+ * Builds the prefix arguments to pass to weblogic.deploy.
+ * These arguments are generic across all actions.
+ * @return A StringBuffer containing the prefix arguments.
+ * The action-specific build methods will append to this StringBuffer.
+ */
+ protected StringBuffer buildArgsPrefix() {
+ ServerDeploy task = getTask();
+ // constructs the "-url <url> -debug <action> <password>" portion
+ // of the commmand line
+ return new StringBuffer(STRING_BUFFER_SIZE)
+ .append((getServer() != null)
+ ? "-url " + getServer()
+ : "")
+ .append(" ")
+ .append(debug ? "-debug " : "")
+ .append((getUserName() != null)
+ ? "-username " + getUserName()
+ : "")
+ .append(" ")
+ .append(task.getAction()).append(" ")
+ .append(getPassword()).append(" ");
+ }
+
+ /**
+ * Builds the arguments to pass to weblogic.deploy for deployment actions
+ * ("deploy" and "update").
+ * @return A String containing the full argument string for weblogic.deploy.
+ */
+ protected String buildDeployArgs() {
+ String args = buildArgsPrefix()
+ .append(application).append(" ")
+ .append(getTask().getSource())
+ .toString();
+
+ if (component != null) {
+ args = "-component " + component + " " + args;
+ }
+
+ return args;
+ }
+
+ /**
+ * Builds the arguments to pass to weblogic.deploy for undeployment actions
+ * ("undeploy" and "delete").
+ * @return A String containing the full argument string for weblogic.deploy.
+ */
+ protected String buildUndeployArgs() {
+ return buildArgsPrefix()
+ .append(application).append(" ")
+ .toString();
+ }
+
+ /**
+ * Builds the arguments to pass to weblogic.deploy for the list action
+ * @return A String containing the full argument string for weblogic.deploy.
+ */
+ protected String buildListArgs() {
+ return buildArgsPrefix()
+ .toString();
+ }
+
+ /**
+ * If set to true, additional information will be
+ * printed during the deployment process; optional.
+ * @param debug A boolean representing weblogic.deploy "-debug" flag.
+ */
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ }
+
+ /**
+ * The name of the application being deployed; required.
+ * @param application A String representing the application portion of the
+ * weblogic.deploy command line.
+ */
+ public void setApplication(String application) {
+ this.application = application;
+ }
+
+ /**
+ * the component string for the deployment targets; optional.
+ * It is in the form <code>&lt;component&gt;:&lt;target1&gt;,&lt;target2&gt;...</code>
+ * Where component is the archive name (minus the .jar, .ear, .war
+ * extension). Targets are the servers where the components will be deployed
+
+ * @param component A String representing the value of the "-component"
+ * argument of the weblogic.deploy command line argument.
+ */
+ public void setComponent(String component) {
+ this.component = component;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJDoc.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJDoc.java
new file mode 100644
index 00000000..a4dc0f48
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJDoc.java
@@ -0,0 +1,226 @@
+/*
+ * 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.tools.ant.taskdefs.optional.javacc;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Runs the JJDoc compiler compiler.
+ *
+ */
+public class JJDoc extends Task {
+
+ // keys to optional attributes
+ private static final String OUTPUT_FILE = "OUTPUT_FILE";
+ private static final String TEXT = "TEXT";
+ private static final String ONE_TABLE = "ONE_TABLE";
+
+ private final Hashtable optionalAttrs = new Hashtable();
+
+ private String outputFile = null;
+ private boolean plainText = false;
+
+ private static final String DEFAULT_SUFFIX_HTML = ".html";
+ private static final String DEFAULT_SUFFIX_TEXT = ".txt";
+
+ // required attributes
+ private File targetFile = null;
+ private File javaccHome = null;
+
+ private CommandlineJava cmdl = new CommandlineJava();
+
+ private String maxMemory = null;
+
+ /**
+ * Sets the TEXT BNF documentation option.
+ * @param plainText a <code>boolean</code> value.
+ */
+ public void setText(boolean plainText) {
+ optionalAttrs.put(TEXT, plainText ? Boolean.TRUE : Boolean.FALSE);
+ this.plainText = plainText;
+ }
+
+ /**
+ * Sets the ONE_TABLE documentation option.
+ * @param oneTable a <code>boolean</code> value.
+ */
+ public void setOnetable(boolean oneTable) {
+ optionalAttrs.put(ONE_TABLE, oneTable ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * The outputfile to write the generated BNF documentation file to.
+ * If not set, the file is written with the same name as
+ * the JavaCC grammar file with a suffix .html or .txt.
+ * @param outputFile the name of the output file.
+ */
+ public void setOutputfile(String outputFile) {
+ this.outputFile = outputFile;
+ }
+
+ /**
+ * The javacc grammar file to process.
+ * @param target the grammar file.
+ */
+ public void setTarget(File target) {
+ this.targetFile = target;
+ }
+
+ /**
+ * The directory containing the JavaCC distribution.
+ * @param javaccHome the home directory.
+ */
+ public void setJavacchome(File javaccHome) {
+ this.javaccHome = javaccHome;
+ }
+
+ /**
+ * Corresponds -Xmx.
+ *
+ * @param max max memory parameter.
+ * @since Ant 1.8.3
+ */
+ public void setMaxmemory(String max) {
+ maxMemory = max;
+ }
+
+ /**
+ * Constructor
+ */
+ public JJDoc() {
+ cmdl.setVm(JavaEnvUtils.getJreExecutable("java"));
+ }
+
+ /**
+ * Do the task.
+ * @throws BuildException if there is an error.
+ */
+ public void execute() throws BuildException {
+
+ // load command line with optional attributes
+ Enumeration iter = optionalAttrs.keys();
+ while (iter.hasMoreElements()) {
+ String name = (String) iter.nextElement();
+ Object value = optionalAttrs.get(name);
+ cmdl.createArgument()
+ .setValue("-" + name + ":" + value.toString());
+ }
+
+ if (targetFile == null || !targetFile.isFile()) {
+ throw new BuildException("Invalid target: " + targetFile);
+ }
+
+ if (outputFile != null) {
+ cmdl.createArgument() .setValue("-" + OUTPUT_FILE + ":"
+ + outputFile.replace('\\', '/'));
+ }
+
+ // use the directory containing the target as the output directory
+ File javaFile = new File(createOutputFileName(targetFile, outputFile,
+ plainText));
+
+ if (javaFile.exists()
+ && targetFile.lastModified() < javaFile.lastModified()) {
+ log("Target is already built - skipping (" + targetFile + ")",
+ Project.MSG_VERBOSE);
+ return;
+ }
+
+ cmdl.createArgument().setValue(targetFile.getAbsolutePath());
+
+ final Path classpath = cmdl.createClasspath(getProject());
+ final File javaccJar = JavaCC.getArchiveFile(javaccHome);
+ classpath.createPathElement().setPath(javaccJar.getAbsolutePath());
+ classpath.addJavaRuntime();
+
+ cmdl.setClassname(JavaCC.getMainClass(classpath,
+ JavaCC.TASKDEF_TYPE_JJDOC));
+
+ cmdl.setMaxmemory(maxMemory);
+ final Commandline.Argument arg = cmdl.createVmArgument();
+ arg.setValue("-Dinstall.root=" + javaccHome.getAbsolutePath());
+
+ final Execute process =
+ new Execute(new LogStreamHandler(this,
+ Project.MSG_INFO,
+ Project.MSG_INFO),
+ null);
+ log(cmdl.describeCommand(), Project.MSG_VERBOSE);
+ process.setCommandline(cmdl.getCommandline());
+
+ try {
+ if (process.execute() != 0) {
+ throw new BuildException("JJDoc failed.");
+ }
+ } catch (IOException e) {
+ throw new BuildException("Failed to launch JJDoc", e);
+ }
+ }
+
+ private String createOutputFileName(File destFile, String optionalOutputFile,
+ boolean plain) {
+ String suffix = DEFAULT_SUFFIX_HTML;
+ String javaccFile = destFile.getAbsolutePath().replace('\\', '/');
+
+ if (plain) {
+ suffix = DEFAULT_SUFFIX_TEXT;
+ }
+
+ if ((optionalOutputFile == null) || optionalOutputFile.equals("")) {
+ int filePos = javaccFile.lastIndexOf("/");
+
+ if (filePos >= 0) {
+ javaccFile = javaccFile.substring(filePos + 1);
+ }
+
+ int suffixPos = javaccFile.lastIndexOf('.');
+
+ if (suffixPos == -1) {
+ optionalOutputFile = javaccFile + suffix;
+ } else {
+ String currentSuffix = javaccFile.substring(suffixPos);
+
+ if (currentSuffix.equals(suffix)) {
+ optionalOutputFile = javaccFile + suffix;
+ } else {
+ optionalOutputFile = javaccFile.substring(0, suffixPos)
+ + suffix;
+ }
+ }
+ } else {
+ optionalOutputFile = optionalOutputFile.replace('\\', '/');
+ }
+
+ return (getProject().getBaseDir() + "/" + optionalOutputFile)
+ .replace('\\', '/');
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJTree.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJTree.java
new file mode 100644
index 00000000..f5d126ee
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJTree.java
@@ -0,0 +1,416 @@
+/*
+ * 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.tools.ant.taskdefs.optional.javacc;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Runs the JJTree compiler compiler.
+ *
+ */
+public class JJTree extends Task {
+
+ // keys to optional attributes
+ private static final String OUTPUT_FILE = "OUTPUT_FILE";
+ private static final String BUILD_NODE_FILES = "BUILD_NODE_FILES";
+ private static final String MULTI = "MULTI";
+ private static final String NODE_DEFAULT_VOID = "NODE_DEFAULT_VOID";
+ private static final String NODE_FACTORY = "NODE_FACTORY";
+ private static final String NODE_SCOPE_HOOK = "NODE_SCOPE_HOOK";
+ private static final String NODE_USES_PARSER = "NODE_USES_PARSER";
+ private static final String STATIC = "STATIC";
+ private static final String VISITOR = "VISITOR";
+
+ private static final String NODE_PACKAGE = "NODE_PACKAGE";
+ private static final String VISITOR_EXCEPTION = "VISITOR_EXCEPTION";
+ private static final String NODE_PREFIX = "NODE_PREFIX";
+
+ private final Hashtable optionalAttrs = new Hashtable();
+
+ private String outputFile = null;
+
+ private static final String DEFAULT_SUFFIX = ".jj";
+
+ // required attributes
+ private File outputDirectory = null;
+ private File targetFile = null;
+ private File javaccHome = null;
+
+ private CommandlineJava cmdl = new CommandlineJava();
+
+ private String maxMemory = null;
+
+ /**
+ * Sets the BUILD_NODE_FILES grammar option.
+ * @param buildNodeFiles a <code>boolean</code> value.
+ */
+ public void setBuildnodefiles(boolean buildNodeFiles) {
+ optionalAttrs.put(BUILD_NODE_FILES, buildNodeFiles ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the MULTI grammar option.
+ * @param multi a <code>boolean</code> value.
+ */
+ public void setMulti(boolean multi) {
+ optionalAttrs.put(MULTI, multi ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the NODE_DEFAULT_VOID grammar option.
+ * @param nodeDefaultVoid a <code>boolean</code> value.
+ */
+ public void setNodedefaultvoid(boolean nodeDefaultVoid) {
+ optionalAttrs.put(NODE_DEFAULT_VOID, nodeDefaultVoid ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the NODE_FACTORY grammar option.
+ * @param nodeFactory a <code>boolean</code> value.
+ */
+ public void setNodefactory(boolean nodeFactory) {
+ optionalAttrs.put(NODE_FACTORY, nodeFactory ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the NODE_SCOPE_HOOK grammar option.
+ * @param nodeScopeHook a <code>boolean</code> value.
+ */
+ public void setNodescopehook(boolean nodeScopeHook) {
+ optionalAttrs.put(NODE_SCOPE_HOOK, nodeScopeHook ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the NODE_USES_PARSER grammar option.
+ * @param nodeUsesParser a <code>boolean</code> value.
+ */
+ public void setNodeusesparser(boolean nodeUsesParser) {
+ optionalAttrs.put(NODE_USES_PARSER, nodeUsesParser ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the STATIC grammar option.
+ * @param staticParser a <code>boolean</code> value.
+ */
+ public void setStatic(boolean staticParser) {
+ optionalAttrs.put(STATIC, staticParser ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the VISITOR grammar option.
+ * @param visitor a <code>boolean</code> value.
+ */
+ public void setVisitor(boolean visitor) {
+ optionalAttrs.put(VISITOR, visitor ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the NODE_PACKAGE grammar option.
+ * @param nodePackage the option to use.
+ */
+ public void setNodepackage(String nodePackage) {
+ optionalAttrs.put(NODE_PACKAGE, nodePackage);
+ }
+
+ /**
+ * Sets the VISITOR_EXCEPTION grammar option.
+ * @param visitorException the option to use.
+ */
+ public void setVisitorException(String visitorException) {
+ optionalAttrs.put(VISITOR_EXCEPTION, visitorException);
+ }
+
+ /**
+ * Sets the NODE_PREFIX grammar option.
+ * @param nodePrefix the option to use.
+ */
+ public void setNodeprefix(String nodePrefix) {
+ optionalAttrs.put(NODE_PREFIX, nodePrefix);
+ }
+
+ /**
+ * The directory to write the generated JavaCC grammar and node files to.
+ * If not set, the files are written to the directory
+ * containing the grammar file.
+ * @param outputDirectory the output directory.
+ */
+ public void setOutputdirectory(File outputDirectory) {
+ this.outputDirectory = outputDirectory;
+ }
+
+ /**
+ * The outputfile to write the generated JavaCC grammar file to.
+ * If not set, the file is written with the same name as
+ * the JJTree grammar file with a suffix .jj.
+ * @param outputFile the output file name.
+ */
+ public void setOutputfile(String outputFile) {
+ this.outputFile = outputFile;
+ }
+
+ /**
+ * The jjtree grammar file to process.
+ * @param targetFile the grammar file.
+ */
+ public void setTarget(File targetFile) {
+ this.targetFile = targetFile;
+ }
+
+ /**
+ * The directory containing the JavaCC distribution.
+ * @param javaccHome the directory containing JavaCC.
+ */
+ public void setJavacchome(File javaccHome) {
+ this.javaccHome = javaccHome;
+ }
+
+ /**
+ * Corresponds -Xmx.
+ *
+ * @param max max memory parameter.
+ * @since Ant 1.8.3
+ */
+ public void setMaxmemory(String max) {
+ maxMemory = max;
+ }
+
+ /**
+ * Constructor
+ */
+ public JJTree() {
+ cmdl.setVm(JavaEnvUtils.getJreExecutable("java"));
+ }
+
+ /**
+ * Run the task.
+ * @throws BuildException on error.
+ */
+ public void execute() throws BuildException {
+
+ // load command line with optional attributes
+ Enumeration iter = optionalAttrs.keys();
+ while (iter.hasMoreElements()) {
+ String name = (String) iter.nextElement();
+ Object value = optionalAttrs.get(name);
+ cmdl.createArgument().setValue("-" + name + ":" + value.toString());
+ }
+
+ if (targetFile == null || !targetFile.isFile()) {
+ throw new BuildException("Invalid target: " + targetFile);
+ }
+
+ File javaFile = null;
+
+ // use the directory containing the target as the output directory
+ if (outputDirectory == null) {
+ // convert backslashes to slashes, otherwise jjtree will
+ // put this as comments and this seems to confuse javacc
+ cmdl.createArgument().setValue("-OUTPUT_DIRECTORY:"
+ + getDefaultOutputDirectory());
+
+ javaFile = new File(createOutputFileName(targetFile, outputFile,
+ null));
+ } else {
+ if (!outputDirectory.isDirectory()) {
+ throw new BuildException("'outputdirectory' " + outputDirectory
+ + " is not a directory.");
+ }
+
+ // convert backslashes to slashes, otherwise jjtree will
+ // put this as comments and this seems to confuse javacc
+ cmdl.createArgument().setValue("-OUTPUT_DIRECTORY:"
+ + outputDirectory.getAbsolutePath()
+ .replace('\\', '/'));
+
+ javaFile = new File(createOutputFileName(targetFile, outputFile,
+ outputDirectory
+ .getPath()));
+ }
+
+ if (javaFile.exists()
+ && targetFile.lastModified() < javaFile.lastModified()) {
+ log("Target is already built - skipping (" + targetFile + ")",
+ Project.MSG_VERBOSE);
+ return;
+ }
+
+ if (outputFile != null) {
+ cmdl.createArgument().setValue("-" + OUTPUT_FILE + ":"
+ + outputFile.replace('\\', '/'));
+ }
+
+ cmdl.createArgument().setValue(targetFile.getAbsolutePath());
+
+ final Path classpath = cmdl.createClasspath(getProject());
+ final File javaccJar = JavaCC.getArchiveFile(javaccHome);
+ classpath.createPathElement().setPath(javaccJar.getAbsolutePath());
+ classpath.addJavaRuntime();
+
+ cmdl.setClassname(JavaCC.getMainClass(classpath,
+ JavaCC.TASKDEF_TYPE_JJTREE));
+
+ cmdl.setMaxmemory(maxMemory);
+ final Commandline.Argument arg = cmdl.createVmArgument();
+ arg.setValue("-Dinstall.root=" + javaccHome.getAbsolutePath());
+
+ final Execute process =
+ new Execute(new LogStreamHandler(this,
+ Project.MSG_INFO,
+ Project.MSG_INFO),
+ null);
+ log(cmdl.describeCommand(), Project.MSG_VERBOSE);
+ process.setCommandline(cmdl.getCommandline());
+
+ try {
+ if (process.execute() != 0) {
+ throw new BuildException("JJTree failed.");
+ }
+ } catch (IOException e) {
+ throw new BuildException("Failed to launch JJTree", e);
+ }
+ }
+
+ private String createOutputFileName(File destFile, String optionalOutputFile,
+ String outputDir) {
+ optionalOutputFile = validateOutputFile(optionalOutputFile,
+ outputDir);
+ String jjtreeFile = destFile.getAbsolutePath().replace('\\', '/');
+
+ if ((optionalOutputFile == null) || optionalOutputFile.equals("")) {
+ int filePos = jjtreeFile.lastIndexOf("/");
+
+ if (filePos >= 0) {
+ jjtreeFile = jjtreeFile.substring(filePos + 1);
+ }
+
+ int suffixPos = jjtreeFile.lastIndexOf('.');
+
+ if (suffixPos == -1) {
+ optionalOutputFile = jjtreeFile + DEFAULT_SUFFIX;
+ } else {
+ String currentSuffix = jjtreeFile.substring(suffixPos);
+
+ if (currentSuffix.equals(DEFAULT_SUFFIX)) {
+ optionalOutputFile = jjtreeFile + DEFAULT_SUFFIX;
+ } else {
+ optionalOutputFile = jjtreeFile.substring(0, suffixPos)
+ + DEFAULT_SUFFIX;
+ }
+ }
+ }
+
+ if ((outputDir == null) || outputDir.equals("")) {
+ outputDir = getDefaultOutputDirectory();
+ }
+
+ return (outputDir + "/" + optionalOutputFile).replace('\\', '/');
+ }
+
+ /**
+ * When running JJTree from an Ant taskdesk the -OUTPUT_DIRECTORY must
+ * always be set. But when -OUTPUT_DIRECTORY is set, -OUTPUT_FILE is
+ * handled as if relative of this -OUTPUT_DIRECTORY. Thus when the
+ * -OUTPUT_FILE is absolute or contains a drive letter we have a problem.
+ *
+ * @param destFile
+ * @param outputDir
+ * @return validation file, relative if possible; <tt>null</tt> if not set
+ * @throws BuildException
+ */
+ private String validateOutputFile(String destFile,
+ String outputDir)
+ throws BuildException {
+ if (destFile == null) {
+ return null;
+ }
+
+ if ((outputDir == null)
+ && (destFile.startsWith("/") || destFile.startsWith("\\"))) {
+ String relativeOutputFile = makeOutputFileRelative(destFile);
+ setOutputfile(relativeOutputFile);
+
+ return relativeOutputFile;
+ }
+
+ String root = getRoot(new File(destFile)).getAbsolutePath();
+
+ if ((root.length() > 1)
+ && destFile.startsWith(root.substring(0, root.length() - 1))) {
+ throw new BuildException("Drive letter in 'outputfile' not "
+ + "supported: " + destFile);
+ }
+
+ return destFile;
+ }
+
+ private String makeOutputFileRelative(String destFile) {
+ StringBuffer relativePath = new StringBuffer();
+ String defaultOutputDirectory = getDefaultOutputDirectory();
+ int nextPos = defaultOutputDirectory.indexOf('/');
+ int startPos = nextPos + 1;
+
+ while (startPos > -1 && startPos < defaultOutputDirectory.length()) {
+ relativePath.append("/..");
+ nextPos = defaultOutputDirectory.indexOf('/', startPos);
+
+ if (nextPos == -1) {
+ startPos = nextPos;
+ } else {
+ startPos = nextPos + 1;
+ }
+ }
+
+ relativePath.append(destFile);
+
+ return relativePath.toString();
+ }
+
+ private String getDefaultOutputDirectory() {
+ return getProject().getBaseDir().getAbsolutePath().replace('\\', '/');
+ }
+
+ /**
+ * Determine root directory for a given file.
+ *
+ * @param file
+ * @return file's root directory
+ */
+ private File getRoot(File file) {
+ File root = file.getAbsoluteFile();
+
+ while (root.getParent() != null) {
+ root = root.getParentFile();
+ }
+
+ return root;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java
new file mode 100644
index 00000000..219cc9c8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java
@@ -0,0 +1,579 @@
+/*
+ * 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.tools.ant.taskdefs.optional.javacc;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * JavaCC compiler compiler task.
+ *
+ */
+public class JavaCC extends Task {
+
+ // keys to optional attributes
+ private static final String LOOKAHEAD = "LOOKAHEAD";
+ private static final String CHOICE_AMBIGUITY_CHECK = "CHOICE_AMBIGUITY_CHECK";
+ private static final String OTHER_AMBIGUITY_CHECK = "OTHER_AMBIGUITY_CHECK";
+
+ private static final String STATIC = "STATIC";
+ private static final String DEBUG_PARSER = "DEBUG_PARSER";
+ private static final String DEBUG_LOOKAHEAD = "DEBUG_LOOKAHEAD";
+ private static final String DEBUG_TOKEN_MANAGER = "DEBUG_TOKEN_MANAGER";
+ private static final String OPTIMIZE_TOKEN_MANAGER = "OPTIMIZE_TOKEN_MANAGER";
+ private static final String ERROR_REPORTING = "ERROR_REPORTING";
+ private static final String JAVA_UNICODE_ESCAPE = "JAVA_UNICODE_ESCAPE";
+ private static final String UNICODE_INPUT = "UNICODE_INPUT";
+ private static final String IGNORE_CASE = "IGNORE_CASE";
+ private static final String COMMON_TOKEN_ACTION = "COMMON_TOKEN_ACTION";
+ private static final String USER_TOKEN_MANAGER = "USER_TOKEN_MANAGER";
+ private static final String USER_CHAR_STREAM = "USER_CHAR_STREAM";
+ private static final String BUILD_PARSER = "BUILD_PARSER";
+ private static final String BUILD_TOKEN_MANAGER = "BUILD_TOKEN_MANAGER";
+ private static final String SANITY_CHECK = "SANITY_CHECK";
+ private static final String FORCE_LA_CHECK = "FORCE_LA_CHECK";
+ private static final String CACHE_TOKENS = "CACHE_TOKENS";
+ private static final String KEEP_LINE_COLUMN = "KEEP_LINE_COLUMN";
+ private static final String JDK_VERSION = "JDK_VERSION";
+
+ private final Hashtable optionalAttrs = new Hashtable();
+
+ // required attributes
+ private File outputDirectory = null;
+ private File targetFile = null;
+ private File javaccHome = null;
+
+ private CommandlineJava cmdl = new CommandlineJava();
+
+ protected static final int TASKDEF_TYPE_JAVACC = 1;
+ protected static final int TASKDEF_TYPE_JJTREE = 2;
+ protected static final int TASKDEF_TYPE_JJDOC = 3;
+
+ protected static final String[] ARCHIVE_LOCATIONS =
+ new String[] {
+ "JavaCC.zip",
+ "bin/lib/JavaCC.zip",
+ "bin/lib/javacc.jar",
+ "javacc.jar", // used by jpackage for JavaCC 3.x
+ };
+
+ protected static final int[] ARCHIVE_LOCATIONS_VS_MAJOR_VERSION =
+ new int[] {
+ 1,
+ 2,
+ 3,
+ 3,
+ };
+
+ protected static final String COM_PACKAGE = "COM.sun.labs.";
+ protected static final String COM_JAVACC_CLASS = "javacc.Main";
+ protected static final String COM_JJTREE_CLASS = "jjtree.Main";
+ protected static final String COM_JJDOC_CLASS = "jjdoc.JJDocMain";
+
+ protected static final String ORG_PACKAGE_3_0 = "org.netbeans.javacc.";
+ protected static final String ORG_PACKAGE_3_1 = "org.javacc.";
+ protected static final String ORG_JAVACC_CLASS = "parser.Main";
+ protected static final String ORG_JJTREE_CLASS = COM_JJTREE_CLASS;
+ protected static final String ORG_JJDOC_CLASS = COM_JJDOC_CLASS;
+
+ private String maxMemory = null;
+
+ /**
+ * Sets the LOOKAHEAD grammar option.
+ * @param lookahead an <code>int</code> value.
+ */
+ public void setLookahead(int lookahead) {
+ optionalAttrs.put(LOOKAHEAD, new Integer(lookahead));
+ }
+
+ /**
+ * Sets the CHOICE_AMBIGUITY_CHECK grammar option.
+ * @param choiceAmbiguityCheck an <code>int</code> value.
+ */
+ public void setChoiceambiguitycheck(int choiceAmbiguityCheck) {
+ optionalAttrs.put(CHOICE_AMBIGUITY_CHECK, new Integer(choiceAmbiguityCheck));
+ }
+
+ /**
+ * Sets the OTHER_AMBIGUITY_CHECK grammar option.
+ * @param otherAmbiguityCheck an <code>int</code> value.
+ */
+ public void setOtherambiguityCheck(int otherAmbiguityCheck) {
+ optionalAttrs.put(OTHER_AMBIGUITY_CHECK, new Integer(otherAmbiguityCheck));
+ }
+
+ /**
+ * Sets the STATIC grammar option.
+ * @param staticParser a <code>boolean</code> value.
+ */
+ public void setStatic(boolean staticParser) {
+ optionalAttrs.put(STATIC, staticParser ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the DEBUG_PARSER grammar option.
+ * @param debugParser a <code>boolean</code> value.
+ */
+ public void setDebugparser(boolean debugParser) {
+ optionalAttrs.put(DEBUG_PARSER, debugParser ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the DEBUG_LOOKAHEAD grammar option.
+ * @param debugLookahead a <code>boolean</code> value.
+ */
+ public void setDebuglookahead(boolean debugLookahead) {
+ optionalAttrs.put(DEBUG_LOOKAHEAD, debugLookahead ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the DEBUG_TOKEN_MANAGER grammar option.
+ * @param debugTokenManager a <code>boolean</code> value.
+ */
+ public void setDebugtokenmanager(boolean debugTokenManager) {
+ optionalAttrs.put(DEBUG_TOKEN_MANAGER, debugTokenManager ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the OPTIMIZE_TOKEN_MANAGER grammar option.
+ * @param optimizeTokenManager a <code>boolean</code> value.
+ */
+ public void setOptimizetokenmanager(boolean optimizeTokenManager) {
+ optionalAttrs.put(OPTIMIZE_TOKEN_MANAGER,
+ optimizeTokenManager ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the ERROR_REPORTING grammar option.
+ * @param errorReporting a <code>boolean</code> value.
+ */
+ public void setErrorreporting(boolean errorReporting) {
+ optionalAttrs.put(ERROR_REPORTING, errorReporting ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the JAVA_UNICODE_ESCAPE grammar option.
+ * @param javaUnicodeEscape a <code>boolean</code> value.
+ */
+ public void setJavaunicodeescape(boolean javaUnicodeEscape) {
+ optionalAttrs.put(JAVA_UNICODE_ESCAPE, javaUnicodeEscape ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the UNICODE_INPUT grammar option.
+ * @param unicodeInput a <code>boolean</code> value.
+ */
+ public void setUnicodeinput(boolean unicodeInput) {
+ optionalAttrs.put(UNICODE_INPUT, unicodeInput ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the IGNORE_CASE grammar option.
+ * @param ignoreCase a <code>boolean</code> value.
+ */
+ public void setIgnorecase(boolean ignoreCase) {
+ optionalAttrs.put(IGNORE_CASE, ignoreCase ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the COMMON_TOKEN_ACTION grammar option.
+ * @param commonTokenAction a <code>boolean</code> value.
+ */
+ public void setCommontokenaction(boolean commonTokenAction) {
+ optionalAttrs.put(COMMON_TOKEN_ACTION, commonTokenAction ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the USER_TOKEN_MANAGER grammar option.
+ * @param userTokenManager a <code>boolean</code> value.
+ */
+ public void setUsertokenmanager(boolean userTokenManager) {
+ optionalAttrs.put(USER_TOKEN_MANAGER, userTokenManager ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the USER_CHAR_STREAM grammar option.
+ * @param userCharStream a <code>boolean</code> value.
+ */
+ public void setUsercharstream(boolean userCharStream) {
+ optionalAttrs.put(USER_CHAR_STREAM, userCharStream ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the BUILD_PARSER grammar option.
+ * @param buildParser a <code>boolean</code> value.
+ */
+ public void setBuildparser(boolean buildParser) {
+ optionalAttrs.put(BUILD_PARSER, buildParser ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the BUILD_TOKEN_MANAGER grammar option.
+ * @param buildTokenManager a <code>boolean</code> value.
+ */
+ public void setBuildtokenmanager(boolean buildTokenManager) {
+ optionalAttrs.put(BUILD_TOKEN_MANAGER, buildTokenManager ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the SANITY_CHECK grammar option.
+ * @param sanityCheck a <code>boolean</code> value.
+ */
+ public void setSanitycheck(boolean sanityCheck) {
+ optionalAttrs.put(SANITY_CHECK, sanityCheck ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the FORCE_LA_CHECK grammar option.
+ * @param forceLACheck a <code>boolean</code> value.
+ */
+ public void setForcelacheck(boolean forceLACheck) {
+ optionalAttrs.put(FORCE_LA_CHECK, forceLACheck ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the CACHE_TOKENS grammar option.
+ * @param cacheTokens a <code>boolean</code> value.
+ */
+ public void setCachetokens(boolean cacheTokens) {
+ optionalAttrs.put(CACHE_TOKENS, cacheTokens ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the KEEP_LINE_COLUMN grammar option.
+ * @param keepLineColumn a <code>boolean</code> value.
+ */
+ public void setKeeplinecolumn(boolean keepLineColumn) {
+ optionalAttrs.put(KEEP_LINE_COLUMN, keepLineColumn ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Sets the JDK_VERSION option.
+ * @param jdkVersion the version to use.
+ * @since Ant1.7
+ */
+ public void setJDKversion(String jdkVersion) {
+ optionalAttrs.put(JDK_VERSION, jdkVersion);
+ }
+
+ /**
+ * The directory to write the generated files to.
+ * If not set, the files are written to the directory
+ * containing the grammar file.
+ * @param outputDirectory the output directory.
+ */
+ public void setOutputdirectory(File outputDirectory) {
+ this.outputDirectory = outputDirectory;
+ }
+
+ /**
+ * The grammar file to process.
+ * @param targetFile the grammar file.
+ */
+ public void setTarget(File targetFile) {
+ this.targetFile = targetFile;
+ }
+
+ /**
+ * The directory containing the JavaCC distribution.
+ * @param javaccHome the directory.
+ */
+ public void setJavacchome(File javaccHome) {
+ this.javaccHome = javaccHome;
+ }
+
+ /**
+ * Corresponds -Xmx.
+ *
+ * @param max max memory parameter.
+ * @since Ant 1.8.3
+ */
+ public void setMaxmemory(String max) {
+ maxMemory = max;
+ }
+
+ /**
+ * Constructor
+ */
+ public JavaCC() {
+ cmdl.setVm(JavaEnvUtils.getJreExecutable("java"));
+ }
+
+ /**
+ * Run the task.
+ * @throws BuildException on error.
+ */
+ public void execute() throws BuildException {
+
+ // load command line with optional attributes
+ Enumeration iter = optionalAttrs.keys();
+ while (iter.hasMoreElements()) {
+ String name = (String) iter.nextElement();
+ Object value = optionalAttrs.get(name);
+ cmdl.createArgument().setValue("-" + name + ":" + value.toString());
+ }
+
+ // check the target is a file
+ if (targetFile == null || !targetFile.isFile()) {
+ throw new BuildException("Invalid target: " + targetFile);
+ }
+
+ // use the directory containing the target as the output directory
+ if (outputDirectory == null) {
+ outputDirectory = new File(targetFile.getParent());
+ } else if (!outputDirectory.isDirectory()) {
+ throw new BuildException("Outputdir not a directory.");
+ }
+ cmdl.createArgument().setValue("-OUTPUT_DIRECTORY:"
+ + outputDirectory.getAbsolutePath());
+
+ // determine if the generated java file is up-to-date
+ final File javaFile = getOutputJavaFile(outputDirectory, targetFile);
+ if (javaFile.exists()
+ && targetFile.lastModified() < javaFile.lastModified()) {
+ log("Target is already built - skipping (" + targetFile + ")",
+ Project.MSG_VERBOSE);
+ return;
+ }
+ cmdl.createArgument().setValue(targetFile.getAbsolutePath());
+
+ final Path classpath = cmdl.createClasspath(getProject());
+ final File javaccJar = JavaCC.getArchiveFile(javaccHome);
+ classpath.createPathElement().setPath(javaccJar.getAbsolutePath());
+ classpath.addJavaRuntime();
+
+ cmdl.setClassname(JavaCC.getMainClass(classpath,
+ JavaCC.TASKDEF_TYPE_JAVACC));
+
+ cmdl.setMaxmemory(maxMemory);
+ final Commandline.Argument arg = cmdl.createVmArgument();
+ arg.setValue("-Dinstall.root=" + javaccHome.getAbsolutePath());
+
+ Execute.runCommand(this, cmdl.getCommandline());
+ }
+
+ /**
+ * Helper method to retrieve the path used to store the JavaCC.zip
+ * or javacc.jar which is different from versions.
+ *
+ * @param home the javacc home path directory.
+ * @throws BuildException thrown if the home directory is invalid
+ * or if the archive could not be found despite attempts to do so.
+ * @return the file object pointing to the JavaCC archive.
+ */
+ protected static File getArchiveFile(File home) throws BuildException {
+ return new File(home,
+ ARCHIVE_LOCATIONS[getArchiveLocationIndex(home)]);
+ }
+
+ /**
+ * Helper method to retrieve main class which is different from versions.
+ * @param home the javacc home path directory.
+ * @param type the taskdef.
+ * @throws BuildException thrown if the home directory is invalid
+ * or if the archive could not be found despite attempts to do so.
+ * @return the main class for the taskdef.
+ */
+ protected static String getMainClass(File home, int type)
+ throws BuildException {
+
+ Path p = new Path(null);
+ p.createPathElement().setLocation(getArchiveFile(home));
+ p.addJavaRuntime();
+ return getMainClass(p, type);
+ }
+
+ /**
+ * Helper method to retrieve main class which is different from versions.
+ * @param path classpath to search in.
+ * @param type the taskdef.
+ * @throws BuildException thrown if the home directory is invalid
+ * or if the archive could not be found despite attempts to do so.
+ * @return the main class for the taskdef.
+ * @since Ant 1.7
+ */
+ protected static String getMainClass(Path path, int type)
+ throws BuildException {
+ String packagePrefix = null;
+ String mainClass = null;
+
+ AntClassLoader l = null;
+ try {
+ l = AntClassLoader.newAntClassLoader(null, null,
+ path
+ .concatSystemClasspath("ignore"),
+ true);
+ String javaccClass = COM_PACKAGE + COM_JAVACC_CLASS;
+ InputStream is = l.getResourceAsStream(javaccClass.replace('.', '/')
+ + ".class");
+ if (is != null) {
+ packagePrefix = COM_PACKAGE;
+ switch (type) {
+ case TASKDEF_TYPE_JAVACC:
+ mainClass = COM_JAVACC_CLASS;
+
+ break;
+
+ case TASKDEF_TYPE_JJTREE:
+ mainClass = COM_JJTREE_CLASS;
+
+ break;
+
+ case TASKDEF_TYPE_JJDOC:
+ mainClass = COM_JJDOC_CLASS;
+
+ break;
+ default:
+ // Fall Through
+ }
+ } else {
+ javaccClass = ORG_PACKAGE_3_1 + ORG_JAVACC_CLASS;
+ is = l.getResourceAsStream(javaccClass.replace('.', '/')
+ + ".class");
+ if (is != null) {
+ packagePrefix = ORG_PACKAGE_3_1;
+ } else {
+ javaccClass = ORG_PACKAGE_3_0 + ORG_JAVACC_CLASS;
+ is = l.getResourceAsStream(javaccClass.replace('.', '/')
+ + ".class");
+ if (is != null) {
+ packagePrefix = ORG_PACKAGE_3_0;
+ }
+ }
+
+ if (is != null) {
+ switch (type) {
+ case TASKDEF_TYPE_JAVACC:
+ mainClass = ORG_JAVACC_CLASS;
+
+ break;
+
+ case TASKDEF_TYPE_JJTREE:
+ mainClass = ORG_JJTREE_CLASS;
+
+ break;
+
+ case TASKDEF_TYPE_JJDOC:
+ mainClass = ORG_JJDOC_CLASS;
+
+ break;
+ default:
+ // Fall Through
+ }
+ }
+ }
+
+ if (packagePrefix == null) {
+ throw new BuildException("failed to load JavaCC");
+ }
+ if (mainClass == null) {
+ throw new BuildException("unknown task type " + type);
+ }
+ return packagePrefix + mainClass;
+ } finally {
+ if (l != null) {
+ l.cleanup();
+ }
+ }
+ }
+
+ /**
+ * Helper method to determine the archive location index.
+ *
+ * @param home the javacc home path directory.
+ * @throws BuildException thrown if the home directory is invalid
+ * or if the archive could not be found despite attempts to do so.
+ * @return the archive location index
+ */
+ private static int getArchiveLocationIndex(File home)
+ throws BuildException {
+
+ if (home == null || !home.isDirectory()) {
+ throw new BuildException("JavaCC home must be a valid directory.");
+ }
+
+ for (int i = 0; i < ARCHIVE_LOCATIONS.length; i++) {
+ File f = new File(home, ARCHIVE_LOCATIONS[i]);
+
+ if (f.exists()) {
+ return i;
+ }
+ }
+
+ throw new BuildException("Could not find a path to JavaCC.zip "
+ + "or javacc.jar from '" + home + "'.");
+ }
+
+ /**
+ * Helper method to determine the major version number of JavaCC.
+ *
+ * @param home the javacc home path directory.
+ * @throws BuildException thrown if the home directory is invalid
+ * or if the archive could not be found despite attempts to do so.
+ * @return a the major version number
+ */
+ protected static int getMajorVersionNumber(File home)
+ throws BuildException {
+
+ return
+ ARCHIVE_LOCATIONS_VS_MAJOR_VERSION[getArchiveLocationIndex(home)];
+ }
+
+ /**
+ * Determines the output Java file to be generated by the given grammar
+ * file.
+ *
+ */
+ private File getOutputJavaFile(File outputdir, File srcfile) {
+ String path = srcfile.getPath();
+
+ // Extract file's base-name
+ int startBasename = path.lastIndexOf(File.separator);
+ if (startBasename != -1) {
+ path = path.substring(startBasename + 1);
+ }
+
+ // Replace the file's extension with '.java'
+ int startExtn = path.lastIndexOf('.');
+ if (startExtn != -1) {
+ path = path.substring(0, startExtn) + ".java";
+ } else {
+ path += ".java";
+ }
+
+ // Change the directory
+ if (outputdir != null) {
+ path = outputdir + File.separator + path;
+ }
+
+ return new File(path);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Gcjh.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Gcjh.java
new file mode 100644
index 00000000..712bb76a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Gcjh.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.tools.ant.taskdefs.optional.javah;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.optional.Javah;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Adapter to the native gcjh compiler.
+ *
+ * @since Ant 1.8.2
+ */
+public class Gcjh implements JavahAdapter {
+
+ public static final String IMPLEMENTATION_NAME = "gcjh";
+
+ /**
+ * Performs the actual compilation.
+ */
+ public boolean compile(Javah javah) throws BuildException {
+ Commandline cmd = setupGcjhCommand(javah);
+ try {
+ Execute.runCommand(javah, cmd.getCommandline());
+ return true;
+ } catch (BuildException e) {
+ if (e.getMessage().indexOf("failed with return code") == -1) {
+ throw e;
+ }
+ }
+ return false;
+ }
+
+ private Commandline setupGcjhCommand(Javah javah) {
+ Commandline cmd = new Commandline();
+ cmd.setExecutable(JavaEnvUtils.getJdkExecutable("gcjh"));
+
+ if (javah.getDestdir() != null) {
+ cmd.createArgument().setValue("-d");
+ cmd.createArgument().setFile(javah.getDestdir());
+ }
+
+ if (javah.getOutputfile() != null) {
+ cmd.createArgument().setValue("-o");
+ cmd.createArgument().setFile(javah.getOutputfile());
+ }
+
+ Path cp = new Path(javah.getProject());
+ if (javah.getBootclasspath() != null) {
+ cp.append(javah.getBootclasspath());
+ }
+ cp = cp.concatSystemBootClasspath("ignore");
+ if (javah.getClasspath() != null) {
+ cp.append(javah.getClasspath());
+ }
+ if (cp.size() > 0) {
+ cmd.createArgument().setValue("--classpath");
+ cmd.createArgument().setPath(cp);
+ }
+
+ if (!javah.getOld()) {
+ cmd.createArgument().setValue("-jni");
+ }
+
+ cmd.addArguments(javah.getCurrentArgs());
+
+ javah.logAndAddFiles(cmd);
+ return cmd;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapter.java
new file mode 100644
index 00000000..121fa429
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapter.java
@@ -0,0 +1,38 @@
+/*
+ * 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.tools.ant.taskdefs.optional.javah;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.optional.Javah;
+
+/**
+ * Interface for different backend implementations of the Javah task.
+ *
+ * @since Ant 1.6.3
+ */
+public interface JavahAdapter {
+ /**
+ * Performs the actual compilation.
+ * @param javah the calling javah task.
+ * @return true if the compilation was successful.
+ * @throws BuildException if there is an error.
+ * @since Ant 1.6.3
+ */
+ boolean compile(Javah javah) throws BuildException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapterFactory.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapterFactory.java
new file mode 100644
index 00000000..d98b4276
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapterFactory.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.tools.ant.taskdefs.optional.javah;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.ClasspathUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Creates the JavahAdapter based on the user choice and
+ * potentially the VM vendor.
+ *
+ * @since Ant 1.6.3
+ */
+// CheckStyle:HideUtilityClassConstructorCheck OFF (bc)
+public class JavahAdapterFactory {
+
+ /**
+ * Determines the default choice of adapter based on the VM
+ * vendor.
+ *
+ * @return the default choice of adapter based on the VM
+ * vendor
+ */
+ public static String getDefault() {
+ if (JavaEnvUtils.isKaffe()) {
+ return Kaffeh.IMPLEMENTATION_NAME;
+ } else if (JavaEnvUtils.isGij()) {
+ return Gcjh.IMPLEMENTATION_NAME;
+ }
+ return SunJavah.IMPLEMENTATION_NAME;
+ }
+
+ /**
+ * Creates the JavahAdapter based on the user choice and
+ * potentially the VM vendor.
+ *
+ * @param choice the user choice (if any).
+ * @param log a ProjectComponent instance used to access Ant's
+ * logging system.
+ * @return The adapter to use.
+ * @throws BuildException if there is an error.
+ */
+ public static JavahAdapter getAdapter(String choice,
+ ProjectComponent log)
+ throws BuildException {
+ return getAdapter(choice, log, null);
+ }
+
+ /**
+ * Creates the JavahAdapter based on the user choice and
+ * potentially the VM vendor.
+ *
+ * @param choice the user choice (if any).
+ * @param log a ProjectComponent instance used to access Ant's
+ * logging system.
+ * @param classpath the classpath to use when looking up an
+ * adapter class
+ * @return The adapter to use.
+ * @throws BuildException if there is an error.
+ * @since Ant 1.8.0
+ */
+ public static JavahAdapter getAdapter(String choice,
+ ProjectComponent log,
+ Path classpath)
+ throws BuildException {
+ if ((JavaEnvUtils.isKaffe() && choice == null)
+ || Kaffeh.IMPLEMENTATION_NAME.equals(choice)) {
+ return new Kaffeh();
+ } else if ((JavaEnvUtils.isGij() && choice == null)
+ || Gcjh.IMPLEMENTATION_NAME.equals(choice)) {
+ return new Gcjh();
+ } else if (SunJavah.IMPLEMENTATION_NAME.equals(choice)) {
+ return new SunJavah();
+ } else if (choice != null) {
+ return resolveClassName(choice,
+ // Memory leak in line below
+ log.getProject()
+ .createClassLoader(classpath));
+ }
+
+ // This default has been good enough until Ant 1.6.3, so stick
+ // with it
+ return new SunJavah();
+ }
+
+ /**
+ * Tries to resolve the given classname into a javah adapter.
+ * Throws a fit if it can't.
+ *
+ * @param className The fully qualified classname to be created.
+ * @param loader the classloader to use
+ * @throws BuildException This is the fit that is thrown if className
+ * isn't an instance of JavahAdapter.
+ */
+ private static JavahAdapter resolveClassName(String className,
+ ClassLoader loader)
+ throws BuildException {
+ return (JavahAdapter) ClasspathUtils.newInstance(className,
+ loader != null ? loader :
+ JavahAdapterFactory.class.getClassLoader(), JavahAdapter.class);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Kaffeh.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Kaffeh.java
new file mode 100644
index 00000000..d37f7717
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/Kaffeh.java
@@ -0,0 +1,94 @@
+/*
+ * 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.tools.ant.taskdefs.optional.javah;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.optional.Javah;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Adapter to the native kaffeh compiler.
+ *
+ * @since Ant 1.6.3
+ */
+public class Kaffeh implements JavahAdapter {
+
+ /** the name of the javah adapter - kaffeh */
+ public static final String IMPLEMENTATION_NAME = "kaffeh";
+
+ /**
+ * Performs the actual compilation.
+ * @param javah the calling javah task.
+ * @return true if the compilation was successful.
+ * @throws BuildException if there is an error.
+ * @since Ant 1.6.3
+ */
+ public boolean compile(Javah javah) throws BuildException {
+ Commandline cmd = setupKaffehCommand(javah);
+ try {
+ Execute.runCommand(javah, cmd.getCommandline());
+ return true;
+ } catch (BuildException e) {
+ if (e.getMessage().indexOf("failed with return code") == -1) {
+ throw e;
+ }
+ }
+ return false;
+ }
+
+ private Commandline setupKaffehCommand(Javah javah) {
+ Commandline cmd = new Commandline();
+ cmd.setExecutable(JavaEnvUtils.getJdkExecutable("kaffeh"));
+
+ if (javah.getDestdir() != null) {
+ cmd.createArgument().setValue("-d");
+ cmd.createArgument().setFile(javah.getDestdir());
+ }
+
+ if (javah.getOutputfile() != null) {
+ cmd.createArgument().setValue("-o");
+ cmd.createArgument().setFile(javah.getOutputfile());
+ }
+
+ Path cp = new Path(javah.getProject());
+ if (javah.getBootclasspath() != null) {
+ cp.append(javah.getBootclasspath());
+ }
+ cp = cp.concatSystemBootClasspath("ignore");
+ if (javah.getClasspath() != null) {
+ cp.append(javah.getClasspath());
+ }
+ if (cp.size() > 0) {
+ cmd.createArgument().setValue("-classpath");
+ cmd.createArgument().setPath(cp);
+ }
+
+ if (!javah.getOld()) {
+ cmd.createArgument().setValue("-jni");
+ }
+
+ cmd.addArguments(javah.getCurrentArgs());
+
+ javah.logAndAddFiles(cmd);
+ return cmd;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/SunJavah.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/SunJavah.java
new file mode 100644
index 00000000..0b1655ad
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/javah/SunJavah.java
@@ -0,0 +1,123 @@
+/*
+ * 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.tools.ant.taskdefs.optional.javah;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.launch.Locator;
+import org.apache.tools.ant.taskdefs.ExecuteJava;
+import org.apache.tools.ant.taskdefs.optional.Javah;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+
+/**
+ * Adapter to com.sun.tools.javah.oldjavah.Main or com.sun.tools.javah.Main.
+ *
+ * @since Ant 1.6.3
+ */
+public class SunJavah implements JavahAdapter {
+
+ /** the name of the javah adapter - sun */
+ public static final String IMPLEMENTATION_NAME = "sun";
+
+ /**
+ * Performs the actual compilation.
+ * @param javah the calling javah task.
+ * @return true if the compilation was successful.
+ * @throws BuildException if there is an error.
+ * @since Ant 1.6.3
+ */
+ public boolean compile(Javah javah) throws BuildException {
+ Commandline cmd = setupJavahCommand(javah);
+ ExecuteJava ej = new ExecuteJava();
+ Class c = null;
+ try {
+ try {
+ // first search for the "old" javah class in 1.4.2 tools.jar
+ c = Class.forName("com.sun.tools.javah.oldjavah.Main");
+ } catch (ClassNotFoundException cnfe) {
+ // assume older than 1.4.2 tools.jar
+ c = Class.forName("com.sun.tools.javah.Main");
+ }
+ } catch (ClassNotFoundException ex) {
+ throw new BuildException(
+ "Can't load javah", ex, javah.getLocation());
+ }
+ cmd.setExecutable(c.getName());
+ ej.setJavaCommand(cmd);
+ File f = Locator.getClassSource(c);
+ if (f != null) {
+ ej.setClasspath(new Path(javah.getProject(), f.getPath()));
+ }
+ return ej.fork(javah) == 0;
+ }
+
+ private Commandline setupJavahCommand(Javah javah) {
+ Commandline cmd = new Commandline();
+
+ if (javah.getDestdir() != null) {
+ cmd.createArgument().setValue("-d");
+ cmd.createArgument().setFile(javah.getDestdir());
+ }
+
+ if (javah.getOutputfile() != null) {
+ cmd.createArgument().setValue("-o");
+ cmd.createArgument().setFile(javah.getOutputfile());
+ }
+
+ if (javah.getClasspath() != null) {
+ cmd.createArgument().setValue("-classpath");
+ cmd.createArgument().setPath(javah.getClasspath());
+ }
+
+ if (javah.getVerbose()) {
+ cmd.createArgument().setValue("-verbose");
+ }
+ if (javah.getOld()) {
+ cmd.createArgument().setValue("-old");
+ }
+ if (javah.getForce()) {
+ cmd.createArgument().setValue("-force");
+ }
+ if (javah.getStubs() && !javah.getOld()) {
+ throw new BuildException(
+ "stubs only available in old mode.", javah.getLocation());
+ }
+
+ if (javah.getStubs()) {
+ cmd.createArgument().setValue("-stubs");
+ }
+ Path bcp = new Path(javah.getProject());
+ if (javah.getBootclasspath() != null) {
+ bcp.append(javah.getBootclasspath());
+ }
+ bcp = bcp.concatSystemBootClasspath("ignore");
+ if (bcp.size() > 0) {
+ cmd.createArgument().setValue("-bootclasspath");
+ cmd.createArgument().setPath(bcp);
+ }
+
+ cmd.addArguments(javah.getCurrentArgs());
+
+ javah.logAndAddFiles(cmd);
+ return cmd;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTask.java
new file mode 100644
index 00000000..21f03fe7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTask.java
@@ -0,0 +1,684 @@
+/*
+ * 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.tools.ant.taskdefs.optional.jdepend;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteWatchdog;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.LoaderUtils;
+
+/**
+ * Runs JDepend tests.
+ *
+ * <p>JDepend is a tool to generate design quality metrics for each Java package.
+ * It has been initially created by Mike Clark. JDepend can be found at <a
+ * href="http://www.clarkware.com/software/JDepend.html">http://www.clarkware.com/software/JDepend.html</a>.
+ *
+ * The current implementation spawn a new Java VM.
+ *
+ */
+public class JDependTask extends Task {
+ //private CommandlineJava commandline = new CommandlineJava();
+
+ // required attributes
+ private Path sourcesPath; // Deprecated!
+ private Path classesPath; // Use this going forward
+
+ // optional attributes
+ private File outputFile;
+ private File dir;
+ private Path compileClasspath;
+ private boolean haltonerror = false;
+ private boolean fork = false;
+ private Long timeout = null;
+
+ private String jvm = null;
+ private String format = "text";
+ private PatternSet defaultPatterns = new PatternSet();
+
+ private static Constructor packageFilterC;
+ private static Method setFilter;
+
+ private boolean includeRuntime = false;
+ private Path runtimeClasses = null;
+
+ static {
+ try {
+ Class packageFilter =
+ Class.forName("jdepend.framework.PackageFilter");
+ packageFilterC =
+ packageFilter.getConstructor(new Class[] {java.util.Collection.class});
+ setFilter =
+ jdepend.textui.JDepend.class.getDeclaredMethod("setFilter",
+ new Class[] {packageFilter});
+ } catch (Throwable t) {
+ if (setFilter == null) {
+ packageFilterC = null;
+ }
+ }
+ }
+
+ /**
+ * If true,
+ * include jdepend.jar in the forked VM.
+ *
+ * @param b include ant run time yes or no
+ * @since Ant 1.6
+ */
+ public void setIncluderuntime(boolean b) {
+ includeRuntime = b;
+ }
+
+ /**
+ * Set the timeout value (in milliseconds).
+ *
+ * <p>If the operation is running for more than this value, the jdepend
+ * will be canceled. (works only when in 'fork' mode).</p>
+ * @param value the maximum time (in milliseconds) allowed before
+ * declaring the test as 'timed-out'
+ * @see #setFork(boolean)
+ */
+ public void setTimeout(Long value) {
+ timeout = value;
+ }
+
+ /**
+ * @return the timeout value
+ */
+ public Long getTimeout() {
+ return timeout;
+ }
+
+ /**
+ * The output file name.
+ *
+ * @param outputFile the output file name
+ */
+ public void setOutputFile(File outputFile) {
+ this.outputFile = outputFile;
+ }
+
+ /**
+ * @return the output file name
+ */
+ public File getOutputFile() {
+ return outputFile;
+ }
+
+ /**
+ * Whether or not to halt on failure. Default: false.
+ * @param haltonerror the value to set
+ */
+ public void setHaltonerror(boolean haltonerror) {
+ this.haltonerror = haltonerror;
+ }
+
+ /**
+ * @return the value of the haltonerror attribute
+ */
+ public boolean getHaltonerror() {
+ return haltonerror;
+ }
+
+ /**
+ * If true, forks into a new JVM. Default: false.
+ *
+ * @param value <tt>true</tt> if a JVM should be forked,
+ * otherwise <tt>false<tt>
+ */
+ public void setFork(boolean value) {
+ fork = value;
+ }
+
+ /**
+ * @return the value of the fork attribute
+ */
+ public boolean getFork() {
+ return fork;
+ }
+
+ /**
+ * The command used to invoke a forked Java Virtual Machine.
+ *
+ * Default is <tt>java</tt>. Ignored if no JVM is forked.
+ * @param value the new VM to use instead of <tt>java</tt>
+ * @see #setFork(boolean)
+ */
+ public void setJvm(String value) {
+ jvm = value;
+
+ }
+
+ /**
+ * Adds a path to source code to analyze.
+ * @return a source path
+ * @deprecated since 1.6.x.
+ */
+ public Path createSourcespath() {
+ if (sourcesPath == null) {
+ sourcesPath = new Path(getProject());
+ }
+ return sourcesPath.createPath();
+ }
+
+ /**
+ * Gets the sourcepath.
+ * @return the sources path
+ * @deprecated since 1.6.x.
+ */
+ public Path getSourcespath() {
+ return sourcesPath;
+ }
+
+ /**
+ * Adds a path to class code to analyze.
+ * @return a classes path
+ */
+ public Path createClassespath() {
+ if (classesPath == null) {
+ classesPath = new Path(getProject());
+ }
+ return classesPath.createPath();
+ }
+
+ /**
+ * Gets the classespath.
+ * @return the classes path
+ */
+ public Path getClassespath() {
+ return classesPath;
+ }
+
+ /**
+ * The directory to invoke the VM in. Ignored if no JVM is forked.
+ * @param dir the directory to invoke the JVM from.
+ * @see #setFork(boolean)
+ */
+ public void setDir(File dir) {
+ this.dir = dir;
+ }
+
+ /**
+ * @return the dir attribute
+ */
+ public File getDir() {
+ return dir;
+ }
+
+ /**
+ * Set the classpath to be used for this compilation.
+ * @param classpath a class path to be used
+ */
+ public void setClasspath(Path classpath) {
+ if (compileClasspath == null) {
+ compileClasspath = classpath;
+ } else {
+ compileClasspath.append(classpath);
+ }
+ }
+
+ /**
+ * Gets the classpath to be used for this compilation.
+ * @return the class path used for compilation
+ */
+ public Path getClasspath() {
+ return compileClasspath;
+ }
+
+ /**
+ * Adds a path to the classpath.
+ * @return a classpath
+ */
+ public Path createClasspath() {
+ if (compileClasspath == null) {
+ compileClasspath = new Path(getProject());
+ }
+ return compileClasspath.createPath();
+ }
+
+ /**
+ * Create a new JVM argument. Ignored if no JVM is forked.
+ * @param commandline the commandline to create the argument on
+ * @return create a new JVM argument so that any argument can
+ * be passed to the JVM.
+ * @see #setFork(boolean)
+ */
+ public Commandline.Argument createJvmarg(CommandlineJava commandline) {
+ return commandline.createVmArgument();
+ }
+
+ /**
+ * Adds a reference to a classpath defined elsewhere.
+ * @param r a classpath reference
+ */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * add a name entry on the exclude list
+ * @return a pattern for the excludes
+ */
+ public PatternSet.NameEntry createExclude() {
+ return defaultPatterns.createExclude();
+ }
+
+ /**
+ * @return the excludes patterns
+ */
+ public PatternSet getExcludes() {
+ return defaultPatterns;
+ }
+
+ /**
+ * The format to write the output in, "xml" or "text".
+ *
+ * @param ea xml or text
+ */
+ public void setFormat(FormatAttribute ea) {
+ format = ea.getValue();
+ }
+
+ /**
+ * A class for the enumerated attribute format,
+ * values are xml and text.
+ * @see EnumeratedAttribute
+ */
+ public static class FormatAttribute extends EnumeratedAttribute {
+ private String [] formats = new String[]{"xml", "text"};
+
+ /**
+ * @return the enumerated values
+ */
+ public String[] getValues() {
+ return formats;
+ }
+ }
+
+ /**
+ * No problems with this test.
+ */
+ private static final int SUCCESS = 0;
+ /**
+ * An error occurred.
+ */
+ private static final int ERRORS = 1;
+
+ /**
+ * Search for the given resource and add the directory or archive
+ * that contains it to the classpath.
+ *
+ * <p>Doesn't work for archives in JDK 1.1 as the URL returned by
+ * getResource doesn't contain the name of the archive.</p>
+ *
+ * @param resource resource that one wants to lookup
+ * @since Ant 1.6
+ */
+ private void addClasspathEntry(String resource) {
+ /*
+ * pre Ant 1.6 this method used to call getClass().getResource
+ * while Ant 1.6 will call ClassLoader.getResource().
+ *
+ * The difference is that Class.getResource expects a leading
+ * slash for "absolute" resources and will strip it before
+ * delegating to ClassLoader.getResource - so we now have to
+ * emulate Class's behavior.
+ */
+ if (resource.startsWith("/")) {
+ resource = resource.substring(1);
+ } else {
+ resource = "org/apache/tools/ant/taskdefs/optional/jdepend/"
+ + resource;
+ }
+
+ File f = LoaderUtils.getResourceSource(getClass().getClassLoader(),
+ resource);
+ if (f != null) {
+ log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG);
+ runtimeClasses.createPath().setLocation(f);
+ } else {
+ log("Couldn\'t find " + resource, Project.MSG_DEBUG);
+ }
+ }
+
+ /**
+ * execute the task
+ *
+ * @exception BuildException if an error occurs
+ */
+ public void execute() throws BuildException {
+
+ CommandlineJava commandline = new CommandlineJava();
+
+ if ("text".equals(format)) {
+ commandline.setClassname("jdepend.textui.JDepend");
+ } else
+ if ("xml".equals(format)) {
+ commandline.setClassname("jdepend.xmlui.JDepend");
+ }
+
+ if (jvm != null) {
+ commandline.setVm(jvm);
+ }
+ if (getSourcespath() == null && getClassespath() == null) {
+ throw new BuildException("Missing classespath required argument");
+ } else if (getClassespath() == null) {
+ String msg =
+ "sourcespath is deprecated in JDepend >= 2.5 "
+ + "- please convert to classespath";
+ log(msg);
+ }
+
+ // execute the test and get the return code
+ int exitValue = JDependTask.ERRORS;
+ boolean wasKilled = false;
+ if (!getFork()) {
+ exitValue = executeInVM(commandline);
+ } else {
+ ExecuteWatchdog watchdog = createWatchdog();
+ exitValue = executeAsForked(commandline, watchdog);
+ // null watchdog means no timeout, you'd better not check with null
+ if (watchdog != null) {
+ wasKilled = watchdog.killedProcess();
+ }
+ }
+
+ // if there is an error/failure and that it should halt, stop
+ // everything otherwise just log a statement
+ boolean errorOccurred = exitValue == JDependTask.ERRORS || wasKilled;
+
+ if (errorOccurred) {
+ String errorMessage = "JDepend FAILED"
+ + (wasKilled ? " - Timed out" : "");
+
+ if (getHaltonerror()) {
+ throw new BuildException(errorMessage, getLocation());
+ } else {
+ log(errorMessage, Project.MSG_ERR);
+ }
+ }
+ }
+
+ // this comment extract from JUnit Task may also apply here
+ // "in VM is not very nice since it could probably hang the
+ // whole build. IMHO this method should be avoided and it would be best
+ // to remove it in future versions. TBD. (SBa)"
+
+ /**
+ * Execute inside VM.
+ *
+ * @param commandline the command line
+ * @return the return value of the mvm
+ * @exception BuildException if an error occurs
+ */
+ public int executeInVM(CommandlineJava commandline) throws BuildException {
+ jdepend.textui.JDepend jdepend;
+
+ if ("xml".equals(format)) {
+ jdepend = new jdepend.xmlui.JDepend();
+ } else {
+ jdepend = new jdepend.textui.JDepend();
+ }
+
+ FileWriter fw = null;
+ PrintWriter pw = null;
+ if (getOutputFile() != null) {
+ try {
+ fw = new FileWriter(getOutputFile().getPath());
+ } catch (IOException e) {
+ String msg = "JDepend Failed when creating the output file: "
+ + e.getMessage();
+ log(msg);
+ throw new BuildException(msg);
+ }
+ pw = new PrintWriter(fw);
+ jdepend.setWriter(pw);
+ log("Output to be stored in " + getOutputFile().getPath());
+ }
+
+
+ try {
+ if (getClassespath() != null) {
+ // This is the new, better way - use classespath instead
+ // of sourcespath. The code is currently the same - you
+ // need class files in a directory to use this or jar files.
+ String[] cP = getClassespath().list();
+ for (int i = 0; i < cP.length; i++) {
+ File f = new File(cP[i]);
+ // not necessary as JDepend would fail, but why loose
+ // some time?
+ if (!f.exists()) {
+ String msg = "\""
+ + f.getPath()
+ + "\" does not represent a valid"
+ + " file or directory. JDepend would fail.";
+ log(msg);
+ throw new BuildException(msg);
+ }
+ try {
+ jdepend.addDirectory(f.getPath());
+ } catch (IOException e) {
+ String msg =
+ "JDepend Failed when adding a class directory: "
+ + e.getMessage();
+ log(msg);
+ throw new BuildException(msg);
+ }
+ }
+
+ } else if (getSourcespath() != null) {
+
+ // This is the old way and is deprecated - classespath is
+ // the right way to do this and is above
+ String[] sP = getSourcespath().list();
+ for (int i = 0; i < sP.length; i++) {
+ File f = new File(sP[i]);
+
+ // not necessary as JDepend would fail, but why loose
+ // some time?
+ if (!f.exists() || !f.isDirectory()) {
+ String msg = "\""
+ + f.getPath()
+ + "\" does not represent a valid"
+ + " directory. JDepend would fail.";
+ log(msg);
+ throw new BuildException(msg);
+ }
+ try {
+ jdepend.addDirectory(f.getPath());
+ } catch (IOException e) {
+ String msg =
+ "JDepend Failed when adding a source directory: "
+ + e.getMessage();
+ log(msg);
+ throw new BuildException(msg);
+ }
+ }
+ }
+
+ // This bit turns <exclude> child tags into patters to ignore
+ String[] patterns = defaultPatterns.getExcludePatterns(getProject());
+ if (patterns != null && patterns.length > 0) {
+ if (setFilter != null) {
+ Vector v = new Vector();
+ for (int i = 0; i < patterns.length; i++) {
+ v.addElement(patterns[i]);
+ }
+ try {
+ Object o = packageFilterC.newInstance(new Object[] {v});
+ setFilter.invoke(jdepend, new Object[] {o});
+ } catch (Throwable e) {
+ log("excludes will be ignored as JDepend doesn't like me: "
+ + e.getMessage(), Project.MSG_WARN);
+ }
+ } else {
+ log("Sorry, your version of JDepend doesn't support excludes",
+ Project.MSG_WARN);
+ }
+ }
+
+ jdepend.analyze();
+ if (pw.checkError()) {
+ throw new IOException("Encountered an error writing JDepend"
+ + " output");
+ }
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ } finally {
+ FileUtils.close(pw);
+ FileUtils.close(fw);
+ }
+ return SUCCESS;
+ }
+
+
+ /**
+ * Execute the task by forking a new JVM. The command will block until
+ * it finishes. To know if the process was destroyed or not, use the
+ * <tt>killedProcess()</tt> method of the watchdog class.
+ * @param commandline the commandline for forked jvm
+ * @param watchdog the watchdog in charge of cancelling the test if it
+ * exceeds a certain amount of time. Can be <tt>null</tt>.
+ * @return the result of running the jdepend
+ * @throws BuildException in case of error
+ */
+ // JL: comment extracted from JUnitTask (and slightly modified)
+ public int executeAsForked(CommandlineJava commandline,
+ ExecuteWatchdog watchdog) throws BuildException {
+ runtimeClasses = new Path(getProject());
+ addClasspathEntry("/jdepend/textui/JDepend.class");
+
+ // if not set, auto-create the ClassPath from the project
+ createClasspath();
+
+ // not sure whether this test is needed but cost nothing to put.
+ // hope it will be reviewed by anybody competent
+ if (getClasspath().toString().length() > 0) {
+ createJvmarg(commandline).setValue("-classpath");
+ createJvmarg(commandline).setValue(getClasspath().toString());
+ }
+
+ if (includeRuntime) {
+ Map/*<String, String>*/ env = Execute.getEnvironmentVariables();
+ String cp = (String) env.get("CLASSPATH");
+ if (cp != null) {
+ commandline.createClasspath(getProject()).createPath()
+ .append(new Path(getProject(), cp));
+ }
+ log("Implicitly adding " + runtimeClasses + " to CLASSPATH",
+ Project.MSG_VERBOSE);
+ commandline.createClasspath(getProject()).createPath()
+ .append(runtimeClasses);
+ }
+
+ if (getOutputFile() != null) {
+ // having a space between the file and its path causes commandline
+ // to add quotes around the argument thus making JDepend not taking
+ // it into account. Thus we split it in two
+ commandline.createArgument().setValue("-file");
+ commandline.createArgument().setValue(outputFile.getPath());
+ // we have to find a cleaner way to put this output
+ }
+
+ if (getSourcespath() != null) {
+ // This is deprecated - use classespath in the future
+ String[] sP = getSourcespath().list();
+ for (int i = 0; i < sP.length; i++) {
+ File f = new File(sP[i]);
+
+ // not necessary as JDepend would fail, but why loose
+ // some time?
+ if (!f.exists() || !f.isDirectory()) {
+ throw new BuildException("\"" + f.getPath()
+ + "\" does not represent a valid"
+ + " directory. JDepend would"
+ + " fail.");
+ }
+ commandline.createArgument().setValue(f.getPath());
+ }
+ }
+
+ if (getClassespath() != null) {
+ // This is the new way - use classespath - code is the
+ // same for now
+ String[] cP = getClassespath().list();
+ for (int i = 0; i < cP.length; i++) {
+ File f = new File(cP[i]);
+ // not necessary as JDepend would fail, but why loose
+ // some time?
+ if (!f.exists()) {
+ throw new BuildException("\"" + f.getPath()
+ + "\" does not represent a valid"
+ + " file or directory. JDepend would"
+ + " fail.");
+ }
+ commandline.createArgument().setValue(f.getPath());
+ }
+ }
+
+ Execute execute = new Execute(new LogStreamHandler(this,
+ Project.MSG_INFO, Project.MSG_WARN), watchdog);
+ execute.setCommandline(commandline.getCommandline());
+ if (getDir() != null) {
+ execute.setWorkingDirectory(getDir());
+ execute.setAntRun(getProject());
+ }
+
+ if (getOutputFile() != null) {
+ log("Output to be stored in " + getOutputFile().getPath());
+ }
+ log(commandline.describeCommand(), Project.MSG_VERBOSE);
+ try {
+ return execute.execute();
+ } catch (IOException e) {
+ throw new BuildException("Process fork failed.", e, getLocation());
+ }
+ }
+
+ /**
+ * @return <tt>null</tt> if there is a timeout value, otherwise the
+ * watchdog instance.
+ * @throws BuildException in case of error
+ */
+ protected ExecuteWatchdog createWatchdog() throws BuildException {
+ if (getTimeout() == null) {
+ return null;
+ }
+ return new ExecuteWatchdog(getTimeout().longValue());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java
new file mode 100644
index 00000000..20e9fc51
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.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.tools.ant.taskdefs.optional.jlink;
+
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Reads just enough of a class file to determine the class' full name.
+ *
+ * <p>Extremely minimal constant pool implementation, mainly to support extracting
+ * strings from a class file.
+ */
+class ConstantPool {
+ // CheckStyle:VisibilityModifier OFF - bc
+ static final
+ byte UTF8 = 1, UNUSED = 2, INTEGER = 3, FLOAT = 4, LONG = 5, DOUBLE = 6,
+ CLASS = 7, STRING = 8, FIELDREF = 9, METHODREF = 10,
+ INTERFACEMETHODREF = 11, NAMEANDTYPE = 12;
+
+ byte[] types;
+
+ Object[] values;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Create a constant pool.
+ * @param data the data input containing the class.
+ * @throws IOException if there is an error.
+ */
+ ConstantPool(DataInput data) throws IOException {
+ super();
+
+ int count = data.readUnsignedShort();
+ types = new byte [ count ];
+ values = new Object [ count ];
+ // read in all constant pool entries.
+ for (int i = 1; i < count; i++) {
+ byte type = data.readByte();
+ types[i] = type;
+ switch (type) {
+ case UTF8 :
+ values[i] = data.readUTF();
+ break;
+
+ case UNUSED :
+ break;
+
+ case INTEGER :
+ values[i] = new Integer(data.readInt());
+ break;
+
+ case FLOAT :
+ values[i] = new Float(data.readFloat());
+ break;
+
+ case LONG :
+ values[i] = new Long(data.readLong());
+ ++i;
+ break;
+
+ case DOUBLE :
+ values[i] = new Double(data.readDouble());
+ ++i;
+ break;
+
+ case CLASS :
+ case STRING :
+ values[i] = new Integer(data.readUnsignedShort());
+ break;
+
+ case FIELDREF :
+ case METHODREF :
+ case INTERFACEMETHODREF :
+ case NAMEANDTYPE :
+ values[i] = new Integer(data.readInt());
+ break;
+ default:
+ // Do nothing
+ }
+ }
+ }
+}
+
+/**
+ * Provides a quick and dirty way to determine the true name of a class
+ * given just an InputStream. Reads in just enough to perform this
+ * minimal task only.
+ */
+public class ClassNameReader extends Object {
+ private static final int CLASS_MAGIC_NUMBER = 0xCAFEBABE;
+
+ /**
+ * Get the class name of a class in an input stream.
+ *
+ * @param input an <code>InputStream</code> value
+ * @return the name of the class
+ * @exception IOException if an error occurs
+ */
+ public static String getClassName(InputStream input) throws IOException {
+ DataInputStream data = new DataInputStream(input);
+ // verify this is a valid class file.
+ int cookie = data.readInt();
+ if (cookie != CLASS_MAGIC_NUMBER) {
+ return null;
+ }
+ /* int version = */ data.readInt();
+ // read the constant pool.
+ ConstantPool constants = new ConstantPool(data);
+ Object[] values = constants.values;
+ // read access flags and class index.
+ /* int accessFlags = */ data.readUnsignedShort();
+ int classIndex = data.readUnsignedShort();
+ Integer stringIndex = (Integer) values[classIndex];
+ String className = (String) values[stringIndex.intValue()];
+ return className;
+ }
+
+
+}
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java
new file mode 100644
index 00000000..f5767e67
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java
@@ -0,0 +1,183 @@
+/*
+ * 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.tools.ant.taskdefs.optional.jlink;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * This class defines objects that can link together various jar and
+ * zip files.
+ *
+ * <p>It is basically a wrapper for the jlink code written originally
+ * by <a href="mailto:beard@netscape.com">Patrick Beard</a>. The
+ * classes org.apache.tools.ant.taskdefs.optional.jlink.Jlink and
+ * org.apache.tools.ant.taskdefs.optional.jlink.ClassNameReader
+ * support this class.</p>
+ *
+ * <p>For example:
+ * <code>
+ * <pre>
+ * &lt;jlink compress=&quot;false&quot; outfile=&quot;out.jar&quot;/&gt;
+ * &lt;mergefiles&gt;
+ * &lt;pathelement path=&quot;${build.dir}/mergefoo.jar&quot;/&gt;
+ * &lt;pathelement path=&quot;${build.dir}/mergebar.jar&quot;/&gt;
+ * &lt;/mergefiles&gt;
+ * &lt;addfiles&gt;
+ * &lt;pathelement path=&quot;${build.dir}/mac.jar&quot;/&gt;
+ * &lt;pathelement path=&quot;${build.dir}/pc.zip&quot;/&gt;
+ * &lt;/addfiles&gt;
+ * &lt;/jlink&gt;
+ * </pre>
+ * </code>
+ *
+ * @ant.task ignore="true"
+ */
+public class JlinkTask extends MatchingTask {
+
+ /**
+ * The output file for this run of jlink. Usually a jar or zip file.
+ * @param outfile the output file
+ */
+ public void setOutfile(File outfile) {
+ this.outfile = outfile;
+ }
+
+ /**
+ * Establishes the object that contains the files to
+ * be merged into the output.
+ * @return a path to be configured
+ */
+ public Path createMergefiles() {
+ if (this.mergefiles == null) {
+ this.mergefiles = new Path(getProject());
+ }
+ return this.mergefiles.createPath();
+ }
+
+ /**
+ * Sets the files to be merged into the output.
+ * @param mergefiles a path
+ */
+ public void setMergefiles(Path mergefiles) {
+ if (this.mergefiles == null) {
+ this.mergefiles = mergefiles;
+ } else {
+ this.mergefiles.append(mergefiles);
+ }
+ }
+
+ /**
+ * Establishes the object that contains the files to
+ * be added to the output.
+ * @return a path to be configured
+ */
+ public Path createAddfiles() {
+ if (this.addfiles == null) {
+ this.addfiles = new Path(getProject());
+ }
+ return this.addfiles.createPath();
+ }
+
+ /**
+ * Sets the files to be added into the output.
+ * @param addfiles a path
+ */
+ public void setAddfiles(Path addfiles) {
+ if (this.addfiles == null) {
+ this.addfiles = addfiles;
+ } else {
+ this.addfiles.append(addfiles);
+ }
+ }
+
+ /**
+ * Defines whether or not the output should be compacted.
+ * @param compress a <code>boolean</code> value
+ */
+ public void setCompress(boolean compress) {
+ this.compress = compress;
+ }
+
+ /**
+ * Does the adding and merging.
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ //Be sure everything has been set.
+ if (outfile == null) {
+ throw new BuildException("outfile attribute is required! "
+ + "Please set.");
+ }
+ if (!haveAddFiles() && !haveMergeFiles()) {
+ throw new BuildException("addfiles or mergefiles required! "
+ + "Please set.");
+ }
+ log("linking: " + outfile.getPath());
+ log("compression: " + compress, Project.MSG_VERBOSE);
+ jlink linker = new jlink();
+ linker.setOutfile(outfile.getPath());
+ linker.setCompression(compress);
+ if (haveMergeFiles()) {
+ log("merge files: " + mergefiles.toString(), Project.MSG_VERBOSE);
+ linker.addMergeFiles(mergefiles.list());
+ }
+ if (haveAddFiles()) {
+ log("add files: " + addfiles.toString(), Project.MSG_VERBOSE);
+ linker.addAddFiles(addfiles.list());
+ }
+ try {
+ linker.link();
+ } catch (Exception ex) {
+ throw new BuildException(ex, getLocation());
+ }
+ }
+
+ private boolean haveAddFiles() {
+ return haveEntries(addfiles);
+ }
+
+ private boolean haveMergeFiles() {
+ return haveEntries(mergefiles);
+ }
+
+ private boolean haveEntries(Path p) {
+ if (p == null) {
+ return false;
+ }
+ if (p.size() > 0) {
+ return true;
+ }
+ return false;
+ }
+
+ private File outfile = null;
+
+ private Path mergefiles = null;
+
+ private Path addfiles = null;
+
+ private boolean compress = false;
+
+}
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java
new file mode 100644
index 00000000..499cca27
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java
@@ -0,0 +1,458 @@
+/*
+ * 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.
+ *
+ */
+/**
+ * jlink.java links together multiple .jar files Original code by Patrick
+ * Beard. Modifications to work with ANT by Matthew Kuperus Heun.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.jlink;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.tools.ant.util.FileUtils;
+
+// CheckStyle:TypeNameCheck OFF - bc
+/**
+ * jlink links together multiple .jar files.
+ */
+public class jlink {
+ private static final int BUFFER_SIZE = 8192;
+ private static final int VECTOR_INIT_SIZE = 10;
+
+ private String outfile = null;
+
+ private Vector mergefiles = new Vector(VECTOR_INIT_SIZE);
+
+ private Vector addfiles = new Vector(VECTOR_INIT_SIZE);
+
+ private boolean compression = false;
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /** The file that will be created by this instance of jlink.
+ * @param outfile the file to create.
+ */
+ public void setOutfile(String outfile) {
+ if (outfile == null) {
+ return;
+ }
+ this.outfile = outfile;
+ }
+
+
+ /**
+ * Adds a file to be merged into the output.
+ * @param fileToMerge the file to merge into the output.
+ */
+ public void addMergeFile(String fileToMerge) {
+ if (fileToMerge == null) {
+ return;
+ }
+ mergefiles.addElement(fileToMerge);
+ }
+
+
+ /** Adds a file to be added into the output.
+ * @param fileToAdd the file to add to the output.
+ */
+ public void addAddFile(String fileToAdd) {
+ if (fileToAdd == null) {
+ return;
+ }
+ addfiles.addElement(fileToAdd);
+ }
+
+
+ /**
+ * Adds several files to be merged into the output.
+ * @param filesToMerge an array of files to merge into the output.
+ */
+ public void addMergeFiles(String[] filesToMerge) {
+ if (filesToMerge == null) {
+ return;
+ }
+ for (int i = 0; i < filesToMerge.length; i++) {
+ addMergeFile(filesToMerge[i]);
+ }
+ }
+
+
+ /**
+ * Adds several file to be added into the output.
+ * @param filesToAdd an array of files to add to the output.
+ */
+ public void addAddFiles(String[] filesToAdd) {
+ if (filesToAdd == null) {
+ return;
+ }
+ for (int i = 0; i < filesToAdd.length; i++) {
+ addAddFile(filesToAdd[i]);
+ }
+ }
+
+
+ /**
+ * Determines whether output will be compressed.
+ * @param compress if true use compression.
+ */
+ public void setCompression(boolean compress) {
+ this.compression = compress;
+ }
+
+
+ /**
+ * Performs the linking of files. Addfiles are added to the output as-is.
+ * For example, a jar file is added to the output as a jar file. However,
+ * mergefiles are first examined for their type. If it is a jar or zip
+ * file, the contents will be extracted from the mergefile and entered
+ * into the output. If a zip or jar file is encountered in a subdirectory
+ * it will be added, not merged. If a directory is encountered, it becomes
+ * the root entry of all the files below it. Thus, you can provide
+ * multiple, disjoint directories, as addfiles: they will all be added in
+ * a rational manner to outfile.
+ * @throws Exception on error.
+ */
+ public void link() throws Exception {
+ ZipOutputStream output = new ZipOutputStream(new FileOutputStream(outfile));
+
+ if (compression) {
+ output.setMethod(ZipOutputStream.DEFLATED);
+ output.setLevel(Deflater.DEFAULT_COMPRESSION);
+ } else {
+ output.setMethod(ZipOutputStream.STORED);
+ }
+
+ Enumeration merges = mergefiles.elements();
+
+ while (merges.hasMoreElements()) {
+ String path = (String) merges.nextElement();
+ File f = new File(path);
+
+ if (f.getName().endsWith(".jar") || f.getName().endsWith(".zip")) {
+ //Do the merge
+ mergeZipJarContents(output, f);
+ } else {
+ //Add this file to the addfiles Vector and add it
+ //later at the top level of the output file.
+ addAddFile(path);
+ }
+ }
+
+ Enumeration adds = addfiles.elements();
+
+ while (adds.hasMoreElements()) {
+ String name = (String) adds.nextElement();
+ File f = new File(name);
+
+ if (f.isDirectory()) {
+ //System.out.println("in jlink: adding directory contents of " + f.getPath());
+ addDirContents(output, f, f.getName() + '/', compression);
+ } else {
+ addFile(output, f, "", compression);
+ }
+ }
+ FileUtils.close(output);
+ }
+
+
+ /**
+ * The command line entry point for jlink.
+ * @param args an array of arguments
+ */
+ public static void main(String[] args) {
+ // jlink output input1 ... inputN
+ if (args.length < 2) {
+ System.out.println("usage: jlink output input1 ... inputN");
+ System.exit(1);
+ }
+ jlink linker = new jlink();
+
+ linker.setOutfile(args[0]);
+ // To maintain compatibility with the command-line version,
+ // we will only add files to be merged.
+ for (int i = 1; i < args.length; i++) {
+ linker.addMergeFile(args[i]);
+ }
+ try {
+ linker.link();
+ } catch (Exception ex) {
+ System.err.print(ex.getMessage());
+ }
+ }
+
+
+ /*
+ * Actually performs the merging of f into the output.
+ * f should be a zip or jar file.
+ */
+ private void mergeZipJarContents(ZipOutputStream output, File f) throws IOException {
+ //Check to see that the file with name "name" exists.
+ if (!f.exists()) {
+ return;
+ }
+ ZipFile zipf = new ZipFile(f);
+ Enumeration entries = zipf.entries();
+
+ while (entries.hasMoreElements()) {
+ ZipEntry inputEntry = (ZipEntry) entries.nextElement();
+ //Ignore manifest entries. They're bound to cause conflicts between
+ //files that are being merged. User should supply their own
+ //manifest file when doing the merge.
+ String inputEntryName = inputEntry.getName();
+ int index = inputEntryName.indexOf("META-INF");
+
+ if (index < 0) {
+ //META-INF not found in the name of the entry. Go ahead and process it.
+ try {
+ output.putNextEntry(processEntry(zipf, inputEntry));
+ } catch (ZipException ex) {
+ //If we get here, it could be because we are trying to put a
+ //directory entry that already exists.
+ //For example, we're trying to write "com", but a previous
+ //entry from another mergefile was called "com".
+ //In that case, just ignore the error and go on to the
+ //next entry.
+ String mess = ex.getMessage();
+
+ if (mess.indexOf("duplicate") >= 0) {
+ //It was the duplicate entry.
+ continue;
+ } else {
+ // I hate to admit it, but we don't know what happened
+ // here. Throw the Exception.
+ throw ex;
+ }
+ }
+
+ InputStream in = zipf.getInputStream(inputEntry);
+ int len = buffer.length;
+ int count = -1;
+
+ while ((count = in.read(buffer, 0, len)) > 0) {
+ output.write(buffer, 0, count);
+ }
+ in.close();
+ output.closeEntry();
+ }
+ }
+ zipf.close();
+ }
+
+
+ /*
+ * Adds contents of a directory to the output.
+ */
+ private void addDirContents(ZipOutputStream output, File dir, String prefix,
+ boolean compress) throws IOException {
+ String[] contents = dir.list();
+
+ for (int i = 0; i < contents.length; ++i) {
+ String name = contents[i];
+ File file = new File(dir, name);
+
+ if (file.isDirectory()) {
+ addDirContents(output, file, prefix + name + '/', compress);
+ } else {
+ addFile(output, file, prefix, compress);
+ }
+ }
+ }
+
+
+ /*
+ * Gets the name of an entry in the file. This is the real name
+ * which for a class is the name of the package with the class
+ * name appended.
+ */
+ private String getEntryName(File file, String prefix) {
+ String name = file.getName();
+
+ if (!name.endsWith(".class")) {
+ // see if the file is in fact a .class file, and determine its actual name.
+ InputStream input = null;
+ try {
+ input = new FileInputStream(file);
+ String className = ClassNameReader.getClassName(input);
+
+ if (className != null) {
+ return className.replace('.', '/') + ".class";
+ }
+ } catch (IOException ioe) {
+ //do nothing
+ } finally {
+ if (input != null) {
+ try {
+ input.close();
+ } catch (IOException e) {
+ //do nothing
+ }
+ }
+ }
+ }
+ System.out.println("From " + file.getPath() + " and prefix " + prefix
+ + ", creating entry " + prefix + name);
+ return (prefix + name);
+ }
+
+
+ /*
+ * Adds a file to the output stream.
+ */
+ private void addFile(ZipOutputStream output, File file, String prefix,
+ boolean compress) throws IOException {
+ //Make sure file exists
+ if (!file.exists()) {
+ return;
+ }
+ ZipEntry entry = new ZipEntry(getEntryName(file, prefix));
+
+ entry.setTime(file.lastModified());
+ entry.setSize(file.length());
+ if (!compress) {
+ entry.setCrc(calcChecksum(file));
+ }
+ FileInputStream input = new FileInputStream(file);
+
+ addToOutputStream(output, input, entry);
+ }
+
+
+ /*
+ * A convenience method that several other methods might call.
+ */
+ private void addToOutputStream(ZipOutputStream output, InputStream input,
+ ZipEntry ze) throws IOException {
+ try {
+ output.putNextEntry(ze);
+ } catch (ZipException zipEx) {
+ //This entry already exists. So, go with the first one.
+ input.close();
+ return;
+ }
+
+ int numBytes = -1;
+
+ while ((numBytes = input.read(buffer)) > 0) {
+ output.write(buffer, 0, numBytes);
+ }
+ output.closeEntry();
+ input.close();
+ }
+
+
+ /*
+ * A method that does the work on a given entry in a mergefile.
+ * The big deal is to set the right parameters in the ZipEntry
+ * on the output stream.
+ */
+ private ZipEntry processEntry(ZipFile zip, ZipEntry inputEntry) {
+ /*
+ First, some notes.
+ On MRJ 2.2.2, getting the size, compressed size, and CRC32 from the
+ ZipInputStream does not work for compressed (deflated) files. Those calls return -1.
+ For uncompressed (stored) files, those calls do work.
+ However, using ZipFile.getEntries() works for both compressed and
+ uncompressed files.
+
+ Now, from some simple testing I did, it seems that the value of CRC-32 is
+ independent of the compression setting. So, it should be easy to pass this
+ information on to the output entry.
+ */
+ String name = inputEntry.getName();
+
+ if (!(inputEntry.isDirectory() || name.endsWith(".class"))) {
+ try {
+ InputStream input = zip.getInputStream(zip.getEntry(name));
+ String className = ClassNameReader.getClassName(input);
+
+ input.close();
+ if (className != null) {
+ name = className.replace('.', '/') + ".class";
+ }
+ } catch (IOException ioe) {
+ //do nothing
+ }
+ }
+ ZipEntry outputEntry = new ZipEntry(name);
+
+ outputEntry.setTime(inputEntry.getTime());
+ outputEntry.setExtra(inputEntry.getExtra());
+ outputEntry.setComment(inputEntry.getComment());
+ outputEntry.setTime(inputEntry.getTime());
+ if (compression) {
+ outputEntry.setMethod(ZipEntry.DEFLATED);
+ //Note, don't need to specify size or crc for compressed files.
+ } else {
+ outputEntry.setMethod(ZipEntry.STORED);
+ outputEntry.setCrc(inputEntry.getCrc());
+ outputEntry.setSize(inputEntry.getSize());
+ }
+ return outputEntry;
+ }
+
+
+ /*
+ * Necessary in the case where you add a entry that
+ * is not compressed.
+ */
+ private long calcChecksum(File f) throws IOException {
+ BufferedInputStream in = new BufferedInputStream(new FileInputStream(f));
+
+ return calcChecksum(in);
+ }
+
+
+ /*
+ * Necessary in the case where you add a entry that
+ * is not compressed.
+ */
+ private long calcChecksum(InputStream in) throws IOException {
+ CRC32 crc = new CRC32();
+ int len = buffer.length;
+ int count = -1;
+ int haveRead = 0;
+
+ while ((count = in.read(buffer, 0, len)) > 0) {
+ haveRead += count;
+ crc.update(buffer, 0, count);
+ }
+ in.close();
+ return crc.getValue();
+ }
+
+
+}
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/Jasper41Mangler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/Jasper41Mangler.java
new file mode 100644
index 00000000..609938c9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/Jasper41Mangler.java
@@ -0,0 +1,93 @@
+/*
+ * 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.tools.ant.taskdefs.optional.jsp;
+
+import java.io.File;
+
+/**
+ * this class implements the name mangling rules of the jasper in tomcat4.1.x
+ * which is likely to remain for some time
+ * @see "org.apache.jasper.JspCompilationContext"
+ */
+public class Jasper41Mangler implements JspMangler {
+
+
+ /**
+ * map from a jsp file to a java filename; does not do packages
+ *
+ * @param jspFile file
+ * @return java filename
+ */
+ public String mapJspToJavaName(File jspFile) {
+ String jspUri = jspFile.getAbsolutePath();
+ int start = jspUri.lastIndexOf(File.separatorChar) + 1;
+ int end = jspUri.length();
+ StringBuffer modifiedClassName;
+ modifiedClassName = new StringBuffer(jspUri.length() - start);
+ if (!Character.isJavaIdentifierStart(jspUri.charAt(start))
+ || jspUri.charAt(start) == '_') {
+ // If the first char is not a start of Java identifier or is _
+ // prepend a '_'.
+ modifiedClassName.append('_');
+ }
+ for (int i = start; i < end; i++) {
+ char ch = jspUri.charAt(i);
+ if (Character.isJavaIdentifierPart(ch)) {
+ modifiedClassName.append(ch);
+ } else if (ch == '.') {
+ modifiedClassName.append('_');
+ } else {
+ modifiedClassName.append(mangleChar(ch));
+ }
+ }
+ return modifiedClassName.toString();
+ }
+
+ /**
+ * Mangle the specified character to create a legal Java class name.
+ */
+ private static String mangleChar(char ch) {
+ // CheckStyle:MagicNumber OFF
+ String s = Integer.toHexString(ch);
+ int nzeros = 5 - s.length();
+ char[] result = new char[6];
+ result[0] = '_';
+ for (int i = 1; i <= nzeros; i++) {
+ result[i] = '0';
+ }
+ for (int i = nzeros + 1, j = 0; i < 6; i++, j++) {
+ result[i] = s.charAt(j);
+ }
+ return new String(result);
+ // CheckStyle:MagicNumber ON
+ }
+
+
+ /**
+ * taking in the substring representing the path relative to the source dir
+ * return a new string representing the destination path
+ * @param path not used.
+ * @return null as this is not implemented.
+ * @todo
+ */
+ public String mapPath(String path) {
+ return null;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspC.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspC.java
new file mode 100644
index 00000000..4274bf0d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspC.java
@@ -0,0 +1,709 @@
+/*
+ * 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.tools.ant.taskdefs.optional.jsp;
+
+import java.io.File;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.taskdefs.optional.jsp.compilers.JspCompilerAdapter;
+import org.apache.tools.ant.taskdefs.optional.jsp.compilers.JspCompilerAdapterFactory;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Runs a JSP compiler.
+ *
+ * <p> This task takes the given jsp files and compiles them into java
+ * files. It is then up to the user to compile the java files into classes.
+ *
+ * <p> The task requires the srcdir and destdir attributes to be
+ * set. This Task is a MatchingTask, so the files to be compiled can be
+ * specified using includes/excludes attributes or nested include/exclude
+ * elements. Optional attributes are verbose (set the verbosity level passed
+ * to jasper), package (name of the destination package for generated java
+ * classes and classpath (the classpath to use when running the jsp
+ * compiler).
+ * <p> This task supports the nested elements classpath (A Path) and
+ * classpathref (A Reference) which can be used in preference to the
+ * attribute classpath, if the jsp compiler is not already in the ant
+ * classpath.
+ *
+ * <p><h4>Usage</h4>
+ * <pre>
+ * &lt;jspc srcdir="${basedir}/src/war"
+ * destdir="${basedir}/gensrc"
+ * package="com.i3sp.jsp"
+ * verbose="9"&gt;
+ * &lt;include name="**\/*.jsp" /&gt;
+ * &lt;/jspc&gt;
+ * </pre>
+ *
+ * <p> Large Amount of cutting and pasting from the Javac task...
+ * @since 1.5
+ */
+public class JspC extends MatchingTask {
+ private Path classpath;
+ private Path compilerClasspath;
+ private Path src;
+ private File destDir;
+ private String packageName;
+ /** name of the compiler to use */
+ private String compilerName = "jasper";
+
+ /**
+ * -ieplugin &lt;clsid&gt; Java Plugin classid for Internet Explorer
+ */
+ private String iepluginid;
+ private boolean mapped;
+ private int verbose = 0;
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected Vector compileList = new Vector();
+ Vector javaFiles = new Vector();
+
+ /**
+ * flag to control action on execution trouble
+ */
+ protected boolean failOnError = true;
+
+ /**
+ * -uriroot &lt;dir&gt; The root directory that uri files should be resolved
+ * against,
+ */
+ private File uriroot;
+
+ /**
+ * -webinc &lt;file&gt; Creates partial servlet mappings for the -webapp option
+ */
+ private File webinc;
+
+ /**
+ * -webxml &lt;file&gt; Creates a complete web.xml when using the -webapp option.
+ */
+
+ private File webxml;
+
+ /**
+ * web apps
+ */
+ protected WebAppParameter webApp;
+
+
+
+ private static final String FAIL_MSG
+ = "Compile failed, messages should have been provided.";
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Set the path for source JSP files.
+ * @param srcDir the source path.
+ */
+ public void setSrcDir(Path srcDir) {
+ if (src == null) {
+ src = srcDir;
+ } else {
+ src.append(srcDir);
+ }
+ }
+
+ /**
+ * Get the source dir.
+ * @return the source path.
+ */
+ public Path getSrcDir() {
+ return src;
+ }
+
+ /**
+ * Set the destination directory into which the JSP source
+ * files should be compiled.
+ * @param destDir the destination directory.
+ */
+ public void setDestdir(File destDir) {
+ this.destDir = destDir;
+ }
+
+ /**
+ * Get the destination directory.
+ * @return the directory.
+ */
+ public File getDestdir() {
+ return destDir;
+ }
+
+ /**
+ * Set the name of the package the compiled jsp files should be in.
+ * @param pkg the name of the package.
+ */
+ public void setPackage(String pkg) {
+ this.packageName = pkg;
+ }
+
+ /**
+ * Get the name of the package.
+ * @return the package.
+ */
+ public String getPackage() {
+ return packageName;
+ }
+
+ /**
+ * Set the verbose level of the compiler
+ * @param i the verbose level to use.
+ */
+ public void setVerbose(int i) {
+ verbose = i;
+ }
+
+ /**
+ * Get the verbose level.
+ * @return the level.
+ */
+ public int getVerbose() {
+ return verbose;
+ }
+
+ /**
+ * Whether or not the build should halt if compilation fails.
+ * Defaults to <code>true</code>.
+ * @param fail a <code>boolean</code> value.
+ */
+ public void setFailonerror(boolean fail) {
+ failOnError = fail;
+ }
+ /**
+ * Gets the failonerror flag.
+ * @return the flag.
+ */
+ public boolean getFailonerror() {
+ return failOnError;
+ }
+
+ /**
+ * Get the IE CLASSID value.
+ * @return the value.
+ */
+ public String getIeplugin() {
+ return iepluginid;
+ }
+ /**
+ * Java Plugin CLASSID for Internet Explorer
+ * @param iepluginid the id to use.
+ */
+ public void setIeplugin(String iepluginid) {
+ this.iepluginid = iepluginid;
+ }
+
+ /**
+ * If true, generate separate write() calls for each HTML line
+ * in the JSP.
+ * @return mapping status
+ */
+ public boolean isMapped() {
+ return mapped;
+ }
+
+ /**
+ * If true, generate separate write() calls for each HTML line
+ * in the JSP.
+ * @param mapped a <code>boolean</code> value.
+ */
+ public void setMapped(boolean mapped) {
+ this.mapped = mapped;
+ }
+
+ /**
+ * The URI context of relative URI references in the JSP pages.
+ * If it does not exist then it is derived from the location
+ * of the file relative to the declared or derived value of uriroot.
+ *
+ * @param uribase The new Uribase value
+ */
+ public void setUribase(File uribase) {
+ log("Uribase is currently an unused parameter", Project.MSG_WARN);
+ }
+
+ /**
+ * Get the uri base value.
+ * @return the value.
+ */
+ public File getUribase() {
+ return uriroot;
+ }
+
+ /**
+ * The root directory that uri files should be resolved
+ * against. (Default is the directory jspc is invoked from)
+ *
+ * @param uriroot The new Uribase value
+ */
+ public void setUriroot(File uriroot) {
+ this.uriroot = uriroot;
+ }
+
+ /**
+ * Get the uri root value.
+ * @return the value.
+ */
+ public File getUriroot() {
+ return uriroot;
+ }
+
+
+ /**
+ * Set the classpath to be used for this compilation.
+ * @param cp the path to be used.
+ */
+ public void setClasspath(Path cp) {
+ if (classpath == null) {
+ classpath = cp;
+ } else {
+ classpath.append(cp);
+ }
+ }
+
+ /**
+ * Adds a path to the classpath.
+ * @return a path to be configured.
+ */
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(getProject());
+ }
+ return classpath.createPath();
+ }
+
+ /**
+ * Adds a reference to a classpath defined elsewhere
+ * @param r a reference to a classpath.
+ */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * Get the classpath.
+ * @return the classpath.
+ */
+ public Path getClasspath() {
+ return classpath;
+ }
+
+ /**
+ * Set the classpath to be used to find this compiler adapter
+ * @param cp the compiler classpath.
+ */
+ public void setCompilerclasspath(Path cp) {
+ if (compilerClasspath == null) {
+ compilerClasspath = cp;
+ } else {
+ compilerClasspath.append(cp);
+ }
+ }
+
+ /**
+ * get the classpath used to find the compiler adapter
+ * @return the compiler classpath.
+ */
+ public Path getCompilerclasspath() {
+ return compilerClasspath;
+ }
+
+ /**
+ * Support nested compiler classpath, used to locate compiler adapter
+ * @return a path to be configured.
+ */
+ public Path createCompilerclasspath() {
+ if (compilerClasspath == null) {
+ compilerClasspath = new Path(getProject());
+ }
+ return compilerClasspath.createPath();
+ }
+
+ /**
+ * Filename for web.xml.
+ *
+ * @param webxml The new Webxml value
+ */
+ public void setWebxml(File webxml) {
+ this.webxml = webxml;
+ }
+
+ /**
+ * Filename for web.xml.
+ * @return The filename for web.xml.
+ */
+ public File getWebxml() {
+ return this.webxml;
+ }
+
+ /**
+ * output filename for the fraction of web.xml that lists
+ * servlets.
+ * @param webinc The new Webinc value
+ */
+ public void setWebinc(File webinc) {
+ this.webinc = webinc;
+ }
+
+ /**
+ * Get the webinc attribute.
+ * @return the webinc attribute.
+ */
+ public File getWebinc() {
+ return this.webinc;
+ }
+
+ /**
+ * Adds a single webapp.
+ *
+ * @param webappParam add a web app parameter
+ * @throws BuildException if more than one webapp is specified.
+ */
+ public void addWebApp(WebAppParameter webappParam)
+ throws BuildException {
+ //demand create vector of filesets
+ if (webApp == null) {
+ webApp = webappParam;
+ } else {
+ throw new BuildException("Only one webapp can be specified");
+ }
+ }
+
+ /**
+ * Get the web app.
+ * @return the web app attribute.
+ */
+ public WebAppParameter getWebApp() {
+ return webApp;
+ }
+
+ /**
+ * Class name of a JSP compiler adapter.
+ * @param compiler the compiler class name.
+ */
+ public void setCompiler(String compiler) {
+ this.compilerName = compiler;
+ }
+
+ /**
+ * get the list of files to compile
+ * @return the list of files.
+ */
+ public Vector getCompileList() {
+ return compileList;
+ }
+
+ /**
+ * execute by building up a list of files that
+ * have changed and hand them off to a jsp compiler
+ * @throws BuildException on error.
+ */
+ public void execute()
+ throws BuildException {
+
+ // make sure that we've got a destdir
+ if (destDir == null) {
+ throw new BuildException("destdir attribute must be set!",
+ getLocation());
+ }
+
+ if (!destDir.isDirectory()) {
+ throw new BuildException("destination directory \"" + destDir
+ + "\" does not exist or is not a directory",
+ getLocation());
+ }
+
+ File dest = getActualDestDir();
+
+ AntClassLoader al = null;
+ try {
+ //bind to a compiler
+ JspCompilerAdapter compiler =
+ JspCompilerAdapterFactory
+ .getCompiler(compilerName, this,
+ al = getProject().createClassLoader(compilerClasspath));
+
+ //if we are a webapp, hand off to the compiler, which had
+ //better handle it
+ if (webApp != null) {
+ doCompilation(compiler);
+ return;
+ }
+
+ // make sure that we've got a srcdir
+ if (src == null) {
+ throw new BuildException("srcdir attribute must be set!",
+ getLocation());
+ }
+ String [] list = src.list();
+ if (list.length == 0) {
+ throw new BuildException("srcdir attribute must be set!",
+ getLocation());
+ }
+
+
+ // if the compiler does its own dependency stuff, we just
+ // call it right now
+ if (compiler.implementsOwnDependencyChecking()) {
+ doCompilation(compiler);
+ return;
+ }
+
+ //the remainder of this method is only for compilers that
+ //need their dependency work done
+ JspMangler mangler = compiler.createMangler();
+
+ // scan source directories and dest directory to build up both copy
+ // lists and compile lists
+ resetFileLists();
+ int filecount = 0;
+ for (int i = 0; i < list.length; i++) {
+ File srcDir = getProject().resolveFile(list[i]);
+ if (!srcDir.exists()) {
+ throw new BuildException("srcdir \"" + srcDir.getPath()
+ + "\" does not exist!",
+ getLocation());
+ }
+ DirectoryScanner ds = this.getDirectoryScanner(srcDir);
+ String[] files = ds.getIncludedFiles();
+ filecount = files.length;
+ scanDir(srcDir, dest, mangler, files);
+ }
+
+ // compile the source files
+
+ log("compiling " + compileList.size() + " files",
+ Project.MSG_VERBOSE);
+
+ if (compileList.size() > 0) {
+
+ log("Compiling " + compileList.size() + " source file"
+ + (compileList.size() == 1 ? "" : "s")
+ + " to "
+ + dest);
+ doCompilation(compiler);
+
+ } else {
+ if (filecount == 0) {
+ log("there were no files to compile", Project.MSG_INFO);
+ } else {
+ log("all files are up to date", Project.MSG_VERBOSE);
+ }
+ }
+ } finally {
+ if (al != null) {
+ al.cleanup();
+ }
+ }
+ }
+
+ /**
+ * calculate where the files will end up:
+ * this is destDir or it id destDir + the package name
+ */
+ private File getActualDestDir() {
+ File dest = null;
+ if (packageName == null) {
+ dest = destDir;
+ } else {
+ String path = destDir.getPath() + File.separatorChar
+ + packageName.replace('.', File.separatorChar);
+ dest = new File(path);
+ }
+ return dest;
+ }
+
+ /**
+ * do the compile
+ */
+ private void doCompilation(JspCompilerAdapter compiler)
+ throws BuildException {
+ // now we need to populate the compiler adapter
+ compiler.setJspc(this);
+
+ // finally, lets execute the compiler!!
+ if (!compiler.execute()) {
+ if (failOnError) {
+ throw new BuildException(FAIL_MSG, getLocation());
+ } else {
+ log(FAIL_MSG, Project.MSG_ERR);
+ }
+ }
+ }
+
+ /**
+ * Clear the list of files to be compiled and copied..
+ */
+ protected void resetFileLists() {
+ compileList.removeAllElements();
+ }
+
+ /**
+ * Scans the directory looking for source files to be compiled.
+ * The results are returned in the class variable compileList
+ * @param srcDir the source directory.
+ * @param dest the destination directory.
+ * @param mangler the jsp filename mangler.
+ * @param files the file names to mangle.
+ */
+ protected void scanDir(File srcDir, File dest, JspMangler mangler,
+ String[] files) {
+
+ long now = (new Date()).getTime();
+
+ for (int i = 0; i < files.length; i++) {
+ String filename = files[i];
+ File srcFile = new File(srcDir, filename);
+ File javaFile = mapToJavaFile(mangler, srcFile, srcDir, dest);
+ if (javaFile == null) {
+ continue;
+ }
+
+ if (srcFile.lastModified() > now) {
+ log("Warning: file modified in the future: " + filename,
+ Project.MSG_WARN);
+ }
+ boolean shouldCompile = false;
+ shouldCompile = isCompileNeeded(srcFile, javaFile);
+ if (shouldCompile) {
+ compileList.addElement(srcFile.getAbsolutePath());
+ javaFiles.addElement(javaFile);
+ }
+ }
+ }
+
+ /**
+ * Test whether or not compilation is needed. A return value of
+ * <code>true<code> means yes, <code>false</code> means
+ * our tests do not indicate this, but as the TLDs are
+ * not used for dependency checking this is not guaranteed.
+ * The current tests are
+ * <ol>
+ * <li>no dest file
+ * <li>dest file out of date w.r.t source
+ * <li>dest file zero bytes long
+ * </ol>
+ * @param srcFile JSP source file
+ * @param javaFile JSP dest file
+ * @return true if a compile is definitely needed.
+ *
+ */
+ private boolean isCompileNeeded(File srcFile, File javaFile) {
+ boolean shouldCompile = false;
+ if (!javaFile.exists()) {
+ shouldCompile = true;
+ log("Compiling " + srcFile.getPath()
+ + " because java file " + javaFile.getPath()
+ + " does not exist", Project.MSG_VERBOSE);
+ } else {
+ if (srcFile.lastModified() > javaFile.lastModified()) {
+ shouldCompile = true;
+ log("Compiling " + srcFile.getPath()
+ + " because it is out of date with respect to "
+ + javaFile.getPath(),
+ Project.MSG_VERBOSE);
+ } else {
+ if (javaFile.length() == 0) {
+ shouldCompile = true;
+ log("Compiling " + srcFile.getPath()
+ + " because java file " + javaFile.getPath()
+ + " is empty", Project.MSG_VERBOSE);
+ }
+ }
+ }
+ return shouldCompile;
+ }
+
+
+ /**
+ * get a filename from our jsp file.
+ * @param mangler the jsp filename manager.
+ * @param srcFile the source file.
+ * @param srcDir the source directory.
+ * @param dest the destination directory.
+ * @return the filename.
+ * @todo support packages and subdirs
+ */
+ protected File mapToJavaFile(JspMangler mangler, File srcFile, File srcDir,
+ File dest) {
+ if (!srcFile.getName().endsWith(".jsp")) {
+ return null;
+ }
+ String javaFileName = mangler.mapJspToJavaName(srcFile);
+ // String srcFileDir=srcFile.getParent();
+ return new File(dest, javaFileName);
+ }
+
+ /**
+ * delete any java output files that are empty
+ * this is to get around a little defect in jasper: when it
+ * fails, it leaves incomplete files around.
+ */
+ public void deleteEmptyJavaFiles() {
+ if (javaFiles != null) {
+ Enumeration e = javaFiles.elements();
+ while (e.hasMoreElements()) {
+ File file = (File) e.nextElement();
+ if (file.exists() && file.length() == 0) {
+ log("deleting empty output file " + file);
+ file.delete();
+ }
+ }
+ }
+ }
+
+ /**
+ * static inner class used as a parameter element
+ */
+ public static class WebAppParameter {
+
+ /**
+ * the sole option
+ */
+ private File directory;
+
+ /**
+ * query current directory
+ * @return the directory.
+ */
+ public File getDirectory() {
+ return directory;
+ }
+
+ /**
+ * set directory; alternate syntax
+ * @param directory the base dir.
+ */
+ public void setBaseDir(File directory) {
+ this.directory = directory;
+ }
+ //end inner class
+ }
+
+
+ //end class
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspMangler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspMangler.java
new file mode 100644
index 00000000..f62492c3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspMangler.java
@@ -0,0 +1,47 @@
+/*
+ * 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.tools.ant.taskdefs.optional.jsp;
+
+import java.io.File;
+
+/**
+ * This is an interface to the Mangler service that jspc needs to map
+ * JSP file names to java files.
+ * Note the complete lack of correlation
+ * with Jasper's mangler interface.
+ */
+public interface JspMangler {
+
+
+ /**
+ * map from a jsp file to a java filename; does not do packages
+ *
+ * @param jspFile file
+ * @return java filename
+ */
+ String mapJspToJavaName(File jspFile);
+
+ /**
+ * taking in the substring representing the path relative to the source dir
+ * return a new string representing the destination path
+ * @param path the path to map.
+ * @return the mapped path.
+ */
+ String mapPath(String path);
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspNameMangler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspNameMangler.java
new file mode 100644
index 00000000..6e08e7d5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspNameMangler.java
@@ -0,0 +1,155 @@
+/*
+ * 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.tools.ant.taskdefs.optional.jsp;
+import java.io.File;
+
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * This is a class derived from the Jasper code
+ * (org.apache.jasper.compiler.CommandLineCompiler) to map from a JSP filename
+ * to a valid Java classname.
+ *
+ */
+public class JspNameMangler implements JspMangler {
+
+ // CheckStyle:ConstantNameCheck OFF - bc
+
+ /**
+ * this is the list of keywords which can not be used as classnames
+ */
+ public static final String[] keywords = {
+ "assert",
+ "abstract", "boolean", "break", "byte",
+ "case", "catch", "char", "class",
+ "const", "continue", "default", "do",
+ "double", "else", "extends", "final",
+ "finally", "float", "for", "goto",
+ "if", "implements", "import",
+ "instanceof", "int", "interface",
+ "long", "native", "new", "package",
+ "private", "protected", "public",
+ "return", "short", "static", "super",
+ "switch", "synchronized", "this",
+ "throw", "throws", "transient",
+ "try", "void", "volatile", "while"
+ };
+
+ // CheckStyle:ConstantNameCheck ON
+
+ /**
+ * map from a jsp file to a java filename; does not do packages
+ *
+ * @param jspFile file
+ * @return java filename
+ */
+ public String mapJspToJavaName(File jspFile) {
+ return mapJspToBaseName(jspFile) + ".java";
+ }
+
+
+ /**
+ * map from a jsp file to a base name; does not deal with extensions
+ *
+ * @param jspFile jspFile file
+ * @return exensionless potentially remapped name
+ */
+ private String mapJspToBaseName(File jspFile) {
+ String className;
+ className = stripExtension(jspFile);
+
+ // since we don't mangle extensions like the servlet does,
+ // we need to check for keywords as class names
+ for (int i = 0; i < keywords.length; ++i) {
+ if (className.equals(keywords[i])) {
+ className += "%";
+ break;
+ }
+ }
+
+ // Fix for invalid characters. If you think of more add to the list.
+ StringBuffer modifiedClassName = new StringBuffer(className.length());
+ // first char is more restrictive than the rest
+ char firstChar = className.charAt(0);
+ if (Character.isJavaIdentifierStart(firstChar)) {
+ modifiedClassName.append(firstChar);
+ } else {
+ modifiedClassName.append(mangleChar(firstChar));
+ }
+ // this is the rest
+ for (int i = 1; i < className.length(); i++) {
+ char subChar = className.charAt(i);
+ if (Character.isJavaIdentifierPart(subChar)) {
+ modifiedClassName.append(subChar);
+ } else {
+ modifiedClassName.append(mangleChar(subChar));
+ }
+ }
+ return modifiedClassName.toString();
+ }
+
+
+ /**
+ * get short filename from file
+ *
+ * @param jspFile file in
+ * @return file without any jsp extension
+ */
+ private String stripExtension(File jspFile) {
+ return StringUtils.removeSuffix(jspFile.getName(), ".jsp");
+ }
+
+
+ /**
+ * definition of the char escaping algorithm
+ *
+ * @param ch char to mangle
+ * @return mangled string; 5 digit hex value
+ */
+ private static String mangleChar(char ch) {
+ // CheckStyle:MagicNumber OFF
+ if (ch == File.separatorChar) {
+ ch = '/';
+ }
+ String s = Integer.toHexString(ch);
+ int nzeros = 5 - s.length();
+ char[] result = new char[6];
+ result[0] = '_';
+ for (int i = 1; i <= nzeros; ++i) {
+ result[i] = '0';
+ }
+ int resultIndex = 0;
+ for (int i = nzeros + 1; i < 6; ++i) {
+ result[i] = s.charAt(resultIndex++);
+ }
+ return new String(result);
+ // CheckStyle:MagicNumber ON
+ }
+
+ /**
+ * taking in the substring representing the path relative to the source dir
+ * return a new string representing the destination path
+ * not supported, as jasper in tomcat4.0 doesn't either
+ * @param path not used
+ * @return null always.
+ */
+ public String mapPath(String path) {
+ return null;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/WLJspc.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/WLJspc.java
new file mode 100644
index 00000000..45a427ad
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/WLJspc.java
@@ -0,0 +1,334 @@
+/*
+ * 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.tools.ant.taskdefs.optional.jsp;
+
+//apache/ant imports
+import java.io.File;
+import java.util.Date;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Precompiles JSP's using WebLogic's JSP compiler (weblogic.jspc).
+ *
+ * Tested only on Weblogic 4.5.1 - NT4.0 and Solaris 5.7
+ *
+ * required attributes
+ * src : root of source tree for JSP, ie, the document root for your weblogic server
+ * dest : root of destination directory, what you have set as
+ * WorkingDir in the weblogic properties
+ * package : start package name under which your JSP's would be compiled
+ *
+ * other attributes
+ * classpath
+ *
+ * A classpath should be set which contains the weblogic classes as well as all
+ * application classes referenced by the JSP. The system classpath is also
+ * appended when the jspc is called, so you may choose to put everything in
+ * the classpath while calling Ant. However, since presumably the JSP's will
+ * reference classes being build by Ant, it would be better to explicitly add
+ * the classpath in the task
+ *
+ * The task checks timestamps on the JSP's and the generated classes, and compiles
+ * only those files that have changed.
+ *
+ * It follows the weblogic naming convention of putting classes in
+ * <b> _dirName/_fileName.class for dirname/fileName.jsp </b>
+ *
+ * Limitation: It compiles the files thru the Classic compiler only.
+ * Limitation: Since it is my experience that weblogic jspc throws out of
+ * memory error on being given too many files at one go, it is
+ * called multiple times with one jsp file each.
+ *
+ * <pre>
+ * example
+ * &lt;target name="jspcompile" depends="compile"&gt;
+ * &lt;wljspc src="c:\\weblogic\\myserver\\public_html"
+ * dest="c:\\weblogic\\myserver\\serverclasses" package="myapp.jsp"&gt;
+ * &lt;classpath&gt;
+ * &lt;pathelement location="${weblogic.classpath}" /&gt;
+ * &lt;pathelement path="${compile.dest}" /&gt;
+ * &lt;/classpath&gt;
+ *
+ * &lt;/wljspc&gt;
+ * &lt;/target&gt;
+ * </pre>
+ *
+ */
+
+public class WLJspc extends MatchingTask {
+ //TODO Test on other versions of weblogic
+ //TODO add more attributes to the task, to take care of all jspc options
+ //TODO Test on Unix
+
+ /** root of compiled files tree */
+ private File destinationDirectory;
+
+ /** root of source files tree */
+ private File sourceDirectory;
+
+ /** package under which resultant classes will reside */
+ private String destinationPackage;
+
+ /** classpath used to compile the jsp files. */
+ private Path compileClasspath;
+
+ //private String compilerPath; //fully qualified name for the compiler executable
+
+ private String pathToPackage = "";
+ private Vector filesToDo = new Vector();
+
+ /**
+ * Run the task.
+ * @throws BuildException if there is an error.
+ */
+ public void execute() throws BuildException {
+ if (!destinationDirectory.isDirectory()) {
+ throw new BuildException("destination directory "
+ + destinationDirectory.getPath() + " is not valid");
+ }
+
+ if (!sourceDirectory.isDirectory()) {
+ throw new BuildException("src directory "
+ + sourceDirectory.getPath() + " is not valid");
+ }
+
+ if (destinationPackage == null) {
+ throw new BuildException("package attribute must be present.",
+ getLocation());
+ }
+
+
+ pathToPackage
+ = this.destinationPackage.replace('.', File.separatorChar);
+ // get all the files in the sourceDirectory
+ DirectoryScanner ds = super.getDirectoryScanner(sourceDirectory);
+
+ //use the systemclasspath as well, to include the ant jar
+ if (compileClasspath == null) {
+ compileClasspath = new Path(getProject());
+ }
+
+ compileClasspath = compileClasspath.concatSystemClasspath();
+ String[] files = ds.getIncludedFiles();
+
+ //Weblogic.jspc calls System.exit() ... have to fork
+ // Therefore, takes loads of time
+ // Can pass directories at a time (*.jsp) but easily runs out of
+ // memory on hefty dirs (even on a Sun)
+ Java helperTask = new Java(this);
+ helperTask.setFork(true);
+ helperTask.setClassname("weblogic.jspc");
+ helperTask.setTaskName(getTaskName());
+ // CheckStyle:MagicNumber OFF
+ String[] args = new String[12];
+ // CheckStyle:MagicNumber ON
+
+ File jspFile = null;
+ String parents = "";
+ int j = 0;
+ //TODO this array stuff is a remnant of prev trials.. gotta remove.
+ args[j++] = "-d";
+ args[j++] = destinationDirectory.getAbsolutePath().trim();
+ args[j++] = "-docroot";
+ args[j++] = sourceDirectory.getAbsolutePath().trim();
+ args[j++] = "-keepgenerated";
+ //Call compiler as class... dont want to fork again
+ //Use classic compiler -- can be parameterised?
+ args[j++] = "-compilerclass";
+ args[j++] = "sun.tools.javac.Main";
+ //Weblogic jspc does not seem to work unless u explicitly set this...
+ // Does not take the classpath from the env....
+ // Am i missing something about the Java task??
+ args[j++] = "-classpath";
+ args[j++] = compileClasspath.toString();
+
+ this.scanDir(files);
+ log("Compiling " + filesToDo.size() + " JSP files");
+
+ final int size = filesToDo.size();
+ for (int i = 0; i < size; i++) {
+ //TODO
+ // All this to get package according to weblogic standards
+ // Can be written better... this is too hacky!
+ // Careful.. similar code in scanDir , but slightly different!!
+ String filename = (String) filesToDo.elementAt(i);
+ jspFile = new File(filename);
+ args[j] = "-package";
+ parents = jspFile.getParent();
+ if ((parents != null) && (!("").equals(parents))) {
+ parents = this.replaceString(parents, File.separator, "_.");
+ args[j + 1] = destinationPackage + "." + "_" + parents;
+ } else {
+ args[j + 1] = destinationPackage;
+ }
+
+
+ args[j + 2] = sourceDirectory + File.separator + filename;
+ helperTask.clearArgs();
+
+ // CheckStyle:MagicNumber OFF
+ for (int x = 0; x < j + 3; x++) {
+ helperTask.createArg().setValue(args[x]);
+ }
+ // CheckStyle:MagicNumber ON
+
+ helperTask.setClasspath(compileClasspath);
+ if (helperTask.executeJava() != 0) {
+ log(filename + " failed to compile", Project.MSG_WARN);
+ }
+ }
+ }
+
+
+
+ /**
+ * Set the classpath to be used for this compilation.
+ * @param classpath the classpath to use.
+ */
+ public void setClasspath(Path classpath) {
+ if (compileClasspath == null) {
+ compileClasspath = classpath;
+ } else {
+ compileClasspath.append(classpath);
+ }
+ }
+
+ /**
+ * Maybe creates a nested classpath element.
+ * @return a path to be configured.
+ */
+ public Path createClasspath() {
+ if (compileClasspath == null) {
+ compileClasspath = new Path(getProject());
+ }
+ return compileClasspath;
+ }
+
+ /**
+ * Set the directory containing the source jsp's
+ *
+ *
+ * @param dirName the directory containg the source jsp's
+ */
+ public void setSrc(File dirName) {
+
+ sourceDirectory = dirName;
+ }
+
+ /**
+ * Set the directory containing the source jsp's
+ *
+ *
+ * @param dirName the directory containg the source jsp's
+ */
+ public void setDest(File dirName) {
+
+ destinationDirectory = dirName;
+ }
+
+ /**
+ * Set the package under which the compiled classes go
+ *
+ * @param packageName the package name for the classes
+ */
+ public void setPackage(String packageName) {
+
+ destinationPackage = packageName;
+ }
+
+ /**
+ * Scan the array of files and add the jsp
+ * files that need to be compiled to the filesToDo field.
+ * @param files the files to scan.
+ */
+ protected void scanDir(String[] files) {
+
+ long now = (new Date()).getTime();
+ File jspFile = null;
+ String parents = null;
+ String pack = "";
+ for (int i = 0; i < files.length; i++) {
+ File srcFile = new File(this.sourceDirectory, files[i]);
+ //TODO
+ // All this to convert source to destination directory according
+ // to weblogic standards Can be written better... this is too hacky!
+ jspFile = new File(files[i]);
+ parents = jspFile.getParent();
+
+ if ((parents != null) && (!("").equals(parents))) {
+ parents = this.replaceString(parents, File.separator, "_/");
+ pack = pathToPackage + File.separator + "_" + parents;
+ } else {
+ pack = pathToPackage;
+ }
+
+ String filePath = pack + File.separator + "_";
+ int startingIndex = files[i].lastIndexOf(File.separator) != -1
+ ? files[i].lastIndexOf(File.separator) + 1 : 0;
+ int endingIndex = files[i].indexOf(".jsp");
+ if (endingIndex == -1) {
+ log("Skipping " + files[i] + ". Not a JSP",
+ Project.MSG_VERBOSE);
+ continue;
+ }
+
+ filePath += files[i].substring(startingIndex, endingIndex);
+ filePath += ".class";
+ File classFile = new File(this.destinationDirectory, filePath);
+
+ if (srcFile.lastModified() > now) {
+ log("Warning: file modified in the future: "
+ + files[i], Project.MSG_WARN);
+ }
+ if (srcFile.lastModified() > classFile.lastModified()) {
+ filesToDo.addElement(files[i]);
+ log("Recompiling File " + files[i], Project.MSG_VERBOSE);
+ }
+ }
+ }
+
+
+ /**
+ * Replace occurrences of a string with a replacement string.
+ * @param inpString the string to convert.
+ * @param escapeChars the string to replace.
+ * @param replaceChars the string to place.
+ * @return the converted string.
+ */
+ protected String replaceString(String inpString, String escapeChars,
+ String replaceChars) {
+ String localString = "";
+ int numTokens = 0;
+ StringTokenizer st = new StringTokenizer(inpString, escapeChars, true);
+ numTokens = st.countTokens();
+ for (int i = 0; i < numTokens; i++) {
+ String test = st.nextToken();
+ test = (test.equals(escapeChars) ? replaceChars : test);
+ localString += test;
+ }
+ return localString;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/DefaultJspCompilerAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/DefaultJspCompilerAdapter.java
new file mode 100644
index 00000000..5c4d0e3b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/DefaultJspCompilerAdapter.java
@@ -0,0 +1,153 @@
+/*
+ * 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.tools.ant.taskdefs.optional.jsp.compilers;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspC;
+import org.apache.tools.ant.types.CommandlineJava;
+
+/**
+ * This is the default implementation for the JspCompilerAdapter interface.
+ * This is currently very light on the ground since only one compiler type is
+ * supported.
+ *
+ */
+public abstract class DefaultJspCompilerAdapter
+ implements JspCompilerAdapter {
+
+ private static String lSep = System.getProperty("line.separator");
+
+ /**
+ * Logs the compilation parameters, adds the files to compile and logs the
+ * &quot;niceSourceList&quot;
+ * @param jspc the compiler task for logging
+ * @param compileList the list of files to compile
+ * @param cmd the command line used
+ */
+ protected void logAndAddFilesToCompile(JspC jspc,
+ Vector compileList,
+ CommandlineJava cmd) {
+ jspc.log("Compilation " + cmd.describeJavaCommand(),
+ Project.MSG_VERBOSE);
+
+ StringBuffer niceSourceList = new StringBuffer("File");
+ if (compileList.size() != 1) {
+ niceSourceList.append("s");
+ }
+ niceSourceList.append(" to be compiled:");
+
+ niceSourceList.append(lSep);
+
+ Enumeration e = compileList.elements();
+ while (e.hasMoreElements()) {
+ String arg = (String) e.nextElement();
+ cmd.createArgument().setValue(arg);
+ niceSourceList.append(" ");
+ niceSourceList.append(arg);
+ niceSourceList.append(lSep);
+ }
+
+ jspc.log(niceSourceList.toString(), Project.MSG_VERBOSE);
+ }
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /**
+ * our owner
+ */
+ protected JspC owner;
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * set the owner
+ * @param owner the owner JspC compiler
+ */
+ public void setJspc(JspC owner) {
+ this.owner = owner;
+ }
+
+ /** get the owner
+ * @return the owner; should never be null
+ */
+ public JspC getJspc() {
+ return owner;
+ }
+
+
+ /**
+ * add an argument oneple to the argument list, if the value aint null
+ * @param cmd the command line
+ * @param argument The argument
+ */
+ protected void addArg(CommandlineJava cmd, String argument) {
+ if (argument != null && argument.length() != 0) {
+ cmd.createArgument().setValue(argument);
+ }
+ }
+
+
+ /**
+ * add an argument tuple to the argument list, if the value aint null
+ * @param cmd the command line
+ * @param argument The argument
+ * @param value the parameter
+ */
+ protected void addArg(CommandlineJava cmd, String argument, String value) {
+ if (value != null) {
+ cmd.createArgument().setValue(argument);
+ cmd.createArgument().setValue(value);
+ }
+ }
+
+ /**
+ * add an argument tuple to the arg list, if the file parameter aint null
+ * @param cmd the command line
+ * @param argument The argument
+ * @param file the parameter
+ */
+ protected void addArg(CommandlineJava cmd, String argument, File file) {
+ if (file != null) {
+ cmd.createArgument().setValue(argument);
+ cmd.createArgument().setFile(file);
+ }
+ }
+
+ /**
+ * ask if compiler can sort out its own dependencies
+ * @return true if the compiler wants to do its own
+ * depends
+ */
+ public boolean implementsOwnDependencyChecking() {
+ return false;
+ }
+
+ /**
+ * get our project
+ * @return owner project data
+ */
+ public Project getProject() {
+ return getJspc().getProject();
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JasperC.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JasperC.java
new file mode 100644
index 00000000..f0becacb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JasperC.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.tools.ant.taskdefs.optional.jsp.compilers;
+
+import java.io.File;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspC;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspMangler;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * The implementation of the jasper compiler.
+ * This is a cut-and-paste of the original Jspc task.
+ *
+ * @since ant1.5
+ */
+public class JasperC extends DefaultJspCompilerAdapter {
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /**
+ * what produces java classes from .jsp files
+ */
+ JspMangler mangler;
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Constructor for JasperC.
+ * @param mangler a filename converter
+ */
+ public JasperC(JspMangler mangler) {
+ this.mangler = mangler;
+ }
+
+ /**
+ * Our execute method.
+ * @return true if successful
+ * @throws BuildException on error
+ */
+ public boolean execute()
+ throws BuildException {
+ getJspc().log("Using jasper compiler", Project.MSG_VERBOSE);
+ CommandlineJava cmd = setupJasperCommand();
+
+ try {
+ // Create an instance of the compiler, redirecting output to
+ // the project log
+ Java java = new Java(owner);
+ Path p = getClasspath();
+ if (getJspc().getClasspath() != null) {
+ getProject().log("using user supplied classpath: " + p,
+ Project.MSG_DEBUG);
+ } else {
+ getProject().log("using system classpath: " + p,
+ Project.MSG_DEBUG);
+ }
+ java.setClasspath(p);
+ java.setDir(getProject().getBaseDir());
+ java.setClassname("org.apache.jasper.JspC");
+ //this is really irritating; we need a way to set stuff
+ String []args = cmd.getJavaCommand().getArguments();
+ for (int i = 0; i < args.length; i++) {
+ java.createArg().setValue(args[i]);
+ }
+ java.setFailonerror(getJspc().getFailonerror());
+ //we are forking here to be sure that if JspC calls
+ //System.exit() it doesn't halt the build
+ java.setFork(true);
+ java.setTaskName("jasperc");
+ java.execute();
+ return true;
+ } catch (Exception ex) {
+ if (ex instanceof BuildException) {
+ throw (BuildException) ex;
+ } else {
+ throw new BuildException("Error running jsp compiler: ",
+ ex, getJspc().getLocation());
+ }
+ } finally {
+ getJspc().deleteEmptyJavaFiles();
+ }
+ }
+
+
+
+ /**
+ * build up a command line
+ * @return a command line for jasper
+ */
+ private CommandlineJava setupJasperCommand() {
+ CommandlineJava cmd = new CommandlineJava();
+ JspC jspc = getJspc();
+ addArg(cmd, "-d", jspc.getDestdir());
+ addArg(cmd, "-p", jspc.getPackage());
+
+ if (!isTomcat5x()) {
+ addArg(cmd, "-v" + jspc.getVerbose());
+ } else {
+ getProject().log("this task doesn't support Tomcat 5.x properly, "
+ + "please use the Tomcat provided jspc task "
+ + "instead");
+ }
+
+ addArg(cmd, "-uriroot", jspc.getUriroot());
+ addArg(cmd, "-uribase", jspc.getUribase());
+ addArg(cmd, "-ieplugin", jspc.getIeplugin());
+ addArg(cmd, "-webinc", jspc.getWebinc());
+ addArg(cmd, "-webxml", jspc.getWebxml());
+ addArg(cmd, "-die9");
+
+ if (jspc.isMapped()) {
+ addArg(cmd, "-mapped");
+ }
+ if (jspc.getWebApp() != null) {
+ File dir = jspc.getWebApp().getDirectory();
+ addArg(cmd, "-webapp", dir);
+ }
+ logAndAddFilesToCompile(getJspc(), getJspc().getCompileList(), cmd);
+ return cmd;
+ }
+
+ /**
+ * @return an instance of the mangler this compiler uses
+ */
+
+ public JspMangler createMangler() {
+ return mangler;
+ }
+
+ /**
+ * @since Ant 1.6.2
+ */
+ private Path getClasspath() {
+ Path p = getJspc().getClasspath();
+ if (p == null) {
+ p = new Path(getProject());
+ return p.concatSystemClasspath("only");
+ } else {
+ return p.concatSystemClasspath("ignore");
+ }
+ }
+
+ /**
+ * @since Ant 1.6.2
+ */
+ private boolean isTomcat5x() {
+ AntClassLoader l = null;
+ try {
+ l = getProject().createClassLoader(getClasspath());
+ l.loadClass("org.apache.jasper.tagplugins.jstl.If");
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ } finally {
+ if (l != null) {
+ l.cleanup();
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapter.java
new file mode 100644
index 00000000..16b67f9d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapter.java
@@ -0,0 +1,64 @@
+/*
+ * 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.tools.ant.taskdefs.optional.jsp.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspC;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspMangler;
+
+/**
+ * The interface that all jsp compiler adapters must adher to.
+ *
+ * <p>A compiler adapter is an adapter that interprets the jspc's
+ * parameters in preparation to be passed off to the compiler this
+ * adapter represents. As all the necessary values are stored in the
+ * Jspc task itself, the only thing all adapters need is the jsp
+ * task, the execute command and a parameterless constructor (for
+ * reflection).</p>
+ *
+ */
+
+public interface JspCompilerAdapter {
+
+ /**
+ * Sets the compiler attributes, which are stored in the Jspc task.
+ * @param attributes the jsp compiler attributes
+ */
+ void setJspc(JspC attributes);
+
+ /**
+ * Executes the task.
+ *
+ * @return has the compilation been successful
+ * @throws BuildException on error
+ */
+ boolean execute() throws BuildException;
+
+ /**
+ * @return an instance of the mangler this compiler uses
+ */
+
+ JspMangler createMangler();
+
+ /**
+ * ask if compiler can sort out its own dependencies
+ * @return true if the compiler wants to do its own
+ * depends
+ */
+ boolean implementsOwnDependencyChecking();
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapterFactory.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapterFactory.java
new file mode 100644
index 00000000..2876ba0c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapterFactory.java
@@ -0,0 +1,122 @@
+/*
+ * 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.tools.ant.taskdefs.optional.jsp.compilers;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.optional.jsp.Jasper41Mangler;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspNameMangler;
+
+
+/**
+ * Creates the necessary compiler adapter, given basic criteria.
+ *
+ */
+public final class JspCompilerAdapterFactory {
+
+ /** This is a singleton -- can't create instances!! */
+ private JspCompilerAdapterFactory() {
+ }
+
+ /**
+ * Based on the parameter passed in, this method creates the necessary
+ * factory desired.
+ *
+ * The current mapping for compiler names are as follows:
+ * <ul><li>jasper = jasper compiler (the default)
+ * <li><i>a fully qualified classname</i> = the name of a jsp compiler
+ * adapter
+ * </ul>
+ *
+ * @param compilerType either the name of the desired compiler, or the
+ * full classname of the compiler's adapter.
+ * @param task a task to log through.
+ * @return the compiler
+ * @throws BuildException if the compiler type could not be resolved into
+ * a compiler adapter.
+ */
+ public static JspCompilerAdapter getCompiler(String compilerType, Task task)
+ throws BuildException {
+ return getCompiler(compilerType, task,
+ // Memory-Leak in line below
+ task.getProject().createClassLoader(null));
+ }
+
+ /**
+ * Based on the parameter passed in, this method creates the necessary
+ * factory desired.
+ *
+ * The current mapping for compiler names are as follows:
+ * <ul><li>jasper = jasper compiler (the default)
+ * <li><i>a fully qualified classname</i> = the name of a jsp compiler
+ * adapter
+ * </ul>
+ *
+ * @param compilerType either the name of the desired compiler, or the
+ * full classname of the compiler's adapter.
+ * @param task a task to log through.
+ * @param loader AntClassLoader with which the compiler should be loaded
+ * @return the compiler
+ * @throws BuildException if the compiler type could not be resolved into
+ * a compiler adapter.
+ */
+ public static JspCompilerAdapter getCompiler(String compilerType, Task task,
+ AntClassLoader loader)
+ throws BuildException {
+
+ if (compilerType.equalsIgnoreCase("jasper")) {
+ //tomcat4.0 gets the old mangler
+ return new JasperC(new JspNameMangler());
+ }
+ if (compilerType.equalsIgnoreCase("jasper41")) {
+ //tomcat4.1 gets the new one
+ return new JasperC(new Jasper41Mangler());
+ }
+ return resolveClassName(compilerType, loader);
+ }
+
+ /**
+ * Tries to resolve the given classname into a compiler adapter.
+ * Throws a fit if it can't.
+ *
+ * @param className The fully qualified classname to be created.
+ * @param classloader Classloader with which to load the class
+ * @throws BuildException This is the fit that is thrown if className
+ * isn't an instance of JspCompilerAdapter.
+ */
+ private static JspCompilerAdapter resolveClassName(String className,
+ AntClassLoader classloader)
+ throws BuildException {
+ try {
+ Class c = classloader.findClass(className);
+ Object o = c.newInstance();
+ return (JspCompilerAdapter) o;
+ } catch (ClassNotFoundException cnfe) {
+ throw new BuildException(className + " can\'t be found.", cnfe);
+ } catch (ClassCastException cce) {
+ throw new BuildException(className + " isn\'t the classname of "
+ + "a compiler adapter.", cce);
+ } catch (Throwable t) {
+ // for all other possibilities
+ throw new BuildException(className + " caused an interesting "
+ + "exception.", t);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java
new file mode 100644
index 00000000..ec3506d4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java
@@ -0,0 +1,346 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Delete;
+import org.apache.tools.ant.taskdefs.TempFile;
+import org.apache.tools.ant.taskdefs.XSLTProcess;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.URLResource;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.w3c.dom.Document;
+
+/**
+ * Transform a JUnit xml report.
+ * The default transformation generates an html report in either framed or non-framed
+ * style. The non-framed style is convenient to have a concise report via mail, the
+ * framed report is much more convenient if you want to browse into different
+ * packages or testcases since it is a Javadoc like report.
+ *
+ */
+public class AggregateTransformer {
+ /**
+ * name of the frames format.
+ */
+ public static final String FRAMES = "frames";
+
+ /**
+ * name of the no frames format.
+ */
+ public static final String NOFRAMES = "noframes";
+
+ /**
+ * defines acceptable formats.
+ */
+ public static class Format extends EnumeratedAttribute {
+ /**
+ * list authorized values.
+ * @return authorized values.
+ */
+ public String[] getValues() {
+ return new String[]{FRAMES, NOFRAMES};
+ }
+ }
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ /** Task */
+ protected Task task;
+
+ /** the xml document to process */
+ protected Document document;
+
+ /** the style directory. XSLs should be read from here if necessary */
+ protected File styleDir;
+
+ /** the destination directory, this is the root from where html should be generated */
+ protected File toDir;
+
+ /**
+ * The internal XSLT task used to perform the transformation.
+ *
+ * @since Ant 1.9.5
+ */
+ private XSLTProcess xsltTask;
+
+ /**
+ * Instance of a utility class to use for file operations.
+ *
+ * @since Ant 1.7
+ */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Used to ensure the uniqueness of a property
+ */
+ private static int counter = 0;
+
+ /** the format to use for the report. Must be <tt>FRAMES</tt> or <tt>NOFRAMES</tt> */
+ protected String format = FRAMES;
+
+ /** XML Parser factory */
+ private static DocumentBuilderFactory privateDBFactory;
+
+ /** XML Parser factory accessible to subclasses */
+ protected static DocumentBuilderFactory dbfactory;
+
+ static {
+ privateDBFactory = DocumentBuilderFactory.newInstance();
+ dbfactory = privateDBFactory;
+ }
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * constructor creating the transformer from the junitreport task.
+ * @param task task delegating to this class
+ */
+ public AggregateTransformer(Task task) {
+ this.task = task;
+ xsltTask = new XSLTProcess();
+ xsltTask.bindToOwner(task);
+ }
+
+ /**
+ * Get the Document Builder Factory
+ *
+ * @return the DocumentBuilderFactory instance in use
+ */
+ protected static DocumentBuilderFactory getDocumentBuilderFactory() {
+ return privateDBFactory;
+ }
+
+ /**
+ * sets the format.
+ * @param format Must be <tt>FRAMES</tt> or <tt>NOFRAMES</tt>
+ */
+ public void setFormat(Format format) {
+ this.format = format.getValue();
+ }
+
+ /**
+ * sets the input document.
+ * @param doc input dom tree
+ */
+ public void setXmlDocument(Document doc) {
+ this.document = doc;
+ }
+
+ /**
+ * Set the xml file to be processed. This is a helper if you want
+ * to set the file directly. Much more for testing purposes.
+ * @param xmlfile xml file to be processed
+ * @throws BuildException if the document cannot be parsed.
+ */
+ protected void setXmlfile(File xmlfile) throws BuildException {
+ try {
+ DocumentBuilder builder = privateDBFactory.newDocumentBuilder();
+ InputStream in = new FileInputStream(xmlfile);
+ try {
+ Document doc = builder.parse(in);
+ setXmlDocument(doc);
+ } finally {
+ in.close();
+ }
+ } catch (Exception e) {
+ throw new BuildException("Error while parsing document: " + xmlfile, e);
+ }
+ }
+
+ /**
+ * set the style directory. It is optional and will override the
+ * default xsl used.
+ * @param styledir the directory containing the xsl files if the user
+ * would like to override with its own style.
+ */
+ public void setStyledir(File styledir) {
+ this.styleDir = styledir;
+ }
+
+ /** set the destination directory.
+ * @param todir the destination directory
+ */
+ public void setTodir(File todir) {
+ this.toDir = todir;
+ }
+
+ /** set the extension of the output files
+ * @param ext extension.
+ */
+ public void setExtension(String ext) {
+ task.log("extension is not used anymore", Project.MSG_WARN);
+ }
+
+ /**
+ * Create an instance of an XSL parameter for configuration by Ant.
+ *
+ * @return an instance of the Param class to be configured.
+ * @since Ant 1.7
+ */
+ public XSLTProcess.Param createParam() {
+ return xsltTask.createParam();
+ }
+
+ /**
+ * Creates a classpath to be used for the internal XSLT task.
+ *
+ * @return the classpath to be configured
+ * @since Ant 1.9.5
+ */
+ public Path createClasspath() {
+ return xsltTask.createClasspath();
+ }
+
+ /**
+ * Creates a factory configuration to be used for the internal XSLT task.
+ *
+ * @return the factory description to be configured
+ * @since Ant 1.9.5
+ */
+ public XSLTProcess.Factory createFactory() {
+ return xsltTask.createFactory();
+ }
+
+ /**
+ * transformation
+ * @throws BuildException exception if something goes wrong with the transformation.
+ */
+ public void transform() throws BuildException {
+ checkOptions();
+ Project project = task.getProject();
+
+ TempFile tempFileTask = new TempFile();
+ tempFileTask.bindToOwner(task);
+
+ xsltTask.setXslResource(getStylesheet());
+
+ // acrobatic cast.
+ xsltTask.setIn(((XMLResultAggregator) task).getDestinationFile());
+ File outputFile = null;
+ if (format.equals(FRAMES)) {
+ String tempFileProperty = getClass().getName() + String.valueOf(counter++);
+ File tmp = FILE_UTILS.resolveFile(project.getBaseDir(), project
+ .getProperty("java.io.tmpdir"));
+ tempFileTask.setDestDir(tmp);
+ tempFileTask.setProperty(tempFileProperty);
+ tempFileTask.execute();
+ outputFile = new File(project.getProperty(tempFileProperty));
+ } else {
+ outputFile = new File(toDir, "junit-noframes.html");
+ }
+ xsltTask.setOut(outputFile);
+ XSLTProcess.Param paramx = xsltTask.createParam();
+ paramx.setProject(task.getProject());
+ paramx.setName("output.dir");
+ paramx.setExpression(toDir.getAbsolutePath());
+ final long t0 = System.currentTimeMillis();
+ try {
+ xsltTask.execute();
+ } catch (Exception e) {
+ throw new BuildException("Errors while applying transformations: " + e.getMessage(), e);
+ }
+ final long dt = System.currentTimeMillis() - t0;
+ task.log("Transform time: " + dt + "ms");
+ if (format.equals(FRAMES)) {
+ Delete delete = new Delete();
+ delete.bindToOwner(task);
+ delete.setFile(outputFile);
+ delete.execute();
+ }
+ }
+
+ /**
+ * access the stylesheet to be used as a resource.
+ * @return stylesheet as a resource
+ */
+ protected Resource getStylesheet() {
+ String xslname = "junit-frames.xsl";
+ if (NOFRAMES.equals(format)) {
+ xslname = "junit-noframes.xsl";
+ }
+ if (styleDir == null) {
+ // If style dir is not specified we have to retrieve
+ // the stylesheet from the classloader
+ URL stylesheetURL = getClass().getClassLoader().getResource(
+ "org/apache/tools/ant/taskdefs/optional/junit/xsl/" + xslname);
+ return new URLResource(stylesheetURL);
+ }
+ // If we are here, then the style dir is here and we
+ // should read the stylesheet from the filesystem
+ return new FileResource(new File(styleDir, xslname));
+ }
+
+ /** check for invalid options
+ * @throws BuildException if something goes wrong.
+ */
+ protected void checkOptions() throws BuildException {
+ // set the destination directory relative from the project if needed.
+ if (toDir == null) {
+ toDir = task.getProject().resolveFile(".");
+ } else if (!toDir.isAbsolute()) {
+ toDir = task.getProject().resolveFile(toDir.getPath());
+ }
+ }
+
+ /**
+ * Get the systemid of the appropriate stylesheet based on its
+ * name and styledir. If no styledir is defined it will load
+ * it as a java resource in the xsl child package, otherwise it
+ * will get it from the given directory.
+ * @return system ID of the stylesheet.
+ * @throws IOException thrown if the requested stylesheet does
+ * not exist.
+ */
+ protected String getStylesheetSystemId() throws IOException {
+ String xslname = "junit-frames.xsl";
+ if (NOFRAMES.equals(format)) {
+ xslname = "junit-noframes.xsl";
+ }
+ if (styleDir == null) {
+ URL url = getClass().getResource("xsl/" + xslname);
+ if (url == null) {
+ throw new FileNotFoundException("Could not find jar resource " + xslname);
+ }
+ return url.toExternalForm();
+ }
+ File file = new File(styleDir, xslname);
+ if (!file.exists()) {
+ throw new FileNotFoundException("Could not find file '" + file + "'");
+ }
+ return JAXPUtils.getSystemId(file);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java
new file mode 100644
index 00000000..55e7a5d5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java
@@ -0,0 +1,241 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.File;
+import java.util.Vector;
+
+/**
+ * Baseclass for BatchTest and JUnitTest.
+ *
+ */
+public abstract class BaseTest {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected boolean haltOnError = false;
+ protected boolean haltOnFail = false;
+ protected boolean filtertrace = true;
+ protected boolean fork = false;
+ protected String ifProperty = null;
+ protected String unlessProperty = null;
+ protected Vector formatters = new Vector();
+ /** destination directory */
+ protected File destDir = null;
+
+ protected String failureProperty;
+ protected String errorProperty;
+ // CheckStyle:VisibilityModifier ON
+
+ private Object ifCond, unlessCond;
+ private boolean skipNonTests;
+
+ /**
+ * Set the filtertrace attribute.
+ * @param value a <code>boolean</code> value.
+ */
+ public void setFiltertrace(boolean value) {
+ filtertrace = value;
+ }
+
+ /**
+ * Get the filtertrace attribute.
+ * @return the attribute.
+ */
+ public boolean getFiltertrace() {
+ return filtertrace;
+ }
+
+ /**
+ * Set the fork attribute.
+ * @param value a <code>boolean</code> value.
+ */
+ public void setFork(boolean value) {
+ fork = value;
+ }
+
+ /**
+ * Get the fork attribute.
+ * @return the attribute.
+ */
+ public boolean getFork() {
+ return fork;
+ }
+
+ /**
+ * Set the haltonerror attribute.
+ * @param value a <code>boolean</code> value.
+ */
+ public void setHaltonerror(boolean value) {
+ haltOnError = value;
+ }
+
+ /**
+ * Set the haltonfailure attribute.
+ * @param value a <code>boolean</code> value.
+ */
+ public void setHaltonfailure(boolean value) {
+ haltOnFail = value;
+ }
+
+ /**
+ * Get the haltonerror attribute.
+ * @return the attribute.
+ */
+ public boolean getHaltonerror() {
+ return haltOnError;
+ }
+
+ /**
+ * Get the haltonfailure attribute.
+ * @return the attribute.
+ */
+ public boolean getHaltonfailure() {
+ return haltOnFail;
+ }
+
+ /**
+ * Set the if attribute.
+ * If this expression evaluates to true or the name of a property
+ * which is present in project, the test will be run.
+ * @param ifCondition the expression to evaluate
+ * @since Ant 1.8.0
+ */
+ public void setIf(Object ifCondition) {
+ ifCond = ifCondition;
+ ifProperty = ifCondition != null ? String.valueOf(ifCondition) : null;
+ }
+
+ /**
+ * Set the if attribute.
+ * If this expression evaluates to true or the name of a property
+ * which is present in project, the test will be run.
+ * @param propertyName the expression to evaluate
+ */
+ public void setIf(String propertyName) {
+ setIf((Object) propertyName);
+ }
+
+ /**
+ * The if expression
+ * @since Ant 1.8.0
+ */
+ public Object getIfCondition() {
+ return ifCond;
+ }
+
+ /**
+ * Set the unless attribute. If this expression evaluates to
+ * false or the name of a property which is not present in
+ * project, the test will be run.
+ * @param unlessCondition the expression to evaluate
+ * @since Ant 1.8.0
+ */
+ public void setUnless(Object unlessCondition) {
+ unlessCond = unlessCondition;
+ unlessProperty = unlessCondition != null
+ ? String.valueOf(unlessCondition) : null;
+ }
+
+ /**
+ * Set the unless attribute. If this expression evaluates to
+ * false or the name of a property which is not present in
+ * project, the test will be run.
+ * @param propertyName the expression to evaluate
+ */
+ public void setUnless(String propertyName) {
+ setUnless((Object) propertyName);
+ }
+
+ /**
+ * The unless expression
+ * @since Ant 1.8.0
+ */
+ public Object getUnlessCondition() {
+ return unlessCond;
+ }
+
+ /**
+ * Allow a formatter nested element.
+ * @param elem a formatter nested element.
+ */
+ public void addFormatter(FormatterElement elem) {
+ formatters.addElement(elem);
+ }
+
+ /**
+ * Sets the destination directory.
+ * @param destDir the destination directory.
+ */
+ public void setTodir(File destDir) {
+ this.destDir = destDir;
+ }
+
+ /**
+ * Get the destination directory.
+ * @return the destination directory as an absolute path if it exists
+ * otherwise return <tt>null</tt>
+ */
+ public String getTodir() {
+ if (destDir != null) {
+ return destDir.getAbsolutePath();
+ }
+ return null;
+ }
+
+ /**
+ * Get the failure property name.
+ * @return the name of the property to set on failure.
+ */
+ public String getFailureProperty() {
+ return failureProperty;
+ }
+
+ /**
+ * Set the name of the failure property.
+ * @param failureProperty the name of the property to set if
+ * the test fails.
+ */
+ public void setFailureProperty(String failureProperty) {
+ this.failureProperty = failureProperty;
+ }
+
+ /**
+ * Get the failure property name.
+ * @return the name of the property to set on failure.
+ */
+ public String getErrorProperty() {
+ return errorProperty;
+ }
+
+ /**
+ * Set the name of the error property.
+ * @param errorProperty the name of the property to set if
+ * the test has an error.
+ */
+ public void setErrorProperty(String errorProperty) {
+ this.errorProperty = errorProperty;
+ }
+
+ public void setSkipNonTests(boolean skipNonTests) {
+ this.skipNonTests = skipNonTests;
+ }
+
+ public boolean isSkipNonTests() {
+ return skipNonTests;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java
new file mode 100644
index 00000000..f41b96f1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java
@@ -0,0 +1,202 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.Resources;
+
+/**
+ * <p> Create then run <code>JUnitTest</code>'s based on the list of files
+ * given by the fileset attribute.
+ *
+ * <p> Every <code>.java</code> or <code>.class</code> file in the fileset is
+ * assumed to be a testcase.
+ * A <code>JUnitTest</code> is created for each of these named classes with
+ * basic setup inherited from the parent <code>BatchTest</code>.
+ *
+ * @see JUnitTest
+ */
+public final class BatchTest extends BaseTest {
+
+ /** the reference to the project */
+ private Project project;
+
+ /** the list of filesets containing the testcase filename rules */
+ private Resources resources = new Resources();
+
+ /**
+ * create a new batchtest instance
+ * @param project the project it depends on.
+ */
+ public BatchTest(Project project) {
+ this.project = project;
+ resources.setCache(true);
+ }
+
+ /**
+ * Add a new fileset instance to this batchtest. Whatever the fileset is,
+ * only filename that are <tt>.java</tt> or <tt>.class</tt> will be
+ * considered as 'candidates'.
+ * @param fs the new fileset containing the rules to get the testcases.
+ */
+ public void addFileSet(FileSet fs) {
+ add(fs);
+
+ // this one is here because the changes to support ResourceCollections
+ // have broken Magic's JUnitTestTask.
+ //
+ // The task adds a FileSet to a BatchTest instance using the
+ // Java API and without telling the FileSet about its project
+ // instance. The original code would pass in project on the
+ // call to getDirectoryScanner - which is now hidden deep into
+ // Resources.iterator() and not reachable.
+ if (fs.getProject() == null) {
+ fs.setProject(project);
+ }
+ }
+
+
+ /**
+ * Add a new ResourceCollection instance to this
+ * batchtest. Whatever the collection is, only names that are
+ * <tt>.java</tt> or <tt>.class</tt> will be considered as
+ * 'candidates'.
+ * @param rc the new ResourceCollection containing the rules to
+ * get the testcases.
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection rc) {
+ resources.add(rc);
+ }
+
+ /**
+ * Return all <tt>JUnitTest</tt> instances obtain by applying the fileset rules.
+ * @return an enumeration of all elements of this batchtest that are
+ * a <tt>JUnitTest</tt> instance.
+ */
+ public Enumeration elements() {
+ JUnitTest[] tests = createAllJUnitTest();
+ return Enumerations.fromArray(tests);
+ }
+
+ /**
+ * Convenient method to merge the <tt>JUnitTest</tt>s of this batchtest
+ * to a <tt>Vector</tt>.
+ * @param v the vector to which should be added all individual tests of this
+ * batch test.
+ */
+ void addTestsTo(Vector v) {
+ JUnitTest[] tests = createAllJUnitTest();
+ v.ensureCapacity(v.size() + tests.length);
+ for (int i = 0; i < tests.length; i++) {
+ v.addElement(tests[i]);
+ }
+ }
+
+ /**
+ * Create all <tt>JUnitTest</tt>s based on the filesets. Each instance
+ * is configured to match this instance properties.
+ * @return the array of all <tt>JUnitTest</tt>s that belongs to this batch.
+ */
+ private JUnitTest[] createAllJUnitTest() {
+ String[] filenames = getFilenames();
+ JUnitTest[] tests = new JUnitTest[filenames.length];
+ for (int i = 0; i < tests.length; i++) {
+ String classname = javaToClass(filenames[i]);
+ tests[i] = createJUnitTest(classname);
+ }
+ return tests;
+ }
+
+ /**
+ * Iterate over all filesets and return the filename of all files
+ * that end with <tt>.java</tt> or <tt>.class</tt>. This is to avoid
+ * wrapping a <tt>JUnitTest</tt> over an xml file for example. A Testcase
+ * is obviously a java file (compiled or not).
+ * @return an array of filenames without their extension. As they should
+ * normally be taken from their root, filenames should match their fully
+ * qualified class name (If it is not the case it will fail when running the test).
+ * For the class <tt>org/apache/Whatever.class</tt> it will return <tt>org/apache/Whatever</tt>.
+ */
+ private String[] getFilenames() {
+ Vector v = new Vector();
+ for (Resource r : resources) {
+ if (r.isExists()) {
+ String pathname = r.getName();
+ if (pathname.endsWith(".java")) {
+ v.addElement(pathname.substring(0, pathname.length() - ".java".length()));
+ } else if (pathname.endsWith(".class")) {
+ v.addElement(pathname.substring(0, pathname.length() - ".class".length()));
+ }
+ }
+ }
+
+ String[] files = new String[v.size()];
+ v.copyInto(files);
+ return files;
+ }
+
+ /**
+ * Convenient method to convert a pathname without extension to a
+ * fully qualified classname. For example <tt>org/apache/Whatever</tt> will
+ * be converted to <tt>org.apache.Whatever</tt>
+ * @param filename the filename to "convert" to a classname.
+ * @return the classname matching the filename.
+ */
+ public static String javaToClass(String filename) {
+ return filename.replace(File.separatorChar, '.').replace('/', '.')
+ .replace('\\', '.');
+ }
+
+ /**
+ * Create a <tt>JUnitTest</tt> that has the same property as this
+ * <tt>BatchTest</tt> instance.
+ * @param classname the name of the class that should be run as a
+ * <tt>JUnitTest</tt>. It must be a fully qualified name.
+ * @return the <tt>JUnitTest</tt> over the given classname.
+ */
+ private JUnitTest createJUnitTest(String classname) {
+ JUnitTest test = new JUnitTest();
+ test.setName(classname);
+ test.setHaltonerror(this.haltOnError);
+ test.setHaltonfailure(this.haltOnFail);
+ test.setFiltertrace(this.filtertrace);
+ test.setFork(this.fork);
+ test.setIf(getIfCondition());
+ test.setUnless(getUnlessCondition());
+ test.setTodir(this.destDir);
+ test.setFailureProperty(failureProperty);
+ test.setErrorProperty(errorProperty);
+ test.setSkipNonTests(isSkipNonTests());
+ Enumeration list = this.formatters.elements();
+ while (list.hasMoreElements()) {
+ test.addFormatter((FormatterElement) list.nextElement());
+ }
+ return test;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java
new file mode 100644
index 00000000..46d6c616
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java
@@ -0,0 +1,300 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.text.NumberFormat;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Prints plain text output of the test to a specified Writer.
+ * Inspired by the PlainJUnitResultFormatter.
+ *
+ * @see FormatterElement
+ * @see PlainJUnitResultFormatter
+ */
+public class BriefJUnitResultFormatter implements JUnitResultFormatter, IgnoredTestListener {
+
+ private static final double ONE_SECOND = 1000.0;
+
+ /**
+ * Where to write the log to.
+ */
+ private OutputStream out;
+
+ /**
+ * Used for writing the results.
+ */
+ private BufferedWriter output;
+
+ /**
+ * Used as part of formatting the results.
+ */
+ private StringWriter results;
+
+ /**
+ * Used for writing formatted results to.
+ */
+ private BufferedWriter resultWriter;
+
+ /**
+ * Formatter for timings.
+ */
+ private NumberFormat numberFormat = NumberFormat.getInstance();
+
+ /**
+ * Output suite has written to System.out
+ */
+ private String systemOutput = null;
+
+ /**
+ * Output suite has written to System.err
+ */
+ private String systemError = null;
+
+ /**
+ * Constructor for BriefJUnitResultFormatter.
+ */
+ public BriefJUnitResultFormatter() {
+ results = new StringWriter();
+ resultWriter = new BufferedWriter(results);
+ }
+
+ /**
+ * Sets the stream the formatter is supposed to write its results to.
+ * @param out the output stream to write to
+ */
+ public void setOutput(OutputStream out) {
+ this.out = out;
+ output = new BufferedWriter(new java.io.OutputStreamWriter(out));
+ }
+
+ /**
+ * @see JUnitResultFormatter#setSystemOutput(String)
+ */
+ /** {@inheritDoc}. */
+ public void setSystemOutput(String out) {
+ systemOutput = out;
+ }
+
+ /**
+ * @see JUnitResultFormatter#setSystemError(String)
+ */
+ /** {@inheritDoc}. */
+ public void setSystemError(String err) {
+ systemError = err;
+ }
+
+
+ /**
+ * The whole testsuite started.
+ * @param suite the test suite
+ */
+ public void startTestSuite(JUnitTest suite) {
+ if (output == null) {
+ return; // Quick return - no output do nothing.
+ }
+ StringBuffer sb = new StringBuffer("Testsuite: ");
+ sb.append(suite.getName());
+ sb.append(StringUtils.LINE_SEP);
+ try {
+ output.write(sb.toString());
+ output.flush();
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+
+ /**
+ * The whole testsuite ended.
+ * @param suite the test suite
+ */
+ public void endTestSuite(JUnitTest suite) {
+ StringBuffer sb = new StringBuffer("Tests run: ");
+ sb.append(suite.runCount());
+ sb.append(", Failures: ");
+ sb.append(suite.failureCount());
+ sb.append(", Errors: ");
+ sb.append(suite.errorCount());
+ sb.append(", Skipped: ");
+ sb.append(suite.skipCount());
+ sb.append(", Time elapsed: ");
+ sb.append(numberFormat.format(suite.getRunTime() / ONE_SECOND));
+ sb.append(" sec");
+ sb.append(StringUtils.LINE_SEP);
+ sb.append(StringUtils.LINE_SEP);
+
+ // append the err and output streams to the log
+ if (systemOutput != null && systemOutput.length() > 0) {
+ sb.append("------------- Standard Output ---------------")
+ .append(StringUtils.LINE_SEP)
+ .append(systemOutput)
+ .append("------------- ---------------- ---------------")
+ .append(StringUtils.LINE_SEP);
+ }
+
+ if (systemError != null && systemError.length() > 0) {
+ sb.append("------------- Standard Error -----------------")
+ .append(StringUtils.LINE_SEP)
+ .append(systemError)
+ .append("------------- ---------------- ---------------")
+ .append(StringUtils.LINE_SEP);
+ }
+
+ if (output != null) {
+ try {
+ output.write(sb.toString());
+ resultWriter.close();
+ output.write(results.toString());
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ } finally {
+ try {
+ output.flush();
+ } catch (IOException ex) {
+ // swallow, there has likely been an exception before this
+ }
+ if (out != System.out && out != System.err) {
+ FileUtils.close(out);
+ }
+ }
+ }
+ }
+
+ /**
+ * A test started.
+ * @param test a test
+ */
+ public void startTest(Test test) {
+ }
+
+ /**
+ * A test ended.
+ * @param test a test
+ */
+ public void endTest(Test test) {
+ }
+
+ /**
+ * Interface TestListener for JUnit &lt;= 3.4.
+ *
+ * <p>A Test failed.
+ * @param test a test
+ * @param t the exception thrown by the test
+ */
+ public void addFailure(Test test, Throwable t) {
+ formatError("\tFAILED", test, t);
+ }
+
+ /**
+ * Interface TestListener for JUnit &gt; 3.4.
+ *
+ * <p>A Test failed.
+ * @param test a test
+ * @param t the assertion failed by the test
+ */
+ public void addFailure(Test test, AssertionFailedError t) {
+ addFailure(test, (Throwable) t);
+ }
+
+ /**
+ * A test caused an error.
+ * @param test a test
+ * @param error the error thrown by the test
+ */
+ public void addError(Test test, Throwable error) {
+ formatError("\tCaused an ERROR", test, error);
+ }
+
+ /**
+ * Format the test for printing..
+ * @param test a test
+ * @return the formatted testname
+ */
+ protected String formatTest(Test test) {
+ if (test == null) {
+ return "Null Test: ";
+ } else {
+ return "Testcase: " + test.toString() + ":";
+ }
+ }
+
+ /**
+ * Format an error and print it.
+ * @param type the type of error
+ * @param test the test that failed
+ * @param error the exception that the test threw
+ */
+ protected synchronized void formatError(String type, Test test,
+ Throwable error) {
+ if (test != null) {
+ endTest(test);
+ }
+
+ try {
+ resultWriter.write(formatTest(test) + type);
+ resultWriter.newLine();
+ resultWriter.write(String.valueOf(error.getMessage()));
+ resultWriter.newLine();
+ String strace = JUnitTestRunner.getFilteredTrace(error);
+ resultWriter.write(strace);
+ resultWriter.newLine();
+ resultWriter.newLine();
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+
+
+ public void testIgnored(Test test) {
+ formatSkip(test, JUnitVersionHelper.getIgnoreMessage(test));
+ }
+
+
+ public void formatSkip(Test test, String message) {
+ if (test != null) {
+ endTest(test);
+ }
+
+ try {
+ resultWriter.write(formatTest(test) + "SKIPPED");
+ if (message != null) {
+ resultWriter.write(": ");
+ resultWriter.write(message);
+ }
+ resultWriter.newLine();
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+
+ }
+
+ public void testAssumptionFailure(Test test, Throwable cause) {
+ formatSkip(test, cause.getMessage());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Constants.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Constants.java
new file mode 100644
index 00000000..368c72e2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Constants.java
@@ -0,0 +1,46 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+/**
+ * Constants, like filenames shared between various classes in this package.
+ */
+public class Constants {
+
+ private Constants() {
+ }
+
+ static final String METHOD_NAMES = "methods=";
+ static final String HALT_ON_ERROR = "haltOnError=";
+ static final String HALT_ON_FAILURE = "haltOnFailure=";
+ static final String FILTERTRACE = "filtertrace=";
+ static final String CRASHFILE = "crashfile=";
+ static final String BEFORE_FIRST_TEST = "BeforeFirstTest";
+ static final String PROPSFILE = "propsfile=";
+ static final String SHOWOUTPUT = "showoutput=";
+ static final String OUTPUT_TO_FORMATTERS = "outputtoformatters=";
+ static final String FORMATTER = "formatter=";
+ static final String LOGTESTLISTENEREVENTS = "logtestlistenerevents=";
+ static final String TESTSFILE = "testsfile=";
+ static final String TERMINATED_SUCCESSFULLY = "terminated successfully";
+ static final String LOG_FAILED_TESTS="logfailedtests=";
+ static final String SKIP_NON_TESTS = "skipNonTests=";
+ /** @since Ant 1.9.4 */
+ static final String THREADID="threadid=";
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/CustomJUnit4TestAdapterCache.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/CustomJUnit4TestAdapterCache.java
new file mode 100644
index 00000000..8ad40dd6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/CustomJUnit4TestAdapterCache.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.tools.ant.taskdefs.optional.junit;
+
+import junit.framework.JUnit4TestAdapter;
+import junit.framework.JUnit4TestAdapterCache;
+import junit.framework.TestResult;
+
+import org.junit.runner.Description;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+import org.junit.runner.notification.RunNotifier;
+
+/**
+ * Provides a custom implementation of the notifier for a JUnit4TestAdapter
+ * so that skipped and ignored tests can be reported to the existing
+ * <tt>TestListener</tt>s.
+ *
+ */
+public class CustomJUnit4TestAdapterCache extends JUnit4TestAdapterCache {
+
+ private static final CustomJUnit4TestAdapterCache INSTANCE = new CustomJUnit4TestAdapterCache();
+
+ public static CustomJUnit4TestAdapterCache getInstance() {
+ return INSTANCE;
+ }
+
+ private CustomJUnit4TestAdapterCache() {
+ super();
+ }
+
+ public RunNotifier getNotifier(final TestResult result, final JUnit4TestAdapter adapter) {
+ return getNotifier(result);
+ }
+
+ public RunNotifier getNotifier(final TestResult result) {
+
+ final IgnoredTestResult resultWrapper = (IgnoredTestResult) result;
+
+ RunNotifier notifier = new RunNotifier();
+ notifier.addListener(new RunListener() {
+ @Override
+ public void testFailure(Failure failure) throws Exception {
+ result.addError(asTest(failure.getDescription()), failure.getException());
+ }
+
+ @Override
+ public void testFinished(Description description) throws Exception {
+ result.endTest(asTest(description));
+ }
+
+ @Override
+ public void testStarted(Description description) throws Exception {
+ result.startTest(asTest(description));
+ }
+
+ @Override
+ public void testIgnored(Description description) throws Exception {
+ if (resultWrapper != null) {
+ resultWrapper.testIgnored(asTest(description));
+ }
+ }
+
+ @Override
+ public void testAssumptionFailure(Failure failure) {
+ if (resultWrapper != null) {
+ resultWrapper.testAssumptionFailure(asTest(failure.getDescription()), failure.getException());
+ }
+ }
+ });
+
+ return notifier;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java
new file mode 100644
index 00000000..325f44cf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java
@@ -0,0 +1,228 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.util.Vector;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Comment;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+
+/**
+ * Some utilities that might be useful when manipulating DOM trees.
+ *
+ */
+public final class DOMUtil {
+
+ /** unused constructor */
+ private DOMUtil() {
+ }
+
+ /**
+ * Filter interface to be applied when iterating over a DOM tree.
+ * Just think of it like a <tt>FileFilter</tt> clone.
+ */
+ public interface NodeFilter {
+ /**
+ * @param node the node to check for acceptance.
+ * @return <tt>true</tt> if the node is accepted by this filter,
+ * otherwise <tt>false</tt>
+ */
+ boolean accept(Node node);
+ }
+
+ /**
+ * list a set of node that match a specific filter. The list can be made
+ * recursively or not.
+ * @param parent the parent node to search from
+ * @param filter the filter that children should match.
+ * @param recurse <tt>true</tt> if you want the list to be made recursively
+ * otherwise <tt>false</tt>.
+ * @return the node list that matches the filter.
+ */
+ public static NodeList listChildNodes(Node parent, NodeFilter filter, boolean recurse) {
+ NodeListImpl matches = new NodeListImpl();
+ NodeList children = parent.getChildNodes();
+ if (children != null) {
+ final int len = children.getLength();
+ for (int i = 0; i < len; i++) {
+ Node child = children.item(i);
+ if (filter.accept(child)) {
+ matches.addElement(child);
+ }
+ if (recurse) {
+ NodeList recmatches = listChildNodes(child, filter, recurse);
+ final int reclength = recmatches.getLength();
+ for (int j = 0; j < reclength; j++) {
+ matches.addElement(recmatches.item(i));
+ }
+ }
+ }
+ }
+ return matches;
+ }
+
+ /** custom implementation of a nodelist */
+ public static class NodeListImpl extends Vector implements NodeList {
+ private static final long serialVersionUID = 3175749150080946423L;
+
+ /**
+ * Get the number of nodes in the list.
+ * @return the length of the list.
+ */
+ public int getLength() {
+ return size();
+ }
+ /**
+ * Get a particular node.
+ * @param i the index of the node to get.
+ * @return the node if the index is in bounds, null otherwise.
+ */
+ public Node item(int i) {
+ try {
+ return (Node) elementAt(i);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return null; // conforming to NodeList interface
+ }
+ }
+ }
+
+ /**
+ * return the attribute value of an element.
+ * @param node the node to get the attribute from.
+ * @param name the name of the attribute we are looking for the value.
+ * @return the value of the requested attribute or <tt>null</tt> if the
+ * attribute was not found or if <tt>node</tt> is not an <tt>Element</tt>.
+ */
+ public static String getNodeAttribute(Node node, String name) {
+ if (node instanceof Element) {
+ Element element = (Element) node;
+ return element.getAttribute(name);
+ }
+ return null;
+ }
+
+
+ /**
+ * Iterate over the children of a given node and return the first node
+ * that has a specific name.
+ * @param parent the node to search child from. Can be <tt>null</tt>.
+ * @param tagname the child name we are looking for. Cannot be <tt>null</tt>.
+ * @return the first child that matches the given name or <tt>null</tt> if
+ * the parent is <tt>null</tt> or if a child does not match the
+ * given name.
+ */
+ public static Element getChildByTagName (Node parent, String tagname) {
+ if (parent == null) {
+ return null;
+ }
+ NodeList childList = parent.getChildNodes();
+ final int len = childList.getLength();
+ for (int i = 0; i < len; i++) {
+ Node child = childList.item(i);
+ if (child != null && child.getNodeType() == Node.ELEMENT_NODE
+ && child.getNodeName().equals(tagname)) {
+ return (Element) child;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Simple tree walker that will clone recursively a node. This is to
+ * avoid using parser-specific API such as Sun's <tt>changeNodeOwner</tt>
+ * when we are dealing with DOM L1 implementations since <tt>cloneNode(boolean)</tt>
+ * will not change the owner document.
+ * <tt>changeNodeOwner</tt> is much faster and avoid the costly cloning process.
+ * <tt>importNode</tt> is in the DOM L2 interface.
+ * @param parent the node parent to which we should do the import to.
+ * @param child the node to clone recursively. Its clone will be
+ * appended to <tt>parent</tt>.
+ * @return the cloned node that is appended to <tt>parent</tt>
+ */
+ public static Node importNode(Node parent, Node child) {
+ Node copy = null;
+ final Document doc = parent.getOwnerDocument();
+
+ switch (child.getNodeType()) {
+ case Node.CDATA_SECTION_NODE:
+ copy = doc.createCDATASection(((CDATASection) child).getData());
+ break;
+ case Node.COMMENT_NODE:
+ copy = doc.createComment(((Comment) child).getData());
+ break;
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ copy = doc.createDocumentFragment();
+ break;
+ case Node.ELEMENT_NODE:
+ final Element elem = doc.createElement(((Element) child).getTagName());
+ copy = elem;
+ final NamedNodeMap attributes = child.getAttributes();
+ if (attributes != null) {
+ final int size = attributes.getLength();
+ for (int i = 0; i < size; i++) {
+ final Attr attr = (Attr) attributes.item(i);
+ elem.setAttribute(attr.getName(), attr.getValue());
+ }
+ }
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ copy = doc.createEntityReference(child.getNodeName());
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ final ProcessingInstruction pi = (ProcessingInstruction) child;
+ copy = doc.createProcessingInstruction(pi.getTarget(), pi.getData());
+ break;
+ case Node.TEXT_NODE:
+ copy = doc.createTextNode(((Text) child).getData());
+ break;
+ default:
+ // this should never happen
+ throw new IllegalStateException("Invalid node type: " + child.getNodeType());
+ }
+
+ // okay we have a copy of the child, now the child becomes the parent
+ // and we are iterating recursively over its children.
+ try {
+ final NodeList children = child.getChildNodes();
+ if (children != null) {
+ final int size = children.getLength();
+ for (int i = 0; i < size; i++) {
+ final Node newChild = children.item(i);
+ if (newChild != null) {
+ importNode(copy, newChild);
+ }
+ }
+ }
+ } catch (DOMException ignored) {
+ // Ignore
+ }
+
+ // bingo append it. (this should normally not be done here)
+ parent.appendChild(copy);
+ return copy;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java
new file mode 100644
index 00000000..327547ef
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java
@@ -0,0 +1,177 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/**
+ * A couple of methods related to enumerations that might be useful.
+ * This class should probably disappear once the required JDK is set to 1.2
+ * instead of 1.1.
+ *
+ */
+public final class Enumerations {
+
+ private Enumerations() {
+ }
+
+ /**
+ * creates an enumeration from an array of objects.
+ * @param array the array of object to enumerate.
+ * @return the enumeration over the array of objects.
+ */
+ public static Enumeration fromArray(Object[] array) {
+ return new ArrayEnumeration(array);
+ }
+
+ /**
+ * creates an enumeration from an array of enumeration. The created enumeration
+ * will sequentially enumerate over all elements of each enumeration and skip
+ * <tt>null</tt> enumeration elements in the array.
+ * @param enums the array of enumerations.
+ * @return the enumeration over the array of enumerations.
+ */
+ public static Enumeration fromCompound(Enumeration[] enums) {
+ return new CompoundEnumeration(enums);
+ }
+
+}
+
+
+/**
+ * Convenient enumeration over an array of objects.
+ */
+class ArrayEnumeration implements Enumeration {
+
+ /** object array */
+ private Object[] array;
+
+ /** current index */
+ private int pos;
+
+ /**
+ * Initialize a new enumeration that wraps an array.
+ * @param array the array of object to enumerate.
+ */
+ public ArrayEnumeration(Object[] array) {
+ this.array = array;
+ this.pos = 0;
+ }
+ /**
+ * Tests if this enumeration contains more elements.
+ *
+ * @return <code>true</code> if and only if this enumeration object
+ * contains at least one more element to provide;
+ * <code>false</code> otherwise.
+ */
+ public boolean hasMoreElements() {
+ return (pos < array.length);
+ }
+
+ /**
+ * Returns the next element of this enumeration if this enumeration
+ * object has at least one more element to provide.
+ *
+ * @return the next element of this enumeration.
+ * @throws NoSuchElementException if no more elements exist.
+ */
+ public Object nextElement() throws NoSuchElementException {
+ if (hasMoreElements()) {
+ Object o = array[pos];
+ pos++;
+ return o;
+ }
+ throw new NoSuchElementException();
+ }
+}
+/**
+ * Convenient enumeration over an array of enumeration. For example:
+ * <pre>
+ * Enumeration e1 = v1.elements();
+ * while (e1.hasMoreElements()) {
+ * // do something
+ * }
+ * Enumeration e2 = v2.elements();
+ * while (e2.hasMoreElements()) {
+ * // do the same thing
+ * }
+ * </pre>
+ * can be written as:
+ * <pre>
+ * Enumeration[] enums = { v1.elements(), v2.elements() };
+ * Enumeration e = Enumerations.fromCompound(enums);
+ * while (e.hasMoreElements()) {
+ * // do something
+ * }
+ * </pre>
+ * Note that the enumeration will skip null elements in the array. The following is
+ * thus possible:
+ * <pre>
+ * Enumeration[] enums = { v1.elements(), null, v2.elements() }; // a null enumeration in the array
+ * Enumeration e = Enumerations.fromCompound(enums);
+ * while (e.hasMoreElements()) {
+ * // do something
+ * }
+ * </pre>
+ */
+ class CompoundEnumeration implements Enumeration {
+
+ /** enumeration array */
+ private Enumeration[] enumArray;
+
+ /** index in the enums array */
+ private int index = 0;
+
+ public CompoundEnumeration(Enumeration[] enumarray) {
+ this.enumArray = enumarray;
+ }
+
+ /**
+ * Tests if this enumeration contains more elements.
+ *
+ * @return <code>true</code> if and only if this enumeration object
+ * contains at least one more element to provide;
+ * <code>false</code> otherwise.
+ */
+ public boolean hasMoreElements() {
+ while (index < enumArray.length) {
+ if (enumArray[index] != null && enumArray[index].hasMoreElements()) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the next element of this enumeration if this enumeration
+ * object has at least one more element to provide.
+ *
+ * @return the next element of this enumeration.
+ * @throws NoSuchElementException if no more elements exist.
+ */
+ public Object nextElement() throws NoSuchElementException {
+ if (hasMoreElements()) {
+ return enumArray[index].nextElement();
+ }
+ throw new NoSuchElementException();
+ }
+}
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FailureRecorder.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FailureRecorder.java
new file mode 100644
index 00000000..3046b75a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FailureRecorder.java
@@ -0,0 +1,448 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.Vector;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * <p>Collects all failing test <i>cases</i> and creates a new JUnit test class containing
+ * a suite() method which calls these failed tests.</p>
+ * <p>Having classes <i>A</i> ... <i>D</i> with each several testcases you could earn a new
+ * test class like
+ * <pre>
+ * // generated on: 2007.08.06 09:42:34,555
+ * import junit.framework.*;
+ * public class FailedTests extends TestCase {
+ * public FailedTests(String testname) {
+ * super(testname);
+ * }
+ * public static Test suite() {
+ * TestSuite suite = new TestSuite();
+ * suite.addTest( new B("test04") );
+ * suite.addTest( new org.D("test10") );
+ * return suite;
+ * }
+ * }
+ * </pre>
+ *
+ * Because each running test case gets its own formatter, we collect
+ * the failing test cases in a static list. Because we dont have a finalizer
+ * method in the formatters "lifecycle", we register this formatter as
+ * BuildListener and generate the new java source on taskFinished event.
+ *
+ * @since Ant 1.8.0
+ */
+public class FailureRecorder extends ProjectComponent implements JUnitResultFormatter, BuildListener {
+
+ /**
+ * This is the name of a magic System property ({@value}). The value of this
+ * <b>System</b> property should point to the location where to store the
+ * generated class (without suffix).
+ * Default location and name is defined in DEFAULT_CLASS_LOCATION.
+ * @see #DEFAULT_CLASS_LOCATION
+ */
+ public static final String MAGIC_PROPERTY_CLASS_LOCATION
+ = "ant.junit.failureCollector";
+
+ /** Default location and name for the generated JUnit class file,
+ * in the temp directory + FailedTests */
+ public static final String DEFAULT_CLASS_LOCATION
+ = System.getProperty("java.io.tmpdir") + "FailedTests";
+
+ /** Prefix for logging. {@value} */
+ private static final String LOG_PREFIX = " [junit]";
+
+ /** Class names of failed tests without duplicates. */
+ private static SortedSet/*<TestInfos>*/ failedTests = new TreeSet();
+
+ /** A writer for writing the generated source to. */
+ private BufferedWriter writer;
+
+ /**
+ * Location and name of the generated JUnit class.
+ * Lazy instantiated via getLocationName().
+ */
+ private static String locationName;
+
+ /**
+ * Returns the (lazy evaluated) location for the collector class.
+ * Order for evaluation: System property > Ant property > default value
+ * @return location for the collector class
+ * @see #MAGIC_PROPERTY_CLASS_LOCATION
+ * @see #DEFAULT_CLASS_LOCATION
+ */
+ private String getLocationName() {
+ if (locationName == null) {
+ String syspropValue = System.getProperty(MAGIC_PROPERTY_CLASS_LOCATION);
+ String antpropValue = getProject().getProperty(MAGIC_PROPERTY_CLASS_LOCATION);
+
+ if (syspropValue != null) {
+ locationName = syspropValue;
+ verbose("System property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' set, so use "
+ + "its value '" + syspropValue + "' as location for collector class.");
+ } else if (antpropValue != null) {
+ locationName = antpropValue;
+ verbose("Ant property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' set, so use "
+ + "its value '" + antpropValue + "' as location for collector class.");
+ } else {
+ locationName = DEFAULT_CLASS_LOCATION;
+ verbose("System property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' not set, so use "
+ + "value as location for collector class: '"
+ + DEFAULT_CLASS_LOCATION + "'");
+ }
+
+ File locationFile = new File(locationName);
+ if (!locationFile.isAbsolute()) {
+ File f = new File(getProject().getBaseDir(), locationName);
+ locationName = f.getAbsolutePath();
+ verbose("Location file is relative (" + locationFile + ")"
+ + " use absolute path instead (" + locationName + ")");
+ }
+ }
+
+ return locationName;
+ }
+
+ /**
+ * This method is called by the Ant runtime by reflection. We use the project reference for
+ * registration of this class as BuildListener.
+ *
+ * @param project
+ * project reference
+ */
+ public void setProject(Project project) {
+ // store project reference for logging
+ super.setProject(project);
+ // check if already registered
+ boolean alreadyRegistered = false;
+ Vector allListeners = project.getBuildListeners();
+ final int size = allListeners.size();
+ for (int i = 0; i < size; i++) {
+ Object listener = allListeners.get(i);
+ if (listener instanceof FailureRecorder) {
+ alreadyRegistered = true;
+ break;
+ }
+ }
+ // register if needed
+ if (!alreadyRegistered) {
+ verbose("Register FailureRecorder (@" + this.hashCode() + ") as BuildListener");
+ project.addBuildListener(this);
+ }
+ }
+
+ // ===== JUnitResultFormatter =====
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void endTestSuite(JUnitTest suite) throws BuildException {
+ }
+
+ /**
+ * Add the failed test to the list.
+ * @param test the test that errored.
+ * @param throwable the reason it errored.
+ * @see junit.framework.TestListener#addError(junit.framework.Test, java.lang.Throwable)
+ */
+ public void addError(Test test, Throwable throwable) {
+ failedTests.add(new TestInfos(test));
+ }
+
+ // CheckStyle:LineLengthCheck OFF - @see is long
+ /**
+ * Add the failed test to the list.
+ * @param test the test that failed.
+ * @param error the assertion that failed.
+ * @see junit.framework.TestListener#addFailure(junit.framework.Test, junit.framework.AssertionFailedError)
+ */
+ // CheckStyle:LineLengthCheck ON
+ public void addFailure(Test test, AssertionFailedError error) {
+ failedTests.add(new TestInfos(test));
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void setOutput(OutputStream out) {
+ // unused, close output file so it can be deleted before the VM exits
+ if (out != System.out) {
+ FileUtils.close(out);
+ }
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void setSystemError(String err) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void setSystemOutput(String out) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void startTestSuite(JUnitTest suite) throws BuildException {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void endTest(Test test) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void startTest(Test test) {
+ }
+
+ // ===== "Templates" for generating the JUnit class =====
+
+ private void writeJavaClass() {
+ try {
+ File sourceFile = new File((getLocationName() + ".java"));
+ verbose("Write collector class to '" + sourceFile.getAbsolutePath() + "'");
+
+ if (sourceFile.exists() && !sourceFile.delete()) {
+ throw new IOException("could not delete " + sourceFile);
+ }
+ writer = new BufferedWriter(new FileWriter(sourceFile));
+
+ createClassHeader();
+ createSuiteMethod();
+ createClassFooter();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ FileUtils.close(writer);
+ }
+ }
+
+ private void createClassHeader() throws IOException {
+ String className = getLocationName().replace('\\', '/');
+ if (className.indexOf('/') > -1) {
+ className = className.substring(className.lastIndexOf('/') + 1);
+ }
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss,SSS");
+ writer.write("// generated on: ");
+ writer.write(sdf.format(new Date()));
+ writer.newLine();
+ writer.write("import junit.framework.*;");
+ writer.newLine();
+ writer.write("public class ");
+ writer.write(className);
+ // If this class does not extend TC, Ant doesn't run these
+ writer.write(" extends TestCase {");
+ writer.newLine();
+ // standard String-constructor
+ writer.write(" public ");
+ writer.write(className);
+ writer.write("(String testname) {");
+ writer.newLine();
+ writer.write(" super(testname);");
+ writer.newLine();
+ writer.write(" }");
+ writer.newLine();
+ }
+
+ private void createSuiteMethod() throws IOException {
+ writer.write(" public static Test suite() {");
+ writer.newLine();
+ writer.write(" TestSuite suite = new TestSuite();");
+ writer.newLine();
+ for (Iterator iter = failedTests.iterator(); iter.hasNext();) {
+ TestInfos testInfos = (TestInfos) iter.next();
+ writer.write(" suite.addTest(");
+ writer.write(String.valueOf(testInfos));
+ writer.write(");");
+ writer.newLine();
+ }
+ writer.write(" return suite;");
+ writer.newLine();
+ writer.write(" }");
+ writer.newLine();
+ }
+
+ private void createClassFooter() throws IOException {
+ writer.write("}");
+ writer.newLine();
+ }
+
+ // ===== Helper classes and methods =====
+
+ /**
+ * Logging facade in INFO-mode.
+ * @param message Log-message
+ */
+ public void log(String message) {
+ getProject().log(LOG_PREFIX + " " + message, Project.MSG_INFO);
+ }
+
+ /**
+ * Logging facade in VERBOSE-mode.
+ * @param message Log-message
+ */
+ public void verbose(String message) {
+ getProject().log(LOG_PREFIX + " " + message, Project.MSG_VERBOSE);
+ }
+
+ /**
+ * TestInfos holds information about a given test for later use.
+ */
+ public static class TestInfos implements Comparable {
+
+ /** The class name of the test. */
+ private final String className;
+
+ /** The method name of the testcase. */
+ private final String methodName;
+
+ /**
+ * This constructor extracts the needed information from the given test.
+ * @param test Test to analyze
+ */
+ public TestInfos(Test test) {
+ className = test.getClass().getName();
+ String _methodName = test.toString();
+ methodName = _methodName.substring(0, _methodName.indexOf('('));
+ }
+
+ /**
+ * This String-Representation can directly be used for instantiation of
+ * the JUnit testcase.
+ * @return the string representation.
+ * @see java.lang.Object#toString()
+ * @see FailureRecorder#createSuiteMethod()
+ */
+ public String toString() {
+ return "new " + className + "(\"" + methodName + "\")";
+ }
+
+ /**
+ * The SortedMap needs comparable elements.
+ * @param other the object to compare to.
+ * @return the result of the comparison.
+ * @see java.lang.Comparable#compareTo
+ * @see SortedSet#comparator()
+ */
+ public int compareTo(Object other) {
+ if (other instanceof TestInfos) {
+ TestInfos otherInfos = (TestInfos) other;
+ return toString().compareTo(otherInfos.toString());
+ } else {
+ return -1;
+ }
+ }
+ public boolean equals(Object obj) {
+ return obj instanceof TestInfos && toString().equals(obj.toString());
+ }
+ public int hashCode() {
+ return toString().hashCode();
+ }
+ }
+
+ // ===== BuildListener =====
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void buildFinished(BuildEvent event) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void buildStarted(BuildEvent event) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void messageLogged(BuildEvent event) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void targetFinished(BuildEvent event) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void targetStarted(BuildEvent event) {
+ }
+
+ /**
+ * The task outside of this JUnitResultFormatter is the <junit> task. So all tests passed
+ * and we could create the new java class.
+ * @param event not used
+ * @see org.apache.tools.ant.BuildListener#taskFinished(org.apache.tools.ant.BuildEvent)
+ */
+ public void taskFinished(BuildEvent event) {
+ if (!failedTests.isEmpty()) {
+ writeJavaClass();
+ }
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void taskStarted(BuildEvent event) {
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java
new file mode 100644
index 00000000..f9fbcb0a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java
@@ -0,0 +1,401 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.util.KeepAliveOutputStream;
+
+/**
+ * <p> A wrapper for the implementations of <code>JUnitResultFormatter</code>.
+ * In particular, used as a nested <code>&lt;formatter&gt;</code> element in
+ * a <code>&lt;junit&gt;</code> task.
+ * <p> For example,
+ * <code><pre>
+ * &lt;junit printsummary="no" haltonfailure="yes" fork="false"&gt;
+ * &lt;formatter type="plain" usefile="false" /&gt;
+ * &lt;test name="org.apache.ecs.InternationalCharTest" /&gt;
+ * &lt;/junit&gt;</pre></code>
+ * adds a <code>plain</code> type implementation
+ * (<code>PlainJUnitResultFormatter</code>) to display the results of the test.
+ *
+ * <p> Either the <code>type</code> or the <code>classname</code> attribute
+ * must be set.
+ *
+ * @see JUnitTask
+ * @see XMLJUnitResultFormatter
+ * @see BriefJUnitResultFormatter
+ * @see PlainJUnitResultFormatter
+ * @see FailureRecorder
+ * @see JUnitResultFormatter
+ */
+public class FormatterElement {
+
+ private String classname;
+ private String extension;
+ private OutputStream out = new KeepAliveOutputStream(System.out);
+ private File outFile;
+ private boolean useFile = true;
+ private Object ifCond;
+ private Object unlessCond;
+
+ /**
+ * Store the project reference for passing it to nested components.
+ * @since Ant 1.8
+ */
+ private Project project;
+
+ /** xml formatter class */
+ public static final String XML_FORMATTER_CLASS_NAME =
+ "org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter";
+ /** brief formatter class */
+ public static final String BRIEF_FORMATTER_CLASS_NAME =
+ "org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter";
+ /** plain formatter class */
+ public static final String PLAIN_FORMATTER_CLASS_NAME =
+ "org.apache.tools.ant.taskdefs.optional.junit.PlainJUnitResultFormatter";
+ /** failure recorder class */
+ public static final String FAILURE_RECORDER_CLASS_NAME =
+ "org.apache.tools.ant.taskdefs.optional.junit.FailureRecorder";
+
+ /**
+ * <p> Quick way to use a standard formatter.
+ *
+ * <p> At the moment, there are three supported standard formatters.
+ * <ul>
+ * <li> The <code>xml</code> type uses a <code>XMLJUnitResultFormatter</code>.
+ * <li> The <code>brief</code> type uses a <code>BriefJUnitResultFormatter</code>.
+ * <li> The <code>plain</code> type (the default) uses a <code>PlainJUnitResultFormatter</code>.
+ * <li> The <code>failure</code> type uses a <code>FailureRecorder</code>.
+ * </ul>
+ *
+ * <p> Sets <code>classname</code> attribute - so you can't use that
+ * attribute if you use this one.
+ * @param type the enumerated value to use.
+ */
+ public void setType(TypeAttribute type) {
+ if ("xml".equals(type.getValue())) {
+ setClassname(XML_FORMATTER_CLASS_NAME);
+ } else {
+ if ("brief".equals(type.getValue())) {
+ setClassname(BRIEF_FORMATTER_CLASS_NAME);
+ } else {
+ if ("failure".equals(type.getValue())) {
+ setClassname(FAILURE_RECORDER_CLASS_NAME);
+ } else { // must be plain, ensured by TypeAttribute
+ setClassname(PLAIN_FORMATTER_CLASS_NAME);
+ }
+ }
+ }
+ }
+
+ /**
+ * <p> Set name of class to be used as the formatter.
+ *
+ * <p> This class must implement <code>JUnitResultFormatter</code>
+ * @param classname the name of the formatter class.
+ */
+ public void setClassname(String classname) {
+ this.classname = classname;
+ if (XML_FORMATTER_CLASS_NAME.equals(classname)) {
+ setExtension(".xml");
+ } else if (PLAIN_FORMATTER_CLASS_NAME.equals(classname)) {
+ setExtension(".txt");
+ } else if (BRIEF_FORMATTER_CLASS_NAME.equals(classname)) {
+ setExtension(".txt");
+ }
+ }
+
+ /**
+ * Get name of class to be used as the formatter.
+ * @return the name of the class.
+ */
+ public String getClassname() {
+ return classname;
+ }
+
+ /**
+ * Set the extension to use for the report file.
+ * @param ext the extension to use.
+ */
+ public void setExtension(String ext) {
+ this.extension = ext;
+ }
+
+ /**
+ * Get the extension used for the report file.
+ * @return the extension.
+ */
+ public String getExtension() {
+ return extension;
+ }
+
+ /**
+ * <p> Set the file which the formatte should log to.
+ *
+ * <p> Note that logging to file must be enabled .
+ */
+ void setOutfile(File out) {
+ this.outFile = out;
+ }
+
+ /**
+ * <p> Set output stream for formatter to use.
+ *
+ * <p> Defaults to standard out.
+ * @param out the output stream to use.
+ */
+ public void setOutput(OutputStream out) {
+ if (out == System.out || out == System.err) {
+ out = new KeepAliveOutputStream(out);
+ }
+ this.out = out;
+ }
+
+ /**
+ * Set whether the formatter should log to file.
+ * @param useFile if true use a file, if false send
+ * to standard out.
+ */
+ public void setUseFile(boolean useFile) {
+ this.useFile = useFile;
+ }
+
+ /**
+ * Get whether the formatter should log to file.
+ */
+ boolean getUseFile() {
+ return useFile;
+ }
+
+ /**
+ * Set whether this formatter should be used. It will be used if
+ * the expression evaluates to true or the name of a property
+ * which has been set, otherwise it won't.
+ * @param ifCond name of property
+ * @since Ant 1.8.0
+ */
+ public void setIf(Object ifCond) {
+ this.ifCond = ifCond;
+ }
+
+ /**
+ * Set whether this formatter should be used. It will be used if
+ * the expression evaluates to true or the name of a property
+ * which has been set, otherwise it won't.
+ * @param ifCond name of property
+ */
+ public void setIf(String ifCond) {
+ setIf((Object) ifCond);
+ }
+
+ /**
+ * Set whether this formatter should NOT be used. It will be used
+ * if the expression evaluates to false or the name of a property
+ * which has not been set, orthwise it will not be used.
+ * @param unlessCond name of property
+ * @since Ant 1.8.0
+ */
+ public void setUnless(Object unlessCond) {
+ this.unlessCond = unlessCond;
+ }
+
+ /**
+ * Set whether this formatter should NOT be used. It will be used
+ * if the expression evaluates to false or the name of a property
+ * which has not been set, orthwise it will not be used.
+ * @param unlessCond name of property
+ */
+ public void setUnless(String unlessCond) {
+ setUnless((Object) unlessCond);
+ }
+
+ /**
+ * Ensures that the selector passes the conditions placed
+ * on it with <code>if</code> and <code>unless</code> properties.
+ * @param t the task the this formatter is used in.
+ * @return true if the formatter should be used.
+ */
+ public boolean shouldUse(Task t) {
+ PropertyHelper ph = PropertyHelper.getPropertyHelper(t.getProject());
+ return ph.testIfCondition(ifCond)
+ && ph.testUnlessCondition(unlessCond);
+ }
+
+ /**
+ * @since Ant 1.2
+ */
+ JUnitTaskMirror.JUnitResultFormatterMirror createFormatter() throws BuildException {
+ return createFormatter(null);
+ }
+
+ /**
+ * Store the project reference for passing it to nested components.
+ * @param project the reference
+ * @since Ant 1.8
+ */
+ public void setProject(Project project) {
+ this.project = project;
+ }
+
+
+ /**
+ * @since Ant 1.6
+ */
+ JUnitTaskMirror.JUnitResultFormatterMirror createFormatter(ClassLoader loader)
+ throws BuildException {
+
+ if (classname == null) {
+ throw new BuildException("you must specify type or classname");
+ }
+ //although this code appears to duplicate that of ClasspathUtils.newInstance,
+ //we cannot use that because this formatter may run in a forked process,
+ //without that class.
+ Class f = null;
+ try {
+ if (loader == null) {
+ f = Class.forName(classname);
+ } else {
+ f = Class.forName(classname, true, loader);
+ }
+ } catch (ClassNotFoundException e) {
+ throw new BuildException(
+ "Using loader " + loader + " on class " + classname
+ + ": " + e, e);
+ } catch (NoClassDefFoundError e) {
+ throw new BuildException(
+ "Using loader " + loader + " on class " + classname
+ + ": " + e, e);
+ }
+
+ Object o = null;
+ try {
+ o = f.newInstance();
+ } catch (InstantiationException e) {
+ throw new BuildException(e);
+ } catch (IllegalAccessException e) {
+ throw new BuildException(e);
+ }
+
+ if (!(o instanceof JUnitTaskMirror.JUnitResultFormatterMirror)) {
+ throw new BuildException(classname + " is not a JUnitResultFormatter");
+ }
+ JUnitTaskMirror.JUnitResultFormatterMirror r =
+ (JUnitTaskMirror.JUnitResultFormatterMirror) o;
+ if (useFile && outFile != null) {
+ out = new DelayedFileOutputStream(outFile);
+ }
+ r.setOutput(out);
+
+
+ boolean needToSetProjectReference = true;
+ try {
+ Field field = r.getClass().getField("project");
+ Object value = field.get(r);
+ if (value instanceof Project) {
+ // there is already a project reference so dont overwrite this
+ needToSetProjectReference = false;
+ }
+ } catch (NoSuchFieldException e) {
+ // no field present, so no previous reference exists
+ } catch (IllegalAccessException e) {
+ throw new BuildException(e);
+ }
+
+ if (needToSetProjectReference) {
+ Method setter;
+ try {
+ setter = r.getClass().getMethod("setProject", new Class[] {Project.class});
+ setter.invoke(r, new Object[] {project});
+ } catch (NoSuchMethodException e) {
+ // no setProject to invoke; just ignore
+ } catch (IllegalAccessException e) {
+ throw new BuildException(e);
+ } catch (InvocationTargetException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ return r;
+ }
+
+ /**
+ * <p> Enumerated attribute with the values "plain", "xml", "brief" and "failure".
+ *
+ * <p> Use to enumerate options for <code>type</code> attribute.
+ */
+ public static class TypeAttribute extends EnumeratedAttribute {
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return new String[] {"plain", "xml", "brief", "failure"};
+ }
+ }
+
+ /**
+ * A standard FileOutputStream creates a file as soon as it's opened. This
+ * class delays the creation of the file until the first time a caller attempts
+ * to write to it so we don't end up with empty files if the listeners don't use
+ * them.
+ */
+ private static class DelayedFileOutputStream extends OutputStream {
+
+ private BufferedOutputStream outputStream;
+ private final File file;
+
+ public DelayedFileOutputStream(File file) {
+ this.file = file;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ synchronized (this) {
+ if (outputStream == null) {
+ outputStream = new BufferedOutputStream(new FileOutputStream(file));
+ }
+ }
+ outputStream.write(b);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ if (outputStream != null) {
+ outputStream.flush();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (outputStream != null) {
+ outputStream.close();
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestListener.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestListener.java
new file mode 100644
index 00000000..6741912e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestListener.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.tools.ant.taskdefs.optional.junit;
+
+import junit.framework.Test;
+import junit.framework.TestListener;
+
+/**
+ * Provides the functionality for TestListeners to be able to be notified of
+ * the necessary JUnit4 events for test being ignored (@Ignore annotation)
+ * or skipped (Assume failures). Tests written in JUnit4 will report against
+ * the methods in this interface alongside the methods in the existing TestListener
+ */
+public interface IgnoredTestListener extends TestListener {
+
+ /**
+ * Reports when a test has been marked with the @Ignore annotation. The parameter
+ * should normally be typed to JUnit's {@link junit.framework.JUnit4TestCaseFacade}
+ * so implementing classes should be able to get the details of the ignore by casting
+ * the argument and retrieving the descriptor from the test.
+ * @param test the details of the test and failure that have triggered this report.
+ */
+ void testIgnored(Test test);
+
+ /**
+ * Receive a report that a test has failed an assumption. Within JUnit4
+ * this is normally treated as a test being skipped, although how any
+ * listener handles this is up to that specific listener.<br />
+ * <b>Note:</b> Tests that throw assumption failures will still report
+ * the endTest method, which may differ from how the addError and addFailure
+ * methods work, it's up for any implementing classes to handle this.
+ * @param test the details of the test and failure that have triggered this report.
+ * @param exception the AssumptionViolatedException thrown from the current assumption failure.
+ */
+ void testAssumptionFailure(Test test, Throwable exception);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestResult.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestResult.java
new file mode 100644
index 00000000..c3bb18da
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestResult.java
@@ -0,0 +1,99 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+
+/**
+ * Records ignored and skipped tests reported as part of the execution of
+ * JUnit 4 tests.
+ *
+ */
+public class IgnoredTestResult extends TestResult {
+
+
+ private List<IgnoredTestListener> listeners = new ArrayList<IgnoredTestListener>();
+ private List<TestIgnored> ignored = new ArrayList<TestIgnored>();
+ private List<TestIgnored> skipped = new ArrayList<TestIgnored>();
+
+ public IgnoredTestResult() {
+ super();
+ }
+
+
+ public synchronized void addListener(TestListener listener) {
+ if (listener instanceof IgnoredTestListener) {
+ listeners.add((IgnoredTestListener)listener);
+ }
+ super.addListener(listener);
+ }
+
+ public synchronized void removeListener(TestListener listener) {
+ if (listener instanceof IgnoredTestListener) {
+ listeners.remove(listener);
+ }
+ super.removeListener(listener);
+ }
+
+ /**
+ * Record a test as having been ignored, normally by the @Ignore annotation.
+ * @param test the test that was ignored.
+ * @throws Exception is the listener thrown an exception on handling the notification.
+ */
+ public synchronized void testIgnored(Test test) throws Exception {
+ ignored.add(new TestIgnored(test));
+ for (IgnoredTestListener listener : listeners) {
+ listener.testIgnored(test);
+ }
+ }
+
+ /**
+ * Report how many tests were ignored.
+ * @return the number of tests reported as ignored during the current execution.
+ */
+ public long ignoredCount() {
+ return ignored.size();
+ }
+
+ /**
+ * Records a test as having an assumption failure so JUnit will no longer be executing it.
+ * Under normal circumstances this would be counted as a skipped test.
+ * @param test the test to record
+ * @param cause the details of the test and assumption failure.
+ */
+ public void testAssumptionFailure(Test test, Throwable cause) {
+ skipped.add(new TestIgnored(test));
+ for (IgnoredTestListener listener : listeners) {
+ listener.testAssumptionFailure(test, cause);
+ }
+ }
+
+ /**
+ * Report how many tests has assumption failures.
+ * @return the number of tests that reported assumption failures during the current execution.
+ */
+ public long skippedCount() {
+ return skipped.size();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.java
new file mode 100644
index 00000000..f03a409b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.java
@@ -0,0 +1,193 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import org.junit.runner.Description;
+import org.junit.runner.Request;
+import org.junit.runner.Runner;
+import org.junit.runner.manipulation.Filter;
+
+/**
+ * Adapter between JUnit 3.8.x API and JUnit 4.x API for execution of tests
+ * and listening of events (test start, test finish, test failure, test skipped).
+ * The constructor is passed a JUnit 4 test class and a list of name of methods
+ * in it that should be executed. Method {@link #run run(TestResult)} executes
+ * the given JUnit-4-style test methods and notifies the given {@code TestResult}
+ * object using its old (JUnit 3.8.x style) API.
+ *
+ * @author Marian Petras
+ */
+public class JUnit4TestMethodAdapter implements Test {
+
+ private final Class testClass;
+ private final String[] methodNames;
+ private final Runner runner;
+ private final CustomJUnit4TestAdapterCache cache;
+
+ /**
+ * Creates a new adapter for the given class and a method within the class.
+ *
+ * @param testClass test class containing the method to be executed
+ * @param methodNames names of the test methods that are to be executed
+ * @exception java.lang.IllegalArgumentException
+ * if any of the arguments is {@code null}
+ * or if any of the given method names is {@code null} or empty
+ */
+ public JUnit4TestMethodAdapter(final Class testClass,
+ final String[] methodNames) {
+ if (testClass == null) {
+ throw new IllegalArgumentException("testClass is <null>");
+ }
+ if (methodNames == null) {
+ throw new IllegalArgumentException("methodNames is <null>");
+ }
+ for (int i = 0; i < methodNames.length; i++) {
+ if (methodNames[i] == null) {
+ throw new IllegalArgumentException("method name #" + i + " is <null>");
+ }
+ if (methodNames[i].length() == 0) {
+ throw new IllegalArgumentException("method name #" + i + " is empty");
+ }
+ }
+ this.testClass = testClass;
+ this.methodNames = methodNames.clone();
+ this.cache = CustomJUnit4TestAdapterCache.getInstance();
+
+ // Warning: If 'testClass' is an old-style (pre-JUnit-4) class,
+ // then all its test methods will be executed by the returned runner!
+ Request request;
+ if (methodNames.length == 1) {
+ request = Request.method(testClass, methodNames[0]);
+ } else {
+ request = Request.aClass(testClass).filterWith(
+ new MultipleMethodsFilter(testClass, methodNames));
+ }
+ runner = request.getRunner();
+ }
+
+ public int countTestCases() {
+ return runner.testCount();
+ }
+
+ public Description getDescription() {
+ return runner.getDescription();
+ }
+
+ public List/*<Test>*/ getTests() {
+ return cache.asTestList(getDescription());
+ }
+
+ public Class getTestClass() {
+ return testClass;
+ }
+
+ public void run(final TestResult result) {
+ runner.run(cache.getNotifier(result));
+ }
+
+ @Override
+ public String toString() {
+ String testClassName = testClass.getName();
+ StringBuilder buf = new StringBuilder(testClassName.length()
+ + 12 * methodNames.length)
+ .append(':');
+ if (methodNames.length != 0) {
+ buf.append(methodNames[0]);
+ for (int i = 1; i < methodNames.length; i++) {
+ buf.append(',')
+ .append(methodNames[i]);
+ }
+ }
+ return buf.toString();
+ }
+
+ private static final class MultipleMethodsFilter extends Filter {
+
+ private final Description methodsListDescription;
+ private final Class testClass;
+ private final String[] methodNames;
+
+ private MultipleMethodsFilter(Class testClass, String[] methodNames) {
+ if (testClass == null) {
+ throw new IllegalArgumentException("testClass is <null>");
+ }
+ if (methodNames == null) {
+ throw new IllegalArgumentException("methodNames is <null>");
+ }
+ methodsListDescription = Description.createSuiteDescription(testClass);
+ for (int i = 0; i < methodNames.length; i++) {
+ methodsListDescription.addChild(
+ Description.createTestDescription(testClass, methodNames[i]));
+ }
+ this.testClass = testClass;
+ this.methodNames = methodNames;
+ }
+
+ @Override
+ public boolean shouldRun(Description description) {
+ if (methodNames.length == 0) {
+ return false;
+ }
+ if (description.isTest()) {
+ Iterator/*<Description>*/ it = methodsListDescription.getChildren().iterator();
+ while (it.hasNext()) {
+ Description methodDescription = (Description) it.next();
+ if (methodDescription.equals(description)) {
+ return true;
+ }
+ }
+ } else {
+ Iterator/*<Description>*/ it = description.getChildren().iterator();
+ while (it.hasNext()) {
+ Description each = (Description) it.next();
+ if (shouldRun(each)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String describe() {
+ StringBuilder buf = new StringBuilder(40);
+ if (methodNames.length == 0) {
+ buf.append("No methods");
+ } else {
+ buf.append(methodNames.length == 1 ? "Method" : "Methods");
+ buf.append(' ');
+ buf.append(methodNames[0]);
+ for (int i = 1; i < methodNames.length; i++) {
+ buf.append(',').append(methodNames[i]);
+ }
+ }
+ buf.append('(').append(testClass.getName()).append(')');
+ return buf.toString();
+ }
+
+ }
+
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java
new file mode 100644
index 00000000..2119fc94
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java
@@ -0,0 +1,65 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.OutputStream;
+
+import junit.framework.TestListener;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * This Interface describes classes that format the results of a JUnit
+ * testrun.
+ *
+ */
+public interface JUnitResultFormatter
+ extends TestListener, JUnitTaskMirror.JUnitResultFormatterMirror {
+ /**
+ * The whole testsuite started.
+ * @param suite the suite.
+ * @throws BuildException on error.
+ */
+ void startTestSuite(JUnitTest suite) throws BuildException;
+
+ /**
+ * The whole testsuite ended.
+ * @param suite the suite.
+ * @throws BuildException on error.
+ */
+ void endTestSuite(JUnitTest suite) throws BuildException;
+
+ /**
+ * Sets the stream the formatter is supposed to write its results to.
+ * @param out the output stream to use.
+ */
+ void setOutput(OutputStream out);
+
+ /**
+ * This is what the test has written to System.out
+ * @param out the string to write.
+ */
+ void setSystemOutput(String out);
+
+ /**
+ * This is what the test has written to System.err
+ * @param err the string to write.
+ */
+ void setSystemError(String err);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java
new file mode 100644
index 00000000..459bd3d5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java
@@ -0,0 +1,2283 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Constructor;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteWatchdog;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.taskdefs.PumpStreamHandler;
+import org.apache.tools.ant.types.Assertions;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Permissions;
+import org.apache.tools.ant.types.PropertySet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.LoaderUtils;
+import org.apache.tools.ant.util.SplitClassLoader;
+
+/**
+ * Runs JUnit tests.
+ *
+ * <p> JUnit is a framework to create unit tests. It has been initially
+ * created by Erich Gamma and Kent Beck. JUnit can be found at <a
+ * href="http://www.junit.org">http://www.junit.org</a>.
+ *
+ * <p> <code>JUnitTask</code> can run a single specific
+ * <code>JUnitTest</code> using the <code>test</code> element.</p>
+ * For example, the following target <code><pre>
+ * &lt;target name="test-int-chars" depends="jar-test"&gt;
+ * &lt;echo message="testing international characters"/&gt;
+ * &lt;junit printsummary="no" haltonfailure="yes" fork="false"&gt;
+ * &lt;classpath refid="classpath"/&gt;
+ * &lt;formatter type="plain" usefile="false" /&gt;
+ * &lt;test name="org.apache.ecs.InternationalCharTest" /&gt;
+ * &lt;/junit&gt;
+ * &lt;/target&gt;
+ * </pre></code>
+ * <p>runs a single junit test
+ * (<code>org.apache.ecs.InternationalCharTest</code>) in the current
+ * VM using the path with id <code>classpath</code> as classpath and
+ * presents the results formatted using the standard
+ * <code>plain</code> formatter on the command line.</p>
+ *
+ * <p> This task can also run batches of tests. The
+ * <code>batchtest</code> element creates a <code>BatchTest</code>
+ * based on a fileset. This allows, for example, all classes found in
+ * directory to be run as testcases.</p>
+ *
+ * <p>For example,</p><code><pre>
+ * &lt;target name="run-tests" depends="dump-info,compile-tests" if="junit.present"&gt;
+ * &lt;junit printsummary="no" haltonfailure="yes" fork="${junit.fork}"&gt;
+ * &lt;jvmarg value="-classic"/&gt;
+ * &lt;classpath refid="tests-classpath"/&gt;
+ * &lt;sysproperty key="build.tests" value="${build.tests}"/&gt;
+ * &lt;formatter type="brief" usefile="false" /&gt;
+ * &lt;batchtest&gt;
+ * &lt;fileset dir="${tests.dir}"&gt;
+ * &lt;include name="**&#047;*Test*" /&gt;
+ * &lt;/fileset&gt;
+ * &lt;/batchtest&gt;
+ * &lt;/junit&gt;
+ * &lt;/target&gt;
+ * </pre></code>
+ * <p>this target finds any classes with a <code>test</code> directory
+ * anywhere in their path (under the top <code>${tests.dir}</code>, of
+ * course) and creates <code>JUnitTest</code>'s for each one.</p>
+ *
+ * <p> Of course, <code>&lt;junit&gt;</code> and
+ * <code>&lt;batch&gt;</code> elements can be combined for more
+ * complex tests. For an example, see the ant <code>build.xml</code>
+ * target <code>run-tests</code> (the second example is an edited
+ * version).</p>
+ *
+ * <p> To spawn a new Java VM to prevent interferences between
+ * different testcases, you need to enable <code>fork</code>. A
+ * number of attributes and elements allow you to set up how this JVM
+ * runs.
+ *
+ *
+ * @since Ant 1.2
+ *
+ * @see JUnitTest
+ * @see BatchTest
+ */
+public class JUnitTask extends Task {
+
+ private static final String LINE_SEP
+ = System.getProperty("line.separator");
+ private static final String CLASSPATH = "CLASSPATH";
+ private CommandlineJava commandline;
+ private final Vector<JUnitTest> tests = new Vector<JUnitTest>();
+ private final Vector<BatchTest> batchTests = new Vector<BatchTest>();
+ private final Vector<FormatterElement> formatters = new Vector<FormatterElement>();
+ private File dir = null;
+
+ private Integer timeout = null;
+ private boolean summary = false;
+ private boolean reloading = true;
+ private String summaryValue = "";
+ private JUnitTaskMirror.JUnitTestRunnerMirror runner = null;
+
+ private boolean newEnvironment = false;
+ private final Environment env = new Environment();
+
+ private boolean includeAntRuntime = true;
+ private Path antRuntimeClasses = null;
+
+ // Do we send output to System.out/.err in addition to the formatters?
+ private boolean showOutput = false;
+
+ // Do we send output to the formatters ?
+ private boolean outputToFormatters = true;
+
+ private boolean logFailedTests = true;
+
+ private File tmpDir;
+ private AntClassLoader classLoader = null;
+ private Permissions perm = null;
+ private ForkMode forkMode = new ForkMode("perTest");
+
+ private boolean splitJUnit = false;
+ private boolean enableTestListenerEvents = false;
+ private JUnitTaskMirror delegate;
+ private ClassLoader mirrorLoader;
+
+ /** A boolean on whether to get the forked path for ant classes */
+ private boolean forkedPathChecked = false;
+
+ /* set when a test fails/errs with haltonfailure/haltonerror and >1 thread to stop other threads */
+ private volatile BuildException caughtBuildException = null;
+
+ // Attributes for basetest
+ private boolean haltOnError = false;
+ private boolean haltOnFail = false;
+ private boolean filterTrace = true;
+ private boolean fork = false;
+ private int threads = 1;
+ private String failureProperty;
+ private String errorProperty;
+
+ private static final int STRING_BUFFER_SIZE = 128;
+ /**
+ * @since Ant 1.7
+ */
+ public static final String TESTLISTENER_PREFIX =
+ "junit.framework.TestListener: ";
+
+ /**
+ * Name of magic property that enables test listener events.
+ */
+ public static final String ENABLE_TESTLISTENER_EVENTS =
+ "ant.junit.enabletestlistenerevents";
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * If true, force ant to re-classload all classes for each JUnit TestCase
+ *
+ * @param value force class reloading for each test case
+ */
+ public void setReloading(final boolean value) {
+ reloading = value;
+ }
+
+ /**
+ * If true, smartly filter the stack frames of
+ * JUnit errors and failures before reporting them.
+ *
+ * <p>This property is applied on all BatchTest (batchtest) and
+ * JUnitTest (test) however it can possibly be overridden by their
+ * own properties.</p>
+ * @param value <tt>false</tt> if it should not filter, otherwise
+ * <tt>true<tt>
+ *
+ * @since Ant 1.5
+ */
+ public void setFiltertrace(final boolean value) {
+ this.filterTrace = value;
+ }
+
+ /**
+ * If true, stop the build process when there is an error in a test.
+ * This property is applied on all BatchTest (batchtest) and JUnitTest
+ * (test) however it can possibly be overridden by their own
+ * properties.
+ * @param value <tt>true</tt> if it should halt, otherwise
+ * <tt>false</tt>
+ *
+ * @since Ant 1.2
+ */
+ public void setHaltonerror(final boolean value) {
+ this.haltOnError = value;
+ }
+
+ /**
+ * Property to set to "true" if there is a error in a test.
+ *
+ * <p>This property is applied on all BatchTest (batchtest) and
+ * JUnitTest (test), however, it can possibly be overridden by
+ * their own properties.</p>
+ * @param propertyName the name of the property to set in the
+ * event of an error.
+ *
+ * @since Ant 1.4
+ */
+ public void setErrorProperty(final String propertyName) {
+ this.errorProperty = propertyName;
+ }
+
+ /**
+ * If true, stop the build process if a test fails
+ * (errors are considered failures as well).
+ * This property is applied on all BatchTest (batchtest) and
+ * JUnitTest (test) however it can possibly be overridden by their
+ * own properties.
+ * @param value <tt>true</tt> if it should halt, otherwise
+ * <tt>false</tt>
+ *
+ * @since Ant 1.2
+ */
+ public void setHaltonfailure(final boolean value) {
+ this.haltOnFail = value;
+ }
+
+ /**
+ * Property to set to "true" if there is a failure in a test.
+ *
+ * <p>This property is applied on all BatchTest (batchtest) and
+ * JUnitTest (test), however, it can possibly be overridden by
+ * their own properties.</p>
+ * @param propertyName the name of the property to set in the
+ * event of an failure.
+ *
+ * @since Ant 1.4
+ */
+ public void setFailureProperty(final String propertyName) {
+ this.failureProperty = propertyName;
+ }
+
+ /**
+ * If true, JVM should be forked for each test.
+ *
+ * <p>It avoids interference between testcases and possibly avoids
+ * hanging the build. this property is applied on all BatchTest
+ * (batchtest) and JUnitTest (test) however it can possibly be
+ * overridden by their own properties.</p>
+ * @param value <tt>true</tt> if a JVM should be forked, otherwise
+ * <tt>false</tt>
+ * @see #setTimeout
+ *
+ * @since Ant 1.2
+ */
+ public void setFork(final boolean value) {
+ this.fork = value;
+ }
+
+ /**
+ * Set the behavior when {@link #setFork fork} fork has been enabled.
+ *
+ * <p>Possible values are "once", "perTest" and "perBatch". If
+ * set to "once", only a single Java VM will be forked for all
+ * tests, with "perTest" (the default) each test will run in a
+ * fresh Java VM and "perBatch" will run all tests from the same
+ * &lt;batchtest&gt; in the same Java VM.</p>
+ *
+ * <p>This attribute will be ignored if tests run in the same VM
+ * as Ant.</p>
+ *
+ * <p>Only tests with the same configuration of haltonerror,
+ * haltonfailure, errorproperty, failureproperty and filtertrace
+ * can share a forked Java VM, so even if you set the value to
+ * "once", Ant may need to fork multiple VMs.</p>
+ * @param mode the mode to use.
+ * @since Ant 1.6.2
+ */
+ public void setForkMode(final ForkMode mode) {
+ this.forkMode = mode;
+ }
+
+ /**
+ * Set the number of test threads to be used for parallel test
+ * execution. The default is 1, which is the same behavior as
+ * before parallel test execution was possible.
+ *
+ * <p>This attribute will be ignored if tests run in the same VM
+ * as Ant.</p>
+ *
+ * @since Ant 1.9.4
+ */
+ public void setThreads(final int threads) {
+ if (threads >= 0) {
+ this.threads = threads;
+ }
+ }
+
+ /**
+ * If true, print one-line statistics for each test, or "withOutAndErr"
+ * to also show standard output and error.
+ *
+ * Can take the values on, off, and withOutAndErr.
+ * @param value <tt>true</tt> to print a summary,
+ * <tt>withOutAndErr</tt> to include the test&apos;s output as
+ * well, <tt>false</tt> otherwise.
+ * @see SummaryJUnitResultFormatter
+ *
+ * @since Ant 1.2
+ */
+ public void setPrintsummary(final SummaryAttribute value) {
+ summaryValue = value.getValue();
+ summary = value.asBoolean();
+ }
+
+ /**
+ * Print summary enumeration values.
+ */
+ public static class SummaryAttribute extends EnumeratedAttribute {
+ /**
+ * list the possible values
+ * @return array of allowed values
+ */
+ @Override
+ public String[] getValues() {
+ return new String[] {"true", "yes", "false", "no",
+ "on", "off", "withOutAndErr"};
+ }
+
+ /**
+ * gives the boolean equivalent of the authorized values
+ * @return boolean equivalent of the value
+ */
+ public boolean asBoolean() {
+ final String v = getValue();
+ return "true".equals(v)
+ || "on".equals(v)
+ || "yes".equals(v)
+ || "withOutAndErr".equals(v);
+ }
+ }
+
+ /**
+ * Set the timeout value (in milliseconds).
+ *
+ * <p>If the test is running for more than this value, the test
+ * will be canceled. (works only when in 'fork' mode).</p>
+ * @param value the maximum time (in milliseconds) allowed before
+ * declaring the test as 'timed-out'
+ * @see #setFork(boolean)
+ *
+ * @since Ant 1.2
+ */
+ public void setTimeout(final Integer value) {
+ timeout = value;
+ }
+
+ /**
+ * Set the maximum memory to be used by all forked JVMs.
+ * @param max the value as defined by <tt>-mx</tt> or <tt>-Xmx</tt>
+ * in the java command line options.
+ *
+ * @since Ant 1.2
+ */
+ public void setMaxmemory(final String max) {
+ getCommandline().setMaxmemory(max);
+ }
+
+ /**
+ * The command used to invoke the Java Virtual Machine,
+ * default is 'java'. The command is resolved by
+ * java.lang.Runtime.exec(). Ignored if fork is disabled.
+ *
+ * @param value the new VM to use instead of <tt>java</tt>
+ * @see #setFork(boolean)
+ *
+ * @since Ant 1.2
+ */
+ public void setJvm(final String value) {
+ getCommandline().setVm(value);
+ }
+
+ /**
+ * Adds a JVM argument; ignored if not forking.
+ *
+ * @return create a new JVM argument so that any argument can be
+ * passed to the JVM.
+ * @see #setFork(boolean)
+ *
+ * @since Ant 1.2
+ */
+ public Commandline.Argument createJvmarg() {
+ return getCommandline().createVmArgument();
+ }
+
+ /**
+ * The directory to invoke the VM in. Ignored if no JVM is forked.
+ * @param dir the directory to invoke the JVM from.
+ * @see #setFork(boolean)
+ *
+ * @since Ant 1.2
+ */
+ public void setDir(final File dir) {
+ this.dir = dir;
+ }
+
+ /**
+ * Adds a system property that tests can access.
+ * This might be useful to transfer Ant properties to the
+ * testcases when JVM forking is not enabled.
+ *
+ * @since Ant 1.3
+ * @deprecated since ant 1.6
+ * @param sysp environment variable to add
+ */
+ @Deprecated
+ public void addSysproperty(final Environment.Variable sysp) {
+
+ getCommandline().addSysproperty(sysp);
+ }
+
+ /**
+ * Adds a system property that tests can access.
+ * This might be useful to transfer Ant properties to the
+ * testcases when JVM forking is not enabled.
+ * @param sysp new environment variable to add
+ * @since Ant 1.6
+ */
+ public void addConfiguredSysproperty(final Environment.Variable sysp) {
+ // get a build exception if there is a missing key or value
+ // see bugzilla report 21684
+ final String testString = sysp.getContent();
+ getProject().log("sysproperty added : " + testString, Project.MSG_DEBUG);
+ getCommandline().addSysproperty(sysp);
+ }
+
+ /**
+ * Adds a set of properties that will be used as system properties
+ * that tests can access.
+ *
+ * This might be useful to transfer Ant properties to the
+ * testcases when JVM forking is not enabled.
+ *
+ * @param sysp set of properties to be added
+ * @since Ant 1.6
+ */
+ public void addSyspropertyset(final PropertySet sysp) {
+ getCommandline().addSyspropertyset(sysp);
+ }
+
+ /**
+ * Adds path to classpath used for tests.
+ *
+ * @return reference to the classpath in the embedded java command line
+ * @since Ant 1.2
+ */
+ public Path createClasspath() {
+ return getCommandline().createClasspath(getProject()).createPath();
+ }
+
+ /**
+ * Adds a path to the bootclasspath.
+ * @return reference to the bootclasspath in the embedded java command line
+ * @since Ant 1.6
+ */
+ public Path createBootclasspath() {
+ return getCommandline().createBootclasspath(getProject()).createPath();
+ }
+
+ /**
+ * Adds an environment variable; used when forking.
+ *
+ * <p>Will be ignored if we are not forking a new VM.</p>
+ * @param var environment variable to be added
+ * @since Ant 1.5
+ */
+ public void addEnv(final Environment.Variable var) {
+ env.addVariable(var);
+ }
+
+ /**
+ * If true, use a new environment when forked.
+ *
+ * <p>Will be ignored if we are not forking a new VM.</p>
+ *
+ * @param newenv boolean indicating if setting a new environment is wished
+ * @since Ant 1.5
+ */
+ public void setNewenvironment(final boolean newenv) {
+ newEnvironment = newenv;
+ }
+
+ /**
+ * Preset the attributes of the test
+ * before configuration in the build
+ * script.
+ * This allows attributes in the <junit> task
+ * be be defaults for the tests, but allows
+ * individual tests to override the defaults.
+ */
+ private void preConfigure(final BaseTest test) {
+ test.setFiltertrace(filterTrace);
+ test.setHaltonerror(haltOnError);
+ if (errorProperty != null) {
+ test.setErrorProperty(errorProperty);
+ }
+ test.setHaltonfailure(haltOnFail);
+ if (failureProperty != null) {
+ test.setFailureProperty(failureProperty);
+ }
+ test.setFork(fork);
+ }
+
+ /**
+ * Add a new single testcase.
+ * @param test a new single testcase
+ * @see JUnitTest
+ *
+ * @since Ant 1.2
+ */
+ public void addTest(final JUnitTest test) {
+ tests.addElement(test);
+ preConfigure(test);
+ }
+
+ /**
+ * Adds a set of tests based on pattern matching.
+ *
+ * @return a new instance of a batch test.
+ * @see BatchTest
+ *
+ * @since Ant 1.2
+ */
+ public BatchTest createBatchTest() {
+ final BatchTest test = new BatchTest(getProject());
+ batchTests.addElement(test);
+ preConfigure(test);
+ return test;
+ }
+
+ /**
+ * Add a new formatter to all tests of this task.
+ *
+ * @param fe formatter element
+ * @since Ant 1.2
+ */
+ public void addFormatter(final FormatterElement fe) {
+ formatters.addElement(fe);
+ }
+
+ /**
+ * If true, include ant.jar, optional.jar and junit.jar in the forked VM.
+ *
+ * @param b include ant run time yes or no
+ * @since Ant 1.5
+ */
+ public void setIncludeantruntime(final boolean b) {
+ includeAntRuntime = b;
+ }
+
+ /**
+ * If true, send any output generated by tests to Ant's logging system
+ * as well as to the formatters.
+ * By default only the formatters receive the output.
+ *
+ * <p>Output will always be passed to the formatters and not by
+ * shown by default. This option should for example be set for
+ * tests that are interactive and prompt the user to do
+ * something.</p>
+ *
+ * @param showOutput if true, send output to Ant's logging system too
+ * @since Ant 1.5
+ */
+ public void setShowOutput(final boolean showOutput) {
+ this.showOutput = showOutput;
+ }
+
+ /**
+ * If true, send any output generated by tests to the formatters.
+ *
+ * @param outputToFormatters if true, send output to formatters (Default
+ * is true).
+ * @since Ant 1.7.0
+ */
+ public void setOutputToFormatters(final boolean outputToFormatters) {
+ this.outputToFormatters = outputToFormatters;
+ }
+
+ /**
+ * If true, write a single "FAILED" line for failed tests to Ant's
+ * log system.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setLogFailedTests(final boolean logFailedTests) {
+ this.logFailedTests = logFailedTests;
+ }
+
+ /**
+ * Assertions to enable in this program (if fork=true)
+ * @since Ant 1.6
+ * @param asserts assertion set
+ */
+ public void addAssertions(final Assertions asserts) {
+ if (getCommandline().getAssertions() != null) {
+ throw new BuildException("Only one assertion declaration is allowed");
+ }
+ getCommandline().setAssertions(asserts);
+ }
+
+ /**
+ * Sets the permissions for the application run inside the same JVM.
+ * @since Ant 1.6
+ * @return .
+ */
+ public Permissions createPermissions() {
+ if (perm == null) {
+ perm = new Permissions();
+ }
+ return perm;
+ }
+
+ /**
+ * If set, system properties will be copied to the cloned VM - as
+ * well as the bootclasspath unless you have explicitly specified
+ * a bootclasspath.
+ *
+ * <p>Doesn't have any effect unless fork is true.</p>
+ * @param cloneVm a <code>boolean</code> value.
+ * @since Ant 1.7
+ */
+ public void setCloneVm(final boolean cloneVm) {
+ getCommandline().setCloneVm(cloneVm);
+ }
+
+ /**
+ * Creates a new JUnitRunner and enables fork of a new Java VM.
+ *
+ * @throws Exception under ??? circumstances
+ * @since Ant 1.2
+ */
+ public JUnitTask() throws Exception {
+ }
+
+ /**
+ * Where Ant should place temporary files.
+ *
+ * @param tmpDir location where temporary files should go to
+ * @since Ant 1.6
+ */
+ public void setTempdir(final File tmpDir) {
+ if (tmpDir != null) {
+ if (!tmpDir.exists() || !tmpDir.isDirectory()) {
+ throw new BuildException(tmpDir.toString()
+ + " is not a valid temp directory");
+ }
+ }
+ this.tmpDir = tmpDir;
+ }
+
+ /**
+ * Whether test listener events shall be generated.
+ *
+ * <p>Defaults to false.</p>
+ *
+ * <p>This value will be overridden by the magic property
+ * ant.junit.enabletestlistenerevents if it has been set.</p>
+ *
+ * @since Ant 1.8.2
+ */
+ public void setEnableTestListenerEvents(final boolean b) {
+ enableTestListenerEvents = b;
+ }
+
+ /**
+ * Whether test listener events shall be generated.
+ * @since Ant 1.8.2
+ */
+ public boolean getEnableTestListenerEvents() {
+ final String e = getProject().getProperty(ENABLE_TESTLISTENER_EVENTS);
+ if (e != null) {
+ return Project.toBoolean(e);
+ }
+ return enableTestListenerEvents;
+ }
+
+ /**
+ * Adds the jars or directories containing Ant, this task and
+ * JUnit to the classpath - this should make the forked JVM work
+ * without having to specify them directly.
+ *
+ * @since Ant 1.4
+ */
+ @Override
+ public void init() {
+ antRuntimeClasses = new Path(getProject());
+ splitJUnit = !addClasspathResource("/junit/framework/TestCase.class");
+ addClasspathEntry("/org/apache/tools/ant/launch/AntMain.class");
+ addClasspathEntry("/org/apache/tools/ant/Task.class");
+ addClasspathEntry("/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.class");
+ addClasspathEntry("/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.class");
+ }
+
+ private static JUnitTaskMirror createMirror(final JUnitTask task, final ClassLoader loader) {
+ try {
+ loader.loadClass("junit.framework.Test"); // sanity check
+ } catch (final ClassNotFoundException e) {
+ throw new BuildException(
+ "The <classpath> for <junit> must include junit.jar "
+ + "if not in Ant's own classpath",
+ e, task.getLocation());
+ }
+ try {
+ final Class c = loader.loadClass(JUnitTaskMirror.class.getName() + "Impl");
+ if (c.getClassLoader() != loader) {
+ throw new BuildException("Overdelegating loader", task.getLocation());
+ }
+ final Constructor cons = c.getConstructor(new Class[] {JUnitTask.class});
+ return (JUnitTaskMirror) cons.newInstance(new Object[] {task});
+ } catch (final Exception e) {
+ throw new BuildException(e, task.getLocation());
+ }
+ }
+
+ /**
+ * Sets up the delegate that will actually run the tests.
+ *
+ * <p>Will be invoked implicitly once the delegate is needed.</p>
+ *
+ * @since Ant 1.7.1
+ */
+ protected void setupJUnitDelegate() {
+ final ClassLoader myLoader = JUnitTask.class.getClassLoader();
+ if (splitJUnit) {
+ final Path path = new Path(getProject());
+ path.add(antRuntimeClasses);
+ final Path extra = getCommandline().getClasspath();
+ if (extra != null) {
+ path.add(extra);
+ }
+ mirrorLoader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return new SplitClassLoader(myLoader, path, getProject(),
+ new String[] {
+ "BriefJUnitResultFormatter",
+ "JUnit4TestMethodAdapter",
+ "JUnitResultFormatter",
+ "JUnitTaskMirrorImpl",
+ "JUnitTestRunner",
+ "JUnitVersionHelper",
+ "OutErrSummaryJUnitResultFormatter",
+ "PlainJUnitResultFormatter",
+ "SummaryJUnitResultFormatter",
+ "TearDownOnVmCrash",
+ "XMLJUnitResultFormatter",
+ "IgnoredTestListener",
+ "IgnoredTestResult",
+ "CustomJUnit4TestAdapterCache",
+ "TestListenerWrapper"
+ });
+ }
+ });
+ } else {
+ mirrorLoader = myLoader;
+ }
+ delegate = createMirror(this, mirrorLoader);
+ }
+
+ /**
+ * Runs the testcase.
+ *
+ * @throws BuildException in case of test failures or errors
+ * @since Ant 1.2
+ */
+ @Override
+ public void execute() throws BuildException {
+ checkMethodLists();
+
+ setupJUnitDelegate();
+
+ final List<List> testLists = new ArrayList<List>();
+ /* parallel test execution is only supported for multi-process execution */
+ final int threads = ((!fork) || (forkMode.getValue().equals(ForkMode.ONCE))
+ ? 1
+ : this.threads);
+
+ final boolean forkPerTest = forkMode.getValue().equals(ForkMode.PER_TEST);
+ if (forkPerTest || forkMode.getValue().equals(ForkMode.ONCE)) {
+ testLists.addAll(executeOrQueue(getIndividualTests(),
+ forkPerTest));
+ } else { /* forkMode.getValue().equals(ForkMode.PER_BATCH) */
+ final int count = batchTests.size();
+ for (int i = 0; i < count; i++) {
+ final BatchTest batchtest = batchTests.elementAt(i);
+ testLists.addAll(executeOrQueue(batchtest.elements(), false));
+ }
+ testLists.addAll(executeOrQueue(tests.elements(), forkPerTest));
+ }
+
+ try {
+ /* prior to parallel the code in 'oneJunitThread' used to be here. */
+ runTestsInThreads(testLists, threads);
+ } finally {
+ cleanup();
+ }
+ }
+
+ /*
+ * When the list of tests is established, an array of threads is created to pick the
+ * tests off the list one at a time and execute them until the list is empty. Tests are
+ * not assigned to threads until the thread is available.
+ *
+ * This class is the runnable thread subroutine that takes care of passing the shared
+ * list iterator and the handle back to the main class to the test execution subroutine
+ * code 'runTestsInThreads'. One object is created for each thread and each one gets
+ * a unique thread id that can be useful for tracing test starts and stops.
+ *
+ * Because the threads are picking tests off the same list, it is the list *iterator*
+ * that must be shared, not the list itself - and the iterator must have a thread-safe
+ * ability to pop the list - hence the synchronized 'getNextTest'.
+ */
+ private class JunitTestThread implements Runnable {
+
+ JunitTestThread(final JUnitTask master, final Iterator<List> iterator, final int id) {
+ this.masterTask = master;
+ this.iterator = iterator;
+ this.id = id;
+ }
+
+ public void run() {
+ try {
+ masterTask.oneJunitThread(iterator, id);
+ } catch (final BuildException b) {
+ /* saved to rethrow in main thread to be like single-threaded case */
+ caughtBuildException = b;
+ }
+ }
+
+ private final JUnitTask masterTask;
+ private final Iterator<List> iterator;
+ private final int id;
+ }
+
+ /*
+ * Because the threads are picking tests off the same list, it is the list *iterator*
+ * that must be shared, not the list itself - and the iterator must have a thread-safe
+ * ability to pop the list - hence the synchronized 'getNextTest'. We can't have two
+ * threads get the same test, or two threads simultaneously pop the list so that a test
+ * gets skipped!
+ */
+ private List getNextTest(final Iterator<List> iter) {
+ synchronized(iter) {
+ if (iter.hasNext()) {
+ return iter.next();
+ }
+ return null;
+ }
+ }
+
+ /*
+ * This code loops keeps executing the next test or test bunch (depending on fork mode)
+ * on the list of test cases until none are left. Basically this body of code used to
+ * be in the execute routine above; now, several copies (one for each test thread) execute
+ * simultaneously. The while loop was modified to call the new thread-safe atomic list
+ * popping subroutine and the logging messages were added.
+ *
+ * If one thread aborts due to a BuildException (haltOnError, haltOnFailure, or any other
+ * fatal reason, no new tests/batches will be started but the running threads will be
+ * permitted to complete. Additional tests may start in already-running batch-test threads.
+ */
+ private void oneJunitThread(final Iterator<List> iter, final int threadId) {
+
+ List l;
+ log("Starting test thread " + threadId, Project.MSG_VERBOSE);
+ while ((caughtBuildException == null) && ((l = getNextTest(iter)) != null)) {
+ log("Running test " + l.get(0).toString() + "(" + l.size() + ") in thread " + threadId, Project.MSG_VERBOSE);
+ if (l.size() == 1) {
+ execute((JUnitTest) l.get(0), threadId);
+ } else {
+ execute(l, threadId);
+ }
+ }
+ log("Ending test thread " + threadId, Project.MSG_VERBOSE);
+ }
+
+
+ private void runTestsInThreads(final List<List> testList, final int numThreads) {
+
+ Iterator<List> iter = testList.iterator();
+
+ if (numThreads == 1) {
+ /* with just one thread just run the test - don't create any threads */
+ oneJunitThread(iter, 0);
+ } else {
+ final Thread[] threads = new Thread[numThreads];
+ int i;
+ boolean exceptionOccurred;
+
+ /* Need to split apart tests, which are still grouped in batches */
+ /* is there a simpler Java mechanism to do this? */
+ /* I assume we don't want to do this with "per batch" forking. */
+ List<List> newlist = new ArrayList<List>();
+ if (forkMode.getValue().equals(ForkMode.PER_TEST)) {
+ final Iterator<List> i1 = testList.iterator();
+ while (i1.hasNext()) {
+ final List l = i1.next();
+ if (l.size() == 1) {
+ newlist.add(l);
+ } else {
+ final Iterator i2 = l.iterator();
+ while (i2.hasNext()) {
+ final List tmpSingleton = new ArrayList();
+ tmpSingleton.add(i2.next());
+ newlist.add(tmpSingleton);
+ }
+ }
+ }
+ } else {
+ newlist = testList;
+ }
+ iter = newlist.iterator();
+
+ /* create 1 thread using the passthrough class, and let each thread start */
+ for (i = 0; i < numThreads; i++) {
+ threads[i] = new Thread(new JunitTestThread(this, iter, i+1));
+ threads[i].start();
+ }
+
+ /* wait for all of the threads to complete. Not sure if the exception can actually occur in this use case. */
+ do {
+ exceptionOccurred = false;
+
+ try {
+ for (i = 0; i < numThreads; i++) {
+ threads[i].join();
+ }
+ } catch (final InterruptedException e) {
+ exceptionOccurred = true;
+ }
+ } while (exceptionOccurred);
+
+ /* an exception occurred in one of the threads - usually a haltOnError/Failure.
+ throw the exception again so it behaves like the single-thread case */
+ if (caughtBuildException != null) {
+ throw new BuildException(caughtBuildException);
+ }
+
+ /* all threads are completed - that's all there is to do. */
+ /* control will flow back to the test cleanup call and then execute is done. */
+ }
+ }
+
+ /**
+ * Run the tests.
+ * @param arg one JUnitTest
+ * @param thread Identifies which thread is test running in (0 for single-threaded runs)
+ * @throws BuildException in case of test failures or errors
+ */
+ protected void execute(final JUnitTest arg, final int thread) throws BuildException {
+ validateTestName(arg.getName());
+
+ final JUnitTest test = (JUnitTest) arg.clone();
+ test.setThread(thread);
+
+ // set the default values if not specified
+ //@todo should be moved to the test class instead.
+ if (test.getTodir() == null) {
+ test.setTodir(getProject().resolveFile("."));
+ }
+
+ if (test.getOutfile() == null) {
+ test.setOutfile("TEST-" + test.getName());
+ }
+
+ // execute the test and get the return code
+ TestResultHolder result = null;
+ if (!test.getFork()) {
+ result = executeInVM(test);
+ } else {
+ final ExecuteWatchdog watchdog = createWatchdog();
+ result = executeAsForked(test, watchdog, null);
+ // null watchdog means no timeout, you'd better not check with null
+ }
+ actOnTestResult(result, test, "Test " + test.getName());
+ }
+
+ /**
+ * Run the tests.
+ * @param arg one JUnitTest
+ * @throws BuildException in case of test failures or errors
+ */
+ protected void execute(final JUnitTest arg) throws BuildException {
+ execute(arg, 0);
+ }
+
+ /**
+ * Throws a <code>BuildException</code> if the given test name is invalid.
+ * Validity is defined as not <code>null</code>, not empty, and not the
+ * string &quot;null&quot;.
+ * @param testName the test name to be validated
+ * @throws BuildException if <code>testName</code> is not a valid test name
+ */
+ private void validateTestName(final String testName) throws BuildException {
+ if (testName == null || testName.length() == 0
+ || testName.equals("null")) {
+ throw new BuildException("test name must be specified");
+ }
+ }
+
+ /**
+ * Execute a list of tests in a single forked Java VM.
+ * @param testList the list of tests to execute.
+ * @param thread Identifies which thread is test running in (0 for single-threaded runs)
+ * @throws BuildException on error.
+ */
+ protected void execute(final List testList, final int thread) throws BuildException {
+ JUnitTest test = null;
+ // Create a temporary file to pass the test cases to run to
+ // the runner (one test case per line)
+ final File casesFile = createTempPropertiesFile("junittestcases");
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new FileWriter(casesFile));
+
+ log("Creating casesfile '" + casesFile.getAbsolutePath()
+ + "' with content: ", Project.MSG_VERBOSE);
+ final PrintStream logWriter =
+ new PrintStream(new LogOutputStream(this, Project.MSG_VERBOSE));
+
+ final Iterator iter = testList.iterator();
+ while (iter.hasNext()) {
+ test = (JUnitTest) iter.next();
+ test.setThread(thread);
+ printDual(writer, logWriter, test.getName());
+ if (test.getMethods() != null) {
+ printDual(writer, logWriter, ":" + test.getMethodsString().replace(',', '+'));
+ }
+ if (test.getTodir() == null) {
+ printDual(writer, logWriter,
+ "," + getProject().resolveFile("."));
+ } else {
+ printDual(writer, logWriter, "," + test.getTodir());
+ }
+
+ if (test.getOutfile() == null) {
+ printlnDual(writer, logWriter,
+ "," + "TEST-" + test.getName());
+ } else {
+ printlnDual(writer, logWriter, "," + test.getOutfile());
+ }
+ }
+ writer.flush();
+ writer.close();
+ writer = null;
+
+ // execute the test and get the return code
+ final ExecuteWatchdog watchdog = createWatchdog();
+ final TestResultHolder result =
+ executeAsForked(test, watchdog, casesFile);
+ actOnTestResult(result, test, "Tests");
+ } catch (final IOException e) {
+ log(e.toString(), Project.MSG_ERR);
+ throw new BuildException(e);
+ } finally {
+ FileUtils.close(writer);
+
+ try {
+ FILE_UTILS.tryHardToDelete(casesFile);
+ } catch (final Exception e) {
+ log(e.toString(), Project.MSG_ERR);
+ }
+ }
+ }
+
+ /**
+ * Execute a list of tests in a single forked Java VM.
+ * @param testList the list of tests to execute.
+ * @throws BuildException on error.
+ */
+ protected void execute(final List testList) throws BuildException {
+ execute(testList, 0);
+ }
+
+ /**
+ * Execute a testcase by forking a new JVM. The command will block
+ * until it finishes. To know if the process was destroyed or not
+ * or whether the forked Java VM exited abnormally, use the
+ * attributes of the returned holder object.
+ * @param test the testcase to execute.
+ * @param watchdog the watchdog in charge of cancelling the test if it
+ * exceeds a certain amount of time. Can be <tt>null</tt>, in this case
+ * the test could probably hang forever.
+ * @param casesFile list of test cases to execute. Can be <tt>null</tt>,
+ * in this case only one test is executed.
+ * @return the test results from the JVM itself.
+ * @throws BuildException in case of error creating a temporary property file,
+ * or if the junit process can not be forked
+ */
+ private TestResultHolder executeAsForked(JUnitTest test,
+ final ExecuteWatchdog watchdog,
+ final File casesFile)
+ throws BuildException {
+
+ if (perm != null) {
+ log("Permissions ignored when running in forked mode!",
+ Project.MSG_WARN);
+ }
+
+ CommandlineJava cmd;
+ try {
+ cmd = (CommandlineJava) (getCommandline().clone());
+ } catch (final CloneNotSupportedException e) {
+ throw new BuildException("This shouldn't happen", e, getLocation());
+ }
+ if (casesFile == null) {
+ cmd.createArgument().setValue(test.getName());
+ if (test.getMethods() != null) {
+ cmd.createArgument().setValue(Constants.METHOD_NAMES + test.getMethodsString());
+ }
+ } else {
+ log("Running multiple tests in the same VM", Project.MSG_VERBOSE);
+ cmd.createArgument().setValue(Constants.TESTSFILE + casesFile);
+ }
+
+ cmd.createArgument().setValue(Constants.SKIP_NON_TESTS + String.valueOf(test.isSkipNonTests()));
+ cmd.createArgument().setValue(Constants.FILTERTRACE + test.getFiltertrace());
+ cmd.createArgument().setValue(Constants.HALT_ON_ERROR + test.getHaltonerror());
+ cmd.createArgument().setValue(Constants.HALT_ON_FAILURE
+ + test.getHaltonfailure());
+ checkIncludeAntRuntime(cmd);
+
+ checkIncludeSummary(cmd);
+
+ cmd.createArgument().setValue(Constants.SHOWOUTPUT
+ + String.valueOf(showOutput));
+ cmd.createArgument().setValue(Constants.OUTPUT_TO_FORMATTERS
+ + String.valueOf(outputToFormatters));
+ cmd.createArgument().setValue(Constants.LOG_FAILED_TESTS
+ + String.valueOf(logFailedTests));
+ cmd.createArgument().setValue(Constants.THREADID
+ + String.valueOf(test.getThread()));
+
+ // #31885
+ cmd.createArgument().setValue(Constants.LOGTESTLISTENEREVENTS
+ + String.valueOf(getEnableTestListenerEvents()));
+
+ StringBuffer formatterArg = new StringBuffer(STRING_BUFFER_SIZE);
+ final FormatterElement[] feArray = mergeFormatters(test);
+ for (int i = 0; i < feArray.length; i++) {
+ final FormatterElement fe = feArray[i];
+ if (fe.shouldUse(this)) {
+ formatterArg.append(Constants.FORMATTER);
+ formatterArg.append(fe.getClassname());
+ final File outFile = getOutput(fe, test);
+ if (outFile != null) {
+ formatterArg.append(",");
+ formatterArg.append(outFile);
+ }
+ cmd.createArgument().setValue(formatterArg.toString());
+ formatterArg = new StringBuffer();
+ }
+ }
+
+ final File vmWatcher = createTempPropertiesFile("junitvmwatcher");
+ cmd.createArgument().setValue(Constants.CRASHFILE
+ + vmWatcher.getAbsolutePath());
+ final File propsFile = createTempPropertiesFile("junit");
+ cmd.createArgument().setValue(Constants.PROPSFILE
+ + propsFile.getAbsolutePath());
+ final Hashtable p = getProject().getProperties();
+ final Properties props = new Properties();
+ for (final Enumeration e = p.keys(); e.hasMoreElements();) {
+ final Object key = e.nextElement();
+ props.put(key, p.get(key));
+ }
+ try {
+ final FileOutputStream outstream = new FileOutputStream(propsFile);
+ props.store(outstream, "Ant JUnitTask generated properties file");
+ outstream.close();
+ } catch (final java.io.IOException e) {
+ FILE_UTILS.tryHardToDelete(propsFile);
+ throw new BuildException("Error creating temporary properties "
+ + "file.", e, getLocation());
+ }
+
+ final Execute execute = new Execute(
+ new JUnitLogStreamHandler(
+ this,
+ Project.MSG_INFO,
+ Project.MSG_WARN),
+ watchdog);
+ execute.setCommandline(cmd.getCommandline());
+ execute.setAntRun(getProject());
+ if (dir != null) {
+ execute.setWorkingDirectory(dir);
+ }
+
+ final String[] environment = env.getVariables();
+ if (environment != null) {
+ for (int i = 0; i < environment.length; i++) {
+ log("Setting environment variable: " + environment[i],
+ Project.MSG_VERBOSE);
+ }
+ }
+ execute.setNewenvironment(newEnvironment);
+ execute.setEnvironment(environment);
+
+ log(cmd.describeCommand(), Project.MSG_VERBOSE);
+
+ checkForkedPath(cmd);
+
+ final TestResultHolder result = new TestResultHolder();
+ try {
+ result.exitCode = execute.execute();
+ } catch (final IOException e) {
+ throw new BuildException("Process fork failed.", e, getLocation());
+ } finally {
+ String vmCrashString = "unknown";
+ BufferedReader br = null;
+ try {
+ if (vmWatcher.exists()) {
+ br = new BufferedReader(new FileReader(vmWatcher));
+ vmCrashString = br.readLine();
+ } else {
+ vmCrashString = "Monitor file ("
+ + vmWatcher.getAbsolutePath()
+ + ") missing, location not writable,"
+ + " testcase not started or mixing ant versions?";
+ }
+ } catch (final Exception e) {
+ e.printStackTrace();
+ // ignored.
+ } finally {
+ FileUtils.close(br);
+ if (vmWatcher.exists()) {
+ FILE_UTILS.tryHardToDelete(vmWatcher);
+ }
+ }
+
+ final boolean crash = (watchdog != null && watchdog.killedProcess())
+ || !Constants.TERMINATED_SUCCESSFULLY.equals(vmCrashString);
+
+ if (casesFile != null && crash) {
+ test = createDummyTestForBatchTest(test);
+ }
+
+ if (watchdog != null && watchdog.killedProcess()) {
+ result.timedOut = true;
+ logTimeout(feArray, test, vmCrashString);
+ } else if (crash) {
+ result.crashed = true;
+ logVmCrash(feArray, test, vmCrashString);
+ }
+
+ if (!FILE_UTILS.tryHardToDelete(propsFile)) {
+ throw new BuildException("Could not delete temporary "
+ + "properties file '"
+ + propsFile.getAbsolutePath() + "'.");
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Adding ant runtime.
+ * @param cmd command to run
+ */
+ private void checkIncludeAntRuntime(final CommandlineJava cmd) {
+ if (includeAntRuntime) {
+ final Map/*<String, String>*/ env = Execute.getEnvironmentVariables();
+ final String cp = (String) env.get(CLASSPATH);
+ if (cp != null) {
+ cmd.createClasspath(getProject()).createPath()
+ .append(new Path(getProject(), cp));
+ }
+ log("Implicitly adding " + antRuntimeClasses + " to CLASSPATH",
+ Project.MSG_VERBOSE);
+ cmd.createClasspath(getProject()).createPath()
+ .append(antRuntimeClasses);
+ }
+ }
+
+
+ /**
+ * check for the parameter being "withoutanderr" in a locale-independent way.
+ * @param summaryOption the summary option -can be null
+ * @return true if the run should be withoutput and error
+ */
+ private boolean equalsWithOutAndErr(final String summaryOption) {
+ return "withoutanderr".equalsIgnoreCase(summaryOption);
+ }
+
+ private void checkIncludeSummary(final CommandlineJava cmd) {
+ if (summary) {
+ String prefix = "";
+ if (equalsWithOutAndErr(summaryValue)) {
+ prefix = "OutErr";
+ }
+ cmd.createArgument()
+ .setValue(Constants.FORMATTER
+ + "org.apache.tools.ant.taskdefs.optional.junit."
+ + prefix + "SummaryJUnitResultFormatter");
+ }
+ }
+
+ /**
+ * Check the path for multiple different versions of
+ * ant.
+ * @param cmd command to execute
+ */
+ private void checkForkedPath(final CommandlineJava cmd) {
+ if (forkedPathChecked) {
+ return;
+ }
+ forkedPathChecked = true;
+ if (!cmd.haveClasspath()) {
+ return;
+ }
+ AntClassLoader loader = null;
+ try {
+ loader =
+ AntClassLoader.newAntClassLoader(null, getProject(),
+ cmd.createClasspath(getProject()),
+ true);
+ final String projectResourceName =
+ LoaderUtils.classNameToResource(Project.class.getName());
+ URL previous = null;
+ try {
+ for (final Enumeration e = loader.getResources(projectResourceName);
+ e.hasMoreElements();) {
+ final URL current = (URL) e.nextElement();
+ if (previous != null && !urlEquals(current, previous)) {
+ log("WARNING: multiple versions of ant detected "
+ + "in path for junit "
+ + LINE_SEP + " " + previous
+ + LINE_SEP + " and " + current,
+ Project.MSG_WARN);
+ return;
+ }
+ previous = current;
+ }
+ } catch (final Exception ex) {
+ // Ignore exception
+ }
+ } finally {
+ if (loader != null) {
+ loader.cleanup();
+ }
+ }
+ }
+
+ /**
+ * Compares URLs for equality but takes case-sensitivity into
+ * account when comparing file URLs and ignores the jar specific
+ * part of the URL if present.
+ */
+ private static boolean urlEquals(final URL u1, final URL u2) {
+ final String url1 = maybeStripJarAndClass(u1);
+ final String url2 = maybeStripJarAndClass(u2);
+ if (url1.startsWith("file:") && url2.startsWith("file:")) {
+ return new File(FILE_UTILS.fromURI(url1))
+ .equals(new File(FILE_UTILS.fromURI(url2)));
+ }
+ return url1.equals(url2);
+ }
+
+ private static String maybeStripJarAndClass(final URL u) {
+ String s = u.toString();
+ if (s.startsWith("jar:")) {
+ final int pling = s.indexOf('!');
+ s = s.substring(4, pling == -1 ? s.length() : pling);
+ }
+ return s;
+ }
+
+ /**
+ * Create a temporary file to pass the properties to a new process.
+ * Will auto-delete on (graceful) exit.
+ * The file will be in the project basedir unless tmpDir declares
+ * something else.
+ * @param prefix
+ * @return created file
+ */
+ private File createTempPropertiesFile(final String prefix) {
+ final File propsFile =
+ FILE_UTILS.createTempFile(prefix, ".properties",
+ tmpDir != null ? tmpDir : getProject().getBaseDir(), true, true);
+ return propsFile;
+ }
+
+
+ /**
+ * Pass output sent to System.out to the TestRunner so it can
+ * collect it for the formatters.
+ *
+ * @param output output coming from System.out
+ * @since Ant 1.5
+ */
+ @Override
+ protected void handleOutput(final String output) {
+ if (output.startsWith(TESTLISTENER_PREFIX)) {
+ log(output, Project.MSG_VERBOSE);
+ } else if (runner != null) {
+ if (outputToFormatters) {
+ runner.handleOutput(output);
+ }
+ if (showOutput) {
+ super.handleOutput(output);
+ }
+ } else {
+ super.handleOutput(output);
+ }
+ }
+
+ /**
+ * Handle an input request by this task.
+ * @see Task#handleInput(byte[], int, int)
+ * This implementation delegates to a runner if it
+ * present.
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read.
+ *
+ * @return the number of bytes read.
+ * @exception IOException if the data cannot be read.
+ *
+ * @since Ant 1.6
+ */
+ @Override
+ protected int handleInput(final byte[] buffer, final int offset, final int length)
+ throws IOException {
+ if (runner != null) {
+ return runner.handleInput(buffer, offset, length);
+ } else {
+ return super.handleInput(buffer, offset, length);
+ }
+ }
+
+
+ /**
+ * Pass output sent to System.out to the TestRunner so it can
+ * collect ot for the formatters.
+ *
+ * @param output output coming from System.out
+ * @since Ant 1.5.2
+ */
+ @Override
+ protected void handleFlush(final String output) {
+ if (runner != null) {
+ runner.handleFlush(output);
+ if (showOutput) {
+ super.handleFlush(output);
+ }
+ } else {
+ super.handleFlush(output);
+ }
+ }
+
+ /**
+ * Pass output sent to System.err to the TestRunner so it can
+ * collect it for the formatters.
+ *
+ * @param output output coming from System.err
+ * @since Ant 1.5
+ */
+ @Override
+ public void handleErrorOutput(final String output) {
+ if (runner != null) {
+ runner.handleErrorOutput(output);
+ if (showOutput) {
+ super.handleErrorOutput(output);
+ }
+ } else {
+ super.handleErrorOutput(output);
+ }
+ }
+
+
+ /**
+ * Pass output sent to System.err to the TestRunner so it can
+ * collect it for the formatters.
+ *
+ * @param output coming from System.err
+ * @since Ant 1.5.2
+ */
+ @Override
+ public void handleErrorFlush(final String output) {
+ if (runner != null) {
+ runner.handleErrorFlush(output);
+ if (showOutput) {
+ super.handleErrorFlush(output);
+ }
+ } else {
+ super.handleErrorFlush(output);
+ }
+ }
+
+ // in VM is not very nice since it could probably hang the
+ // whole build. IMHO this method should be avoided and it would be best
+ // to remove it in future versions. TBD. (SBa)
+
+ /**
+ * Execute inside VM.
+ * @param arg one JUnitTest
+ * @throws BuildException under unspecified circumstances
+ * @return the results
+ */
+ private TestResultHolder executeInVM(final JUnitTest arg) throws BuildException {
+ if (delegate == null) {
+ setupJUnitDelegate();
+ }
+
+ final JUnitTest test = (JUnitTest) arg.clone();
+ test.setProperties(getProject().getProperties());
+ if (dir != null) {
+ log("dir attribute ignored if running in the same VM",
+ Project.MSG_WARN);
+ }
+
+ if (newEnvironment || null != env.getVariables()) {
+ log("Changes to environment variables are ignored if running in "
+ + "the same VM.", Project.MSG_WARN);
+ }
+
+ if (getCommandline().getBootclasspath() != null) {
+ log("bootclasspath is ignored if running in the same VM.",
+ Project.MSG_WARN);
+ }
+
+ final CommandlineJava.SysProperties sysProperties =
+ getCommandline().getSystemProperties();
+ if (sysProperties != null) {
+ sysProperties.setSystem();
+ }
+
+ try {
+ log("Using System properties " + System.getProperties(),
+ Project.MSG_VERBOSE);
+ if (splitJUnit) {
+ classLoader = (AntClassLoader) delegate.getClass().getClassLoader();
+ } else {
+ createClassLoader();
+ }
+ if (classLoader != null) {
+ classLoader.setThreadContextLoader();
+ }
+ runner = delegate.newJUnitTestRunner(test, test.getMethods(), test.getHaltonerror(),
+ test.getFiltertrace(),
+ test.getHaltonfailure(), false,
+ getEnableTestListenerEvents(),
+ classLoader);
+ if (summary) {
+
+ final JUnitTaskMirror.SummaryJUnitResultFormatterMirror f =
+ delegate.newSummaryJUnitResultFormatter();
+ f.setWithOutAndErr(equalsWithOutAndErr(summaryValue));
+ f.setOutput(getDefaultOutput());
+ runner.addFormatter(f);
+ }
+
+ runner.setPermissions(perm);
+
+ final FormatterElement[] feArray = mergeFormatters(test);
+ for (int i = 0; i < feArray.length; i++) {
+ final FormatterElement fe = feArray[i];
+ if (fe.shouldUse(this)) {
+ final File outFile = getOutput(fe, test);
+ if (outFile != null) {
+ fe.setOutfile(outFile);
+ } else {
+ fe.setOutput(getDefaultOutput());
+ }
+ runner.addFormatter(fe.createFormatter(classLoader));
+ }
+ }
+
+ runner.run();
+ final TestResultHolder result = new TestResultHolder();
+ result.exitCode = runner.getRetCode();
+ return result;
+ } finally {
+ if (sysProperties != null) {
+ sysProperties.restoreSystem();
+ }
+ if (classLoader != null) {
+ classLoader.resetThreadContextLoader();
+ }
+ }
+ }
+
+ /**
+ * @return <tt>null</tt> if there is a timeout value, otherwise the
+ * watchdog instance.
+ *
+ * @throws BuildException under unspecified circumstances
+ * @since Ant 1.2
+ */
+ protected ExecuteWatchdog createWatchdog() throws BuildException {
+ if (timeout == null) {
+ return null;
+ }
+ return new ExecuteWatchdog((long) timeout.intValue());
+ }
+
+ /**
+ * Get the default output for a formatter.
+ *
+ * @return default output stream for a formatter
+ * @since Ant 1.3
+ */
+ protected OutputStream getDefaultOutput() {
+ return new LogOutputStream(this, Project.MSG_INFO);
+ }
+
+ /**
+ * Merge all individual tests from the batchtest with all individual tests
+ * and return an enumeration over all <tt>JUnitTest</tt>.
+ *
+ * @return enumeration over individual tests
+ * @since Ant 1.3
+ */
+ protected Enumeration<JUnitTest> getIndividualTests() {
+ final int count = batchTests.size();
+ final Enumeration[] enums = new Enumeration[ count + 1];
+ for (int i = 0; i < count; i++) {
+ final BatchTest batchtest = batchTests.elementAt(i);
+ enums[i] = batchtest.elements();
+ }
+ enums[enums.length - 1] = tests.elements();
+ return Enumerations.fromCompound(enums);
+ }
+
+ /**
+ * Verifies all <code>test</code> elements having the <code>methods</code>
+ * attribute specified and having the <code>if</code>-condition resolved
+ * to true, that the value of the <code>methods</code> attribute is valid.
+ * @exception BuildException if some of the tests matching the described
+ * conditions has invalid value of the
+ * <code>methods</code> attribute
+ * @since 1.8.2
+ */
+ private void checkMethodLists() throws BuildException {
+ if (tests.isEmpty()) {
+ return;
+ }
+
+ final Enumeration<JUnitTest> testsEnum = tests.elements();
+ while (testsEnum.hasMoreElements()) {
+ final JUnitTest test = testsEnum.nextElement();
+ if (test.hasMethodsSpecified() && test.shouldRun(getProject())) {
+ test.resolveMethods();
+ }
+ }
+ }
+
+ /**
+ * return an enumeration listing each test, then each batchtest
+ * @return enumeration
+ * @since Ant 1.3
+ */
+ protected Enumeration<JUnitTest> allTests() {
+ final Enumeration[] enums = {tests.elements(), batchTests.elements()};
+ return Enumerations.fromCompound(enums);
+ }
+
+ /**
+ * @param test junit test
+ * @return array of FormatterElement
+ * @since Ant 1.3
+ */
+ private FormatterElement[] mergeFormatters(final JUnitTest test) {
+ final Vector<FormatterElement> feVector = (Vector<FormatterElement>) formatters.clone();
+ test.addFormattersTo(feVector);
+ final FormatterElement[] feArray = new FormatterElement[feVector.size()];
+ feVector.copyInto(feArray);
+ return feArray;
+ }
+
+ /**
+ * If the formatter sends output to a file, return that file.
+ * null otherwise.
+ * @param fe formatter element
+ * @param test one JUnit test
+ * @return file reference
+ * @since Ant 1.3
+ */
+ protected File getOutput(final FormatterElement fe, final JUnitTest test) {
+ if (fe.getUseFile()) {
+ String base = test.getOutfile();
+ if (base == null) {
+ base = JUnitTaskMirror.JUnitTestRunnerMirror.IGNORED_FILE_NAME;
+ }
+ final String filename = base + fe.getExtension();
+ final File destFile = new File(test.getTodir(), filename);
+ final String absFilename = destFile.getAbsolutePath();
+ return getProject().resolveFile(absFilename);
+ }
+ return null;
+ }
+
+ /**
+ * Search for the given resource and add the directory or archive
+ * that contains it to the classpath.
+ *
+ * <p>Doesn't work for archives in JDK 1.1 as the URL returned by
+ * getResource doesn't contain the name of the archive.</p>
+ *
+ * @param resource resource that one wants to lookup
+ * @since Ant 1.4
+ */
+ protected void addClasspathEntry(final String resource) {
+ addClasspathResource(resource);
+ }
+
+ /**
+ * Implementation of addClasspathEntry.
+ *
+ * @param resource resource that one wants to lookup
+ * @return true if something was in fact added
+ * @since Ant 1.7.1
+ */
+ private boolean addClasspathResource(String resource) {
+ /*
+ * pre Ant 1.6 this method used to call getClass().getResource
+ * while Ant 1.6 will call ClassLoader.getResource().
+ *
+ * The difference is that Class.getResource expects a leading
+ * slash for "absolute" resources and will strip it before
+ * delegating to ClassLoader.getResource - so we now have to
+ * emulate Class's behavior.
+ */
+ if (resource.startsWith("/")) {
+ resource = resource.substring(1);
+ } else {
+ resource = "org/apache/tools/ant/taskdefs/optional/junit/"
+ + resource;
+ }
+
+ final File f = LoaderUtils.getResourceSource(JUnitTask.class.getClassLoader(),
+ resource);
+ if (f != null) {
+ log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG);
+ antRuntimeClasses.createPath().setLocation(f);
+ return true;
+ } else {
+ log("Couldn\'t find " + resource, Project.MSG_DEBUG);
+ return false;
+ }
+ }
+
+ static final String TIMEOUT_MESSAGE =
+ "Timeout occurred. Please note the time in the report does"
+ + " not reflect the time until the timeout.";
+
+ /**
+ * Take care that some output is produced in report files if the
+ * watchdog kills the test.
+ *
+ * @since Ant 1.5.2
+ */
+ private void logTimeout(final FormatterElement[] feArray, final JUnitTest test,
+ final String testCase) {
+ logVmExit(feArray, test, TIMEOUT_MESSAGE, testCase);
+ }
+
+ /**
+ * Take care that some output is produced in report files if the
+ * forked machine exited before the test suite finished but the
+ * reason is not a timeout.
+ *
+ * @since Ant 1.7
+ */
+ private void logVmCrash(final FormatterElement[] feArray, final JUnitTest test, final String testCase) {
+ logVmExit(
+ feArray, test,
+ "Forked Java VM exited abnormally. Please note the time in the report"
+ + " does not reflect the time until the VM exit.",
+ testCase);
+ }
+
+ /**
+ * Take care that some output is produced in report files if the
+ * forked machine terminated before the test suite finished
+ *
+ * @since Ant 1.7
+ */
+ private void logVmExit(final FormatterElement[] feArray, final JUnitTest test,
+ final String message, final String testCase) {
+ if (delegate == null) {
+ setupJUnitDelegate();
+ }
+
+ try {
+ log("Using System properties " + System.getProperties(),
+ Project.MSG_VERBOSE);
+ if (splitJUnit) {
+ classLoader = (AntClassLoader) delegate.getClass().getClassLoader();
+ } else {
+ createClassLoader();
+ }
+ if (classLoader != null) {
+ classLoader.setThreadContextLoader();
+ }
+
+ test.setCounts(1, 0, 1, 0);
+ test.setProperties(getProject().getProperties());
+ for (int i = 0; i < feArray.length; i++) {
+ final FormatterElement fe = feArray[i];
+ if (fe.shouldUse(this)) {
+ final JUnitTaskMirror.JUnitResultFormatterMirror formatter =
+ fe.createFormatter(classLoader);
+ if (formatter != null) {
+ OutputStream out = null;
+ final File outFile = getOutput(fe, test);
+ if (outFile != null) {
+ try {
+ out = new FileOutputStream(outFile);
+ } catch (final IOException e) {
+ // ignore
+ }
+ }
+ if (out == null) {
+ out = getDefaultOutput();
+ }
+ delegate.addVmExit(test, formatter, out, message,
+ testCase);
+ }
+ }
+ }
+ if (summary) {
+ final JUnitTaskMirror.SummaryJUnitResultFormatterMirror f =
+ delegate.newSummaryJUnitResultFormatter();
+ f.setWithOutAndErr(equalsWithOutAndErr(summaryValue));
+ delegate.addVmExit(test, f, getDefaultOutput(), message, testCase);
+ }
+ } finally {
+ if (classLoader != null) {
+ classLoader.resetThreadContextLoader();
+ }
+ }
+ }
+
+ /**
+ * Creates and configures an AntClassLoader instance from the
+ * nested classpath element.
+ *
+ * @since Ant 1.6
+ */
+ private void createClassLoader() {
+ final Path userClasspath = getCommandline().getClasspath();
+ if (userClasspath != null) {
+ if (reloading || classLoader == null) {
+ deleteClassLoader();
+ final Path classpath = (Path) userClasspath.clone();
+ if (includeAntRuntime) {
+ log("Implicitly adding " + antRuntimeClasses
+ + " to CLASSPATH", Project.MSG_VERBOSE);
+ classpath.append(antRuntimeClasses);
+ }
+ classLoader = getProject().createClassLoader(classpath);
+ if (getClass().getClassLoader() != null
+ && getClass().getClassLoader() != Project.class.getClassLoader()) {
+ classLoader.setParent(getClass().getClassLoader());
+ }
+ classLoader.setParentFirst(false);
+ classLoader.addJavaLibraries();
+ log("Using CLASSPATH " + classLoader.getClasspath(),
+ Project.MSG_VERBOSE);
+ // make sure the test will be accepted as a TestCase
+ classLoader.addSystemPackageRoot("junit");
+ // make sure the test annotation are accepted
+ classLoader.addSystemPackageRoot("org.junit");
+ // will cause trouble in JDK 1.1 if omitted
+ classLoader.addSystemPackageRoot("org.apache.tools.ant");
+ }
+ }
+ }
+
+ /**
+ * Removes resources.
+ *
+ * <p>Is invoked in {@link #execute execute}. Subclasses that
+ * don't invoke execute should invoke this method in a finally
+ * block.</p>
+ *
+ * @since Ant 1.7.1
+ */
+ protected void cleanup() {
+ deleteClassLoader();
+ delegate = null;
+ }
+
+ /**
+ * Removes a classloader if needed.
+ * @since Ant 1.7
+ */
+ private void deleteClassLoader() {
+ if (classLoader != null) {
+ classLoader.cleanup();
+ classLoader = null;
+ }
+ if (mirrorLoader instanceof SplitClassLoader) {
+ ((SplitClassLoader) mirrorLoader).cleanup();
+ }
+ mirrorLoader = null;
+ }
+
+ /**
+ * Get the command line used to run the tests.
+ * @return the command line.
+ * @since Ant 1.6.2
+ */
+ protected CommandlineJava getCommandline() {
+ if (commandline == null) {
+ commandline = new CommandlineJava();
+ commandline.setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
+ }
+ return commandline;
+ }
+
+ /**
+ * Forked test support
+ * @since Ant 1.6.2
+ */
+ private static final class ForkedTestConfiguration {
+ private final boolean filterTrace;
+ private final boolean haltOnError;
+ private final boolean haltOnFailure;
+ private final String errorProperty;
+ private final String failureProperty;
+
+ /**
+ * constructor for forked test configuration
+ * @param filterTrace
+ * @param haltOnError
+ * @param haltOnFailure
+ * @param errorProperty
+ * @param failureProperty
+ */
+ ForkedTestConfiguration(final boolean filterTrace, final boolean haltOnError,
+ final boolean haltOnFailure, final String errorProperty,
+ final String failureProperty) {
+ this.filterTrace = filterTrace;
+ this.haltOnError = haltOnError;
+ this.haltOnFailure = haltOnFailure;
+ this.errorProperty = errorProperty;
+ this.failureProperty = failureProperty;
+ }
+
+ /**
+ * configure from a test; sets member variables to attributes of the test
+ * @param test
+ */
+ ForkedTestConfiguration(final JUnitTest test) {
+ this(test.getFiltertrace(),
+ test.getHaltonerror(),
+ test.getHaltonfailure(),
+ test.getErrorProperty(),
+ test.getFailureProperty());
+ }
+
+ /**
+ * equality test checks all the member variables
+ * @param other
+ * @return true if everything is equal
+ */
+ @Override
+ public boolean equals(final Object other) {
+ if (other == null
+ || other.getClass() != ForkedTestConfiguration.class) {
+ return false;
+ }
+ final ForkedTestConfiguration o = (ForkedTestConfiguration) other;
+ return filterTrace == o.filterTrace
+ && haltOnError == o.haltOnError
+ && haltOnFailure == o.haltOnFailure
+ && ((errorProperty == null && o.errorProperty == null)
+ ||
+ (errorProperty != null
+ && errorProperty.equals(o.errorProperty)))
+ && ((failureProperty == null && o.failureProperty == null)
+ ||
+ (failureProperty != null
+ && failureProperty.equals(o.failureProperty)));
+ }
+
+ /**
+ * hashcode is based only on the boolean members, and returns a value
+ * in the range 0-7.
+ * @return hash code value
+ */
+ @Override
+ public int hashCode() {
+ // CheckStyle:MagicNumber OFF
+ return (filterTrace ? 1 : 0)
+ + (haltOnError ? 2 : 0)
+ + (haltOnFailure ? 4 : 0);
+ // CheckStyle:MagicNumber ON
+ }
+ }
+
+ /**
+ * These are the different forking options
+ * @since 1.6.2
+ */
+ public static final class ForkMode extends EnumeratedAttribute {
+
+ /**
+ * fork once only
+ */
+ public static final String ONCE = "once";
+ /**
+ * fork once per test class
+ */
+ public static final String PER_TEST = "perTest";
+ /**
+ * fork once per batch of tests
+ */
+ public static final String PER_BATCH = "perBatch";
+
+ /** No arg constructor. */
+ public ForkMode() {
+ super();
+ }
+
+ /**
+ * Constructor using a value.
+ * @param value the value to use - once, perTest or perBatch.
+ */
+ public ForkMode(final String value) {
+ super();
+ setValue(value);
+ }
+
+ /** {@inheritDoc}. */
+ @Override
+ public String[] getValues() {
+ return new String[] {ONCE, PER_TEST, PER_BATCH};
+ }
+ }
+
+ /**
+ * Executes all tests that don't need to be forked (or all tests
+ * if the runIndividual argument is true. Returns a collection of
+ * lists of tests that share the same VM configuration and haven't
+ * been executed yet.
+ * @param testList the list of tests to be executed or queued.
+ * @param runIndividual if true execute each test individually.
+ * @return a list of tasks to be executed.
+ * @since 1.6.2
+ */
+ protected Collection<List> executeOrQueue(final Enumeration<JUnitTest> testList,
+ final boolean runIndividual) {
+ final Map<ForkedTestConfiguration, List> testConfigurations = new HashMap<ForkedTestConfiguration, List>();
+ while (testList.hasMoreElements()) {
+ final JUnitTest test = testList.nextElement();
+ if (test.shouldRun(getProject())) {
+ /* with multi-threaded runs need to defer execution of even */
+ /* individual tests so the threads can pick tests off the queue. */
+ if ((runIndividual || !test.getFork()) && (threads == 1)) {
+ execute(test, 0);
+ } else {
+ final ForkedTestConfiguration c =
+ new ForkedTestConfiguration(test);
+ List<JUnitTest> l = testConfigurations.get(c);
+ if (l == null) {
+ l = new ArrayList<JUnitTest>();
+ testConfigurations.put(c, l);
+ }
+ l.add(test);
+ }
+ }
+ }
+ return testConfigurations.values();
+ }
+
+ /**
+ * Logs information about failed tests, potentially stops
+ * processing (by throwing a BuildException) if a failure/error
+ * occurred or sets a property.
+ * @param exitValue the exitValue of the test.
+ * @param wasKilled if true, the test had been killed.
+ * @param test the test in question.
+ * @param name the name of the test.
+ * @since Ant 1.6.2
+ */
+ protected void actOnTestResult(final int exitValue, final boolean wasKilled,
+ final JUnitTest test, final String name) {
+ final TestResultHolder t = new TestResultHolder();
+ t.exitCode = exitValue;
+ t.timedOut = wasKilled;
+ actOnTestResult(t, test, name);
+ }
+
+ /**
+ * Logs information about failed tests, potentially stops
+ * processing (by throwing a BuildException) if a failure/error
+ * occurred or sets a property.
+ * @param result the result of the test.
+ * @param test the test in question.
+ * @param name the name of the test.
+ * @since Ant 1.7
+ */
+ protected void actOnTestResult(final TestResultHolder result, final JUnitTest test,
+ final String name) {
+ // if there is an error/failure and that it should halt, stop
+ // everything otherwise just log a statement
+ final boolean fatal = result.timedOut || result.crashed;
+ final boolean errorOccurredHere =
+ result.exitCode == JUnitTaskMirror.JUnitTestRunnerMirror.ERRORS || fatal;
+ final boolean failureOccurredHere =
+ result.exitCode != JUnitTaskMirror.JUnitTestRunnerMirror.SUCCESS || fatal;
+ if (errorOccurredHere || failureOccurredHere) {
+ if ((errorOccurredHere && test.getHaltonerror())
+ || (failureOccurredHere && test.getHaltonfailure())) {
+ throw new BuildException(name + " failed"
+ + (result.timedOut ? " (timeout)" : "")
+ + (result.crashed ? " (crashed)" : ""), getLocation());
+ } else {
+ if (logFailedTests) {
+ log(name + " FAILED"
+ + (result.timedOut ? " (timeout)" : "")
+ + (result.crashed ? " (crashed)" : ""),
+ Project.MSG_ERR);
+ }
+ if (errorOccurredHere && test.getErrorProperty() != null) {
+ getProject().setNewProperty(test.getErrorProperty(), "true");
+ }
+ if (failureOccurredHere && test.getFailureProperty() != null) {
+ getProject().setNewProperty(test.getFailureProperty(), "true");
+ }
+ }
+ }
+ }
+
+ /**
+ * A value class that contains the result of a test.
+ */
+ protected static class TestResultHolder {
+ // CheckStyle:VisibilityModifier OFF - bc
+ /** the exit code of the test. */
+ public int exitCode = JUnitTaskMirror.JUnitTestRunnerMirror.ERRORS;
+ /** true if the test timed out */
+ public boolean timedOut = false;
+ /** true if the test crashed */
+ public boolean crashed = false;
+ // CheckStyle:VisibilityModifier ON
+ }
+
+ /**
+ * A stream handler for handling the junit task.
+ * @since Ant 1.7
+ */
+ protected static class JUnitLogOutputStream extends LogOutputStream {
+ private final Task task; // local copy since LogOutputStream.task is private
+
+ /**
+ * Constructor.
+ * @param task the task being logged.
+ * @param level the log level used to log data written to this stream.
+ */
+ public JUnitLogOutputStream(final Task task, final int level) {
+ super(task, level);
+ this.task = task;
+ }
+
+ /**
+ * Logs a line.
+ * If the line starts with junit.framework.TestListener: set the level
+ * to MSG_VERBOSE.
+ * @param line the line to log.
+ * @param level the logging level to use.
+ */
+ @Override
+ protected void processLine(final String line, final int level) {
+ if (line.startsWith(TESTLISTENER_PREFIX)) {
+ task.log(line, Project.MSG_VERBOSE);
+ } else {
+ super.processLine(line, level);
+ }
+ }
+ }
+
+ /**
+ * A log stream handler for junit.
+ * @since Ant 1.7
+ */
+ protected static class JUnitLogStreamHandler extends PumpStreamHandler {
+ /**
+ * Constructor.
+ * @param task the task to log.
+ * @param outlevel the level to use for standard output.
+ * @param errlevel the level to use for error output.
+ */
+ public JUnitLogStreamHandler(final Task task, final int outlevel, final int errlevel) {
+ super(new JUnitLogOutputStream(task, outlevel),
+ new LogOutputStream(task, errlevel));
+ }
+ }
+
+ static final String NAME_OF_DUMMY_TEST = "Batch-With-Multiple-Tests";
+
+ /**
+ * Creates a JUnitTest instance that shares all flags with the
+ * passed in instance but has a more meaningful name.
+ *
+ * <p>If a VM running multiple tests crashes, we don't know which
+ * test failed. Prior to Ant 1.8.0 Ant would log the error with
+ * the last test of the batch test, which caused some confusion
+ * since the log might look as if a test had been executed last
+ * that was never started. With Ant 1.8.0 the test's name will
+ * indicate that something went wrong with a test inside the batch
+ * without giving it a real name.</p>
+ *
+ * @see "https://issues.apache.org/bugzilla/show_bug.cgi?id=45227"
+ */
+ private static JUnitTest createDummyTestForBatchTest(final JUnitTest test) {
+ final JUnitTest t = (JUnitTest) test.clone();
+ final int index = test.getName().lastIndexOf('.');
+ // make sure test looks as if it was in the same "package" as
+ // the last test of the batch
+ final String pack = index > 0 ? test.getName().substring(0, index + 1) : "";
+ t.setName(pack + NAME_OF_DUMMY_TEST);
+ return t;
+ }
+
+ private static void printDual(final BufferedWriter w, final PrintStream s, final String text)
+ throws IOException {
+ w.write(String.valueOf(text));
+ s.print(text);
+ }
+
+ private static void printlnDual(final BufferedWriter w, final PrintStream s, final String text)
+ throws IOException {
+ w.write(String.valueOf(text));
+ w.newLine();
+ s.println(text);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirror.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirror.java
new file mode 100644
index 00000000..694e1d8c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirror.java
@@ -0,0 +1,190 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.types.Permissions;
+
+/**
+ * Handles the portions of {@link JUnitTask} which need to directly access
+ * actual JUnit classes, so that junit.jar need not be on Ant's startup classpath.
+ * Neither JUnitTask.java nor JUnitTaskMirror.java nor their transitive static
+ * deps may import any junit.** classes!
+ * Specifically, need to not refer to
+ * - JUnitResultFormatter or its subclasses
+ * - JUnitVersionHelper
+ * - JUnitTestRunner
+ * Cf. JUnitTask.SplitLoader#isSplit(String)
+ * Public only to permit access from classes in this package; do not use directly.
+ *
+ * @since 1.7
+ * @see "bug #38799"
+ */
+public interface JUnitTaskMirror {
+
+ /**
+ * Add the formatter to be called when the jvm exits before
+ * the test suite finishes.
+ * @param test the test.
+ * @param formatter the formatter to use.
+ * @param out the output stream to use.
+ * @param message the message to write out.
+ * @param testCase the name of the test.
+ */
+ void addVmExit(JUnitTest test, JUnitResultFormatterMirror formatter,
+ OutputStream out, String message, String testCase);
+
+ /**
+ * Create a new test runner for a test.
+ * @param test the test to run.
+ * @param methods names of the test methods to be run.
+ * @param haltOnError if true halt the tests if an error occurs.
+ * @param filterTrace if true filter the stack traces.
+ * @param haltOnFailure if true halt the test if a failure occurs.
+ * @param showOutput if true show output.
+ * @param logTestListenerEvents if true log test listener events.
+ * @param classLoader the classloader to use to create the runner.
+ * @return the test runner.
+ */
+ JUnitTestRunnerMirror newJUnitTestRunner(JUnitTest test, String[] methods, boolean haltOnError,
+ boolean filterTrace, boolean haltOnFailure, boolean showOutput,
+ boolean logTestListenerEvents, AntClassLoader classLoader);
+
+ /**
+ * Create a summary result formatter.
+ * @return the created formatter.
+ */
+ SummaryJUnitResultFormatterMirror newSummaryJUnitResultFormatter();
+
+
+ /** The interface that JUnitResultFormatter extends. */
+ public interface JUnitResultFormatterMirror {
+ /**
+ * Set the output stream.
+ * @param outputStream the stream to use.
+ */
+ void setOutput(OutputStream outputStream);
+ }
+
+ /** The interface that SummaryJUnitResultFormatter extends. */
+ public interface SummaryJUnitResultFormatterMirror
+ extends JUnitResultFormatterMirror {
+
+ /**
+ * Set where standard out and standard error should be included.
+ * @param value if true include the outputs in the summary.
+ */
+ void setWithOutAndErr(boolean value);
+ }
+
+ /** Interface that test runners implement. */
+ public interface JUnitTestRunnerMirror {
+
+ /**
+ * Used in formatter arguments as a placeholder for the basename
+ * of the output file (which gets replaced by a test specific
+ * output file name later).
+ *
+ * @since Ant 1.6.3
+ */
+ String IGNORED_FILE_NAME = "IGNORETHIS";
+
+ /**
+ * No problems with this test.
+ */
+ int SUCCESS = 0;
+
+ /**
+ * Some tests failed.
+ */
+ int FAILURES = 1;
+
+ /**
+ * An error occurred.
+ */
+ int ERRORS = 2;
+
+ /**
+ * Permissions for the test run.
+ * @param perm the permissions to use.
+ */
+ void setPermissions(Permissions perm);
+
+ /** Run the test. */
+ void run();
+
+ /**
+ * Add a formatter to the test.
+ * @param formatter the formatter to use.
+ */
+ void addFormatter(JUnitResultFormatterMirror formatter);
+
+ /**
+ * Returns what System.exit() would return in the standalone version.
+ *
+ * @return 2 if errors occurred, 1 if tests failed else 0.
+ */
+ int getRetCode();
+
+ /**
+ * Handle output sent to System.err.
+ *
+ * @param output coming from System.err
+ */
+ void handleErrorFlush(String output);
+
+ /**
+ * Handle output sent to System.err.
+ *
+ * @param output output for System.err
+ */
+ void handleErrorOutput(String output);
+
+ /**
+ * Handle output sent to System.out.
+ *
+ * @param output output for System.out.
+ */
+ void handleOutput(String output);
+
+ /**
+ * Handle an input request.
+ *
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception IOException if the data cannot be read.
+ */
+ int handleInput(byte[] buffer, int offset, int length) throws IOException;
+
+ /**
+ * Handle output sent to System.out.
+ *
+ * @param output output for System.out.
+ */
+ void handleFlush(String output);
+
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirrorImpl.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirrorImpl.java
new file mode 100644
index 00000000..c7dae258
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirrorImpl.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.tools.ant.taskdefs.optional.junit;
+
+import java.io.OutputStream;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+
+import org.apache.tools.ant.AntClassLoader;
+
+/**
+ * Implementation of the part of the junit task which can directly refer to junit.* classes.
+ * Public only to permit use of reflection; do not use directly.
+ * @see JUnitTaskMirror
+ * @see "bug #38799"
+ * @since 1.7
+ */
+public final class JUnitTaskMirrorImpl implements JUnitTaskMirror {
+
+ private final JUnitTask task;
+
+ /**
+ * Constructor.
+ * @param task the junittask that uses this mirror.
+ */
+ public JUnitTaskMirrorImpl(JUnitTask task) {
+ this.task = task;
+ }
+
+ /** {@inheritDoc}. */
+ public void addVmExit(JUnitTest test, JUnitTaskMirror.JUnitResultFormatterMirror aFormatter,
+ OutputStream out, String message, String testCase) {
+ JUnitResultFormatter formatter = (JUnitResultFormatter) aFormatter;
+ formatter.setOutput(out);
+ formatter.startTestSuite(test);
+ //the trick to integrating test output to the formatter, is to
+ //create a special test class that asserts an error
+ //and tell the formatter that it raised.
+ TestCase t = new VmExitErrorTest(message, test, testCase);
+ formatter.startTest(t);
+ formatter.addError(t, new AssertionFailedError(message));
+ formatter.endTestSuite(test);
+ }
+
+ /** {@inheritDoc}. */
+ public JUnitTaskMirror.JUnitTestRunnerMirror newJUnitTestRunner(JUnitTest test,
+ String[] methods,
+ boolean haltOnError, boolean filterTrace, boolean haltOnFailure,
+ boolean showOutput, boolean logTestListenerEvents, AntClassLoader classLoader) {
+ return new JUnitTestRunner(test, methods, haltOnError, filterTrace, haltOnFailure,
+ showOutput, logTestListenerEvents, classLoader);
+ }
+
+ /** {@inheritDoc}. */
+ public JUnitTaskMirror.SummaryJUnitResultFormatterMirror newSummaryJUnitResultFormatter() {
+ return new SummaryJUnitResultFormatter();
+ }
+
+ static class VmExitErrorTest extends TestCase {
+
+ private String message;
+ private JUnitTest test;
+ private String testCase;
+
+ VmExitErrorTest(String aMessage, JUnitTest anOriginalTest, String aTestCase) {
+ message = aMessage;
+ test = anOriginalTest;
+ testCase = aTestCase;
+ }
+
+ public int countTestCases() {
+ return 1;
+ }
+
+ public void run(TestResult r) {
+ throw new AssertionFailedError(message);
+ }
+
+ public String getName() {
+ return testCase;
+ }
+
+ String getClassName() {
+ return test.getName();
+ }
+
+ public String toString() {
+ return test.getName() + ":" + testCase;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java
new file mode 100644
index 00000000..835c013b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java
@@ -0,0 +1,542 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+
+/**
+ * <p> Run a single JUnit test.
+ *
+ * <p> The JUnit test is actually run by {@link JUnitTestRunner}.
+ * So read the doc comments for that class :)
+ *
+ * @since Ant 1.2
+ *
+ * @see JUnitTask
+ * @see JUnitTestRunner
+ */
+public class JUnitTest extends BaseTest implements Cloneable {
+
+ /** the name of the test case */
+ private String name = null;
+
+ /**
+ * whether the list of test methods has been specified
+ * @see #setMethods(java.lang.String)
+ * @see #setMethods(java.lang.String[])
+ */
+ private boolean methodsSpecified = false;
+
+ /** comma-separated list of names of test methods to execute */
+ private String methodsList = null;
+
+ /** the names of test methods to execute */
+ private String[] methods = null;
+
+ /** the name of the result file */
+ private String outfile = null;
+
+ // @todo this is duplicating TestResult information. Only the time is not
+ // part of the result. So we'd better derive a new class from TestResult
+ // and deal with it. (SB)
+ private long runs, failures, errors;
+ /**
+ @since Ant 1.9.0
+ */
+ private long skips;
+
+ private long runTime;
+
+ private int antThreadID;
+
+ // Snapshot of the system properties
+ private Properties props = null;
+
+ /** No arg constructor. */
+ public JUnitTest() {
+ }
+
+ /**
+ * Constructor with name.
+ * @param name the name of the test.
+ */
+ public JUnitTest(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Constructor with options.
+ * @param name the name of the test.
+ * @param haltOnError if true halt the tests if there is an error.
+ * @param haltOnFailure if true halt the tests if there is a failure.
+ * @param filtertrace if true filter stack traces.
+ */
+ public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure,
+ boolean filtertrace) {
+ this(name, haltOnError, haltOnFailure, filtertrace, null, 0);
+ }
+
+ /**
+ * Constructor with options.
+ * @param name the name of the test.
+ * @param haltOnError if true halt the tests if there is an error.
+ * @param haltOnFailure if true halt the tests if there is a failure.
+ * @param filtertrace if true filter stack traces.
+ * @param methods if non-null run only these test methods
+ * @since 1.8.2
+ */
+ public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure,
+ boolean filtertrace, String[] methods) {
+ this(name, haltOnError, haltOnFailure, filtertrace, methods, 0);
+ }
+
+ /**
+ * Constructor with options.
+ * @param name the name of the test.
+ * @param haltOnError if true halt the tests if there is an error.
+ * @param haltOnFailure if true halt the tests if there is a failure.
+ * @param filtertrace if true filter stack traces.
+ * @param methods if non-null run only these test methods
+ * @param thread Ant thread ID in which test is currently running
+ * @since 1.9.4
+ */
+ public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure,
+ boolean filtertrace, String[] methods, int thread) {
+ this.name = name;
+ this.haltOnError = haltOnError;
+ this.haltOnFail = haltOnFailure;
+ this.filtertrace = filtertrace;
+ this.methodsSpecified = methods != null;
+ this.methods = methodsSpecified ? (String[]) methods.clone() : null;
+ this.antThreadID = thread;
+ }
+
+ /**
+ * Sets names of individual test methods to be executed.
+ * @param value comma-separated list of names of individual test methods
+ * to be executed,
+ * or <code>null</code> if all test methods should be executed
+ * @since 1.8.2
+ */
+ public void setMethods(String value) {
+ methodsList = value;
+ methodsSpecified = (value != null);
+ methods = null;
+ }
+
+ /**
+ * Sets names of individual test methods to be executed.
+ * @param value non-empty array of names of test methods to be executed
+ * @see #setMethods(String)
+ * @since 1.8.2
+ */
+ void setMethods(String[] value) {
+ methods = value;
+ methodsSpecified = (value != null);
+ methodsList = null;
+ }
+
+ /**
+ * Set the name of the test class.
+ * @param value the name to use.
+ */
+ public void setName(String value) {
+ name = value;
+ }
+
+ /**
+ * Set the thread id
+ * @param thread the Ant id of the thread running this test
+ * (this is not the system process or thread id)
+ * (this will be 0 in single-threaded mode).
+ * @since Ant 1.9.4
+ */
+ public void setThread(int thread) {
+ this.antThreadID = thread;
+ }
+
+ /**
+ * Set the name of the output file.
+ * @param value the name of the output file to use.
+ */
+ public void setOutfile(String value) {
+ outfile = value;
+ }
+
+ /**
+ * Informs whether a list of test methods has been specified in this test.
+ * @return <code>true</code> if test methods to be executed have been
+ * specified, <code>false</code> otherwise
+ * @see #setMethods(java.lang.String)
+ * @see #setMethods(java.lang.String[])
+ * @since 1.8.2
+ */
+ boolean hasMethodsSpecified() {
+ return methodsSpecified;
+ }
+
+ /**
+ * Get names of individual test methods to be executed.
+ *
+ * @return array of names of the individual test methods to be executed,
+ * or <code>null</code> if all test methods in the suite
+ * defined by the test class will be executed
+ * @since 1.8.2
+ */
+ String[] getMethods() {
+ if (methodsSpecified && (methods == null)) {
+ resolveMethods();
+ }
+ return methods;
+ }
+
+ /**
+ * Gets a comma-separated list of names of methods that are to be executed
+ * by this test.
+ * @return the comma-separated list of test method names, or an empty
+ * string of no method is to be executed, or <code>null</code>
+ * if no method is specified
+ * @since 1.8.2
+ */
+ String getMethodsString() {
+ if ((methodsList == null) && methodsSpecified) {
+ if (methods.length == 0) {
+ methodsList = "";
+ } else if (methods.length == 1) {
+ methodsList = methods[0];
+ } else {
+ StringBuffer buf = new StringBuffer(methods.length * 16);
+ buf.append(methods[0]);
+ for (int i = 1; i < methods.length; i++) {
+ buf.append(',').append(methods[i]);
+ }
+ methodsList = buf.toString();
+ }
+ }
+ return methodsList;
+ }
+
+ /**
+ * Computes the value of the {@link #methods} field from the value
+ * of the {@link #methodsList} field, if it has not been computed yet.
+ * @exception BuildException if the value of the {@link #methodsList} field
+ * was invalid
+ * @since 1.8.2
+ */
+ void resolveMethods() {
+ if ((methods == null) && methodsSpecified) {
+ try {
+ methods = parseTestMethodNamesList(methodsList);
+ } catch (IllegalArgumentException ex) {
+ throw new BuildException(
+ "Invalid specification of test methods: \""
+ + methodsList
+ + "\"; expected: comma-separated list of valid Java identifiers",
+ ex);
+ }
+ }
+ }
+
+ /**
+ * Parses a comma-separated list of method names and check their validity.
+ * @param methodNames comma-separated list of method names to be parsed
+ * @return array of individual test method names
+ * @exception java.lang.IllegalArgumentException
+ * if the given string is <code>null</code> or if it is not
+ * a comma-separated list of valid Java identifiers;
+ * an empty string is acceptable and is handled as an empty
+ * list
+ * @since 1.8.2
+ */
+ public static String[] parseTestMethodNamesList(String methodNames)
+ throws IllegalArgumentException {
+ if (methodNames == null) {
+ throw new IllegalArgumentException("methodNames is <null>");
+ }
+
+ methodNames = methodNames.trim();
+
+ int length = methodNames.length();
+ if (length == 0) {
+ return new String[0];
+ }
+
+ /* strip the trailing comma, if any */
+ if (methodNames.charAt(length - 1) == ',') {
+ methodNames = methodNames.substring(0, length - 1).trim();
+ length = methodNames.length();
+ if (length == 0) {
+ throw new IllegalArgumentException("Empty method name");
+ }
+ }
+
+ final char[] chars = methodNames.toCharArray();
+ /* easy detection of one particular case of illegal string: */
+ if (chars[0] == ',') {
+ throw new IllegalArgumentException("Empty method name");
+ }
+ /* count number of method names: */
+ int wordCount = 1;
+ for (int i = 1; i < chars.length; i++) {
+ if (chars[i] == ',') {
+ wordCount++;
+ }
+ }
+ /* prepare the resulting array: */
+ String[] result = new String[wordCount];
+ /* parse the string: */
+ final int stateBeforeWord = 1;
+ final int stateInsideWord = 2;
+ final int stateAfterWord = 3;
+ //
+ int state = stateBeforeWord;
+ int wordStartIndex = -1;
+ int wordIndex = 0;
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+ switch (state) {
+ case stateBeforeWord:
+ if (c == ',') {
+ throw new IllegalArgumentException("Empty method name");
+ } else if (c == ' ') {
+ // remain in the same state
+ } else if (Character.isJavaIdentifierStart(c)) {
+ wordStartIndex = i;
+ state = stateInsideWord;
+ } else {
+ throw new IllegalArgumentException("Illegal start of method name: " + c);
+ }
+ break;
+ case stateInsideWord:
+ if (c == ',') {
+ result[wordIndex++] = methodNames.substring(wordStartIndex, i);
+ state = stateBeforeWord;
+ } else if (c == ' ') {
+ result[wordIndex++] = methodNames.substring(wordStartIndex, i);
+ state = stateAfterWord;
+ } else if (Character.isJavaIdentifierPart(c)) {
+ // remain in the same state
+ } else {
+ throw new IllegalArgumentException("Illegal character in method name: " + c);
+ }
+ break;
+ case stateAfterWord:
+ if (c == ',') {
+ state = stateBeforeWord;
+ } else if (c == ' ') {
+ // remain in the same state
+ } else {
+ throw new IllegalArgumentException("Space in method name");
+ }
+ break;
+ default:
+ // this should never happen
+ }
+ }
+ switch (state) {
+ case stateBeforeWord:
+ case stateAfterWord:
+ break;
+ case stateInsideWord:
+ result[wordIndex++] = methodNames.substring(wordStartIndex, chars.length);
+ break;
+ default:
+ // this should never happen
+ }
+ return result;
+ }
+
+ /**
+ * Get the name of the test class.
+ * @return the name of the test.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the Ant id of the thread running the test.
+ * @return the thread id
+ */
+ public int getThread() {
+ return antThreadID;
+ }
+
+ /**
+ * Get the name of the output file
+ *
+ * @return the name of the output file.
+ */
+ public String getOutfile() {
+ return outfile;
+ }
+
+ /**
+ * Set the number of runs, failures, errors, and skipped tests.
+ * @param runs the number of runs.
+ * @param failures the number of failures.
+ * @param errors the number of errors.
+ * Kept for backward compatibility with Ant 1.8.4
+ */
+ public void setCounts(long runs, long failures, long errors) {
+ this.runs = runs;
+ this.failures = failures;
+ this.errors = errors;
+ }
+ /**
+ * Set the number of runs, failures, errors, and skipped tests.
+ * @param runs the number of runs.
+ * @param failures the number of failures.
+ * @param errors the number of errors.
+ * @param skips the number of skipped tests.
+ * @since Ant 1.9.0
+ */
+ public void setCounts(long runs, long failures, long errors, long skips) {
+ this.runs = runs;
+ this.failures = failures;
+ this.errors = errors;
+ this.skips = skips;
+ }
+
+ /**
+ * Set the runtime.
+ * @param runTime the time in milliseconds.
+ */
+ public void setRunTime(long runTime) {
+ this.runTime = runTime;
+ }
+
+ /**
+ * Get the number of runs.
+ * @return the number of runs.
+ */
+ public long runCount() {
+ return runs;
+ }
+
+ /**
+ * Get the number of failures.
+ * @return the number of failures.
+ */
+ public long failureCount() {
+ return failures;
+ }
+
+ /**
+ * Get the number of errors.
+ * @return the number of errors.
+ */
+ public long errorCount() {
+ return errors;
+ }
+
+ /**
+ * Get the number of skipped tests.
+ * @return the number of skipped tests.
+ */
+ public long skipCount() {
+ return skips;
+ }
+
+ /**
+ * Get the run time.
+ * @return the run time in milliseconds.
+ */
+ public long getRunTime() {
+ return runTime;
+ }
+
+ /**
+ * Get the properties used in the test.
+ * @return the properties.
+ */
+ public Properties getProperties() {
+ return props;
+ }
+
+ /**
+ * Set the properties to be used in the test.
+ * @param p the properties.
+ * This is a copy of the projects ant properties.
+ */
+ public void setProperties(Hashtable p) {
+ props = new Properties();
+ for (Enumeration e = p.keys(); e.hasMoreElements();) {
+ Object key = e.nextElement();
+ props.put(key, p.get(key));
+ }
+ }
+
+ /**
+ * Check if this test should run based on the if and unless
+ * attributes.
+ * @param p the project to use to check if the if and unless
+ * properties exist in.
+ * @return true if this test or testsuite should be run.
+ */
+ public boolean shouldRun(Project p) {
+ PropertyHelper ph = PropertyHelper.getPropertyHelper(p);
+ return ph.testIfCondition(getIfCondition())
+ && ph.testUnlessCondition(getUnlessCondition());
+ }
+
+ /**
+ * Get the formatters set for this test.
+ * @return the formatters as an array.
+ */
+ public FormatterElement[] getFormatters() {
+ FormatterElement[] fes = new FormatterElement[formatters.size()];
+ formatters.copyInto(fes);
+ return fes;
+ }
+
+ /**
+ * Convenient method to add formatters to a vector
+ */
+ void addFormattersTo(Vector v) {
+ final int count = formatters.size();
+ for (int i = 0; i < count; i++) {
+ v.addElement(formatters.elementAt(i));
+ }
+ }
+
+ /**
+ * @since Ant 1.5
+ * @return a clone of this test.
+ */
+ @Override
+ public Object clone() {
+ try {
+ JUnitTest t = (JUnitTest) super.clone();
+ t.props = props == null ? null : (Properties) props.clone();
+ t.formatters = (Vector) formatters.clone();
+ return t;
+ } catch (CloneNotSupportedException e) {
+ // plain impossible
+ return this;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java
new file mode 100644
index 00000000..a457375e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java
@@ -0,0 +1,1297 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestFailure;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Permissions;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.TeeOutputStream;
+
+/**
+ * Simple Testrunner for JUnit that runs all tests of a testsuite.
+ *
+ * <p>This TestRunner expects a name of a TestCase class as its
+ * argument. If this class provides a static suite() method it will be
+ * called and the resulting Test will be run. So, the signature should be
+ * <pre><code>
+ * public static junit.framework.Test suite()
+ * </code></pre>
+ *
+ * <p> If no such method exists, all public methods starting with
+ * "test" and taking no argument will be run.
+ *
+ * <p> Summary output is generated at the end.
+ *
+ * @since Ant 1.2
+ */
+
+public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestRunnerMirror {
+
+ /**
+ * Holds the registered formatters.
+ */
+ private final Vector<JUnitTaskMirror.JUnitResultFormatterMirror> formatters = new Vector();
+
+ /**
+ * Collects TestResults.
+ */
+ private IgnoredTestResult res;
+
+ /**
+ * Do we filter junit.*.* stack frames out of failure and error exceptions.
+ */
+ private static boolean filtertrace = true;
+
+ /**
+ * Do we send output to System.out/.err in addition to the formatters?
+ */
+ private boolean showOutput = false;
+
+ private boolean outputToFormatters = true;
+
+ /**
+ * The permissions set for the test to run.
+ */
+ private Permissions perm = null;
+
+ private static final String JUNIT_4_TEST_ADAPTER
+ = "junit.framework.JUnit4TestAdapter";
+
+ private static final String[] DEFAULT_TRACE_FILTERS = new String[] {
+ "junit.framework.TestCase",
+ "junit.framework.TestResult",
+ "junit.framework.TestSuite",
+ "junit.framework.Assert.", // don't filter AssertionFailure
+ "junit.swingui.TestRunner",
+ "junit.awtui.TestRunner",
+ "junit.textui.TestRunner",
+ "java.lang.reflect.Method.invoke(",
+ "sun.reflect.",
+ "org.apache.tools.ant.",
+ // JUnit 4 support:
+ "org.junit.",
+ "junit.framework.JUnit4TestAdapter",
+ " more",
+ };
+
+
+ /**
+ * Do we stop on errors.
+ */
+ private boolean haltOnError = false;
+
+ /**
+ * Do we stop on test failures.
+ */
+ private boolean haltOnFailure = false;
+
+ /**
+ * Returncode
+ */
+ private int retCode = SUCCESS;
+
+ /**
+ * The TestSuite we are currently running.
+ */
+ private final JUnitTest junitTest;
+
+ /** output written during the test */
+ private PrintStream systemError;
+
+ /** Error output during the test */
+ private PrintStream systemOut;
+
+ /** is this runner running in forked mode? */
+ private boolean forked = false;
+
+ /** Running more than one test suite? */
+ private static boolean multipleTests = false;
+
+ /** ClassLoader passed in in non-forked mode. */
+ private final ClassLoader loader;
+
+ /** Do we print TestListener events? */
+ private boolean logTestListenerEvents = false;
+
+ /** Turned on if we are using JUnit 4 for this test suite. see #38811 */
+ private boolean junit4;
+
+ /**
+ * The file used to indicate that the build crashed.
+ * File will be empty in case the build did not crash.
+ */
+ private static String crashFile = null;
+
+ /** Names of test methods to execute */
+ private String[] methods = null;
+
+ /**
+ * Constructor for fork=true or when the user hasn't specified a
+ * classpath.
+ * @param test the test to run.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ */
+ public JUnitTestRunner(final JUnitTest test, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure) {
+ this(test, haltOnError, filtertrace, haltOnFailure, false);
+ }
+
+ /**
+ * Constructor for fork=true or when the user hasn't specified a
+ * classpath.
+ * @param test the test to run.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ * @param showOutput whether to send output to System.out/.err as well as formatters.
+ */
+ public JUnitTestRunner(final JUnitTest test, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final boolean showOutput) {
+ this(test, haltOnError, filtertrace, haltOnFailure, showOutput, false);
+ }
+
+ /**
+ * Constructor for fork=true or when the user hasn't specified a
+ * classpath.
+ * @param test the test to run.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ * @param showOutput whether to send output to System.out/.err as well as formatters.
+ * @param logTestListenerEvents whether to print TestListener events.
+ * @since Ant 1.7
+ */
+ public JUnitTestRunner(final JUnitTest test, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final boolean showOutput, final boolean logTestListenerEvents) {
+ this(test, null, haltOnError, filtertrace, haltOnFailure, showOutput,
+ logTestListenerEvents, null);
+ }
+
+ /**
+ * Constructor for fork=true or when the user hasn't specified a
+ * classpath.
+ * @param test the test to run.
+ * @param methods names of methods of the test to be executed.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ * @param showOutput whether to send output to System.out/.err as well as formatters.
+ * @param logTestListenerEvents whether to print TestListener events.
+ * @since 1.8.2
+ */
+ public JUnitTestRunner(final JUnitTest test, final String[] methods, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final boolean showOutput, final boolean logTestListenerEvents) {
+ this(test, methods, haltOnError, filtertrace, haltOnFailure, showOutput,
+ logTestListenerEvents, null);
+ }
+
+ /**
+ * Constructor to use when the user has specified a classpath.
+ * @param test the test to run.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ * @param loader the classloader to use running the test.
+ */
+ public JUnitTestRunner(final JUnitTest test, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final ClassLoader loader) {
+ this(test, haltOnError, filtertrace, haltOnFailure, false, loader);
+ }
+
+ /**
+ * Constructor to use when the user has specified a classpath.
+ * @param test the test to run.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ * @param showOutput whether to send output to System.out/.err as well as formatters.
+ * @param loader the classloader to use running the test.
+ */
+ public JUnitTestRunner(final JUnitTest test, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final boolean showOutput, final ClassLoader loader) {
+ this(test, haltOnError, filtertrace, haltOnFailure, showOutput,
+ false, loader);
+ }
+
+ /**
+ * Constructor to use when the user has specified a classpath.
+ * @param test the test to run.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ * @param showOutput whether to send output to System.out/.err as well as formatters.
+ * @param logTestListenerEvents whether to print TestListener events.
+ * @param loader the classloader to use running the test.
+ * @since Ant 1.7
+ */
+ public JUnitTestRunner(final JUnitTest test, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final boolean showOutput, final boolean logTestListenerEvents,
+ final ClassLoader loader) {
+ this(test, null, haltOnError, filtertrace, haltOnFailure, showOutput,
+ logTestListenerEvents, loader);
+ }
+
+
+ /**
+ * Constructor to use when the user has specified a classpath.
+ * @since 1.8.2
+ */
+ public JUnitTestRunner(final JUnitTest test, final String[] methods, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final boolean showOutput, final boolean logTestListenerEvents,
+ final ClassLoader loader) {
+ super();
+ JUnitTestRunner.filtertrace = filtertrace; // TODO clumsy, should use instance field somehow
+ this.junitTest = test;
+ this.haltOnError = haltOnError;
+ this.haltOnFailure = haltOnFailure;
+ this.showOutput = showOutput;
+ this.logTestListenerEvents = logTestListenerEvents;
+ this.methods = methods != null ? (String[]) methods.clone() : null;
+ this.loader = loader;
+ }
+
+ private PrintStream savedOut = null;
+ private PrintStream savedErr = null;
+
+ private PrintStream createEmptyStream() {
+ return new PrintStream(
+ new OutputStream() {
+ @Override
+ public void write(final int b) {
+ }
+ });
+ }
+
+ private PrintStream createTeePrint(final PrintStream ps1, final PrintStream ps2) {
+ return new PrintStream(new TeeOutputStream(ps1, ps2));
+ }
+
+ private void setupIOStreams(final ByteArrayOutputStream o,
+ final ByteArrayOutputStream e) {
+ systemOut = new PrintStream(o);
+ systemError = new PrintStream(e);
+
+ if (forked) {
+ if (!outputToFormatters) {
+ if (!showOutput) {
+ savedOut = System.out;
+ savedErr = System.err;
+ System.setOut(createEmptyStream());
+ System.setErr(createEmptyStream());
+ }
+ } else {
+ savedOut = System.out;
+ savedErr = System.err;
+ if (!showOutput) {
+ System.setOut(systemOut);
+ System.setErr(systemError);
+ } else {
+ System.setOut(createTeePrint(savedOut, systemOut));
+ System.setErr(createTeePrint(savedErr, systemError));
+ }
+ perm = null;
+ }
+ } else {
+ if (perm != null) {
+ perm.setSecurityManager();
+ }
+ }
+ }
+
+ /**
+ * Run the test.
+ */
+ public void run() {
+ res = new IgnoredTestResult();
+ res.addListener(wrapListener(this));
+ final int size = formatters.size();
+ for (int i = 0; i < size; i++) {
+ res.addListener(wrapListener((TestListener) formatters.elementAt(i)));
+ }
+
+ final ByteArrayOutputStream errStrm = new ByteArrayOutputStream();
+ final ByteArrayOutputStream outStrm = new ByteArrayOutputStream();
+
+ setupIOStreams(outStrm, errStrm);
+
+ Test suite = null;
+ Throwable exception = null;
+ boolean startTestSuiteSuccess = false;
+
+ try {
+
+ try {
+ Class testClass = null;
+ if (loader == null) {
+ testClass = Class.forName(junitTest.getName());
+ } else {
+ testClass = Class.forName(junitTest.getName(), true,
+ loader);
+ }
+
+ final boolean testMethodsSpecified = (methods != null);
+
+ // check for a static suite method first, even when using
+ // JUnit 4
+ Method suiteMethod = null;
+ if (!testMethodsSpecified) {
+ try {
+ // check if there is a suite method
+ suiteMethod = testClass.getMethod("suite", new Class[0]);
+ } catch (final NoSuchMethodException e) {
+ // no appropriate suite method found. We don't report any
+ // error here since it might be perfectly normal.
+ }
+ }
+
+ if (suiteMethod != null) {
+ // if there is a suite method available, then try
+ // to extract the suite from it. If there is an error
+ // here it will be caught below and reported.
+ suite = (Test) suiteMethod.invoke(null, new Object[0]);
+
+ } else {
+ Class junit4TestAdapterClass = null;
+ Class junit4TestAdapterCacheClass = null;
+ boolean useSingleMethodAdapter = false;
+
+ if (junit.framework.TestCase.class.isAssignableFrom(testClass)) {
+ // Do not use JUnit 4 API for running JUnit 3.x
+ // tests - it is not able to run individual test
+ // methods.
+ //
+ // Technical details:
+ // org.junit.runner.Request.method(Class, String).getRunner()
+ // would return a runner which always executes all
+ // test methods. The reason is that the Runner would be
+ // an instance of class
+ // org.junit.internal.runners.OldTestClassRunner
+ // that does not implement interface Filterable - so it
+ // is unable to filter out test methods not matching
+ // the requested name.
+ } else {
+ // Check for JDK 5 first. Will *not* help on JDK 1.4
+ // if only junit-4.0.jar in CP because in that case
+ // linkage of whole task will already have failed! But
+ // will help if CP has junit-3.8.2.jar:junit-4.0.jar.
+
+ // In that case first C.fN will fail with CNFE and we
+ // will avoid UnsupportedClassVersionError.
+
+ try {
+ Class.forName("java.lang.annotation.Annotation");
+ junit4TestAdapterCacheClass = Class.forName("org.apache.tools.ant.taskdefs.optional.junit.CustomJUnit4TestAdapterCache");
+ if (loader == null) {
+ junit4TestAdapterClass =
+ Class.forName(JUNIT_4_TEST_ADAPTER);
+ if (testMethodsSpecified) {
+ /*
+ * We cannot try to load the JUnit4TestAdapter
+ * before trying to load JUnit4TestMethodAdapter
+ * because it might fail with
+ * NoClassDefFoundException, instead of plain
+ * ClassNotFoundException.
+ */
+ junit4TestAdapterClass = Class.forName(
+ "org.apache.tools.ant.taskdefs.optional.junit.JUnit4TestMethodAdapter");
+ useSingleMethodAdapter = true;
+ }
+ } else {
+ junit4TestAdapterClass =
+ Class.forName(JUNIT_4_TEST_ADAPTER,
+ true, loader);
+ if (testMethodsSpecified) {
+ junit4TestAdapterClass =
+ Class.forName(
+ "org.apache.tools.ant.taskdefs.optional.junit.JUnit4TestMethodAdapter",
+ true, loader);
+ useSingleMethodAdapter = true;
+ }
+ }
+ } catch (final ClassNotFoundException e) {
+ // OK, fall back to JUnit 3.
+ }
+ }
+ junit4 = junit4TestAdapterClass != null;
+
+ if (junitTest.isSkipNonTests()) {
+ if (!containsTests(testClass, junit4)) {
+ return;
+ }
+ }
+
+
+ if (junit4) {
+ // Let's use it!
+ Class[] formalParams;
+ Object[] actualParams;
+ if (useSingleMethodAdapter) {
+ formalParams = new Class[] {Class.class, String[].class};
+ actualParams = new Object[] {testClass, methods};
+ } else {
+ formalParams = new Class[] {Class.class, Class.forName("junit.framework.JUnit4TestAdapterCache")};
+ actualParams = new Object[] {testClass, junit4TestAdapterCacheClass.getMethod("getInstance").invoke(null)};
+ }
+ suite =
+ (Test) junit4TestAdapterClass
+ .getConstructor(formalParams).
+ newInstance(actualParams);
+ } else {
+ // Use JUnit 3.
+
+ // try to extract a test suite automatically this
+ // will generate warnings if the class is no
+ // suitable Test
+ if (!testMethodsSpecified) {
+ suite = new TestSuite(testClass);
+ } else if (methods.length == 1) {
+ suite = TestSuite.createTest(testClass, methods[0]);
+ } else {
+ final TestSuite testSuite = new TestSuite(testClass.getName());
+ for (int i = 0; i < methods.length; i++) {
+ testSuite.addTest(
+ TestSuite.createTest(testClass, methods[i]));
+ }
+ suite = testSuite;
+ }
+ }
+
+ }
+
+ } catch (final Throwable e) {
+ retCode = ERRORS;
+ exception = e;
+ }
+
+ final long start = System.currentTimeMillis();
+
+ fireStartTestSuite();
+ startTestSuiteSuccess = true;
+ if (exception != null) { // had an exception constructing suite
+ final int formatterSize = formatters.size();
+ for (int i = 0; i < formatterSize; i++) {
+ ((TestListener) formatters.elementAt(i))
+ .addError(null, exception);
+ }
+ junitTest.setCounts(1, 0, 1, 0);
+ junitTest.setRunTime(0);
+ } else {
+ try {
+ logTestListenerEvent("tests to run: " + suite.countTestCases());
+ suite.run(res);
+ } finally {
+ if (junit4 ||
+ suite.getClass().getName().equals(JUNIT_4_TEST_ADAPTER)) {
+ final int[] cnts = findJUnit4FailureErrorCount(res);
+ junitTest.setCounts(res.runCount() + res.ignoredCount(), cnts[0], cnts[1], res.ignoredCount() + res.skippedCount());
+ } else {
+ junitTest.setCounts(res.runCount() + res.ignoredCount(), res.failureCount(),
+ res.errorCount(), res.ignoredCount() + res.skippedCount());
+ }
+ junitTest.setRunTime(System.currentTimeMillis() - start);
+ }
+ }
+ } finally {
+ if (perm != null) {
+ perm.restoreSecurityManager();
+ }
+ if (savedOut != null) {
+ System.setOut(savedOut);
+ }
+ if (savedErr != null) {
+ System.setErr(savedErr);
+ }
+
+ systemError.close();
+ systemError = null;
+ systemOut.close();
+ systemOut = null;
+ if (startTestSuiteSuccess) {
+ String out, err;
+ try {
+ out = new String(outStrm.toByteArray());
+ } catch (final OutOfMemoryError ex) {
+ out = "out of memory on output stream";
+ }
+ try {
+ err = new String(errStrm.toByteArray());
+ } catch (final OutOfMemoryError ex) {
+ err = "out of memory on error stream";
+ }
+ sendOutAndErr(out, err);
+ }
+ }
+ fireEndTestSuite();
+
+ // junitTest has the correct counts for JUnit4, while res doesn't
+ if (retCode != SUCCESS || junitTest.errorCount() != 0) {
+ retCode = ERRORS;
+ } else if (junitTest.failureCount() != 0) {
+ retCode = FAILURES;
+ }
+ }
+
+ private static boolean containsTests(final Class<?> testClass, final boolean isJUnit4) {
+ Class testAnnotation = null;
+ Class suiteAnnotation = null;
+ Class runWithAnnotation = null;
+
+ try {
+ testAnnotation = Class.forName("org.junit.Test");
+ } catch (final ClassNotFoundException e) {
+ if (isJUnit4) {
+ // odd - we think we're JUnit4 but don't support the test annotation. We therefore can't have any tests!
+ return false;
+ }
+ // else... we're a JUnit3 test and don't need the annotation
+ }
+
+ try {
+ suiteAnnotation = Class.forName("org.junit.Suite.SuiteClasses");
+ } catch(final ClassNotFoundException ex) {
+ // ignore - we don't have this annotation so make sure we don't check for it
+ }
+ try {
+ runWithAnnotation = Class.forName("org.junit.runner.RunWith");
+ } catch(final ClassNotFoundException ex) {
+ // also ignore as this annotation doesn't exist so tests can't use it
+ }
+
+
+ if (!isJUnit4 && !TestCase.class.isAssignableFrom(testClass)) {
+ //a test we think is JUnit3 but does not extend TestCase. Can't really be a test.
+ return false;
+ }
+
+ // check if we have any inner classes that contain suitable test methods
+ for (final Class<?> innerClass : testClass.getDeclaredClasses()) {
+ if (containsTests(innerClass, isJUnit4) || containsTests(innerClass, !isJUnit4)) {
+ return true;
+ }
+ }
+
+ if (Modifier.isAbstract(testClass.getModifiers()) || Modifier.isInterface(testClass.getModifiers())) {
+ // can't instantiate class and no inner classes are tests either
+ return false;
+ }
+
+ if (isJUnit4) {
+ if (suiteAnnotation != null && testClass.getAnnotation(suiteAnnotation) != null) {
+ // class is marked as a suite. Let JUnit try and work its magic on it.
+ return true;
+ }
+ if (runWithAnnotation != null && testClass.getAnnotation(runWithAnnotation) != null) {
+ /* Class is marked with @RunWith. If this class is badly written (no test methods, multiple
+ * constructors, private constructor etc) then the class is automatically run and fails in the
+ * IDEs I've tried... so I'm happy handing the class to JUnit to try and run, and let JUnit
+ * report a failure if a bad test case is provided. Trying to do anything else is likely to
+ * result in us filtering out cases that could be valid for future versions of JUnit so would
+ * just increase future maintenance work.
+ */
+ return true;
+ }
+ }
+
+ for (final Method m : testClass.getMethods()) {
+ if (isJUnit4) {
+ // check if suspected JUnit4 classes have methods with @Test annotation
+ if (m.getAnnotation(testAnnotation) != null) {
+ return true;
+ }
+ } else {
+ // check if JUnit3 class have public or protected no-args methods starting with names starting with test
+ if (m.getName().startsWith("test") && m.getParameterTypes().length == 0
+ && (Modifier.isProtected(m.getModifiers()) || Modifier.isPublic(m.getModifiers()))) {
+ return true;
+ }
+ }
+ // check if JUnit3 or JUnit4 test have a public or protected, static,
+ // no-args 'suite' method
+ if (m.getName().equals("suite") && m.getParameterTypes().length == 0
+ && (Modifier.isProtected(m.getModifiers()) || Modifier.isPublic(m.getModifiers()))
+ && Modifier.isStatic(m.getModifiers())) {
+ return true;
+ }
+ }
+
+ // no test methods found
+ return false;
+ }
+
+ /**
+ * Returns what System.exit() would return in the standalone version.
+ *
+ * @return 2 if errors occurred, 1 if tests failed else 0.
+ */
+ public int getRetCode() {
+ return retCode;
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>A new Test is started.
+ * @param t the test.
+ */
+ public void startTest(final Test t) {
+ final String testName = JUnitVersionHelper.getTestCaseName(t);
+ logTestListenerEvent("startTest(" + testName + ")");
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>A Test is finished.
+ * @param test the test.
+ */
+ public void endTest(final Test test) {
+ final String testName = JUnitVersionHelper.getTestCaseName(test);
+ logTestListenerEvent("endTest(" + testName + ")");
+ }
+
+ private void logTestListenerEvent(String msg) {
+ if (logTestListenerEvents) {
+ final PrintStream out = savedOut != null ? savedOut : System.out;
+ out.flush();
+ if (msg == null) {
+ msg = "null";
+ }
+ final StringTokenizer msgLines = new StringTokenizer(msg, "\r\n", false);
+ while (msgLines.hasMoreTokens()) {
+ out.println(JUnitTask.TESTLISTENER_PREFIX
+ + msgLines.nextToken());
+ }
+ out.flush();
+ }
+ }
+
+ /**
+ * Interface TestListener for JUnit &lt;= 3.4.
+ *
+ * <p>A Test failed.
+ * @param test the test.
+ * @param t the exception thrown by the test.
+ */
+ public void addFailure(final Test test, final Throwable t) {
+ final String testName = JUnitVersionHelper.getTestCaseName(test);
+ logTestListenerEvent("addFailure(" + testName + ", " + t.getMessage() + ")");
+ if (haltOnFailure) {
+ res.stop();
+ }
+ }
+
+ /**
+ * Interface TestListener for JUnit &gt; 3.4.
+ *
+ * <p>A Test failed.
+ * @param test the test.
+ * @param t the assertion thrown by the test.
+ */
+ public void addFailure(final Test test, final AssertionFailedError t) {
+ addFailure(test, (Throwable) t);
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>An error occurred while running the test.
+ * @param test the test.
+ * @param t the error thrown by the test.
+ */
+ public void addError(final Test test, final Throwable t) {
+ final String testName = JUnitVersionHelper.getTestCaseName(test);
+ logTestListenerEvent("addError(" + testName + ", " + t.getMessage() + ")");
+ if (haltOnError) {
+ res.stop();
+ }
+ }
+
+ /**
+ * Permissions for the test run.
+ * @since Ant 1.6
+ * @param permissions the permissions to use.
+ */
+ public void setPermissions(final Permissions permissions) {
+ perm = permissions;
+ }
+
+ /**
+ * Handle a string destined for standard output.
+ * @param output the string to output
+ */
+ public void handleOutput(final String output) {
+ if (!logTestListenerEvents && output.startsWith(JUnitTask.TESTLISTENER_PREFIX)) {
+ // ignore
+ } else if (systemOut != null) {
+ systemOut.print(output);
+ }
+ }
+
+ /**
+ * Handle input.
+ * @param buffer not used.
+ * @param offset not used.
+ * @param length not used.
+ * @return -1 always.
+ * @throws IOException never.
+ * @see org.apache.tools.ant.Task#handleInput(byte[], int, int)
+ *
+ * @since Ant 1.6
+ */
+ public int handleInput(final byte[] buffer, final int offset, final int length)
+ throws IOException {
+ return -1;
+ }
+
+ /** {@inheritDoc}. */
+ public void handleErrorOutput(final String output) {
+ if (systemError != null) {
+ systemError.print(output);
+ }
+ }
+
+ /** {@inheritDoc}. */
+ public void handleFlush(final String output) {
+ if (systemOut != null) {
+ systemOut.print(output);
+ }
+ }
+
+ /** {@inheritDoc}. */
+ public void handleErrorFlush(final String output) {
+ if (systemError != null) {
+ systemError.print(output);
+ }
+ }
+
+ private void sendOutAndErr(final String out, final String err) {
+ final int size = formatters.size();
+ for (int i = 0; i < size; i++) {
+ final JUnitResultFormatter formatter =
+ ((JUnitResultFormatter) formatters.elementAt(i));
+
+ formatter.setSystemOutput(out);
+ formatter.setSystemError(err);
+ }
+ }
+
+ private void fireStartTestSuite() {
+ final int size = formatters.size();
+ for (int i = 0; i < size; i++) {
+ ((JUnitResultFormatter) formatters.elementAt(i))
+ .startTestSuite(junitTest);
+ }
+ }
+
+ private void fireEndTestSuite() {
+ final int size = formatters.size();
+ for (int i = 0; i < size; i++) {
+ ((JUnitResultFormatter) formatters.elementAt(i))
+ .endTestSuite(junitTest);
+ }
+ }
+
+ /**
+ * Add a formatter.
+ * @param f the formatter to add.
+ */
+ public void addFormatter(final JUnitResultFormatter f) {
+ formatters.addElement(f);
+ }
+
+ /** {@inheritDoc}. */
+ public void addFormatter(final JUnitTaskMirror.JUnitResultFormatterMirror f) {
+ formatters.addElement(f);
+ }
+
+ /**
+ * Entry point for standalone (forked) mode.
+ *
+ * Parameters: testcaseclassname plus parameters in the format
+ * key=value, none of which is required.
+ *
+ * <table cols="4" border="1">
+ * <tr><th>key</th><th>description</th><th>default value</th></tr>
+ *
+ * <tr><td>haltOnError</td><td>halt test on
+ * errors?</td><td>false</td></tr>
+ *
+ * <tr><td>haltOnFailure</td><td>halt test on
+ * failures?</td><td>false</td></tr>
+ *
+ * <tr><td>formatter</td><td>A JUnitResultFormatter given as
+ * classname,filename. If filename is omitted, System.out is
+ * assumed.</td><td>none</td></tr>
+ *
+ * <tr><td>showoutput</td><td>send output to System.err/.out as
+ * well as to the formatters?</td><td>false</td></tr>
+ *
+ * <tr><td>logtestlistenerevents</td><td>log TestListener events to
+ * System.out.</td><td>false</td></tr>
+ *
+ * <tr><td>methods</td><td>Comma-separated list of names of individual
+ * test methods to execute.
+ * </td><td>null</td></tr>
+ *
+ * </table>
+ * @param args the command line arguments.
+ * @throws IOException on error.
+ */
+ public static void main(final String[] args) throws IOException {
+ String[] methods = null;
+ boolean haltError = false;
+ boolean haltFail = false;
+ boolean stackfilter = true;
+ final Properties props = new Properties();
+ boolean showOut = false;
+ boolean outputToFormat = true;
+ boolean logFailedTests = true;
+ boolean logTestListenerEvents = false;
+ boolean skipNonTests = false;
+ int antThreadID = 0; /* Ant id of thread running this unit test, 0 in single-threaded mode */
+
+ if (args.length == 0) {
+ System.err.println("required argument TestClassName missing");
+ System.exit(ERRORS);
+ }
+
+ if (args[0].startsWith(Constants.TESTSFILE)) {
+ multipleTests = true;
+ args[0] = args[0].substring(Constants.TESTSFILE.length());
+ }
+
+ for (int i = 1; i < args.length; i++) {
+ if (args[i].startsWith(Constants.METHOD_NAMES)) {
+ try {
+ final String methodsList = args[i].substring(Constants.METHOD_NAMES.length());
+ methods = JUnitTest.parseTestMethodNamesList(methodsList);
+ } catch (final IllegalArgumentException ex) {
+ System.err.println("Invalid specification of test method names: " + args[i]);
+ System.exit(ERRORS);
+ }
+ } else if (args[i].startsWith(Constants.HALT_ON_ERROR)) {
+ haltError = Project.toBoolean(args[i].substring(Constants.HALT_ON_ERROR.length()));
+ } else if (args[i].startsWith(Constants.HALT_ON_FAILURE)) {
+ haltFail = Project.toBoolean(args[i].substring(Constants.HALT_ON_FAILURE.length()));
+ } else if (args[i].startsWith(Constants.FILTERTRACE)) {
+ stackfilter = Project.toBoolean(args[i].substring(Constants.FILTERTRACE.length()));
+ } else if (args[i].startsWith(Constants.CRASHFILE)) {
+ crashFile = args[i].substring(Constants.CRASHFILE.length());
+ registerTestCase(Constants.BEFORE_FIRST_TEST);
+ } else if (args[i].startsWith(Constants.FORMATTER)) {
+ try {
+ createAndStoreFormatter(args[i].substring(Constants.FORMATTER.length()));
+ } catch (final BuildException be) {
+ System.err.println(be.getMessage());
+ System.exit(ERRORS);
+ }
+ } else if (args[i].startsWith(Constants.PROPSFILE)) {
+ final FileInputStream in = new FileInputStream(args[i]
+ .substring(Constants.PROPSFILE.length()));
+ props.load(in);
+ in.close();
+ } else if (args[i].startsWith(Constants.SHOWOUTPUT)) {
+ showOut = Project.toBoolean(args[i].substring(Constants.SHOWOUTPUT.length()));
+ } else if (args[i].startsWith(Constants.LOGTESTLISTENEREVENTS)) {
+ logTestListenerEvents = Project.toBoolean(
+ args[i].substring(Constants.LOGTESTLISTENEREVENTS.length()));
+ } else if (args[i].startsWith(Constants.OUTPUT_TO_FORMATTERS)) {
+ outputToFormat = Project.toBoolean(
+ args[i].substring(Constants.OUTPUT_TO_FORMATTERS.length()));
+ } else if (args[i].startsWith(Constants.LOG_FAILED_TESTS)) {
+ logFailedTests = Project.toBoolean(
+ args[i].substring(Constants.LOG_FAILED_TESTS.length()));
+ } else if (args[i].startsWith(Constants.SKIP_NON_TESTS)) {
+ skipNonTests = Project.toBoolean(
+ args[i].substring(Constants.SKIP_NON_TESTS.length()));
+ } else if (args[i].startsWith(Constants.THREADID)) {
+ antThreadID = Integer.parseInt(args[i].substring(Constants.THREADID.length()));
+ }
+ }
+
+ // Add/overlay system properties on the properties from the Ant project
+ final Hashtable p = System.getProperties();
+ for (final Enumeration e = p.keys(); e.hasMoreElements();) {
+ final Object key = e.nextElement();
+ props.put(key, p.get(key));
+ }
+
+ int returnCode = SUCCESS;
+ if (multipleTests) {
+ try {
+ final java.io.BufferedReader reader =
+ new java.io.BufferedReader(new java.io.FileReader(args[0]));
+ String testCaseName;
+ String[] testMethodNames;
+ int code = 0;
+ boolean errorOccurred = false;
+ boolean failureOccurred = false;
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ final StringTokenizer st = new StringTokenizer(line, ",");
+ final String testListSpec = st.nextToken();
+ final int colonIndex = testListSpec.indexOf(':');
+ if (colonIndex == -1) {
+ testCaseName = testListSpec;
+ testMethodNames = null;
+ } else {
+ testCaseName = testListSpec.substring(0, colonIndex);
+ testMethodNames = JUnitTest.parseTestMethodNamesList(
+ testListSpec
+ .substring(colonIndex + 1)
+ .replace('+', ','));
+ }
+ final JUnitTest t = new JUnitTest(testCaseName);
+ t.setTodir(new File(st.nextToken()));
+ t.setOutfile(st.nextToken());
+ t.setProperties(props);
+ t.setSkipNonTests(skipNonTests);
+ t.setThread(antThreadID);
+ code = launch(t, testMethodNames, haltError, stackfilter, haltFail,
+ showOut, outputToFormat,
+ logTestListenerEvents);
+ errorOccurred = (code == ERRORS);
+ failureOccurred = (code != SUCCESS);
+ if (errorOccurred || failureOccurred) {
+ if ((errorOccurred && haltError)
+ || (failureOccurred && haltFail)) {
+ registerNonCrash();
+ System.exit(code);
+ } else {
+ if (code > returnCode) {
+ returnCode = code;
+ }
+ if (logFailedTests) {
+ System.out.println("TEST " + t.getName()
+ + " FAILED");
+ }
+ }
+ }
+ }
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ } else {
+ final JUnitTest t = new JUnitTest(args[0]);
+ t.setThread(antThreadID);
+ t.setProperties(props);
+ t.setSkipNonTests(skipNonTests);
+ returnCode = launch(
+ t, methods, haltError, stackfilter, haltFail,
+ showOut, outputToFormat, logTestListenerEvents);
+ }
+
+ registerNonCrash();
+ System.exit(returnCode);
+ }
+
+ private static Vector fromCmdLine = new Vector();
+
+ private static void transferFormatters(final JUnitTestRunner runner,
+ final JUnitTest test) {
+ runner.addFormatter(new JUnitResultFormatter() {
+
+ public void startTestSuite(final JUnitTest suite) throws BuildException {
+ }
+
+ public void endTestSuite(final JUnitTest suite) throws BuildException {
+ }
+
+ public void setOutput(final OutputStream out) {
+ }
+
+ public void setSystemOutput(final String out) {
+ }
+
+ public void setSystemError(final String err) {
+ }
+
+ public void addError(final Test arg0, final Throwable arg1) {
+ }
+
+ public void addFailure(final Test arg0, final AssertionFailedError arg1) {
+ }
+
+ public void endTest(final Test arg0) {
+ }
+
+ public void startTest(final Test arg0) {
+ registerTestCase(JUnitVersionHelper.getTestCaseName(arg0));
+ }
+ });
+ final int size = fromCmdLine.size();
+ for (int i = 0; i < size; i++) {
+ final FormatterElement fe = (FormatterElement) fromCmdLine.elementAt(i);
+ if (multipleTests && fe.getUseFile()) {
+ final File destFile =
+ new File(test.getTodir(),
+ test.getOutfile() + fe.getExtension());
+ fe.setOutfile(destFile);
+ }
+ runner.addFormatter((JUnitResultFormatter) fe.createFormatter());
+ }
+ }
+
+ /**
+ * Line format is: formatter=<classname>(,<pathname>)?
+ */
+ private static void createAndStoreFormatter(final String line)
+ throws BuildException {
+ final FormatterElement fe = new FormatterElement();
+ final int pos = line.indexOf(',');
+ if (pos == -1) {
+ fe.setClassname(line);
+ fe.setUseFile(false);
+ } else {
+ fe.setClassname(line.substring(0, pos));
+ fe.setUseFile(true);
+ if (!multipleTests) {
+ fe.setOutfile(new File(line.substring(pos + 1)));
+ } else {
+ final int fName = line.indexOf(IGNORED_FILE_NAME);
+ if (fName > -1) {
+ fe.setExtension(line
+ .substring(fName
+ + IGNORED_FILE_NAME.length()));
+ }
+ }
+ }
+ fromCmdLine.addElement(fe);
+ }
+
+ /**
+ * Returns a filtered stack trace.
+ * This is ripped out of junit.runner.BaseTestRunner.
+ * @param t the exception to filter.
+ * @return the filtered stack trace.
+ */
+ public static String getFilteredTrace(final Throwable t) {
+ final String trace = StringUtils.getStackTrace(t);
+ return JUnitTestRunner.filterStack(trace);
+ }
+
+ /**
+ * Filters stack frames from internal JUnit and Ant classes
+ * @param stack the stack trace to filter.
+ * @return the filtered stack.
+ */
+ public static String filterStack(final String stack) {
+ if (!filtertrace) {
+ return stack;
+ }
+ final StringWriter sw = new StringWriter();
+ final BufferedWriter pw = new BufferedWriter(sw);
+ final StringReader sr = new StringReader(stack);
+ final BufferedReader br = new BufferedReader(sr);
+
+ String line;
+ try {
+ boolean firstLine = true;
+ while ((line = br.readLine()) != null) {
+ if (firstLine || !filterLine(line)) {
+ pw.write(line);
+ pw.newLine();
+ }
+ firstLine = false;
+ }
+ } catch (final Exception e) {
+ return stack; // return the stack unfiltered
+ } finally {
+ FileUtils.close(pw);
+ }
+ return sw.toString();
+ }
+
+ private static boolean filterLine(final String line) {
+ for (int i = 0; i < DEFAULT_TRACE_FILTERS.length; i++) {
+ if (line.indexOf(DEFAULT_TRACE_FILTERS[i]) != -1) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @since Ant 1.6.2
+ */
+ private static int launch(final JUnitTest t, final String[] methods, final boolean haltError,
+ final boolean stackfilter, final boolean haltFail,
+ final boolean showOut, final boolean outputToFormat,
+ final boolean logTestListenerEvents) {
+ final JUnitTestRunner runner =
+ new JUnitTestRunner(t, methods, haltError, stackfilter, haltFail, showOut,
+ logTestListenerEvents, null);
+ runner.forked = true;
+ runner.outputToFormatters = outputToFormat;
+ transferFormatters(runner, t);
+
+ runner.run();
+ return runner.getRetCode();
+ }
+
+ /**
+ * @since Ant 1.7
+ */
+ private static void registerNonCrash()
+ throws IOException {
+ if (crashFile != null) {
+ FileWriter out = null;
+ try {
+ out = new FileWriter(crashFile);
+ out.write(Constants.TERMINATED_SUCCESSFULLY + "\n");
+ out.flush();
+ } finally {
+ FileUtils.close(out);
+ }
+ }
+ }
+
+ private static void registerTestCase(final String testCase) {
+ if (crashFile != null) {
+ try {
+ FileWriter out = null;
+ try {
+ out = new FileWriter(crashFile);
+ out.write(testCase + "\n");
+ out.flush();
+ } finally {
+ FileUtils.close(out);
+ }
+ } catch (final IOException e) {
+ // ignored.
+ }
+ }
+ }
+
+ /**
+ * Modifies a TestListener when running JUnit 4: treats AssertionFailedError
+ * as a failure not an error.
+ *
+ * @since Ant 1.7
+ */
+ private TestListenerWrapper wrapListener(final TestListener testListener) {
+ return new TestListenerWrapper(testListener) {
+ @Override
+ public void addError(final Test test, final Throwable t) {
+ if (junit4 && t instanceof AssertionFailedError) {
+ // JUnit 4 does not distinguish between errors and failures
+ // even in the JUnit 3 adapter.
+ // So we need to help it a bit to retain compatibility for JUnit 3 tests.
+ testListener.addFailure(test, (AssertionFailedError) t);
+ } else if (junit4 && t instanceof AssertionError) {
+ // Not strictly necessary but probably desirable.
+ // JUnit 4-specific test GUIs will show just "failures".
+ // But Ant's output shows "failures" vs. "errors".
+ // We would prefer to show "failure" for things that logically are.
+ final String msg = t.getMessage();
+ final AssertionFailedError failure = msg != null
+ ? new AssertionFailedError(msg) : new AssertionFailedError();
+ failure.setStackTrace(t.getStackTrace());
+ testListener.addFailure(test, failure);
+ } else {
+ testListener.addError(test, t);
+ }
+ }
+ @Override
+ public void addFailure(final Test test, final AssertionFailedError t) {
+ testListener.addFailure(test, t);
+ }
+ public void addFailure(final Test test, final Throwable t) { // pre-3.4
+ if (t instanceof AssertionFailedError) {
+ testListener.addFailure(test, (AssertionFailedError) t);
+ } else {
+ testListener.addError(test, t);
+ }
+ }
+ @Override
+ public void endTest(final Test test) {
+ testListener.endTest(test);
+ }
+ @Override
+ public void startTest(final Test test) {
+ testListener.startTest(test);
+ }
+ };
+ }
+
+ /**
+ * Use instead of TestResult.get{Failure,Error}Count on JUnit 4,
+ * since the adapter claims that all failures are errors.
+ * @since Ant 1.7
+ */
+ private int[] findJUnit4FailureErrorCount(final TestResult result) {
+ int failures = 0;
+ int errors = 0;
+ Enumeration e = result.failures();
+ while (e.hasMoreElements()) {
+ e.nextElement();
+ failures++;
+ }
+ e = result.errors();
+ while (e.hasMoreElements()) {
+ final Throwable t = ((TestFailure) e.nextElement()).thrownException();
+ if (t instanceof AssertionFailedError
+ || t instanceof AssertionError) {
+ failures++;
+ } else {
+ errors++;
+ }
+ }
+ return new int[] {failures, errors};
+ }
+
+} // JUnitTestRunner
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java
new file mode 100644
index 00000000..9a21caee
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java
@@ -0,0 +1,179 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+/**
+ * Work around for some changes to the public JUnit API between
+ * different JUnit releases.
+ * @since Ant 1.7
+ */
+// CheckStyle:HideUtilityClassConstructorCheck OFF (bc)
+public class JUnitVersionHelper {
+
+ private static Method testCaseName = null;
+
+ /**
+ * Name of the JUnit4 class we look for.
+ * {@value}
+ * @since Ant 1.7.1
+ */
+ public static final String JUNIT_FRAMEWORK_JUNIT4_TEST_CASE_FACADE
+ = "junit.framework.JUnit4TestCaseFacade";
+ private static final String UNKNOWN_TEST_CASE_NAME = "unknown";
+
+ static {
+ try {
+ testCaseName = TestCase.class.getMethod("getName", new Class[0]);
+ } catch (NoSuchMethodException e) {
+ // pre JUnit 3.7
+ try {
+ testCaseName = TestCase.class.getMethod("name", new Class[0]);
+ } catch (NoSuchMethodException ignored) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * JUnit 3.7 introduces TestCase.getName() and subsequent versions
+ * of JUnit remove the old name() method. This method provides
+ * access to the name of a TestCase via reflection that is
+ * supposed to work with version before and after JUnit 3.7.
+ *
+ * <p>since Ant 1.5.1 this method will invoke &quot;<code>public
+ * String getName()</code>&quot; on any implementation of Test if
+ * it exists.</p>
+ *
+ * <p>Since Ant 1.7 also checks for JUnit4TestCaseFacade explicitly.
+ * This is used by junit.framework.JUnit4TestAdapter.</p>
+ * @param t the test.
+ * @return the name of the test.
+ */
+ public static String getTestCaseName(Test t) {
+ if (t == null) {
+ return UNKNOWN_TEST_CASE_NAME;
+ }
+ if (t.getClass().getName().equals(JUNIT_FRAMEWORK_JUNIT4_TEST_CASE_FACADE)) {
+ // Self-describing as of JUnit 4 (#38811). But trim "(ClassName)".
+ String name = t.toString();
+ if (name.endsWith(")")) {
+ int paren = name.lastIndexOf('(');
+ return name.substring(0, paren);
+ } else {
+ return name;
+ }
+ }
+ if (t instanceof TestCase && testCaseName != null) {
+ try {
+ return (String) testCaseName.invoke(t, new Object[0]);
+ } catch (Throwable ignored) {
+ // ignore
+ }
+ } else {
+ try {
+ Method getNameMethod = null;
+ try {
+ getNameMethod =
+ t.getClass().getMethod("getName", new Class [0]);
+ } catch (NoSuchMethodException e) {
+ getNameMethod = t.getClass().getMethod("name",
+ new Class [0]);
+ }
+ if (getNameMethod != null
+ && getNameMethod.getReturnType() == String.class) {
+ return (String) getNameMethod.invoke(t, new Object[0]);
+ }
+ } catch (Throwable ignored) {
+ // ignore
+ }
+ }
+ return UNKNOWN_TEST_CASE_NAME;
+ }
+
+ /**
+ * Tries to find the name of the class which a test represents
+ * across JUnit 3 and 4. For JUnit4 it parses the toString() value of the
+ * test, and extracts it from there.
+ * @since Ant 1.7.1 (it was private until then)
+ * @param test test case to look at
+ * @return the extracted class name.
+ */
+ public static String getTestCaseClassName(Test test) {
+ String className = test.getClass().getName();
+ if (test instanceof JUnitTaskMirrorImpl.VmExitErrorTest) {
+ className = ((JUnitTaskMirrorImpl.VmExitErrorTest) test).getClassName();
+ } else
+ if (className.equals(JUNIT_FRAMEWORK_JUNIT4_TEST_CASE_FACADE)) {
+ // JUnit 4 wraps solo tests this way. We can extract
+ // the original test name with a little hack.
+ String name = test.toString();
+ int paren = name.lastIndexOf('(');
+ if (paren != -1 && name.endsWith(")")) {
+ className = name.substring(paren + 1, name.length() - 1);
+ }
+ }
+ return className;
+ }
+
+ public static String getIgnoreMessage(Test test) {
+ String message = null;
+
+ try {
+ Class<?> junit4FacadeClass = Class.forName("junit.framework.JUnit4TestCaseFacade");
+ if (test != null && test.getClass().isAssignableFrom(junit4FacadeClass)) {
+ //try and get the message coded as part of the ignore
+ /*
+ * org.junit.runner.Description contains a getAnnotation(Class) method... but this
+ * wasn't in older versions of JUnit4 so we have to try and do this by reflection
+ */
+ Class<?> testClass = Class.forName(JUnitVersionHelper.getTestCaseClassName(test));
+
+ Method testMethod = testClass.getMethod(JUnitVersionHelper.getTestCaseName(test));
+ Class ignoreAnnotation = Class.forName("org.junit.Ignore");
+ Annotation annotation = testMethod.getAnnotation(ignoreAnnotation);
+ if (annotation != null) {
+ Method valueMethod = annotation.getClass().getMethod("value");
+ String value = (String) valueMethod.invoke(annotation);
+ if (value != null && value.length() > 0) {
+ message = value;
+ }
+ }
+
+ }
+ } catch (NoSuchMethodException e) {
+ // silently ignore - we'll report a skip with no message
+ } catch (ClassNotFoundException e) {
+ // silently ignore - we'll report a skip with no message
+ } catch (InvocationTargetException e) {
+ // silently ignore - we'll report a skip with no message
+ } catch (IllegalAccessException e) {
+ // silently ignore - we'll report a skip with no message
+ }
+ return message;
+
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/OutErrSummaryJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/OutErrSummaryJUnitResultFormatter.java
new file mode 100644
index 00000000..e0dc16c3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/OutErrSummaryJUnitResultFormatter.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.tools.ant.taskdefs.optional.junit;
+
+/**
+ * Used instead of SummaryJUnitResultFormatter in forked tests if
+ * withOutAndErr is requested.
+ */
+
+public class OutErrSummaryJUnitResultFormatter
+ extends SummaryJUnitResultFormatter {
+
+ /**
+ * Empty
+ */
+ public OutErrSummaryJUnitResultFormatter() {
+ super();
+ setWithOutAndErr(true);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java
new file mode 100644
index 00000000..3386ee50
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java
@@ -0,0 +1,317 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.text.NumberFormat;
+import java.util.Hashtable;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+
+/**
+ * Prints plain text output of the test to a specified Writer.
+ *
+ */
+
+public class PlainJUnitResultFormatter implements JUnitResultFormatter, IgnoredTestListener {
+
+ private static final double ONE_SECOND = 1000.0;
+
+ /**
+ * Formatter for timings.
+ */
+ private NumberFormat nf = NumberFormat.getInstance();
+ /**
+ * Timing helper.
+ */
+ private Hashtable testStarts = new Hashtable();
+ /**
+ * Where to write the log to.
+ */
+ private OutputStream out;
+ /**
+ * Helper to store intermediate output.
+ */
+ private StringWriter inner;
+ /**
+ * Convenience layer on top of {@link #inner inner}.
+ */
+ private BufferedWriter wri;
+ /**
+ * Suppress endTest if testcase failed.
+ */
+ private Hashtable failed = new Hashtable();
+
+ private String systemOutput = null;
+ private String systemError = null;
+
+ /** No arg constructor */
+ public PlainJUnitResultFormatter() {
+ inner = new StringWriter();
+ wri = new BufferedWriter(inner);
+ }
+
+ /** {@inheritDoc}. */
+ public void setOutput(OutputStream out) {
+ this.out = out;
+ }
+
+ /** {@inheritDoc}. */
+ public void setSystemOutput(String out) {
+ systemOutput = out;
+ }
+
+ /** {@inheritDoc}. */
+ public void setSystemError(String err) {
+ systemError = err;
+ }
+
+ /**
+ * The whole testsuite started.
+ * @param suite the test suite
+ * @throws BuildException if unable to write the output
+ */
+ public void startTestSuite(JUnitTest suite) throws BuildException {
+ if (out == null) {
+ return; // Quick return - no output do nothing.
+ }
+ StringBuffer sb = new StringBuffer("Testsuite: ");
+ sb.append(suite.getName());
+ sb.append(StringUtils.LINE_SEP);
+ try {
+ out.write(sb.toString().getBytes());
+ out.flush();
+ } catch (IOException ex) {
+ throw new BuildException("Unable to write output", ex);
+ }
+ }
+
+ /**
+ * The whole testsuite ended.
+ * @param suite the test suite
+ * @throws BuildException if unable to write the output
+ */
+ public void endTestSuite(JUnitTest suite) throws BuildException {
+ try {
+ StringBuffer sb = new StringBuffer("Tests run: ");
+ sb.append(suite.runCount());
+ sb.append(", Failures: ");
+ sb.append(suite.failureCount());
+ sb.append(", Errors: ");
+ sb.append(suite.errorCount());
+ sb.append(", Skipped: ");
+ sb.append(suite.skipCount());
+ sb.append(", Time elapsed: ");
+ sb.append(nf.format(suite.getRunTime() / ONE_SECOND));
+ sb.append(" sec");
+ sb.append(StringUtils.LINE_SEP);
+ write(sb.toString());
+
+ // write the err and output streams to the log
+ if (systemOutput != null && systemOutput.length() > 0) {
+ write("------------- Standard Output ---------------");
+ write(StringUtils.LINE_SEP);
+ write(systemOutput);
+ write("------------- ---------------- ---------------");
+ write(StringUtils.LINE_SEP);
+ }
+
+ if (systemError != null && systemError.length() > 0) {
+ write("------------- Standard Error -----------------");
+ write(StringUtils.LINE_SEP);
+ write(systemError);
+ write("------------- ---------------- ---------------");
+ write(StringUtils.LINE_SEP);
+ }
+
+ write(StringUtils.LINE_SEP);
+ if (out != null) {
+ try {
+ wri.flush();
+ write(inner.toString());
+ } catch (IOException ioex) {
+ throw new BuildException("Unable to write output", ioex);
+ }
+ }
+ } finally {
+ if (out != null) {
+ try {
+ wri.close();
+ } catch (IOException ioex) {
+ throw new BuildException("Unable to flush output", ioex);
+ } finally {
+ if (out != System.out && out != System.err) {
+ FileUtils.close(out);
+ }
+ wri = null;
+ out = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>A new Test is started.
+ * @param t the test.
+ */
+ public void startTest(Test t) {
+ testStarts.put(t, new Long(System.currentTimeMillis()));
+ failed.put(t, Boolean.FALSE);
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>A Test is finished.
+ * @param test the test.
+ */
+ public void endTest(Test test) {
+ if (Boolean.TRUE.equals(failed.get(test))) {
+ return;
+ }
+ synchronized (wri) {
+ try {
+ wri.write("Testcase: "
+ + JUnitVersionHelper.getTestCaseName(test));
+ Long l = (Long) testStarts.get(test);
+ double seconds = 0;
+ // can be null if an error occurred in setUp
+ if (l != null) {
+ seconds =
+ (System.currentTimeMillis() - l.longValue()) / ONE_SECOND;
+ }
+
+ wri.write(" took " + nf.format(seconds) + " sec");
+ wri.newLine();
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+ }
+
+ /**
+ * Interface TestListener for JUnit &lt;= 3.4.
+ *
+ * <p>A Test failed.
+ * @param test the test.
+ * @param t the exception.
+ */
+ public void addFailure(Test test, Throwable t) {
+ formatError("\tFAILED", test, t);
+ }
+
+ /**
+ * Interface TestListener for JUnit &gt; 3.4.
+ *
+ * <p>A Test failed.
+ * @param test the test.
+ * @param t the assertion that failed.
+ */
+ public void addFailure(Test test, AssertionFailedError t) {
+ addFailure(test, (Throwable) t);
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>An error occurred while running the test.
+ * @param test the test.
+ * @param t the exception.
+ */
+ public void addError(Test test, Throwable t) {
+ formatError("\tCaused an ERROR", test, t);
+ }
+
+ private void formatError(String type, Test test, Throwable t) {
+ synchronized (wri) {
+ if (test != null) {
+ endTest(test);
+ failed.put(test, Boolean.TRUE);
+ }
+
+ try {
+ wri.write(type);
+ wri.newLine();
+ wri.write(String.valueOf(t.getMessage()));
+ wri.newLine();
+ String strace = JUnitTestRunner.getFilteredTrace(t);
+ wri.write(strace);
+ wri.newLine();
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+ }
+
+ public void testIgnored(Test test) {
+ formatSkip(test, JUnitVersionHelper.getIgnoreMessage(test));
+ }
+
+
+ public void formatSkip(Test test, String message) {
+ if (test != null) {
+ endTest(test);
+ }
+
+ try {
+ wri.write("\tSKIPPED");
+ if (message != null) {
+ wri.write(": ");
+ wri.write(message);
+ }
+ wri.newLine();
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+
+ }
+
+ public void testAssumptionFailure(Test test, Throwable throwable) {
+ formatSkip(test, throwable.getMessage());
+ }
+
+ /**
+ * Print out some text, and flush the output stream; encoding in the platform
+ * local default encoding.
+ * @param text text to write.
+ * @throws BuildException on IO Problems.
+ */
+ private void write(String text) {
+ if (out == null) {
+ return;
+ }
+ try {
+ out.write(text.getBytes());
+ out.flush();
+ } catch (IOException ex) {
+ throw new BuildException("Unable to write output " + ex, ex);
+ }
+ }
+} // PlainJUnitResultFormatter
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java
new file mode 100644
index 00000000..0b09fa20
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java
@@ -0,0 +1,213 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.NumberFormat;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Prints short summary output of the test to Ant's logging system.
+ *
+ */
+
+public class SummaryJUnitResultFormatter
+ implements JUnitResultFormatter, JUnitTaskMirror.SummaryJUnitResultFormatterMirror {
+
+ private static final double ONE_SECOND = 1000.0;
+
+ /**
+ * Formatter for timings.
+ */
+ private NumberFormat nf = NumberFormat.getInstance();
+ /**
+ * OutputStream to write to.
+ */
+ private OutputStream out;
+
+ private boolean withOutAndErr = false;
+ private String systemOutput = null;
+ private String systemError = null;
+
+ /**
+ * Empty
+ */
+ public SummaryJUnitResultFormatter() {
+ }
+
+ /**
+ * Insures that a line of log output is written and flushed as a single
+ * operation, to prevent lines from being spliced into other lines.
+ * (Hopefully this solves the issue of run on lines -
+ * [junit] Tests Run: 2 Failures: 2 [junit] Tests run: 5...
+ * synchronized doesn't seem to be to harsh a penalty since it only
+ * occurs twice per test - at the beginning and end. Note that message
+ * construction occurs outside the locked block.
+ *
+ * @param b data to be written as an unbroken block
+ */
+ private synchronized void writeOutputLine(byte[] b) {
+ try {
+ out.write(b);
+ out.flush();
+ } catch (IOException ioex) {
+ throw new BuildException("Unable to write summary output", ioex);
+ }
+ }
+
+ /**
+ * The testsuite started.
+ * @param suite the testsuite.
+ */
+ public void startTestSuite(JUnitTest suite) {
+ String newLine = System.getProperty("line.separator");
+ StringBuffer sb = new StringBuffer("Running ");
+ int antThreadID = suite.getThread();
+
+ sb.append(suite.getName());
+ /* only write thread id in multi-thread mode so default old way doesn't change output */
+ if (antThreadID > 0) {
+ sb.append(" in thread ");
+ sb.append(antThreadID);
+ }
+ sb.append(newLine);
+ writeOutputLine(sb.toString().getBytes());
+ }
+ /**
+ * Empty
+ * @param t not used.
+ */
+ public void startTest(Test t) {
+ }
+ /**
+ * Empty
+ * @param test not used.
+ */
+ public void endTest(Test test) {
+ }
+ /**
+ * Empty
+ * @param test not used.
+ * @param t not used.
+ */
+ public void addFailure(Test test, Throwable t) {
+ }
+ /**
+ * Interface TestListener for JUnit &gt; 3.4.
+ *
+ * <p>A Test failed.
+ * @param test not used.
+ * @param t not used.
+ */
+ public void addFailure(Test test, AssertionFailedError t) {
+ addFailure(test, (Throwable) t);
+ }
+ /**
+ * Empty
+ * @param test not used.
+ * @param t not used.
+ */
+ public void addError(Test test, Throwable t) {
+ }
+
+ /** {@inheritDoc}. */
+ public void setOutput(OutputStream out) {
+ this.out = out;
+ }
+
+ /** {@inheritDoc}. */
+ public void setSystemOutput(String out) {
+ systemOutput = out;
+ }
+
+ /** {@inheritDoc}. */
+ public void setSystemError(String err) {
+ systemError = err;
+ }
+
+ /**
+ * Should the output to System.out and System.err be written to
+ * the summary.
+ * @param value if true write System.out and System.err to the summary.
+ */
+ public void setWithOutAndErr(boolean value) {
+ withOutAndErr = value;
+ }
+
+ /**
+ * The whole testsuite ended.
+ * @param suite the testsuite.
+ * @throws BuildException if there is an error.
+ */
+ public void endTestSuite(JUnitTest suite) throws BuildException {
+ String newLine = System.getProperty("line.separator");
+ StringBuffer sb = new StringBuffer("Tests run: ");
+ sb.append(suite.runCount());
+ sb.append(", Failures: ");
+ sb.append(suite.failureCount());
+ sb.append(", Errors: ");
+ sb.append(suite.errorCount());
+ sb.append(", Skipped: ");
+ sb.append(suite.skipCount());
+ sb.append(", Time elapsed: ");
+ sb.append(nf.format(suite.getRunTime() / ONE_SECOND));
+ sb.append(" sec");
+
+ /* class name needed with multi-threaded execution because
+ results line may not appear immediately below start line.
+ only write thread id, class name in multi-thread mode so
+ the line still looks as much like the old line as possible. */
+ if (suite.getThread() > 0) {
+ sb.append(", Thread: ");
+ sb.append(suite.getThread());
+ sb.append(", Class: ");
+ sb.append(suite.getName());
+ }
+ sb.append(newLine);
+
+ if (withOutAndErr) {
+ if (systemOutput != null && systemOutput.length() > 0) {
+ sb.append("Output:").append(newLine).append(systemOutput)
+ .append(newLine);
+ }
+
+ if (systemError != null && systemError.length() > 0) {
+ sb.append("Error: ").append(newLine).append(systemError)
+ .append(newLine);
+ }
+ }
+
+ try {
+ writeOutputLine(sb.toString().getBytes());
+ } finally {
+ if (out != System.out && out != System.err) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrash.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrash.java
new file mode 100644
index 00000000..e381a70c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrash.java
@@ -0,0 +1,144 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+/**
+ * Formatter that doesn't create any output but tries to invoke the
+ * tearDown method on a testcase if that test was forked and caused a
+ * timeout or VM crash.
+ *
+ * <p>This formatter has some limitations, for details see the
+ * &lt;junit&gt; task's manual.</p>
+ *
+ * @since Ant 1.8.0
+ */
+public class TearDownOnVmCrash implements JUnitResultFormatter {
+
+ private String suiteName;
+
+ /**
+ * Records the suite's name to later determine the class to invoke
+ * tearDown on.
+ */
+ public void startTestSuite(final JUnitTest suite) {
+ suiteName = suite.getName();
+ if (suiteName != null &&
+ suiteName.endsWith(JUnitTask.NAME_OF_DUMMY_TEST)) {
+ // no way to know which class caused the timeout
+ suiteName = null;
+ }
+ }
+
+ /**
+ * Only invoke tearDown if the suite is known and not the dummy
+ * test we get when a Batch fails and the error is an actual
+ * error generated by Ant.
+ */
+ public void addError(final Test fakeTest, final Throwable t) {
+ if (suiteName != null
+ && fakeTest instanceof JUnitTaskMirrorImpl.VmExitErrorTest) {
+ tearDown();
+ }
+ }
+
+ // no need to implement the rest
+ public void addFailure(Test test, Throwable t) {}
+
+ public void addFailure(Test test, AssertionFailedError t) {}
+
+ public void startTest(Test test) {}
+
+ public void endTest(Test test) {}
+
+ public void endTestSuite(JUnitTest suite) {}
+
+ public void setOutput(OutputStream out) {}
+
+ public void setSystemOutput(String out) {}
+
+ public void setSystemError(String err) {}
+
+ private void tearDown() {
+ try {
+ // first try to load the class and let's hope it is on our
+ // classpath
+ Class testClass = null;
+ if (Thread.currentThread().getContextClassLoader() != null) {
+ try {
+ testClass = Thread.currentThread().getContextClassLoader()
+ .loadClass(suiteName);
+ } catch (ClassNotFoundException cnfe) {
+ // ignore
+ }
+ }
+ if (testClass == null && getClass().getClassLoader() != null) {
+ try {
+ testClass =
+ getClass().getClassLoader().loadClass(suiteName);
+ } catch (ClassNotFoundException cnfe) {
+ // ignore
+ }
+ }
+ if (testClass == null) {
+ // fall back to system classloader
+ testClass = Class.forName(suiteName);
+ }
+
+ // if the test has a suite method, then we can't know
+ // which test of the executed suite timed out, ignore it
+ try {
+ // check if there is a suite method
+ testClass.getMethod("suite", new Class[0]);
+ return;
+ } catch (NoSuchMethodException e) {
+ // no suite method
+ }
+
+ // a loadable class and no suite method
+ // no reason to check for JUnit 4 since JUnit4TestAdapter
+ // doesn't have any tearDown method.
+
+ try {
+ Method td = testClass.getMethod("tearDown", new Class[0]);
+ if (td.getReturnType() == Void.TYPE) {
+ td.invoke(testClass.newInstance(), new Object[0]);
+ }
+ } catch (NoSuchMethodException nsme) {
+ // no tearDown, fine
+ }
+
+ } catch (ClassNotFoundException cnfe) {
+ // class probably is not in our classpath, there is
+ // nothing we can do
+ } catch (InvocationTargetException ite) {
+ System.err.println("Caught an exception while trying to invoke"
+ + " tearDown: " + ite.getMessage());
+ } catch (Throwable t) {
+ System.err.println("Caught an exception while trying to invoke"
+ + " tearDown: " + t.getMessage());
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestIgnored.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestIgnored.java
new file mode 100644
index 00000000..79a88785
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestIgnored.java
@@ -0,0 +1,35 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import junit.framework.Test;
+
+public class TestIgnored {
+
+ private Test test;
+
+ public TestIgnored(Test test) {
+ this.test = test;
+ }
+
+ public Test getTest() {
+ return test;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestListenerWrapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestListenerWrapper.java
new file mode 100644
index 00000000..692e4fc9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestListenerWrapper.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.tools.ant.taskdefs.optional.junit;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestListener;
+
+
+public class TestListenerWrapper implements TestListener, IgnoredTestListener {
+
+ private TestListener wrapped;
+
+ public TestListenerWrapper(TestListener listener) {
+ super();
+ wrapped = listener;
+ }
+
+ public void addError(Test test, Throwable throwable) {
+ wrapped.addError(test, throwable);
+ }
+
+ public void addFailure(Test test, AssertionFailedError assertionFailedError) {
+ wrapped.addFailure(test, assertionFailedError);
+ }
+
+ public void endTest(Test test) {
+ wrapped.endTest(test);
+ }
+
+ public void startTest(Test test) {
+ wrapped.startTest(test);
+ }
+
+ public void testIgnored(Test test) {
+ if (wrapped instanceof IgnoredTestListener) {
+ ((IgnoredTestListener)wrapped).testIgnored(test);
+ }
+ }
+
+ public void testAssumptionFailure(Test test, Throwable throwable) {
+ if (wrapped instanceof IgnoredTestListener) {
+ ((IgnoredTestListener)wrapped).testAssumptionFailure(test, throwable);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java
new file mode 100644
index 00000000..03760cc9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java
@@ -0,0 +1,141 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+/**
+ * <p> Interface groups XML constants.
+ * Interface that groups all constants used throughout the <tt>XML</tt>
+ * documents that are generated by the <tt>XMLJUnitResultFormatter</tt>.
+ * <p>
+ * As of now the DTD is:
+ * <code><pre>
+ * &lt;!ELEMENT testsuites (testsuite*)&gt;
+ *
+ * &lt;!ELEMENT testsuite (properties, testcase*,
+ * failure?, error?,
+ * system-out?, system-err?)&gt;
+ * &lt;!ATTLIST testsuite name CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST testsuite tests CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST testsuite failures CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST testsuite errors CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST testsuite time CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST testsuite package CDATA #IMPLIED&gt;
+ * &lt;!ATTLIST testsuite id CDATA #IMPLIED&gt;
+ *
+ *
+ * &lt;!ELEMENT properties (property*)&gt;
+ *
+ * &lt;!ELEMENT property EMPTY&gt;
+ * &lt;!ATTLIST property name CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST property value CDATA #REQUIRED&gt;
+ *
+ * &lt;!ELEMENT testcase (failure?, error?)&gt;
+ * &lt;!ATTLIST testcase name CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST testcase classname CDATA #IMPLIED&gt;
+ * &lt;!ATTLIST testcase time CDATA #REQUIRED&gt;
+ *
+ * &lt;!ELEMENT failure (#PCDATA)&gt;
+ * &lt;!ATTLIST failure message CDATA #IMPLIED&gt;
+ * &lt;!ATTLIST failure type CDATA #REQUIRED&gt;
+ *
+ * &lt;!ELEMENT error (#PCDATA)&gt;
+ * &lt;!ATTLIST error message CDATA #IMPLIED&gt;
+ * &lt;!ATTLIST error type CDATA #REQUIRED&gt;
+ *
+ * &lt;!ELEMENT system-err (#PCDATA)&gt;
+ *
+ * &lt;!ELEMENT system-out (#PCDATA)&gt;
+ *
+ * </pre></code>
+ * @see XMLJUnitResultFormatter
+ * @see XMLResultAggregator
+ */
+// CheckStyle:InterfaceIsTypeCheck OFF (bc)
+public interface XMLConstants {
+ /** the testsuites element for the aggregate document */
+ String TESTSUITES = "testsuites";
+
+ /** the testsuite element */
+ String TESTSUITE = "testsuite";
+
+ /** the testcase element */
+ String TESTCASE = "testcase";
+
+ /** the error element */
+ String ERROR = "error";
+
+ /** the failure element */
+ String FAILURE = "failure";
+
+ /** the system-err element */
+ String SYSTEM_ERR = "system-err";
+
+ /** the system-out element */
+ String SYSTEM_OUT = "system-out";
+
+ /** package attribute for the aggregate document */
+ String ATTR_PACKAGE = "package";
+
+ /** name attribute for property, testcase and testsuite elements */
+ String ATTR_NAME = "name";
+
+ /** time attribute for testcase and testsuite elements */
+ String ATTR_TIME = "time";
+
+ /** errors attribute for testsuite elements */
+ String ATTR_ERRORS = "errors";
+
+ /** failures attribute for testsuite elements */
+ String ATTR_FAILURES = "failures";
+
+ /** tests attribute for testsuite elements */
+ String ATTR_TESTS = "tests";
+
+ String ATTR_SKIPPED = "skipped";
+
+ /** type attribute for failure and error elements */
+ String ATTR_TYPE = "type";
+
+ /** message attribute for failure elements */
+ String ATTR_MESSAGE = "message";
+
+ /** the properties element */
+ String PROPERTIES = "properties";
+
+ /** the property element */
+ String PROPERTY = "property";
+
+ /** value attribute for property elements */
+ String ATTR_VALUE = "value";
+
+ /** classname attribute for testcase elements */
+ String ATTR_CLASSNAME = "classname";
+
+ /** id attribute */
+ String ATTR_ID = "id";
+
+ /**
+ * timestamp of test cases
+ */
+ String TIMESTAMP = "timestamp";
+
+ /**
+ * name of host running the tests
+ */
+ String HOSTNAME = "hostname";
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java
new file mode 100644
index 00000000..416c10d4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java
@@ -0,0 +1,366 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.DateUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Text;
+
+
+/**
+ * Prints XML output of the test to a specified Writer.
+ *
+ * @see FormatterElement
+ */
+
+public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstants, IgnoredTestListener {
+
+ private static final double ONE_SECOND = 1000.0;
+
+ /** constant for unnnamed testsuites/cases */
+ private static final String UNKNOWN = "unknown";
+
+ private static DocumentBuilder getDocumentBuilder() {
+ try {
+ return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ } catch (final Exception exc) {
+ throw new ExceptionInInitializerError(exc);
+ }
+ }
+
+ /**
+ * The XML document.
+ */
+ private Document doc;
+
+ /**
+ * The wrapper for the whole testsuite.
+ */
+ private Element rootElement;
+
+ /**
+ * Element for the current test.
+ *
+ * The keying of this map is a bit of a hack: tests are keyed by caseName(className) since
+ * the Test we get for Test-start isn't the same as the Test we get during test-assumption-fail,
+ * so we can't easily match Test objects without manually iterating over all keys and checking
+ * individual fields.
+ */
+ private final Hashtable<String, Element> testElements = new Hashtable<String, Element>();
+
+ /**
+ * tests that failed.
+ */
+ private final Hashtable failedTests = new Hashtable();
+
+ /**
+ * Tests that were skipped.
+ */
+ private final Hashtable<String, Test> skippedTests = new Hashtable<String, Test>();
+ /**
+ * Tests that were ignored. See the note above about the key being a bit of a hack.
+ */
+ private final Hashtable<String, Test> ignoredTests = new Hashtable<String, Test>();
+ /**
+ * Timing helper.
+ */
+ private final Hashtable<String, Long> testStarts = new Hashtable<String, Long>();
+ /**
+ * Where to write the log to.
+ */
+ private OutputStream out;
+
+ /** No arg constructor. */
+ public XMLJUnitResultFormatter() {
+ }
+
+ /** {@inheritDoc}. */
+ public void setOutput(final OutputStream out) {
+ this.out = out;
+ }
+
+ /** {@inheritDoc}. */
+ public void setSystemOutput(final String out) {
+ formatOutput(SYSTEM_OUT, out);
+ }
+
+ /** {@inheritDoc}. */
+ public void setSystemError(final String out) {
+ formatOutput(SYSTEM_ERR, out);
+ }
+
+ /**
+ * The whole testsuite started.
+ * @param suite the testsuite.
+ */
+ public void startTestSuite(final JUnitTest suite) {
+ doc = getDocumentBuilder().newDocument();
+ rootElement = doc.createElement(TESTSUITE);
+ final String n = suite.getName();
+ rootElement.setAttribute(ATTR_NAME, n == null ? UNKNOWN : n);
+
+ //add the timestamp
+ final String timestamp = DateUtils.format(new Date(),
+ DateUtils.ISO8601_DATETIME_PATTERN);
+ rootElement.setAttribute(TIMESTAMP, timestamp);
+ //and the hostname.
+ rootElement.setAttribute(HOSTNAME, getHostname());
+
+ // Output properties
+ final Element propsElement = doc.createElement(PROPERTIES);
+ rootElement.appendChild(propsElement);
+ final Properties props = suite.getProperties();
+ if (props != null) {
+ final Enumeration e = props.propertyNames();
+ while (e.hasMoreElements()) {
+ final String name = (String) e.nextElement();
+ final Element propElement = doc.createElement(PROPERTY);
+ propElement.setAttribute(ATTR_NAME, name);
+ propElement.setAttribute(ATTR_VALUE, props.getProperty(name));
+ propsElement.appendChild(propElement);
+ }
+ }
+ }
+
+ /**
+ * get the local hostname
+ * @return the name of the local host, or "localhost" if we cannot work it out
+ */
+ private String getHostname() {
+ String hostname = "localhost";
+ try {
+ final InetAddress localHost = InetAddress.getLocalHost();
+ if (localHost != null) {
+ hostname = localHost.getHostName();
+ }
+ } catch (final UnknownHostException e) {
+ // fall back to default 'localhost'
+ }
+ return hostname;
+ }
+
+ /**
+ * The whole testsuite ended.
+ * @param suite the testsuite.
+ * @throws BuildException on error.
+ */
+ public void endTestSuite(final JUnitTest suite) throws BuildException {
+ rootElement.setAttribute(ATTR_TESTS, "" + suite.runCount());
+ rootElement.setAttribute(ATTR_FAILURES, "" + suite.failureCount());
+ rootElement.setAttribute(ATTR_ERRORS, "" + suite.errorCount());
+ rootElement.setAttribute(ATTR_SKIPPED, "" + suite.skipCount());
+ rootElement.setAttribute(
+ ATTR_TIME, "" + (suite.getRunTime() / ONE_SECOND));
+ if (out != null) {
+ Writer wri = null;
+ try {
+ wri = new BufferedWriter(new OutputStreamWriter(out, "UTF8"));
+ wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+ (new DOMElementWriter()).write(rootElement, wri, 0, " ");
+ } catch (final IOException exc) {
+ throw new BuildException("Unable to write log file", exc);
+ } finally {
+ if (wri != null) {
+ try {
+ wri.flush();
+ } catch (final IOException ex) {
+ // ignore
+ }
+ }
+ if (out != System.out && out != System.err) {
+ FileUtils.close(wri);
+ }
+ }
+ }
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>A new Test is started.
+ * @param t the test.
+ */
+ public void startTest(final Test t) {
+ testStarts.put(createDescription(t), System.currentTimeMillis());
+ }
+
+ private static String createDescription(final Test test) throws BuildException {
+ return JUnitVersionHelper.getTestCaseName(test) + "(" + JUnitVersionHelper.getTestCaseClassName(test) + ")";
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>A Test is finished.
+ * @param test the test.
+ */
+ public void endTest(final Test test) {
+ final String testDescription = createDescription(test);
+
+ // Fix for bug #5637 - if a junit.extensions.TestSetup is
+ // used and throws an exception during setUp then startTest
+ // would never have been called
+ if (!testStarts.containsKey(testDescription)) {
+ startTest(test);
+ }
+ Element currentTest;
+ if (!failedTests.containsKey(test) && !skippedTests.containsKey(testDescription) && !ignoredTests.containsKey(testDescription)) {
+ currentTest = doc.createElement(TESTCASE);
+ final String n = JUnitVersionHelper.getTestCaseName(test);
+ currentTest.setAttribute(ATTR_NAME,
+ n == null ? UNKNOWN : n);
+ // a TestSuite can contain Tests from multiple classes,
+ // even tests with the same name - disambiguate them.
+ currentTest.setAttribute(ATTR_CLASSNAME,
+ JUnitVersionHelper.getTestCaseClassName(test));
+ rootElement.appendChild(currentTest);
+ testElements.put(createDescription(test), currentTest);
+ } else {
+ currentTest = testElements.get(testDescription);
+ }
+
+ final Long l = testStarts.get(createDescription(test));
+ currentTest.setAttribute(ATTR_TIME,
+ "" + ((System.currentTimeMillis() - l) / ONE_SECOND));
+ }
+
+ /**
+ * Interface TestListener for JUnit &lt;= 3.4.
+ *
+ * <p>A Test failed.
+ * @param test the test.
+ * @param t the exception.
+ */
+ public void addFailure(final Test test, final Throwable t) {
+ formatError(FAILURE, test, t);
+ }
+
+ /**
+ * Interface TestListener for JUnit &gt; 3.4.
+ *
+ * <p>A Test failed.
+ * @param test the test.
+ * @param t the assertion.
+ */
+ public void addFailure(final Test test, final AssertionFailedError t) {
+ addFailure(test, (Throwable) t);
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>An error occurred while running the test.
+ * @param test the test.
+ * @param t the error.
+ */
+ public void addError(final Test test, final Throwable t) {
+ formatError(ERROR, test, t);
+ }
+
+ private void formatError(final String type, final Test test, final Throwable t) {
+ if (test != null) {
+ endTest(test);
+ failedTests.put(test, test);
+ }
+
+ final Element nested = doc.createElement(type);
+ Element currentTest;
+ if (test != null) {
+ currentTest = testElements.get(createDescription(test));
+ } else {
+ currentTest = rootElement;
+ }
+
+ currentTest.appendChild(nested);
+
+ final String message = t.getMessage();
+ if (message != null && message.length() > 0) {
+ nested.setAttribute(ATTR_MESSAGE, t.getMessage());
+ }
+ nested.setAttribute(ATTR_TYPE, t.getClass().getName());
+
+ final String strace = JUnitTestRunner.getFilteredTrace(t);
+ final Text trace = doc.createTextNode(strace);
+ nested.appendChild(trace);
+ }
+
+ private void formatOutput(final String type, final String output) {
+ final Element nested = doc.createElement(type);
+ rootElement.appendChild(nested);
+ nested.appendChild(doc.createCDATASection(output));
+ }
+
+ public void testIgnored(final Test test) {
+ formatSkip(test, JUnitVersionHelper.getIgnoreMessage(test));
+ if (test != null) {
+ ignoredTests.put(createDescription(test), test);
+ }
+ }
+
+
+ public void formatSkip(final Test test, final String message) {
+ if (test != null) {
+ endTest(test);
+ }
+
+ final Element nested = doc.createElement("skipped");
+
+ if (message != null) {
+ nested.setAttribute("message", message);
+ }
+
+ Element currentTest;
+ if (test != null) {
+ currentTest = testElements.get(createDescription(test));
+ } else {
+ currentTest = rootElement;
+ }
+
+ currentTest.appendChild(nested);
+
+ }
+
+ public void testAssumptionFailure(final Test test, final Throwable failure) {
+ formatSkip(test, failure.getMessage());
+ skippedTests.put(createDescription(test), test);
+
+ }
+} // XMLJUnitResultFormatter
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java
new file mode 100644
index 00000000..4f76c968
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java
@@ -0,0 +1,329 @@
+/*
+ * 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.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+
+/**
+ * Aggregates all &lt;junit&gt; XML formatter testsuite data under
+ * a specific directory and transforms the results via XSLT.
+ * It is not particularly clean but
+ * should be helpful while I am thinking about another technique.
+ *
+ * <p> The main problem is due to the fact that a JVM can be forked for a testcase
+ * thus making it impossible to aggregate all testcases since the listener is
+ * (obviously) in the forked JVM. A solution could be to write a
+ * TestListener that will receive events from the TestRunner via sockets. This
+ * is IMHO the simplest way to do it to avoid this file hacking thing.
+ *
+ * @ant.task name="junitreport" category="testing"
+ */
+public class XMLResultAggregator extends Task implements XMLConstants {
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ /** the list of all filesets, that should contains the xml to aggregate */
+ protected Vector filesets = new Vector();
+
+ /** the name of the result file */
+ protected String toFile;
+
+ /** the directory to write the file to */
+ protected File toDir;
+
+ protected Vector transformers = new Vector();
+
+ /** The default directory: <tt>&#046;</tt>. It is resolved from the project directory */
+ public static final String DEFAULT_DIR = ".";
+
+ /** the default file name: <tt>TESTS-TestSuites.xml</tt> */
+ public static final String DEFAULT_FILENAME = "TESTS-TestSuites.xml";
+
+ /** the current generated id */
+ protected int generatedId = 0;
+
+ /**
+ * text checked for in tests, {@value}
+ */
+ static final String WARNING_IS_POSSIBLY_CORRUPTED
+ = " is not a valid XML document. It is possibly corrupted.";
+ /**
+ * text checked for in tests, {@value}
+ */
+ static final String WARNING_INVALID_ROOT_ELEMENT
+ = " is not a valid testsuite XML document";
+ /**
+ * text checked for in tests, {@value}
+ */
+ static final String WARNING_EMPTY_FILE
+ = " is empty.\nThis can be caused by the test JVM exiting unexpectedly";
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Generate a report based on the document created by the merge.
+ * @return the report
+ */
+ public AggregateTransformer createReport() {
+ AggregateTransformer transformer = new AggregateTransformer(this);
+ transformers.addElement(transformer);
+ return transformer;
+ }
+
+ /**
+ * Set the name of the aggregegated results file. It must be relative
+ * from the <tt>todir</tt> attribute. If not set it will use {@link #DEFAULT_FILENAME}
+ * @param value the name of the file.
+ * @see #setTodir(File)
+ */
+ public void setTofile(String value) {
+ toFile = value;
+ }
+
+ /**
+ * Set the destination directory where the results should be written. If not
+ * set if will use {@link #DEFAULT_DIR}. When given a relative directory
+ * it will resolve it from the project directory.
+ * @param value the directory where to write the results, absolute or
+ * relative.
+ */
+ public void setTodir(File value) {
+ toDir = value;
+ }
+
+ /**
+ * Add a new fileset containing the XML results to aggregate
+ * @param fs the new fileset of xml results.
+ */
+ public void addFileSet(FileSet fs) {
+ filesets.addElement(fs);
+ }
+
+ /**
+ * Aggregate all testsuites into a single document and write it to the
+ * specified directory and file.
+ * @throws BuildException thrown if there is a serious error while writing
+ * the document.
+ */
+ public void execute() throws BuildException {
+ Element rootElement = createDocument();
+ File destFile = getDestinationFile();
+ // write the document
+ try {
+ writeDOMTree(rootElement.getOwnerDocument(), destFile);
+ } catch (IOException e) {
+ throw new BuildException("Unable to write test aggregate to '" + destFile + "'", e);
+ }
+ // apply transformation
+ Enumeration e = transformers.elements();
+ while (e.hasMoreElements()) {
+ AggregateTransformer transformer =
+ (AggregateTransformer) e.nextElement();
+ transformer.setXmlDocument(rootElement.getOwnerDocument());
+ transformer.transform();
+ }
+ }
+
+ /**
+ * Get the full destination file where to write the result. It is made of
+ * the <tt>todir</tt> and <tt>tofile</tt> attributes.
+ * @return the destination file where should be written the result file.
+ */
+ public File getDestinationFile() {
+ if (toFile == null) {
+ toFile = DEFAULT_FILENAME;
+ }
+ if (toDir == null) {
+ toDir = getProject().resolveFile(DEFAULT_DIR);
+ }
+ return new File(toDir, toFile);
+ }
+
+ /**
+ * Get all <code>.xml</code> files in the fileset.
+ *
+ * @return all files in the fileset that end with a '.xml'.
+ */
+ protected File[] getFiles() {
+ Vector v = new Vector();
+ final int size = filesets.size();
+ for (int i = 0; i < size; i++) {
+ FileSet fs = (FileSet) filesets.elementAt(i);
+ DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+ ds.scan();
+ String[] f = ds.getIncludedFiles();
+ for (int j = 0; j < f.length; j++) {
+ String pathname = f[j];
+ if (pathname.endsWith(".xml")) {
+ File file = new File(ds.getBasedir(), pathname);
+ file = getProject().resolveFile(file.getPath());
+ v.addElement(file);
+ }
+ }
+ }
+
+ File[] files = new File[v.size()];
+ v.copyInto(files);
+ return files;
+ }
+
+ //----- from now, the methods are all related to DOM tree manipulation
+
+ /**
+ * Write the DOM tree to a file.
+ * @param doc the XML document to dump to disk.
+ * @param file the filename to write the document to. Should obviously be a .xml file.
+ * @throws IOException thrown if there is an error while writing the content.
+ */
+ protected void writeDOMTree(Document doc, File file) throws IOException {
+ OutputStream os = new FileOutputStream(file);
+ try {
+ PrintWriter wri = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(os), "UTF8"));
+ wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+ (new DOMElementWriter()).write(doc.getDocumentElement(), wri, 0, " ");
+ wri.flush();
+ // writers do not throw exceptions, so check for them.
+ if (wri.checkError()) {
+ throw new IOException("Error while writing DOM content");
+ }
+ } finally {
+ os.close();
+ }
+ }
+
+ /**
+ * <p> Create a DOM tree.
+ * Has 'testsuites' as firstchild and aggregates all
+ * testsuite results that exists in the base directory.
+ * @return the root element of DOM tree that aggregates all testsuites.
+ */
+ protected Element createDocument() {
+ // create the dom tree
+ DocumentBuilder builder = getDocumentBuilder();
+ Document doc = builder.newDocument();
+ Element rootElement = doc.createElement(TESTSUITES);
+ doc.appendChild(rootElement);
+
+ generatedId = 0;
+
+ // get all files and add them to the document
+ File[] files = getFiles();
+ for (int i = 0; i < files.length; i++) {
+ File file = files[i];
+ try {
+ log("Parsing file: '" + file + "'", Project.MSG_VERBOSE);
+ if (file.length() > 0) {
+ Document testsuiteDoc
+ = builder.parse(
+ FileUtils.getFileUtils().toURI(files[i].getAbsolutePath()));
+ Element elem = testsuiteDoc.getDocumentElement();
+ // make sure that this is REALLY a testsuite.
+ if (TESTSUITE.equals(elem.getNodeName())) {
+ addTestSuite(rootElement, elem);
+ generatedId++;
+ } else {
+ //wrong root element name
+ // issue a warning.
+ log("the file " + file
+ + WARNING_INVALID_ROOT_ELEMENT,
+ Project.MSG_WARN);
+ }
+ } else {
+ log("the file " + file
+ + WARNING_EMPTY_FILE,
+ Project.MSG_WARN);
+ }
+ } catch (SAXException e) {
+ // a testcase might have failed and write a zero-length document,
+ // It has already failed, but hey.... mm. just put a warning
+ log("The file " + file + WARNING_IS_POSSIBLY_CORRUPTED, Project.MSG_WARN);
+ log(StringUtils.getStackTrace(e), Project.MSG_DEBUG);
+ } catch (IOException e) {
+ log("Error while accessing file " + file + ": "
+ + e.getMessage(), Project.MSG_ERR);
+ log("Error while accessing file " + file + ": "
+ + e.getMessage(), e, Project.MSG_VERBOSE);
+ }
+ }
+ return rootElement;
+ }
+
+ /**
+ * <p> Add a new testsuite node to the document.
+ * The main difference is that it
+ * split the previous fully qualified name into a package and a name.
+ * <p> For example: <tt>org.apache.Whatever</tt> will be split into
+ * <tt>org.apache</tt> and <tt>Whatever</tt>.
+ * @param root the root element to which the <tt>testsuite</tt> node should
+ * be appended.
+ * @param testsuite the element to append to the given root. It will slightly
+ * modify the original node to change the name attribute and add
+ * a package one.
+ */
+ protected void addTestSuite(Element root, Element testsuite) {
+ String fullclassname = testsuite.getAttribute(ATTR_NAME);
+ int pos = fullclassname.lastIndexOf('.');
+
+ // a missing . might imply no package at all. Don't get fooled.
+ String pkgName = (pos == -1) ? "" : fullclassname.substring(0, pos);
+ String classname = (pos == -1) ? fullclassname : fullclassname.substring(pos + 1);
+ Element copy = (Element) DOMUtil.importNode(root, testsuite);
+
+ // modify the name attribute and set the package
+ copy.setAttribute(ATTR_NAME, classname);
+ copy.setAttribute(ATTR_PACKAGE, pkgName);
+ copy.setAttribute(ATTR_ID, Integer.toString(generatedId));
+ }
+
+ /**
+ * Create a new document builder. Will issue an <tt>ExceptionInitializerError</tt>
+ * if something is going wrong. It is fatal anyway.
+ * @todo factorize this somewhere else. It is duplicated code.
+ * @return a new document builder to create a DOM
+ */
+ private static DocumentBuilder getDocumentBuilder() {
+ try {
+ return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ } catch (Exception exc) {
+ throw new ExceptionInInitializerError(exc);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/DefaultNative2Ascii.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/DefaultNative2Ascii.java
new file mode 100644
index 00000000..3cd52afe
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/DefaultNative2Ascii.java
@@ -0,0 +1,106 @@
+/*
+ * 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.tools.ant.taskdefs.optional.native2ascii;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.taskdefs.optional.Native2Ascii;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * encapsulates the handling common to diffent Native2Asciiadapter
+ * implementations.
+ *
+ * @since Ant 1.6.3
+ */
+public abstract class DefaultNative2Ascii implements Native2AsciiAdapter {
+
+ /** No-arg constructor. */
+ public DefaultNative2Ascii() {
+ }
+
+ /**
+ * Splits the task into setting up the command line switches
+ * @param args the native 2 ascii arguments.
+ * @param srcFile the source file.
+ * @param destFile the destination file.
+ * @return run if the conversion was successful.
+ * @throws BuildException if there is a problem.
+ * (delegated to {@link #setup setup}), adding the file names
+ * (delegated to {@link #addFiles addFiles}) and running the tool
+ * (delegated to {@link #run run}).
+ */
+ public final boolean convert(Native2Ascii args, File srcFile,
+ File destFile) throws BuildException {
+ Commandline cmd = new Commandline();
+ setup(cmd, args);
+ addFiles(cmd, args, srcFile, destFile);
+ return run(cmd, args);
+ }
+
+ /**
+ * Sets up the initial command line.
+ *
+ * <p>only the -encoding argument and nested arg elements get
+ * handled here.</p>
+ *
+ * @param cmd Command line to add to
+ * @param args provides the user-setting and access to Ant's
+ * logging system.
+ * @throws BuildException if there was a problem.
+ */
+ protected void setup(Commandline cmd, Native2Ascii args)
+ throws BuildException {
+ if (args.getEncoding() != null) {
+ cmd.createArgument().setValue("-encoding");
+ cmd.createArgument().setValue(args.getEncoding());
+ }
+ cmd.addArguments(args.getCurrentArgs());
+ }
+
+ /**
+ * Adds source and dest files to the command line.
+ *
+ * <p>This implementation adds them without any leading
+ * qualifiers, source first.</p>
+ *
+ * @param cmd Command line to add to
+ * @param log provides access to Ant's logging system.
+ * @param src the source file
+ * @param dest the destination file
+ * @throws BuildException if there was a problem.
+ */
+ protected void addFiles(Commandline cmd, ProjectComponent log, File src,
+ File dest) throws BuildException {
+ cmd.createArgument().setFile(src);
+ cmd.createArgument().setFile(dest);
+ }
+
+ /**
+ * Executes the command.
+ *
+ * @param cmd Command line to execute
+ * @param log provides access to Ant's logging system.
+ * @return whether execution was successful
+ * @throws BuildException if there was a problem.
+ */
+ protected abstract boolean run(Commandline cmd, ProjectComponent log)
+ throws BuildException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/KaffeNative2Ascii.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/KaffeNative2Ascii.java
new file mode 100644
index 00000000..da4836fd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/KaffeNative2Ascii.java
@@ -0,0 +1,88 @@
+/*
+ * 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.tools.ant.taskdefs.optional.native2ascii;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.taskdefs.ExecuteJava;
+import org.apache.tools.ant.taskdefs.optional.Native2Ascii;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Adapter to kaffe.tools.native2ascii.Native2Ascii.
+ *
+ * @since Ant 1.6.3
+ */
+public final class KaffeNative2Ascii extends DefaultNative2Ascii {
+
+ // sorted by newest Kaffe version first
+ private static final String[] N2A_CLASSNAMES = new String[] {
+ "gnu.classpath.tools.native2ascii.Native2ASCII",
+ // pre Kaffe 1.1.5
+ "kaffe.tools.native2ascii.Native2Ascii",
+ };
+
+ /**
+ * Identifies this adapter.
+ */
+ public static final String IMPLEMENTATION_NAME = "kaffe";
+
+ /** {@inheritDoc} */
+ protected void setup(Commandline cmd, Native2Ascii args)
+ throws BuildException {
+ if (args.getReverse()) {
+ throw new BuildException("-reverse is not supported by Kaffe");
+ }
+ super.setup(cmd, args);
+ }
+
+ /** {@inheritDoc} */
+ protected boolean run(Commandline cmd, ProjectComponent log)
+ throws BuildException {
+ ExecuteJava ej = new ExecuteJava();
+ Class c = getN2aClass();
+ if (c == null) {
+ throw new BuildException("Couldn't load Kaffe's Native2Ascii"
+ + " class");
+ }
+
+ cmd.setExecutable(c.getName());
+ ej.setJavaCommand(cmd);
+ ej.execute(log.getProject());
+ // otherwise ExecuteJava has thrown an exception
+ return true;
+ }
+
+ /**
+ * tries to load Kaffe Native2Ascii and falls back to the older
+ * class name if necessary.
+ *
+ * @return null if neither class can get loaded.
+ */
+ private static Class getN2aClass() {
+ for (int i = 0; i < N2A_CLASSNAMES.length; i++) {
+ try {
+ return Class.forName(N2A_CLASSNAMES[i]);
+ } catch (ClassNotFoundException cnfe) {
+ // Ignore
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapter.java
new file mode 100644
index 00000000..af414004
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapter.java
@@ -0,0 +1,44 @@
+/*
+ * 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.tools.ant.taskdefs.optional.native2ascii;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.optional.Native2Ascii;
+
+/**
+ * Interface for an adapter to a native2ascii implementation.
+ *
+ * @since Ant 1.6.3
+ */
+// CheckStyle:HideUtilityClassConstructorCheck OFF (bc)
+public interface Native2AsciiAdapter {
+ /**
+ * Convert the encoding of srcFile writing to destFile.
+ *
+ * @param args Task that holds command line arguments and allows
+ * the implementation to send messages to Ant's logging system
+ * @param srcFile the source to convert
+ * @param destFile where to send output to
+ * @return whether the conversion has been successful.
+ * @throws BuildException if there was a problem.
+ */
+ boolean convert(Native2Ascii args, File srcFile, File destFile)
+ throws BuildException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.java
new file mode 100644
index 00000000..ae419039
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.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.tools.ant.taskdefs.optional.native2ascii;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.ClasspathUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Creates the Native2AsciiAdapter based on the user choice and
+ * potentially the VM vendor.
+ *
+ * @since Ant 1.6.3
+ */
+// CheckStyle:HideUtilityClassConstructorCheck OFF (bc)
+public class Native2AsciiAdapterFactory {
+
+ /**
+ * Determines the default choice of adapter based on the VM
+ * vendor.
+ *
+ * @return the default choice of adapter based on the VM
+ * vendor
+ */
+ public static String getDefault() {
+ if (JavaEnvUtils.isKaffe() || JavaEnvUtils.isClasspathBased()) {
+ return KaffeNative2Ascii.IMPLEMENTATION_NAME;
+ }
+ return SunNative2Ascii.IMPLEMENTATION_NAME;
+ }
+
+ /**
+ * Creates the Native2AsciiAdapter based on the user choice and
+ * potentially the VM vendor.
+ *
+ * @param choice the user choice (if any).
+ * @param log a ProjectComponent instance used to access Ant's
+ * logging system.
+ * @return The adapter to use.
+ * @throws BuildException if there was a problem.
+ */
+ public static Native2AsciiAdapter getAdapter(String choice,
+ ProjectComponent log)
+ throws BuildException {
+ return getAdapter(choice, log, null);
+ }
+
+ /**
+ * Creates the Native2AsciiAdapter based on the user choice and
+ * potentially the VM vendor.
+ *
+ * @param choice the user choice (if any).
+ * @param log a ProjectComponent instance used to access Ant's
+ * logging system.
+ * @param classpath the classpath to use when looking up an
+ * adapter class
+ * @return The adapter to use.
+ * @throws BuildException if there was a problem.
+ * @since Ant 1.8.0
+ */
+ public static Native2AsciiAdapter getAdapter(String choice,
+ ProjectComponent log,
+ Path classpath)
+ throws BuildException {
+ if (((JavaEnvUtils.isKaffe() || JavaEnvUtils.isClasspathBased()) && choice == null)
+ || KaffeNative2Ascii.IMPLEMENTATION_NAME.equals(choice)) {
+ return new KaffeNative2Ascii();
+ } else if (SunNative2Ascii.IMPLEMENTATION_NAME.equals(choice)) {
+ return new SunNative2Ascii();
+ } else if (choice != null) {
+ return resolveClassName(choice,
+ // Memory leak in line below
+ log.getProject()
+ .createClassLoader(classpath));
+ }
+
+ // This default has been good enough until Ant 1.6.3, so stick
+ // with it
+ return new SunNative2Ascii();
+ }
+
+ /**
+ * Tries to resolve the given classname into a native2ascii adapter.
+ * Throws a fit if it can't.
+ *
+ * @param className The fully qualified classname to be created.
+ * @param loader the classloader to use
+ * @throws BuildException This is the fit that is thrown if className
+ * isn't an instance of Native2AsciiAdapter.
+ */
+ private static Native2AsciiAdapter resolveClassName(String className,
+ ClassLoader loader)
+ throws BuildException {
+ return (Native2AsciiAdapter) ClasspathUtils.newInstance(className,
+ loader != null ? loader :
+ Native2AsciiAdapterFactory.class.getClassLoader(),
+ Native2AsciiAdapter.class);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/SunNative2Ascii.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/SunNative2Ascii.java
new file mode 100644
index 00000000..fac94b18
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/SunNative2Ascii.java
@@ -0,0 +1,71 @@
+/*
+ * 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.tools.ant.taskdefs.optional.native2ascii;
+
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.taskdefs.optional.Native2Ascii;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Adapter to sun.tools.native2ascii.Main.
+ *
+ * @since Ant 1.6.3
+ */
+public final class SunNative2Ascii extends DefaultNative2Ascii {
+
+ /**
+ * Identifies this adapter.
+ */
+ public static final String IMPLEMENTATION_NAME = "sun";
+
+ /** {@inheritDoc} */
+ protected void setup(Commandline cmd, Native2Ascii args)
+ throws BuildException {
+ if (args.getReverse()) {
+ cmd.createArgument().setValue("-reverse");
+ }
+ super.setup(cmd, args);
+ }
+
+ /** {@inheritDoc} */
+ protected boolean run(Commandline cmd, ProjectComponent log)
+ throws BuildException {
+ try {
+ Class n2aMain = Class.forName("sun.tools.native2ascii.Main");
+ Class[] param = new Class[] {String[].class};
+ Method convert = n2aMain.getMethod("convert", param);
+ if (convert == null) {
+ throw new BuildException("Could not find convert() method in "
+ + "sun.tools.native2ascii.Main");
+ }
+ Object o = n2aMain.newInstance();
+ return ((Boolean) convert.invoke(o,
+ new Object[] {cmd.getArguments()})
+ ).booleanValue();
+ } catch (BuildException ex) {
+ //rethrow
+ throw ex;
+ } catch (Exception ex) {
+ //wrap
+ throw new BuildException("Error starting Sun's native2ascii: ", ex);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTP.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTP.java
new file mode 100644
index 00000000..cef9dda0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTP.java
@@ -0,0 +1,2725 @@
+/*
+ * 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.tools.ant.taskdefs.optional.net;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.commons.net.ftp.FTPReply;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Delete;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.RetryHandler;
+import org.apache.tools.ant.util.Retryable;
+import org.apache.tools.ant.util.VectorSet;
+
+/**
+ * Basic FTP client. Performs the following actions:
+ * <ul>
+ * <li> <strong>send</strong> - send files to a remote server. This is the
+ * default action.</li>
+ * <li> <strong>get</strong> - retrieve files from a remote server.</li>
+ * <li> <strong>del</strong> - delete files from a remote server.</li>
+ * <li> <strong>list</strong> - create a file listing.</li>
+ * <li> <strong>chmod</strong> - change unix file permissions.</li>
+ * <li> <strong>rmdir</strong> - remove directories, if empty, from a
+ * remote server.</li>
+ * </ul>
+ * <strong>Note:</strong> Some FTP servers - notably the Solaris server - seem
+ * to hold data ports open after a "retr" operation, allowing them to timeout
+ * instead of shutting them down cleanly. This happens in active or passive
+ * mode, and the ports will remain open even after ending the FTP session. FTP
+ * "send" operations seem to close ports immediately. This behavior may cause
+ * problems on some systems when downloading large sets of files.
+ *
+ * @since Ant 1.3
+ */
+public class FTP extends Task implements FTPTaskConfig {
+ protected static final int SEND_FILES = 0;
+ protected static final int GET_FILES = 1;
+ protected static final int DEL_FILES = 2;
+ protected static final int LIST_FILES = 3;
+ protected static final int MK_DIR = 4;
+ protected static final int CHMOD = 5;
+ protected static final int RM_DIR = 6;
+ protected static final int SITE_CMD = 7;
+ /** return code of ftp */
+ private static final int CODE_521 = 521;
+ private static final int CODE_550 = 550;
+ private static final int CODE_553 = 553;
+
+ /** adjust uptodate calculations where server timestamps are HH:mm and client's
+ * are HH:mm:ss */
+ private static final long GRANULARITY_MINUTE = 60000L;
+
+ /** Date formatter used in logging, note not thread safe! */
+ private static final SimpleDateFormat TIMESTAMP_LOGGING_SDF =
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ /** Default port for FTP */
+ public static final int DEFAULT_FTP_PORT = 21;
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private String remotedir;
+ private String server;
+ private String userid;
+ private String password;
+ private String account;
+ private File listing;
+ private boolean binary = true;
+ private boolean passive = false;
+ private boolean verbose = false;
+ private boolean newerOnly = false;
+ private long timeDiffMillis = 0;
+ private long granularityMillis = 0L;
+ private boolean timeDiffAuto = false;
+ private int action = SEND_FILES;
+ private Vector filesets = new Vector();
+ private Set dirCache = new HashSet();
+ private int transferred = 0;
+ private String remoteFileSep = "/";
+ private int port = DEFAULT_FTP_PORT;
+ private boolean skipFailedTransfers = false;
+ private int skipped = 0;
+ private boolean ignoreNoncriticalErrors = false;
+ private boolean preserveLastModified = false;
+ private String chmod = null;
+ private String umask = null;
+ private FTPSystemType systemTypeKey = FTPSystemType.getDefault();
+ private String defaultDateFormatConfig = null;
+ private String recentDateFormatConfig = null;
+ private LanguageCode serverLanguageCodeConfig = LanguageCode.getDefault();
+ private String serverTimeZoneConfig = null;
+ private String shortMonthNamesConfig = null;
+ private Granularity timestampGranularity = Granularity.getDefault();
+ private boolean isConfigurationSet = false;
+ private int retriesAllowed = 0;
+ private String siteCommand = null;
+ private String initialSiteCommand = null;
+ private boolean enableRemoteVerification = true;
+
+ protected static final String[] ACTION_STRS = {
+ "sending",
+ "getting",
+ "deleting",
+ "listing",
+ "making directory",
+ "chmod",
+ "removing",
+ "site"
+ };
+
+ protected static final String[] COMPLETED_ACTION_STRS = {
+ "sent",
+ "retrieved",
+ "deleted",
+ "listed",
+ "created directory",
+ "mode changed",
+ "removed",
+ "site command executed"
+ };
+
+ protected static final String[] ACTION_TARGET_STRS = {
+ "files",
+ "files",
+ "files",
+ "files",
+ "directory",
+ "files",
+ "directories",
+ "site command"
+ };
+
+ /**
+ * internal class providing a File-like interface to some of the information
+ * available from the FTP server
+ *
+ */
+ protected static class FTPFileProxy extends File {
+
+ private final FTPFile file;
+ private final String[] parts;
+ private final String name;
+
+ /**
+ * creates a proxy to a FTP file
+ * @param file
+ */
+ public FTPFileProxy(FTPFile file) {
+ super(file.getName());
+ name = file.getName();
+ this.file = file;
+ parts = FileUtils.getPathStack(name);
+ }
+
+ /**
+ * creates a proxy to a FTP directory
+ * @param completePath the remote directory.
+ */
+ public FTPFileProxy(String completePath) {
+ super(completePath);
+ file = null;
+ name = completePath;
+ parts = FileUtils.getPathStack(completePath);
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#exists()
+ */
+ public boolean exists() {
+ return true;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#getAbsolutePath()
+ */
+ public String getAbsolutePath() {
+ return name;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#getName()
+ */
+ public String getName() {
+ return parts.length > 0 ? parts[parts.length - 1] : name;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#getParent()
+ */
+ public String getParent() {
+ String result = "";
+ for(int i = 0; i < parts.length - 1; i++){
+ result += File.separatorChar + parts[i];
+ }
+ return result;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#getPath()
+ */
+ public String getPath() {
+ return name;
+ }
+
+
+ /**
+ * FTP files are stored as absolute paths
+ * @return true
+ */
+ public boolean isAbsolute() {
+ return true;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#isDirectory()
+ */
+ public boolean isDirectory() {
+ return file == null;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#isFile()
+ */
+ public boolean isFile() {
+ return file != null;
+ }
+
+
+ /**
+ * FTP files cannot be hidden
+ *
+ * @return false
+ */
+ public boolean isHidden() {
+ return false;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#lastModified()
+ */
+ public long lastModified() {
+ if (file != null) {
+ return file.getTimestamp().getTimeInMillis();
+ }
+ return 0;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#length()
+ */
+ public long length() {
+ if (file != null) {
+ return file.getSize();
+ }
+ return 0;
+ }
+ }
+
+ /**
+ * internal class allowing to read the contents of a remote file system
+ * using the FTP protocol
+ * used in particular for ftp get operations
+ * differences with DirectoryScanner
+ * "" (the root of the fileset) is never included in the included directories
+ * followSymlinks defaults to false
+ */
+ protected class FTPDirectoryScanner extends DirectoryScanner {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected FTPClient ftp = null;
+ // CheckStyle:VisibilityModifier ON
+
+ private String rootPath = null;
+
+ /**
+ * since ant 1.6
+ * this flag should be set to true on UNIX and can save scanning time
+ */
+ private boolean remoteSystemCaseSensitive = false;
+ private boolean remoteSensitivityChecked = false;
+
+ /**
+ * constructor
+ * @param ftp ftpclient object
+ */
+ public FTPDirectoryScanner(FTPClient ftp) {
+ super();
+ this.ftp = ftp;
+ this.setFollowSymlinks(false);
+ }
+
+
+ /**
+ * scans the remote directory,
+ * storing internally the included files, directories, ...
+ */
+ public void scan() {
+ if (includes == null) {
+ // No includes supplied, so set it to 'matches all'
+ includes = new String[1];
+ includes[0] = "**";
+ }
+ if (excludes == null) {
+ excludes = new String[0];
+ }
+
+ filesIncluded = new VectorSet();
+ filesNotIncluded = new Vector();
+ filesExcluded = new VectorSet();
+ dirsIncluded = new VectorSet();
+ dirsNotIncluded = new Vector();
+ dirsExcluded = new VectorSet();
+
+ try {
+ String cwd = ftp.printWorkingDirectory();
+ // always start from the current ftp working dir
+ forceRemoteSensitivityCheck();
+
+ checkIncludePatterns();
+ clearCaches();
+ ftp.changeWorkingDirectory(cwd);
+ } catch (IOException e) {
+ throw new BuildException("Unable to scan FTP server: ", e);
+ }
+ }
+
+
+ /**
+ * this routine is actually checking all the include patterns in
+ * order to avoid scanning everything under base dir
+ * @since ant1.6
+ */
+ private void checkIncludePatterns() {
+
+ Hashtable newroots = new Hashtable();
+ // put in the newroots vector the include patterns without
+ // wildcard tokens
+ for (int icounter = 0; icounter < includes.length; icounter++) {
+ String newpattern =
+ SelectorUtils.rtrimWildcardTokens(includes[icounter]);
+ newroots.put(newpattern, includes[icounter]);
+ }
+ if (remotedir == null) {
+ try {
+ remotedir = ftp.printWorkingDirectory();
+ } catch (IOException e) {
+ throw new BuildException("could not read current ftp directory",
+ getLocation());
+ }
+ }
+ AntFTPFile baseFTPFile = new AntFTPRootFile(ftp, remotedir);
+ rootPath = baseFTPFile.getAbsolutePath();
+ // construct it
+ if (newroots.containsKey("")) {
+ // we are going to scan everything anyway
+ scandir(rootPath, "", true);
+ } else {
+ // only scan directories that can include matched files or
+ // directories
+ Enumeration enum2 = newroots.keys();
+
+ while (enum2.hasMoreElements()) {
+ String currentelement = (String) enum2.nextElement();
+ String originalpattern = (String) newroots.get(currentelement);
+ AntFTPFile myfile = new AntFTPFile(baseFTPFile, currentelement);
+ boolean isOK = true;
+ boolean traversesSymlinks = false;
+ String path = null;
+
+ if (myfile.exists()) {
+ forceRemoteSensitivityCheck();
+ if (remoteSensitivityChecked
+ && remoteSystemCaseSensitive && isFollowSymlinks()) {
+ // cool case,
+ //we do not need to scan all the subdirs in the relative path
+ path = myfile.getFastRelativePath();
+ } else {
+ // may be on a case insensitive file system. We want
+ // the results to show what's really on the disk, so
+ // we need to double check.
+ try {
+ path = myfile.getRelativePath();
+ traversesSymlinks = myfile.isTraverseSymlinks();
+ } catch (IOException be) {
+ throw new BuildException(be, getLocation());
+ } catch (BuildException be) {
+ isOK = false;
+ }
+ }
+ } else {
+ isOK = false;
+ }
+ if (isOK) {
+ currentelement = path.replace(remoteFileSep.charAt(0), File.separatorChar);
+ if (!isFollowSymlinks()
+ && traversesSymlinks) {
+ continue;
+ }
+
+ if (myfile.isDirectory()) {
+ if (isIncluded(currentelement)
+ && currentelement.length() > 0) {
+ accountForIncludedDir(currentelement, myfile, true);
+ } else {
+ if (currentelement.length() > 0) {
+ if (currentelement.charAt(currentelement
+ .length() - 1)
+ != File.separatorChar) {
+ currentelement =
+ currentelement + File.separatorChar;
+ }
+ }
+ scandir(myfile.getAbsolutePath(), currentelement, true);
+ }
+ } else {
+ if (isCaseSensitive
+ && originalpattern.equals(currentelement)) {
+ accountForIncludedFile(currentelement);
+ } else if (!isCaseSensitive
+ && originalpattern
+ .equalsIgnoreCase(currentelement)) {
+ accountForIncludedFile(currentelement);
+ }
+ }
+ }
+ }
+ }
+ }
+ /**
+ * scans a particular directory. populates the scannedDirs cache.
+ *
+ * @param dir directory to scan
+ * @param vpath relative path to the base directory of the remote fileset
+ * always ended with a File.separator
+ * @param fast seems to be always true in practice
+ */
+ protected void scandir(String dir, String vpath, boolean fast) {
+ // avoid double scanning of directories, can only happen in fast mode
+ if (fast && hasBeenScanned(vpath)) {
+ return;
+ }
+ try {
+ if (!ftp.changeWorkingDirectory(dir)) {
+ return;
+ }
+ String completePath = null;
+ if (!vpath.equals("")) {
+ completePath = rootPath + remoteFileSep
+ + vpath.replace(File.separatorChar, remoteFileSep.charAt(0));
+ } else {
+ completePath = rootPath;
+ }
+ FTPFile[] newfiles = listFiles(completePath, false);
+
+ if (newfiles == null) {
+ ftp.changeToParentDirectory();
+ return;
+ }
+ for (int i = 0; i < newfiles.length; i++) {
+ FTPFile file = newfiles[i];
+ if (file != null
+ && !file.getName().equals(".")
+ && !file.getName().equals("..")) {
+ String name = vpath + file.getName();
+ scannedDirs.put(name, new FTPFileProxy(file));
+ if (isFunctioningAsDirectory(ftp, dir, file)) {
+ boolean slowScanAllowed = true;
+ if (!isFollowSymlinks() && file.isSymbolicLink()) {
+ dirsExcluded.addElement(name);
+ slowScanAllowed = false;
+ } else if (isIncluded(name)) {
+ accountForIncludedDir(name,
+ new AntFTPFile(ftp, file, completePath) , fast);
+ } else {
+ dirsNotIncluded.addElement(name);
+ if (fast && couldHoldIncluded(name)) {
+ scandir(file.getName(),
+ name + File.separator, fast);
+ }
+ }
+ if (!fast && slowScanAllowed) {
+ scandir(file.getName(),
+ name + File.separator, fast);
+ }
+ } else {
+ if (!isFollowSymlinks() && file.isSymbolicLink()) {
+ filesExcluded.addElement(name);
+ } else if (isFunctioningAsFile(ftp, dir, file)) {
+ accountForIncludedFile(name);
+ }
+ }
+ }
+ }
+ ftp.changeToParentDirectory();
+ } catch (IOException e) {
+ throw new BuildException("Error while communicating with FTP "
+ + "server: ", e);
+ }
+ }
+ /**
+ * process included file
+ * @param name path of the file relative to the directory of the fileset
+ */
+ private void accountForIncludedFile(String name) {
+ if (!filesIncluded.contains(name)
+ && !filesExcluded.contains(name)) {
+
+ if (isIncluded(name)) {
+ if (!isExcluded(name)
+ && isSelected(name, (File) scannedDirs.get(name))) {
+ filesIncluded.addElement(name);
+ } else {
+ filesExcluded.addElement(name);
+ }
+ } else {
+ filesNotIncluded.addElement(name);
+ }
+ }
+ }
+
+ /**
+ *
+ * @param name path of the directory relative to the directory of
+ * the fileset
+ * @param file directory as file
+ * @param fast
+ */
+ private void accountForIncludedDir(String name, AntFTPFile file, boolean fast) {
+ if (!dirsIncluded.contains(name)
+ && !dirsExcluded.contains(name)) {
+
+ if (!isExcluded(name)) {
+ if (fast) {
+ if (file.isSymbolicLink()) {
+ try {
+ file.getClient().changeWorkingDirectory(file.curpwd);
+ } catch (IOException ioe) {
+ throw new BuildException("could not change directory to curpwd");
+ }
+ scandir(file.getLink(),
+ name + File.separator, fast);
+ } else {
+ try {
+ file.getClient().changeWorkingDirectory(file.curpwd);
+ } catch (IOException ioe) {
+ throw new BuildException("could not change directory to curpwd");
+ }
+ scandir(file.getName(),
+ name + File.separator, fast);
+ }
+ }
+ dirsIncluded.addElement(name);
+ } else {
+ dirsExcluded.addElement(name);
+ if (fast && couldHoldIncluded(name)) {
+ try {
+ file.getClient().changeWorkingDirectory(file.curpwd);
+ } catch (IOException ioe) {
+ throw new BuildException("could not change directory to curpwd");
+ }
+ scandir(file.getName(),
+ name + File.separator, fast);
+ }
+ }
+ }
+ }
+ /**
+ * temporary table to speed up the various scanning methods below
+ *
+ * @since Ant 1.6
+ */
+ private Map fileListMap = new HashMap();
+ /**
+ * List of all scanned directories.
+ *
+ * @since Ant 1.6
+ */
+
+ private Map scannedDirs = new HashMap();
+
+ /**
+ * Has the directory with the given path relative to the base
+ * directory already been scanned?
+ *
+ * @since Ant 1.6
+ */
+ private boolean hasBeenScanned(String vpath) {
+ return scannedDirs.containsKey(vpath);
+ }
+
+ /**
+ * Clear internal caches.
+ *
+ * @since Ant 1.6
+ */
+ private void clearCaches() {
+ fileListMap.clear();
+ scannedDirs.clear();
+ }
+ /**
+ * list the files present in one directory.
+ * @param directory full path on the remote side
+ * @param changedir if true change to directory directory before listing
+ * @return array of FTPFile
+ */
+ public FTPFile[] listFiles(String directory, boolean changedir) {
+ //getProject().log("listing files in directory " + directory, Project.MSG_DEBUG);
+ String currentPath = directory;
+ if (changedir) {
+ try {
+ boolean result = ftp.changeWorkingDirectory(directory);
+ if (!result) {
+ return null;
+ }
+ currentPath = ftp.printWorkingDirectory();
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, getLocation());
+ }
+ }
+ if (fileListMap.containsKey(currentPath)) {
+ getProject().log("filelist map used in listing files", Project.MSG_DEBUG);
+ return ((FTPFile[]) fileListMap.get(currentPath));
+ }
+ FTPFile[] result = null;
+ try {
+ result = ftp.listFiles();
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, getLocation());
+ }
+ fileListMap.put(currentPath, result);
+ if (!remoteSensitivityChecked) {
+ checkRemoteSensitivity(result, directory);
+ }
+ return result;
+ }
+
+ private void forceRemoteSensitivityCheck() {
+ if (!remoteSensitivityChecked) {
+ try {
+ checkRemoteSensitivity(ftp.listFiles(), ftp.printWorkingDirectory());
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, getLocation());
+ }
+ }
+ }
+ /**
+ * cd into one directory and
+ * list the files present in one directory.
+ * @param directory full path on the remote side
+ * @return array of FTPFile
+ */
+ public FTPFile[] listFiles(String directory) {
+ return listFiles(directory, true);
+ }
+ private void checkRemoteSensitivity(FTPFile[] array, String directory) {
+ if (array == null) {
+ return;
+ }
+ boolean candidateFound = false;
+ String target = null;
+ for (int icounter = 0; icounter < array.length; icounter++) {
+ if (array[icounter] != null && array[icounter].isDirectory()) {
+ if (!array[icounter].getName().equals(".")
+ && !array[icounter].getName().equals("..")) {
+ candidateFound = true;
+ target = fiddleName(array[icounter].getName());
+ getProject().log("will try to cd to "
+ + target + " where a directory called " + array[icounter].getName()
+ + " exists", Project.MSG_DEBUG);
+ for (int pcounter = 0; pcounter < array.length; pcounter++) {
+ if (array[pcounter] != null
+ && pcounter != icounter
+ && target.equals(array[pcounter].getName())) {
+ candidateFound = false;
+ break;
+ }
+ }
+ if (candidateFound) {
+ break;
+ }
+ }
+ }
+ }
+ if (candidateFound) {
+ try {
+ getProject().log("testing case sensitivity, attempting to cd to "
+ + target, Project.MSG_DEBUG);
+ remoteSystemCaseSensitive = !ftp.changeWorkingDirectory(target);
+ } catch (IOException ioe) {
+ remoteSystemCaseSensitive = true;
+ } finally {
+ try {
+ ftp.changeWorkingDirectory(directory);
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, getLocation());
+ }
+ }
+ getProject().log("remote system is case sensitive : " + remoteSystemCaseSensitive,
+ Project.MSG_VERBOSE);
+ remoteSensitivityChecked = true;
+ }
+ }
+ private String fiddleName(String origin) {
+ StringBuffer result = new StringBuffer();
+ for (int icounter = 0; icounter < origin.length(); icounter++) {
+ if (Character.isLowerCase(origin.charAt(icounter))) {
+ result.append(Character.toUpperCase(origin.charAt(icounter)));
+ } else if (Character.isUpperCase(origin.charAt(icounter))) {
+ result.append(Character.toLowerCase(origin.charAt(icounter)));
+ } else {
+ result.append(origin.charAt(icounter));
+ }
+ }
+ return result.toString();
+ }
+ /**
+ * an AntFTPFile is a representation of a remote file
+ * @since Ant 1.6
+ */
+ protected class AntFTPFile {
+ /**
+ * ftp client
+ */
+ private FTPClient client;
+ /**
+ * parent directory of the file
+ */
+ private String curpwd;
+ /**
+ * the file itself
+ */
+ private FTPFile ftpFile;
+ /**
+ *
+ */
+ private AntFTPFile parent = null;
+ private boolean relativePathCalculated = false;
+ private boolean traversesSymlinks = false;
+ private String relativePath = "";
+ /**
+ * constructor
+ * @param client ftp client variable
+ * @param ftpFile the file
+ * @param curpwd absolute remote path where the file is found
+ */
+ public AntFTPFile(FTPClient client, FTPFile ftpFile, String curpwd) {
+ this.client = client;
+ this.ftpFile = ftpFile;
+ this.curpwd = curpwd;
+ }
+ /**
+ * other constructor
+ * @param parent the parent file
+ * @param path a relative path to the parent file
+ */
+ public AntFTPFile(AntFTPFile parent, String path) {
+ this.parent = parent;
+ this.client = parent.client;
+ Vector pathElements = SelectorUtils.tokenizePath(path);
+ try {
+ boolean result = this.client.changeWorkingDirectory(parent.getAbsolutePath());
+ //this should not happen, except if parent has been deleted by another process
+ if (!result) {
+ return;
+ }
+ this.curpwd = parent.getAbsolutePath();
+ } catch (IOException ioe) {
+ throw new BuildException("could not change working dir to "
+ + parent.curpwd);
+ }
+ final int size = pathElements.size();
+ for (int fcount = 0; fcount < size - 1; fcount++) {
+ String currentPathElement = (String) pathElements.elementAt(fcount);
+ try {
+ boolean result = this.client.changeWorkingDirectory(currentPathElement);
+ if (!result && !isCaseSensitive()
+ && (remoteSystemCaseSensitive || !remoteSensitivityChecked)) {
+ currentPathElement = findPathElementCaseUnsensitive(this.curpwd,
+ currentPathElement);
+ if (currentPathElement == null) {
+ return;
+ }
+ } else if (!result) {
+ return;
+ }
+ this.curpwd = getCurpwdPlusFileSep()
+ + currentPathElement;
+ } catch (IOException ioe) {
+ throw new BuildException("could not change working dir to "
+ + (String) pathElements.elementAt(fcount)
+ + " from " + this.curpwd);
+ }
+
+ }
+ String lastpathelement = (String) pathElements.elementAt(size - 1);
+ FTPFile [] theFiles = listFiles(this.curpwd);
+ this.ftpFile = getFile(theFiles, lastpathelement);
+ }
+ /**
+ * find a file in a directory in case unsensitive way
+ * @param parentPath where we are
+ * @param soughtPathElement what is being sought
+ * @return the first file found or null if not found
+ */
+ private String findPathElementCaseUnsensitive(String parentPath,
+ String soughtPathElement) {
+ // we are already in the right path, so the second parameter
+ // is false
+ FTPFile[] theFiles = listFiles(parentPath, false);
+ if (theFiles == null) {
+ return null;
+ }
+ for (int icounter = 0; icounter < theFiles.length; icounter++) {
+ if (theFiles[icounter] != null
+ && theFiles[icounter].getName().equalsIgnoreCase(soughtPathElement)) {
+ return theFiles[icounter].getName();
+ }
+ }
+ return null;
+ }
+ /**
+ * find out if the file exists
+ * @return true if the file exists
+ */
+ public boolean exists() {
+ return (ftpFile != null);
+ }
+ /**
+ * if the file is a symbolic link, find out to what it is pointing
+ * @return the target of the symbolic link
+ */
+ public String getLink() {
+ return ftpFile.getLink();
+ }
+ /**
+ * get the name of the file
+ * @return the name of the file
+ */
+ public String getName() {
+ return ftpFile.getName();
+ }
+ /**
+ * find out the absolute path of the file
+ * @return absolute path as string
+ */
+ public String getAbsolutePath() {
+ return getCurpwdPlusFileSep() + ftpFile.getName();
+ }
+ /**
+ * find out the relative path assuming that the path used to construct
+ * this AntFTPFile was spelled properly with regards to case.
+ * This is OK on a case sensitive system such as UNIX
+ * @return relative path
+ */
+ public String getFastRelativePath() {
+ String absPath = getAbsolutePath();
+ if (absPath.startsWith(rootPath + remoteFileSep)) {
+ return absPath.substring(rootPath.length() + remoteFileSep.length());
+ }
+ return null;
+ }
+ /**
+ * find out the relative path to the rootPath of the enclosing scanner.
+ * this relative path is spelled exactly like on disk,
+ * for instance if the AntFTPFile has been instantiated as ALPHA,
+ * but the file is really called alpha, this method will return alpha.
+ * If a symbolic link is encountered, it is followed, but the name of the link
+ * rather than the name of the target is returned.
+ * (ie does not behave like File.getCanonicalPath())
+ * @return relative path, separated by remoteFileSep
+ * @throws IOException if a change directory fails, ...
+ * @throws BuildException if one of the components of the relative path cannot
+ * be found.
+ */
+ public String getRelativePath() throws IOException, BuildException {
+ if (!relativePathCalculated) {
+ if (parent != null) {
+ traversesSymlinks = parent.isTraverseSymlinks();
+ relativePath = getRelativePath(parent.getAbsolutePath(),
+ parent.getRelativePath());
+ } else {
+ relativePath = getRelativePath(rootPath, "");
+ relativePathCalculated = true;
+ }
+ }
+ return relativePath;
+ }
+ /**
+ * get the relative path of this file
+ * @param currentPath base path
+ * @param currentRelativePath relative path of the base path with regards to remote dir
+ * @return relative path
+ */
+ private String getRelativePath(String currentPath, String currentRelativePath) {
+ Vector pathElements = SelectorUtils.tokenizePath(getAbsolutePath(), remoteFileSep);
+ Vector pathElements2 = SelectorUtils.tokenizePath(currentPath, remoteFileSep);
+ String relPath = currentRelativePath;
+ final int size = pathElements.size();
+ for (int pcount = pathElements2.size(); pcount < size; pcount++) {
+ String currentElement = (String) pathElements.elementAt(pcount);
+ FTPFile[] theFiles = listFiles(currentPath);
+ FTPFile theFile = null;
+ if (theFiles != null) {
+ theFile = getFile(theFiles, currentElement);
+ }
+ if (!relPath.equals("")) {
+ relPath = relPath + remoteFileSep;
+ }
+ if (theFile == null) {
+ // hit a hidden file assume not a symlink
+ relPath = relPath + currentElement;
+ currentPath = currentPath + remoteFileSep + currentElement;
+ log("Hidden file " + relPath
+ + " assumed to not be a symlink.",
+ Project.MSG_VERBOSE);
+ } else {
+ traversesSymlinks = traversesSymlinks || theFile.isSymbolicLink();
+ relPath = relPath + theFile.getName();
+ currentPath = currentPath + remoteFileSep + theFile.getName();
+ }
+ }
+ return relPath;
+ }
+ /**
+ * find a file matching a string in an array of FTPFile.
+ * This method will find "alpha" when requested for "ALPHA"
+ * if and only if the caseSensitive attribute is set to false.
+ * When caseSensitive is set to true, only the exact match is returned.
+ * @param theFiles array of files
+ * @param lastpathelement the file name being sought
+ * @return null if the file cannot be found, otherwise return the matching file.
+ */
+ public FTPFile getFile(FTPFile[] theFiles, String lastpathelement) {
+ if (theFiles == null) {
+ return null;
+ }
+ for (int fcount = 0; fcount < theFiles.length; fcount++) {
+ if (theFiles[fcount] != null) {
+ if (theFiles[fcount].getName().equals(lastpathelement)) {
+ return theFiles[fcount];
+ } else if (!isCaseSensitive()
+ && theFiles[fcount].getName().equalsIgnoreCase(
+ lastpathelement)) {
+ return theFiles[fcount];
+ }
+ }
+ }
+ return null;
+ }
+ /**
+ * tell if a file is a directory.
+ * note that it will return false for symbolic links pointing to directories.
+ * @return <code>true</code> for directories
+ */
+ public boolean isDirectory() {
+ return ftpFile.isDirectory();
+ }
+ /**
+ * tell if a file is a symbolic link
+ * @return <code>true</code> for symbolic links
+ */
+ public boolean isSymbolicLink() {
+ return ftpFile.isSymbolicLink();
+ }
+ /**
+ * return the attached FTP client object.
+ * Warning : this instance is really shared with the enclosing class.
+ * @return FTP client
+ */
+ protected FTPClient getClient() {
+ return client;
+ }
+
+ /**
+ * sets the current path of an AntFTPFile
+ * @param curpwd the current path one wants to set
+ */
+ protected void setCurpwd(String curpwd) {
+ this.curpwd = curpwd;
+ }
+ /**
+ * returns the path of the directory containing the AntFTPFile.
+ * of the full path of the file itself in case of AntFTPRootFile
+ * @return parent directory of the AntFTPFile
+ */
+ public String getCurpwd() {
+ return curpwd;
+ }
+ /**
+ * returns the path of the directory containing the AntFTPFile.
+ * of the full path of the file itself in case of AntFTPRootFile
+ * and appends the remote file separator if necessary.
+ * @return parent directory of the AntFTPFile
+ * @since Ant 1.8.2
+ */
+ public String getCurpwdPlusFileSep() {
+ return curpwd.endsWith(remoteFileSep) ? curpwd
+ : curpwd + remoteFileSep;
+ }
+ /**
+ * find out if a symbolic link is encountered in the relative path of this file
+ * from rootPath.
+ * @return <code>true</code> if a symbolic link is encountered in the relative path.
+ * @throws IOException if one of the change directory or directory listing operations
+ * fails
+ * @throws BuildException if a path component in the relative path cannot be found.
+ */
+ public boolean isTraverseSymlinks() throws IOException, BuildException {
+ if (!relativePathCalculated) {
+ // getRelativePath also finds about symlinks
+ getRelativePath();
+ }
+ return traversesSymlinks;
+ }
+
+ /**
+ * Get a string rep of this object.
+ * @return a string containing the pwd and the file.
+ */
+ public String toString() {
+ return "AntFtpFile: " + curpwd + "%" + ftpFile;
+ }
+ }
+ /**
+ * special class to represent the remote directory itself
+ * @since Ant 1.6
+ */
+ protected class AntFTPRootFile extends AntFTPFile {
+ private String remotedir;
+ /**
+ * constructor
+ * @param aclient FTP client
+ * @param remotedir remote directory
+ */
+ public AntFTPRootFile(FTPClient aclient, String remotedir) {
+ super(aclient, null, remotedir);
+ this.remotedir = remotedir;
+ try {
+ this.getClient().changeWorkingDirectory(this.remotedir);
+ this.setCurpwd(this.getClient().printWorkingDirectory());
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, getLocation());
+ }
+ }
+ /**
+ * find the absolute path
+ * @return absolute path
+ */
+ public String getAbsolutePath() {
+ return this.getCurpwd();
+ }
+ /**
+ * find out the relative path to root
+ * @return empty string
+ * @throws BuildException actually never
+ * @throws IOException actually never
+ */
+ public String getRelativePath() throws BuildException, IOException {
+ return "";
+ }
+ }
+ }
+ /**
+ * check FTPFiles to check whether they function as directories too
+ * the FTPFile API seem to make directory and symbolic links incompatible
+ * we want to find out if we can cd to a symbolic link
+ * @param dir the parent directory of the file to test
+ * @param file the file to test
+ * @return true if it is possible to cd to this directory
+ * @since ant 1.6
+ */
+ private boolean isFunctioningAsDirectory(FTPClient ftp, String dir, FTPFile file) {
+ boolean result = false;
+ String currentWorkingDir = null;
+ if (file.isDirectory()) {
+ return true;
+ } else if (file.isFile()) {
+ return false;
+ }
+ try {
+ currentWorkingDir = ftp.printWorkingDirectory();
+ } catch (IOException ioe) {
+ getProject().log("could not find current working directory " + dir
+ + " while checking a symlink",
+ Project.MSG_DEBUG);
+ }
+ if (currentWorkingDir != null) {
+ try {
+ result = ftp.changeWorkingDirectory(file.getLink());
+ } catch (IOException ioe) {
+ getProject().log("could not cd to " + file.getLink() + " while checking a symlink",
+ Project.MSG_DEBUG);
+ }
+ if (result) {
+ boolean comeback = false;
+ try {
+ comeback = ftp.changeWorkingDirectory(currentWorkingDir);
+ } catch (IOException ioe) {
+ getProject().log("could not cd back to " + dir + " while checking a symlink",
+ Project.MSG_ERR);
+ } finally {
+ if (!comeback) {
+ throw new BuildException("could not cd back to " + dir
+ + " while checking a symlink");
+ }
+ }
+ }
+ }
+ return result;
+ }
+ /**
+ * check FTPFiles to check whether they function as directories too
+ * the FTPFile API seem to make directory and symbolic links incompatible
+ * we want to find out if we can cd to a symbolic link
+ * @param dir the parent directory of the file to test
+ * @param file the file to test
+ * @return true if it is possible to cd to this directory
+ * @since ant 1.6
+ */
+ private boolean isFunctioningAsFile(FTPClient ftp, String dir, FTPFile file) {
+ if (file.isDirectory()) {
+ return false;
+ } else if (file.isFile()) {
+ return true;
+ }
+ return !isFunctioningAsDirectory(ftp, dir, file);
+ }
+ /**
+ * Sets the remote directory where files will be placed. This may be a
+ * relative or absolute path, and must be in the path syntax expected by
+ * the remote server. No correction of path syntax will be performed.
+ *
+ * @param dir the remote directory name.
+ */
+ public void setRemotedir(String dir) {
+ this.remotedir = dir;
+ }
+
+
+ /**
+ * Sets the FTP server to send files to.
+ *
+ * @param server the remote server name.
+ */
+ public void setServer(String server) {
+ this.server = server;
+ }
+
+
+ /**
+ * Sets the FTP port used by the remote server.
+ *
+ * @param port the port on which the remote server is listening.
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+
+ /**
+ * Sets the login user id to use on the specified server.
+ *
+ * @param userid remote system userid.
+ */
+ public void setUserid(String userid) {
+ this.userid = userid;
+ }
+
+
+ /**
+ * Sets the login password for the given user id.
+ *
+ * @param password the password on the remote system.
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ /**
+ * Sets the login account to use on the specified server.
+ *
+ * @param pAccount the account name on remote system
+ * @since Ant 1.7
+ */
+ public void setAccount(String pAccount) {
+ this.account = pAccount;
+ }
+
+
+ /**
+ * If true, uses binary mode, otherwise text mode (default is binary).
+ *
+ * @param binary if true use binary mode in transfers.
+ */
+ public void setBinary(boolean binary) {
+ this.binary = binary;
+ }
+
+
+ /**
+ * Specifies whether to use passive mode. Set to true if you are behind a
+ * firewall and cannot connect without it. Passive mode is disabled by
+ * default.
+ *
+ * @param passive true is passive mode should be used.
+ */
+ public void setPassive(boolean passive) {
+ this.passive = passive;
+ }
+
+
+ /**
+ * Set to true to receive notification about each file as it is
+ * transferred.
+ *
+ * @param verbose true if verbose notifications are required.
+ */
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+
+ /**
+ * A synonym for <tt>depends</tt>. Set to true to transmit only new
+ * or changed files.
+ *
+ * See the related attributes timediffmillis and timediffauto.
+ *
+ * @param newer if true only transfer newer files.
+ */
+ public void setNewer(boolean newer) {
+ this.newerOnly = newer;
+ }
+
+ /**
+ * number of milliseconds to add to the time on the remote machine
+ * to get the time on the local machine.
+ *
+ * use in conjunction with <code>newer</code>
+ *
+ * @param timeDiffMillis number of milliseconds
+ *
+ * @since ant 1.6
+ */
+ public void setTimeDiffMillis(long timeDiffMillis) {
+ this.timeDiffMillis = timeDiffMillis;
+ }
+
+ /**
+ * &quot;true&quot; to find out automatically the time difference
+ * between local and remote machine.
+ *
+ * This requires right to create
+ * and delete a temporary file in the remote directory.
+ *
+ * @param timeDiffAuto true = find automatically the time diff
+ *
+ * @since ant 1.6
+ */
+ public void setTimeDiffAuto(boolean timeDiffAuto) {
+ this.timeDiffAuto = timeDiffAuto;
+ }
+
+ /**
+ * Set to true to preserve modification times for "gotten" files.
+ *
+ * @param preserveLastModified if true preserver modification times.
+ */
+ public void setPreserveLastModified(boolean preserveLastModified) {
+ this.preserveLastModified = preserveLastModified;
+ }
+
+
+ /**
+ * Set to true to transmit only files that are new or changed from their
+ * remote counterparts. The default is to transmit all files.
+ *
+ * @param depends if true only transfer newer files.
+ */
+ public void setDepends(boolean depends) {
+ this.newerOnly = depends;
+ }
+
+
+ /**
+ * Sets the remote file separator character. This normally defaults to the
+ * Unix standard forward slash, but can be manually overridden using this
+ * call if the remote server requires some other separator. Only the first
+ * character of the string is used.
+ *
+ * @param separator the file separator on the remote system.
+ */
+ public void setSeparator(String separator) {
+ remoteFileSep = separator;
+ }
+
+
+ /**
+ * Sets the file permission mode (Unix only) for files sent to the
+ * server.
+ *
+ * @param theMode unix style file mode for the files sent to the remote
+ * system.
+ */
+ public void setChmod(String theMode) {
+ this.chmod = theMode;
+ }
+
+
+ /**
+ * Sets the default mask for file creation on a unix server.
+ *
+ * @param theUmask unix style umask for files created on the remote server.
+ */
+ public void setUmask(String theUmask) {
+ this.umask = theUmask;
+ }
+
+
+ /**
+ * A set of files to upload or download
+ *
+ * @param set the set of files to be added to the list of files to be
+ * transferred.
+ */
+ public void addFileset(FileSet set) {
+ filesets.addElement(set);
+ }
+
+
+ /**
+ * Sets the FTP action to be taken. Currently accepts "put", "get", "del",
+ * "mkdir", "chmod", "list", and "site".
+ *
+ * @deprecated since 1.5.x.
+ * setAction(String) is deprecated and is replaced with
+ * setAction(FTP.Action) to make Ant's Introspection mechanism do the
+ * work and also to encapsulate operations on the type in its own
+ * class.
+ * @ant.attribute ignore="true"
+ *
+ * @param action the FTP action to be performed.
+ *
+ * @throws BuildException if the action is not a valid action.
+ */
+ public void setAction(String action) throws BuildException {
+ log("DEPRECATED - The setAction(String) method has been deprecated."
+ + " Use setAction(FTP.Action) instead.");
+
+ Action a = new Action();
+
+ a.setValue(action);
+ this.action = a.getAction();
+ }
+
+
+ /**
+ * Sets the FTP action to be taken. Currently accepts "put", "get", "del",
+ * "mkdir", "chmod", "list", and "site".
+ *
+ * @param action the FTP action to be performed.
+ *
+ * @throws BuildException if the action is not a valid action.
+ */
+ public void setAction(Action action) throws BuildException {
+ this.action = action.getAction();
+ }
+
+
+ /**
+ * The output file for the "list" action. This attribute is ignored for
+ * any other actions.
+ *
+ * @param listing file in which to store the listing.
+ */
+ public void setListing(File listing) {
+ this.listing = listing;
+ }
+
+
+ /**
+ * If true, enables unsuccessful file put, delete and get
+ * operations to be skipped with a warning and the remainder
+ * of the files still transferred.
+ *
+ * @param skipFailedTransfers true if failures in transfers are ignored.
+ */
+ public void setSkipFailedTransfers(boolean skipFailedTransfers) {
+ this.skipFailedTransfers = skipFailedTransfers;
+ }
+
+
+ /**
+ * set the flag to skip errors on directory creation.
+ * (and maybe later other server specific errors)
+ *
+ * @param ignoreNoncriticalErrors true if non-critical errors should not
+ * cause a failure.
+ */
+ public void setIgnoreNoncriticalErrors(boolean ignoreNoncriticalErrors) {
+ this.ignoreNoncriticalErrors = ignoreNoncriticalErrors;
+ }
+
+ private void configurationHasBeenSet() {
+ this.isConfigurationSet = true;
+ }
+
+ /**
+ * Sets the systemTypeKey attribute.
+ * Method for setting <code>FTPClientConfig</code> remote system key.
+ *
+ * @param systemKey the key to be set - BUT if blank
+ * the default value of null (which signifies "autodetect") will be kept.
+ * @see org.apache.commons.net.ftp.FTPClientConfig
+ */
+ public void setSystemTypeKey(FTPSystemType systemKey) {
+ if (systemKey != null && !systemKey.getValue().equals("")) {
+ this.systemTypeKey = systemKey;
+ configurationHasBeenSet();
+ }
+ }
+
+ /**
+ * Sets the defaultDateFormatConfig attribute.
+ * @param defaultDateFormat configuration to be set, unless it is
+ * null or empty string, in which case ignored.
+ * @see org.apache.commons.net.ftp.FTPClientConfig
+ */
+ public void setDefaultDateFormatConfig(String defaultDateFormat) {
+ if (defaultDateFormat != null && !defaultDateFormat.equals("")) {
+ this.defaultDateFormatConfig = defaultDateFormat;
+ configurationHasBeenSet();
+ }
+ }
+
+ /**
+ * Sets the recentDateFormatConfig attribute.
+ * @param recentDateFormat configuration to be set, unless it is
+ * null or empty string, in which case ignored.
+ * @see org.apache.commons.net.ftp.FTPClientConfig
+ */
+ public void setRecentDateFormatConfig(String recentDateFormat) {
+ if (recentDateFormat != null && !recentDateFormat.equals("")) {
+ this.recentDateFormatConfig = recentDateFormat;
+ configurationHasBeenSet();
+ }
+ }
+
+ /**
+ * Sets the serverLanguageCode attribute.
+ * @param serverLanguageCode configuration to be set, unless it is
+ * null or empty string, in which case ignored.
+ * @see org.apache.commons.net.ftp.FTPClientConfig
+ */
+ public void setServerLanguageCodeConfig(LanguageCode serverLanguageCode) {
+ if (serverLanguageCode != null && !"".equals(serverLanguageCode.getValue())) {
+ this.serverLanguageCodeConfig = serverLanguageCode;
+ configurationHasBeenSet();
+ }
+ }
+
+ /**
+ * Sets the serverTimeZoneConfig attribute.
+ * @param serverTimeZoneId configuration to be set, unless it is
+ * null or empty string, in which case ignored.
+ * @see org.apache.commons.net.ftp.FTPClientConfig
+ */
+ public void setServerTimeZoneConfig(String serverTimeZoneId) {
+ if (serverTimeZoneId != null && !serverTimeZoneId.equals("")) {
+ this.serverTimeZoneConfig = serverTimeZoneId;
+ configurationHasBeenSet();
+ }
+ }
+
+ /**
+ * Sets the shortMonthNamesConfig attribute
+ *
+ * @param shortMonthNames configuration to be set, unless it is
+ * null or empty string, in which case ignored.
+ * @see org.apache.commons.net.ftp.FTPClientConfig
+ */
+ public void setShortMonthNamesConfig(String shortMonthNames) {
+ if (shortMonthNames != null && !shortMonthNames.equals("")) {
+ this.shortMonthNamesConfig = shortMonthNames;
+ configurationHasBeenSet();
+ }
+ }
+
+
+
+ /**
+ * Defines how many times to retry executing FTP command before giving up.
+ * Default is 0 - try once and if failure then give up.
+ *
+ * @param retriesAllowed number of retries to allow. -1 means
+ * keep trying forever. "forever" may also be specified as a
+ * synonym for -1.
+ */
+ public void setRetriesAllowed(String retriesAllowed) {
+ if ("FOREVER".equalsIgnoreCase(retriesAllowed)) {
+ this.retriesAllowed = Retryable.RETRY_FOREVER;
+ } else {
+ try {
+ int retries = Integer.parseInt(retriesAllowed);
+ if (retries < Retryable.RETRY_FOREVER) {
+ throw new BuildException(
+ "Invalid value for retriesAllowed attribute: "
+ + retriesAllowed);
+
+ }
+ this.retriesAllowed = retries;
+ } catch (NumberFormatException px) {
+ throw new BuildException(
+ "Invalid value for retriesAllowed attribute: "
+ + retriesAllowed);
+
+ }
+
+ }
+ }
+ /**
+ * @return Returns the systemTypeKey.
+ */
+ public String getSystemTypeKey() {
+ return systemTypeKey.getValue();
+ }
+ /**
+ * @return Returns the defaultDateFormatConfig.
+ */
+ public String getDefaultDateFormatConfig() {
+ return defaultDateFormatConfig;
+ }
+ /**
+ * @return Returns the recentDateFormatConfig.
+ */
+ public String getRecentDateFormatConfig() {
+ return recentDateFormatConfig;
+ }
+ /**
+ * @return Returns the serverLanguageCodeConfig.
+ */
+ public String getServerLanguageCodeConfig() {
+ return serverLanguageCodeConfig.getValue();
+ }
+ /**
+ * @return Returns the serverTimeZoneConfig.
+ */
+ public String getServerTimeZoneConfig() {
+ return serverTimeZoneConfig;
+ }
+ /**
+ * @return Returns the shortMonthNamesConfig.
+ */
+ public String getShortMonthNamesConfig() {
+ return shortMonthNamesConfig;
+ }
+ /**
+ * @return Returns the timestampGranularity.
+ */
+ Granularity getTimestampGranularity() {
+ return timestampGranularity;
+ }
+ /**
+ * Sets the timestampGranularity attribute
+ * @param timestampGranularity The timestampGranularity to set.
+ */
+ public void setTimestampGranularity(Granularity timestampGranularity) {
+ if (null == timestampGranularity || "".equals(timestampGranularity.getValue())) {
+ return;
+ }
+ this.timestampGranularity = timestampGranularity;
+ }
+ /**
+ * Sets the siteCommand attribute. This attribute
+ * names the command that will be executed if the action
+ * is "site".
+ * @param siteCommand The siteCommand to set.
+ */
+ public void setSiteCommand(String siteCommand) {
+ this.siteCommand = siteCommand;
+ }
+ /**
+ * Sets the initialSiteCommand attribute. This attribute
+ * names a site command that will be executed immediately
+ * after connection.
+ * @param initialCommand The initialSiteCommand to set.
+ */
+ public void setInitialSiteCommand(String initialCommand) {
+ this.initialSiteCommand = initialCommand;
+ }
+
+ /**
+ * Whether to verify that data and control connections are
+ * connected to the same remote host.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setEnableRemoteVerification(boolean b) {
+ enableRemoteVerification = b;
+ }
+
+ /**
+ * Checks to see that all required parameters are set.
+ *
+ * @throws BuildException if the configuration is not valid.
+ */
+ protected void checkAttributes() throws BuildException {
+ if (server == null) {
+ throw new BuildException("server attribute must be set!");
+ }
+ if (userid == null) {
+ throw new BuildException("userid attribute must be set!");
+ }
+ if (password == null) {
+ throw new BuildException("password attribute must be set!");
+ }
+
+ if ((action == LIST_FILES) && (listing == null)) {
+ throw new BuildException("listing attribute must be set for list "
+ + "action!");
+ }
+
+ if (action == MK_DIR && remotedir == null) {
+ throw new BuildException("remotedir attribute must be set for "
+ + "mkdir action!");
+ }
+
+ if (action == CHMOD && chmod == null) {
+ throw new BuildException("chmod attribute must be set for chmod "
+ + "action!");
+ }
+ if (action == SITE_CMD && siteCommand == null) {
+ throw new BuildException("sitecommand attribute must be set for site "
+ + "action!");
+ }
+
+
+ if (this.isConfigurationSet) {
+ try {
+ Class.forName("org.apache.commons.net.ftp.FTPClientConfig");
+ } catch (ClassNotFoundException e) {
+ throw new BuildException(
+ "commons-net.jar >= 1.4.0 is required for at least one"
+ + " of the attributes specified.");
+ }
+ }
+ }
+
+ /**
+ * Executable a retryable object.
+ * @param h the retry handler.
+ * @param r the object that should be retried until it succeeds
+ * or the number of retrys is reached.
+ * @param descr a description of the command that is being run.
+ * @throws IOException if there is a problem.
+ */
+ protected void executeRetryable(RetryHandler h, Retryable r, String descr)
+ throws IOException {
+ h.execute(r, descr);
+ }
+
+
+ /**
+ * For each file in the fileset, do the appropriate action: send, get,
+ * delete, or list.
+ *
+ * @param ftp the FTPClient instance used to perform FTP actions
+ * @param fs the fileset on which the actions are performed.
+ *
+ * @return the number of files to be transferred.
+ *
+ * @throws IOException if there is a problem reading a file
+ * @throws BuildException if there is a problem in the configuration.
+ */
+ protected int transferFiles(final FTPClient ftp, FileSet fs)
+ throws IOException, BuildException {
+ DirectoryScanner ds;
+ if (action == SEND_FILES) {
+ ds = fs.getDirectoryScanner(getProject());
+ } else {
+ ds = new FTPDirectoryScanner(ftp);
+ fs.setupDirectoryScanner(ds, getProject());
+ ds.setFollowSymlinks(fs.isFollowSymlinks());
+ ds.scan();
+ }
+
+ String[] dsfiles = null;
+ if (action == RM_DIR) {
+ dsfiles = ds.getIncludedDirectories();
+ } else {
+ dsfiles = ds.getIncludedFiles();
+ }
+ String dir = null;
+
+ if ((ds.getBasedir() == null)
+ && ((action == SEND_FILES) || (action == GET_FILES))) {
+ throw new BuildException("the dir attribute must be set for send "
+ + "and get actions");
+ } else {
+ if ((action == SEND_FILES) || (action == GET_FILES)) {
+ dir = ds.getBasedir().getAbsolutePath();
+ }
+ }
+
+ // If we are doing a listing, we need the output stream created now.
+ BufferedWriter bw = null;
+
+ try {
+ if (action == LIST_FILES) {
+ File pd = listing.getParentFile();
+
+ if (!pd.exists()) {
+ pd.mkdirs();
+ }
+ bw = new BufferedWriter(new FileWriter(listing));
+ }
+ RetryHandler h = new RetryHandler(this.retriesAllowed, this);
+ if (action == RM_DIR) {
+ // to remove directories, start by the end of the list
+ // the trunk does not let itself be removed before the leaves
+ for (int i = dsfiles.length - 1; i >= 0; i--) {
+ final String dsfile = dsfiles[i];
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ rmDir(ftp, dsfile);
+ }
+ }, dsfile);
+ }
+ } else {
+ final BufferedWriter fbw = bw;
+ final String fdir = dir;
+ if (this.newerOnly) {
+ this.granularityMillis =
+ this.timestampGranularity.getMilliseconds(action);
+ }
+ for (int i = 0; i < dsfiles.length; i++) {
+ final String dsfile = dsfiles[i];
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ switch (action) {
+ case SEND_FILES:
+ sendFile(ftp, fdir, dsfile);
+ break;
+ case GET_FILES:
+ getFile(ftp, fdir, dsfile);
+ break;
+ case DEL_FILES:
+ delFile(ftp, dsfile);
+ break;
+ case LIST_FILES:
+ listFile(ftp, fbw, dsfile);
+ break;
+ case CHMOD:
+ doSiteCommand(ftp, "chmod " + chmod
+ + " " + resolveFile(dsfile));
+ transferred++;
+ break;
+ default:
+ throw new BuildException("unknown ftp action " + action);
+ }
+ }
+ }, dsfile);
+ }
+ }
+ } finally {
+ FileUtils.close(bw);
+ }
+
+ return dsfiles.length;
+ }
+
+
+ /**
+ * Sends all files specified by the configured filesets to the remote
+ * server.
+ *
+ * @param ftp the FTPClient instance used to perform FTP actions
+ *
+ * @throws IOException if there is a problem reading a file
+ * @throws BuildException if there is a problem in the configuration.
+ */
+ protected void transferFiles(FTPClient ftp)
+ throws IOException, BuildException {
+ transferred = 0;
+ skipped = 0;
+
+ if (filesets.size() == 0) {
+ throw new BuildException("at least one fileset must be specified.");
+ } else {
+ // get files from filesets
+ final int size = filesets.size();
+ for (int i = 0; i < size; i++) {
+ FileSet fs = (FileSet) filesets.elementAt(i);
+
+ if (fs != null) {
+ transferFiles(ftp, fs);
+ }
+ }
+ }
+
+ log(transferred + " " + ACTION_TARGET_STRS[action] + " "
+ + COMPLETED_ACTION_STRS[action]);
+ if (skipped != 0) {
+ log(skipped + " " + ACTION_TARGET_STRS[action]
+ + " were not successfully " + COMPLETED_ACTION_STRS[action]);
+ }
+ }
+
+
+ /**
+ * Correct a file path to correspond to the remote host requirements. This
+ * implementation currently assumes that the remote end can handle
+ * Unix-style paths with forward-slash separators. This can be overridden
+ * with the <code>separator</code> task parameter. No attempt is made to
+ * determine what syntax is appropriate for the remote host.
+ *
+ * @param file the remote file name to be resolved
+ *
+ * @return the filename as it will appear on the server.
+ */
+ protected String resolveFile(String file) {
+ return file.replace(System.getProperty("file.separator").charAt(0),
+ remoteFileSep.charAt(0));
+ }
+
+
+ /**
+ * Creates all parent directories specified in a complete relative
+ * pathname. Attempts to create existing directories will not cause
+ * errors.
+ *
+ * @param ftp the FTP client instance to use to execute FTP actions on
+ * the remote server.
+ * @param filename the name of the file whose parents should be created.
+ * @throws IOException under non documented circumstances
+ * @throws BuildException if it is impossible to cd to a remote directory
+ *
+ */
+ protected void createParents(FTPClient ftp, String filename)
+ throws IOException, BuildException {
+
+ File dir = new File(filename);
+ if (dirCache.contains(dir)) {
+ return;
+ }
+
+ Vector parents = new Vector();
+ String dirname;
+
+ while ((dirname = dir.getParent()) != null) {
+ File checkDir = new File(dirname);
+ if (dirCache.contains(checkDir)) {
+ break;
+ }
+ dir = checkDir;
+ parents.addElement(dir);
+ }
+
+ // find first non cached dir
+ int i = parents.size() - 1;
+
+ if (i >= 0) {
+ String cwd = ftp.printWorkingDirectory();
+ String parent = dir.getParent();
+ if (parent != null) {
+ if (!ftp.changeWorkingDirectory(resolveFile(parent))) {
+ throw new BuildException("could not change to "
+ + "directory: " + ftp.getReplyString());
+ }
+ }
+
+ while (i >= 0) {
+ dir = (File) parents.elementAt(i--);
+ // check if dir exists by trying to change into it.
+ if (!ftp.changeWorkingDirectory(dir.getName())) {
+ // could not change to it - try to create it
+ log("creating remote directory "
+ + resolveFile(dir.getPath()), Project.MSG_VERBOSE);
+ if (!ftp.makeDirectory(dir.getName())) {
+ handleMkDirFailure(ftp);
+ }
+ if (!ftp.changeWorkingDirectory(dir.getName())) {
+ throw new BuildException("could not change to "
+ + "directory: " + ftp.getReplyString());
+ }
+ }
+ dirCache.add(dir);
+ }
+ ftp.changeWorkingDirectory(cwd);
+ }
+ }
+ /**
+ * auto find the time difference between local and remote
+ * @param ftp handle to ftp client
+ * @return number of millis to add to remote time to make it comparable to local time
+ * @since ant 1.6
+ */
+ private long getTimeDiff(FTPClient ftp) {
+ long returnValue = 0;
+ File tempFile = findFileName(ftp);
+ try {
+ // create a local temporary file
+ FILE_UTILS.createNewFile(tempFile);
+ long localTimeStamp = tempFile.lastModified();
+ BufferedInputStream instream = new BufferedInputStream(new FileInputStream(tempFile));
+ ftp.storeFile(tempFile.getName(), instream);
+ instream.close();
+ boolean success = FTPReply.isPositiveCompletion(ftp.getReplyCode());
+ if (success) {
+ FTPFile [] ftpFiles = ftp.listFiles(tempFile.getName());
+ if (ftpFiles.length == 1) {
+ long remoteTimeStamp = ftpFiles[0].getTimestamp().getTime().getTime();
+ returnValue = localTimeStamp - remoteTimeStamp;
+ }
+ ftp.deleteFile(ftpFiles[0].getName());
+ }
+ // delegate the deletion of the local temp file to the delete task
+ // because of race conditions occurring on Windows
+ Delete mydelete = new Delete();
+ mydelete.bindToOwner(this);
+ mydelete.setFile(tempFile.getCanonicalFile());
+ mydelete.execute();
+ } catch (Exception e) {
+ throw new BuildException(e, getLocation());
+ }
+ return returnValue;
+ }
+ /**
+ * find a suitable name for local and remote temporary file
+ */
+ private File findFileName(FTPClient ftp) {
+ FTPFile [] theFiles = null;
+ final int maxIterations = 1000;
+ for (int counter = 1; counter < maxIterations; counter++) {
+ File localFile = FILE_UTILS.createTempFile(
+ "ant" + Integer.toString(counter), ".tmp",
+ null, false, false);
+ String fileName = localFile.getName();
+ boolean found = false;
+ try {
+ if (theFiles == null) {
+ theFiles = ftp.listFiles();
+ }
+ for (int counter2 = 0; counter2 < theFiles.length; counter2++) {
+ if (theFiles[counter2] != null
+ && theFiles[counter2].getName().equals(fileName)) {
+ found = true;
+ break;
+ }
+ }
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, getLocation());
+ }
+ if (!found) {
+ localFile.deleteOnExit();
+ return localFile;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Checks to see if the remote file is current as compared with the local
+ * file. Returns true if the target file is up to date.
+ * @param ftp ftpclient
+ * @param localFile local file
+ * @param remoteFile remote file
+ * @return true if the target file is up to date
+ * @throws IOException in unknown circumstances
+ * @throws BuildException if the date of the remote files cannot be found and the action is
+ * GET_FILES
+ */
+ protected boolean isUpToDate(FTPClient ftp, File localFile,
+ String remoteFile)
+ throws IOException, BuildException {
+ log("checking date for " + remoteFile, Project.MSG_VERBOSE);
+
+ FTPFile[] files = ftp.listFiles(remoteFile);
+
+ // For Microsoft's Ftp-Service an Array with length 0 is
+ // returned if configured to return listings in "MS-DOS"-Format
+ if (files == null || files.length == 0) {
+ // If we are sending files, then assume out of date.
+ // If we are getting files, then throw an error
+
+ if (action == SEND_FILES) {
+ log("Could not date test remote file: " + remoteFile
+ + "assuming out of date.", Project.MSG_VERBOSE);
+ return false;
+ } else {
+ throw new BuildException("could not date test remote file: "
+ + ftp.getReplyString());
+ }
+ }
+
+ long remoteTimestamp = files[0].getTimestamp().getTime().getTime();
+ long localTimestamp = localFile.lastModified();
+ long adjustedRemoteTimestamp =
+ remoteTimestamp + this.timeDiffMillis + this.granularityMillis;
+
+ StringBuffer msg;
+ synchronized(TIMESTAMP_LOGGING_SDF) {
+ msg = new StringBuffer(" [")
+ .append(TIMESTAMP_LOGGING_SDF.format(new Date(localTimestamp)))
+ .append("] local");
+ }
+ log(msg.toString(), Project.MSG_VERBOSE);
+
+ synchronized(TIMESTAMP_LOGGING_SDF) {
+ msg = new StringBuffer(" [")
+ .append(TIMESTAMP_LOGGING_SDF.format(new Date(adjustedRemoteTimestamp)))
+ .append("] remote");
+ }
+ if (remoteTimestamp != adjustedRemoteTimestamp) {
+ synchronized(TIMESTAMP_LOGGING_SDF) {
+ msg.append(" - (raw: ")
+ .append(TIMESTAMP_LOGGING_SDF.format(new Date(remoteTimestamp)))
+ .append(")");
+ }
+ }
+ log(msg.toString(), Project.MSG_VERBOSE);
+
+
+
+ if (this.action == SEND_FILES) {
+ return adjustedRemoteTimestamp >= localTimestamp;
+ } else {
+ return localTimestamp >= adjustedRemoteTimestamp;
+ }
+ }
+
+
+ /**
+ * Sends a site command to the ftp server
+ * @param ftp ftp client
+ * @param theCMD command to execute
+ * @throws IOException in unknown circumstances
+ * @throws BuildException in unknown circumstances
+ */
+ protected void doSiteCommand(FTPClient ftp, String theCMD)
+ throws IOException, BuildException {
+ boolean rc;
+ String[] myReply = null;
+
+ log("Doing Site Command: " + theCMD, Project.MSG_VERBOSE);
+
+ rc = ftp.sendSiteCommand(theCMD);
+
+ if (!rc) {
+ log("Failed to issue Site Command: " + theCMD, Project.MSG_WARN);
+ } else {
+
+ myReply = ftp.getReplyStrings();
+
+ for (int x = 0; x < myReply.length; x++) {
+ if (myReply[x] != null && myReply[x].indexOf("200") == -1) {
+ log(myReply[x], Project.MSG_WARN);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Sends a single file to the remote host. <code>filename</code> may
+ * contain a relative path specification. When this is the case, <code>sendFile</code>
+ * will attempt to create any necessary parent directories before sending
+ * the file. The file will then be sent using the entire relative path
+ * spec - no attempt is made to change directories. It is anticipated that
+ * this may eventually cause problems with some FTP servers, but it
+ * simplifies the coding.
+ * @param ftp ftp client
+ * @param dir base directory of the file to be sent (local)
+ * @param filename relative path of the file to be send
+ * locally relative to dir
+ * remotely relative to the remotedir attribute
+ * @throws IOException in unknown circumstances
+ * @throws BuildException in unknown circumstances
+ */
+ protected void sendFile(FTPClient ftp, String dir, String filename)
+ throws IOException, BuildException {
+ InputStream instream = null;
+
+ try {
+ // TODO - why not simply new File(dir, filename)?
+ File file = getProject().resolveFile(new File(dir, filename).getPath());
+
+ if (newerOnly && isUpToDate(ftp, file, resolveFile(filename))) {
+ return;
+ }
+
+ if (verbose) {
+ log("transferring " + file.getAbsolutePath());
+ }
+
+ instream = new BufferedInputStream(new FileInputStream(file));
+
+ createParents(ftp, filename);
+
+ ftp.storeFile(resolveFile(filename), instream);
+
+ boolean success = FTPReply.isPositiveCompletion(ftp.getReplyCode());
+
+ if (!success) {
+ String s = "could not put file: " + ftp.getReplyString();
+
+ if (skipFailedTransfers) {
+ log(s, Project.MSG_WARN);
+ skipped++;
+ } else {
+ throw new BuildException(s);
+ }
+
+ } else {
+ // see if we should issue a chmod command
+ if (chmod != null) {
+ doSiteCommand(ftp, "chmod " + chmod + " " + resolveFile(filename));
+ }
+ log("File " + file.getAbsolutePath() + " copied to " + server,
+ Project.MSG_VERBOSE);
+ transferred++;
+ }
+ } finally {
+ FileUtils.close(instream);
+ }
+ }
+
+
+ /**
+ * Delete a file from the remote host.
+ * @param ftp ftp client
+ * @param filename file to delete
+ * @throws IOException in unknown circumstances
+ * @throws BuildException if skipFailedTransfers is set to false
+ * and the deletion could not be done
+ */
+ protected void delFile(FTPClient ftp, String filename)
+ throws IOException, BuildException {
+ if (verbose) {
+ log("deleting " + filename);
+ }
+
+ if (!ftp.deleteFile(resolveFile(filename))) {
+ String s = "could not delete file: " + ftp.getReplyString();
+
+ if (skipFailedTransfers) {
+ log(s, Project.MSG_WARN);
+ skipped++;
+ } else {
+ throw new BuildException(s);
+ }
+ } else {
+ log("File " + filename + " deleted from " + server,
+ Project.MSG_VERBOSE);
+ transferred++;
+ }
+ }
+
+ /**
+ * Delete a directory, if empty, from the remote host.
+ * @param ftp ftp client
+ * @param dirname directory to delete
+ * @throws IOException in unknown circumstances
+ * @throws BuildException if skipFailedTransfers is set to false
+ * and the deletion could not be done
+ */
+ protected void rmDir(FTPClient ftp, String dirname)
+ throws IOException, BuildException {
+ if (verbose) {
+ log("removing " + dirname);
+ }
+
+ if (!ftp.removeDirectory(resolveFile(dirname))) {
+ String s = "could not remove directory: " + ftp.getReplyString();
+
+ if (skipFailedTransfers) {
+ log(s, Project.MSG_WARN);
+ skipped++;
+ } else {
+ throw new BuildException(s);
+ }
+ } else {
+ log("Directory " + dirname + " removed from " + server,
+ Project.MSG_VERBOSE);
+ transferred++;
+ }
+ }
+
+
+ /**
+ * Retrieve a single file from the remote host. <code>filename</code> may
+ * contain a relative path specification. <p>
+ *
+ * The file will then be retrieved using the entire relative path spec -
+ * no attempt is made to change directories. It is anticipated that this
+ * may eventually cause problems with some FTP servers, but it simplifies
+ * the coding.</p>
+ * @param ftp the ftp client
+ * @param dir local base directory to which the file should go back
+ * @param filename relative path of the file based upon the ftp remote directory
+ * and/or the local base directory (dir)
+ * @throws IOException in unknown circumstances
+ * @throws BuildException if skipFailedTransfers is false
+ * and the file cannot be retrieved.
+ */
+ protected void getFile(FTPClient ftp, String dir, String filename)
+ throws IOException, BuildException {
+ OutputStream outstream = null;
+ try {
+ File file = getProject().resolveFile(new File(dir, filename).getPath());
+
+ if (newerOnly && isUpToDate(ftp, file, resolveFile(filename))) {
+ return;
+ }
+
+ if (verbose) {
+ log("transferring " + filename + " to "
+ + file.getAbsolutePath());
+ }
+
+ File pdir = file.getParentFile();
+
+ if (!pdir.exists()) {
+ pdir.mkdirs();
+ }
+ outstream = new BufferedOutputStream(new FileOutputStream(file));
+ ftp.retrieveFile(resolveFile(filename), outstream);
+
+ if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+ String s = "could not get file: " + ftp.getReplyString();
+
+ if (skipFailedTransfers) {
+ log(s, Project.MSG_WARN);
+ skipped++;
+ } else {
+ throw new BuildException(s);
+ }
+
+ } else {
+ log("File " + file.getAbsolutePath() + " copied from "
+ + server, Project.MSG_VERBOSE);
+ transferred++;
+ if (preserveLastModified) {
+ outstream.close();
+ outstream = null;
+ FTPFile[] remote = ftp.listFiles(resolveFile(filename));
+ if (remote.length > 0) {
+ FILE_UTILS.setFileLastModified(file,
+ remote[0].getTimestamp()
+ .getTime().getTime());
+ }
+ }
+ }
+ } finally {
+ FileUtils.close(outstream);
+ }
+ }
+
+
+ /**
+ * List information about a single file from the remote host. <code>filename</code>
+ * may contain a relative path specification. <p>
+ *
+ * The file listing will then be retrieved using the entire relative path
+ * spec - no attempt is made to change directories. It is anticipated that
+ * this may eventually cause problems with some FTP servers, but it
+ * simplifies the coding.</p>
+ * @param ftp ftp client
+ * @param bw buffered writer
+ * @param filename the directory one wants to list
+ * @throws IOException in unknown circumstances
+ * @throws BuildException in unknown circumstances
+ */
+ protected void listFile(FTPClient ftp, BufferedWriter bw, String filename)
+ throws IOException, BuildException {
+ if (verbose) {
+ log("listing " + filename);
+ }
+ FTPFile[] ftpfiles = ftp.listFiles(resolveFile(filename));
+
+ if (ftpfiles != null && ftpfiles.length > 0) {
+ bw.write(ftpfiles[0].toString());
+ bw.newLine();
+ transferred++;
+ }
+ }
+
+
+ /**
+ * Create the specified directory on the remote host.
+ *
+ * @param ftp The FTP client connection
+ * @param dir The directory to create (format must be correct for host
+ * type)
+ * @throws IOException in unknown circumstances
+ * @throws BuildException if ignoreNoncriticalErrors has not been set to true
+ * and a directory could not be created, for instance because it was
+ * already existing. Precisely, the codes 521, 550 and 553 will trigger
+ * a BuildException
+ */
+ protected void makeRemoteDir(FTPClient ftp, String dir)
+ throws IOException, BuildException {
+ String workingDirectory = ftp.printWorkingDirectory();
+ if (verbose) {
+ if (dir.startsWith("/") || workingDirectory == null) {
+ log("Creating directory: " + dir + " in /");
+ } else {
+ log("Creating directory: " + dir + " in " + workingDirectory);
+ }
+ }
+ if (dir.startsWith("/")) {
+ ftp.changeWorkingDirectory("/");
+ }
+ String subdir = "";
+ StringTokenizer st = new StringTokenizer(dir, "/");
+ while (st.hasMoreTokens()) {
+ subdir = st.nextToken();
+ log("Checking " + subdir, Project.MSG_DEBUG);
+ if (!ftp.changeWorkingDirectory(subdir)) {
+ if (!ftp.makeDirectory(subdir)) {
+ // codes 521, 550 and 553 can be produced by FTP Servers
+ // to indicate that an attempt to create a directory has
+ // failed because the directory already exists.
+ int rc = ftp.getReplyCode();
+ if (!(ignoreNoncriticalErrors
+ && (rc == CODE_550 || rc == CODE_553
+ || rc == CODE_521))) {
+ throw new BuildException("could not create directory: "
+ + ftp.getReplyString());
+ }
+ if (verbose) {
+ log("Directory already exists");
+ }
+ } else {
+ if (verbose) {
+ log("Directory created OK");
+ }
+ ftp.changeWorkingDirectory(subdir);
+ }
+ }
+ }
+ if (workingDirectory != null) {
+ ftp.changeWorkingDirectory(workingDirectory);
+ }
+ }
+
+ /**
+ * look at the response for a failed mkdir action, decide whether
+ * it matters or not. If it does, we throw an exception
+ * @param ftp current ftp connection
+ * @throws BuildException if this is an error to signal
+ */
+ private void handleMkDirFailure(FTPClient ftp)
+ throws BuildException {
+ int rc = ftp.getReplyCode();
+ if (!(ignoreNoncriticalErrors
+ && (rc == CODE_550 || rc == CODE_553 || rc == CODE_521))) {
+ throw new BuildException("could not create directory: "
+ + ftp.getReplyString());
+ }
+ }
+
+ /**
+ * Runs the task.
+ *
+ * @throws BuildException if the task fails or is not configured
+ * correctly.
+ */
+ public void execute() throws BuildException {
+ checkAttributes();
+
+ FTPClient ftp = null;
+
+ try {
+ log("Opening FTP connection to " + server, Project.MSG_VERBOSE);
+
+ ftp = new FTPClient();
+ if (this.isConfigurationSet) {
+ ftp = FTPConfigurator.configure(ftp, this);
+ }
+
+ ftp.setRemoteVerificationEnabled(enableRemoteVerification);
+ ftp.connect(server, port);
+ if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+ throw new BuildException("FTP connection failed: "
+ + ftp.getReplyString());
+ }
+
+ log("connected", Project.MSG_VERBOSE);
+ log("logging in to FTP server", Project.MSG_VERBOSE);
+
+ if ((this.account != null && !ftp.login(userid, password, account))
+ || (this.account == null && !ftp.login(userid, password))) {
+ throw new BuildException("Could not login to FTP server");
+ }
+
+ log("login succeeded", Project.MSG_VERBOSE);
+
+ if (binary) {
+ ftp.setFileType(org.apache.commons.net.ftp.FTP.BINARY_FILE_TYPE);
+ if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+ throw new BuildException("could not set transfer type: "
+ + ftp.getReplyString());
+ }
+ } else {
+ ftp.setFileType(org.apache.commons.net.ftp.FTP.ASCII_FILE_TYPE);
+ if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+ throw new BuildException("could not set transfer type: "
+ + ftp.getReplyString());
+ }
+ }
+
+ if (passive) {
+ log("entering passive mode", Project.MSG_VERBOSE);
+ ftp.enterLocalPassiveMode();
+ if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+ throw new BuildException("could not enter into passive "
+ + "mode: " + ftp.getReplyString());
+ }
+ }
+
+ // If an initial command was configured then send it.
+ // Some FTP servers offer different modes of operation,
+ // E.G. switching between a UNIX file system mode and
+ // a legacy file system.
+ if (this.initialSiteCommand != null) {
+ RetryHandler h = new RetryHandler(this.retriesAllowed, this);
+ final FTPClient lftp = ftp;
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ doSiteCommand(lftp, FTP.this.initialSiteCommand);
+ }
+ }, "initial site command: " + this.initialSiteCommand);
+ }
+
+
+ // For a unix ftp server you can set the default mask for all files
+ // created.
+
+ if (umask != null) {
+ RetryHandler h = new RetryHandler(this.retriesAllowed, this);
+ final FTPClient lftp = ftp;
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ doSiteCommand(lftp, "umask " + umask);
+ }
+ }, "umask " + umask);
+ }
+
+ // If the action is MK_DIR, then the specified remote
+ // directory is the directory to create.
+
+ if (action == MK_DIR) {
+ RetryHandler h = new RetryHandler(this.retriesAllowed, this);
+ final FTPClient lftp = ftp;
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ makeRemoteDir(lftp, remotedir);
+ }
+ }, remotedir);
+ } else if (action == SITE_CMD) {
+ RetryHandler h = new RetryHandler(this.retriesAllowed, this);
+ final FTPClient lftp = ftp;
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ doSiteCommand(lftp, FTP.this.siteCommand);
+ }
+ }, "Site Command: " + this.siteCommand);
+ } else {
+ if (remotedir != null) {
+ log("changing the remote directory to " + remotedir,
+ Project.MSG_VERBOSE);
+ ftp.changeWorkingDirectory(remotedir);
+ if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+ throw new BuildException("could not change remote "
+ + "directory: " + ftp.getReplyString());
+ }
+ }
+ if (newerOnly && timeDiffAuto) {
+ // in this case we want to find how much time span there is between local
+ // and remote
+ timeDiffMillis = getTimeDiff(ftp);
+ }
+ log(ACTION_STRS[action] + " " + ACTION_TARGET_STRS[action]);
+ transferFiles(ftp);
+ }
+
+ } catch (IOException ex) {
+ throw new BuildException("error during FTP transfer: " + ex, ex);
+ } finally {
+ if (ftp != null && ftp.isConnected()) {
+ try {
+ log("disconnecting", Project.MSG_VERBOSE);
+ ftp.logout();
+ ftp.disconnect();
+ } catch (IOException ex) {
+ // ignore it
+ }
+ }
+ }
+ }
+
+
+ /**
+ * an action to perform, one of
+ * "send", "put", "recv", "get", "del", "delete", "list", "mkdir", "chmod",
+ * "rmdir"
+ */
+ public static class Action extends EnumeratedAttribute {
+
+ private static final String[] VALID_ACTIONS = {
+ "send", "put", "recv", "get", "del", "delete", "list", "mkdir",
+ "chmod", "rmdir", "site"
+ };
+
+
+ /**
+ * Get the valid values
+ *
+ * @return an array of the valid FTP actions.
+ */
+ public String[] getValues() {
+ return VALID_ACTIONS;
+ }
+
+
+ /**
+ * Get the symbolic equivalent of the action value.
+ *
+ * @return the SYMBOL representing the given action.
+ */
+ public int getAction() {
+ String actionL = getValue().toLowerCase(Locale.ENGLISH);
+ if (actionL.equals("send") || actionL.equals("put")) {
+ return SEND_FILES;
+ } else if (actionL.equals("recv") || actionL.equals("get")) {
+ return GET_FILES;
+ } else if (actionL.equals("del") || actionL.equals("delete")) {
+ return DEL_FILES;
+ } else if (actionL.equals("list")) {
+ return LIST_FILES;
+ } else if (actionL.equals("chmod")) {
+ return CHMOD;
+ } else if (actionL.equals("mkdir")) {
+ return MK_DIR;
+ } else if (actionL.equals("rmdir")) {
+ return RM_DIR;
+ } else if (actionL.equals("site")) {
+ return SITE_CMD;
+ }
+ return SEND_FILES;
+ }
+ }
+ /**
+ * represents one of the valid timestamp adjustment values
+ * recognized by the <code>timestampGranularity</code> attribute.<p>
+
+ * A timestamp adjustment may be used in file transfers for checking
+ * uptodateness. MINUTE means to add one minute to the server
+ * timestamp. This is done because FTP servers typically list
+ * timestamps HH:mm and client FileSystems typically use HH:mm:ss.
+ *
+ * The default is to use MINUTE for PUT actions and NONE for GET
+ * actions, since GETs have the <code>preserveLastModified</code>
+ * option, which takes care of the problem in most use cases where
+ * this level of granularity is an issue.
+ *
+ */
+ public static class Granularity extends EnumeratedAttribute {
+
+ private static final String[] VALID_GRANULARITIES = {
+ "", "MINUTE", "NONE"
+ };
+
+ /**
+ * Get the valid values.
+ * @return the list of valid Granularity values
+ */
+ public String[] getValues() {
+ return VALID_GRANULARITIES;
+ }
+ /**
+ * returns the number of milliseconds associated with
+ * the attribute, which can vary in some cases depending
+ * on the value of the action parameter.
+ * @param action SEND_FILES or GET_FILES
+ * @return the number of milliseconds associated with
+ * the attribute, in the context of the supplied action
+ */
+ public long getMilliseconds(int action) {
+ String granularityU = getValue().toUpperCase(Locale.ENGLISH);
+ if ("".equals(granularityU)) {
+ if (action == SEND_FILES) {
+ return GRANULARITY_MINUTE;
+ }
+ } else if ("MINUTE".equals(granularityU)) {
+ return GRANULARITY_MINUTE;
+ }
+ return 0L;
+ }
+ static final Granularity getDefault() {
+ Granularity g = new Granularity();
+ g.setValue("");
+ return g;
+ }
+
+ }
+ /**
+ * one of the valid system type keys recognized by the systemTypeKey
+ * attribute.
+ */
+ public static class FTPSystemType extends EnumeratedAttribute {
+
+ private static final String[] VALID_SYSTEM_TYPES = {
+ "", "UNIX", "VMS", "WINDOWS", "OS/2", "OS/400",
+ "MVS"
+ };
+
+
+ /**
+ * Get the valid values.
+ * @return the list of valid system types.
+ */
+ public String[] getValues() {
+ return VALID_SYSTEM_TYPES;
+ }
+
+ static final FTPSystemType getDefault() {
+ FTPSystemType ftpst = new FTPSystemType();
+ ftpst.setValue("");
+ return ftpst;
+ }
+ }
+ /**
+ * Enumerated class for languages.
+ */
+ public static class LanguageCode extends EnumeratedAttribute {
+
+
+ private static final String[] VALID_LANGUAGE_CODES =
+ getValidLanguageCodes();
+
+ private static String[] getValidLanguageCodes() {
+ Collection c = FTPClientConfig.getSupportedLanguageCodes();
+ String[] ret = new String[c.size() + 1];
+ int i = 0;
+ ret[i++] = "";
+ for (Iterator it = c.iterator(); it.hasNext(); i++) {
+ ret[i] = (String) it.next();
+ }
+ return ret;
+ }
+
+
+ /**
+ * Return the value values.
+ * @return the list of valid language types.
+ */
+ public String[] getValues() {
+ return VALID_LANGUAGE_CODES;
+ }
+
+ static final LanguageCode getDefault() {
+ LanguageCode lc = new LanguageCode();
+ lc.setValue("");
+ return lc;
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPConfigurator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPConfigurator.java
new file mode 100644
index 00000000..0604dac9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPConfigurator.java
@@ -0,0 +1,99 @@
+/*
+ * 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.tools.ant.taskdefs.optional.net;
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * The sole purpose of this class is (note that it is package-private
+ * is to serve as a separate, static compilation unit for importing
+ * FTPClientConfig, to enable users who wish to use the FTP task
+ * without using its new features to avoid the need to
+ * upgrade to jakarta-commons-net 1.4.0, where FTPClientConfig was
+ * introduced.
+ */
+// CheckStyle:HideUtilityClassConstructorCheck OFF (bc)
+class FTPConfigurator {
+ /**
+ * configures the supplied FTPClient with the various
+ * attributes set in the supplied FTP task.
+ * @param client the FTPClient to be configured
+ * @param task the FTP task whose attributes are used to
+ * configure the client
+ * @return the client as configured.
+ */
+ static FTPClient configure(FTPClient client, FTPTaskConfig task) {
+ task.log("custom configuration", Project.MSG_VERBOSE);
+ FTPClientConfig config;
+ String systemTypeKey = task.getSystemTypeKey();
+ if (systemTypeKey != null && !"".equals(systemTypeKey)) {
+ config = new FTPClientConfig(systemTypeKey);
+ task.log("custom config: system key = "
+ + systemTypeKey, Project.MSG_VERBOSE);
+ } else {
+ config = new FTPClientConfig();
+ task.log("custom config: system key = default (UNIX)",
+ Project.MSG_VERBOSE);
+ }
+
+ String defaultDateFormatConfig = task.getDefaultDateFormatConfig();
+ if (defaultDateFormatConfig != null) {
+ config.setDefaultDateFormatStr(defaultDateFormatConfig);
+ task.log("custom config: default date format = "
+ + defaultDateFormatConfig, Project.MSG_VERBOSE);
+ }
+
+ String recentDateFormatConfig = task.getRecentDateFormatConfig();
+ if (recentDateFormatConfig != null) {
+ config.setRecentDateFormatStr(recentDateFormatConfig);
+ task.log("custom config: recent date format = "
+ + recentDateFormatConfig, Project.MSG_VERBOSE);
+ }
+
+ String serverLanguageCodeConfig = task.getServerLanguageCodeConfig();
+ if (serverLanguageCodeConfig != null) {
+ if (!"".equals(serverLanguageCodeConfig)
+ && !FTPClientConfig.getSupportedLanguageCodes()
+ .contains(serverLanguageCodeConfig)) {
+ throw new BuildException("unsupported language code" +
+ serverLanguageCodeConfig);
+ }
+ config.setServerLanguageCode(serverLanguageCodeConfig);
+ task.log("custom config: server language code = "
+ + serverLanguageCodeConfig, Project.MSG_VERBOSE);
+ }
+
+ String serverTimeZoneConfig = task.getServerTimeZoneConfig();
+ if (serverTimeZoneConfig != null) {
+ config.setServerTimeZoneId(serverTimeZoneConfig);
+ task.log("custom config: server time zone ID = "
+ + serverTimeZoneConfig, Project.MSG_VERBOSE);
+ }
+
+ String shortMonthNamesConfig = task.getShortMonthNamesConfig();
+ if (shortMonthNamesConfig != null) {
+ config.setShortMonthNames(shortMonthNamesConfig);
+ task.log("custom config: short month names = "
+ + shortMonthNamesConfig, Project.MSG_VERBOSE);
+ }
+ client.configure(config);
+ return client;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTask.java
new file mode 100644
index 00000000..79780c78
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTask.java
@@ -0,0 +1,965 @@
+/*
+ * 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.tools.ant.taskdefs.optional.net;
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.util.Locale;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.LoaderUtils;
+import org.apache.tools.ant.util.Retryable;
+import org.apache.tools.ant.util.SplitClassLoader;
+
+/**
+ * Basic FTP client. Performs the following actions:
+ * <ul>
+ * <li> <strong>send</strong> - send files to a remote server. This is the
+ * default action.</li>
+ * <li> <strong>get</strong> - retrieve files from a remote server.</li>
+ * <li> <strong>del</strong> - delete files from a remote server.</li>
+ * <li> <strong>list</strong> - create a file listing.</li>
+ * <li> <strong>chmod</strong> - change unix file permissions.</li>
+ * <li> <strong>rmdir</strong> - remove directories, if empty, from a
+ * remote server.</li>
+ * </ul>
+ * <strong>Note:</strong> Some FTP servers - notably the Solaris server - seem
+ * to hold data ports open after a "retr" operation, allowing them to timeout
+ * instead of shutting them down cleanly. This happens in active or passive
+ * mode, and the ports will remain open even after ending the FTP session. FTP
+ * "send" operations seem to close ports immediately. This behavior may cause
+ * problems on some systems when downloading large sets of files.
+ *
+ * @since Ant 1.3
+ */
+public class FTPTask extends Task implements FTPTaskConfig {
+ public static final int SEND_FILES = 0;
+ public static final int GET_FILES = 1;
+ public static final int DEL_FILES = 2;
+ public static final int LIST_FILES = 3;
+ public static final int MK_DIR = 4;
+ public static final int CHMOD = 5;
+ public static final int RM_DIR = 6;
+ public static final int SITE_CMD = 7;
+
+ /** adjust uptodate calculations where server timestamps are HH:mm and client's
+ * are HH:mm:ss */
+ private static final long GRANULARITY_MINUTE = 60000L;
+
+ /** Default port for FTP */
+ public static final int DEFAULT_FTP_PORT = 21;
+
+ private String remotedir;
+ private String server;
+ private String userid;
+ private String password;
+ private String account;
+ private File listing;
+ private boolean binary = true;
+ private boolean passive = false;
+ private boolean verbose = false;
+ private boolean newerOnly = false;
+ private long timeDiffMillis = 0;
+ private long granularityMillis = 0L;
+ private boolean timeDiffAuto = false;
+ private int action = SEND_FILES;
+ private Vector filesets = new Vector();
+ private String remoteFileSep = "/";
+ private int port = DEFAULT_FTP_PORT;
+ private boolean skipFailedTransfers = false;
+ private boolean ignoreNoncriticalErrors = false;
+ private boolean preserveLastModified = false;
+ private String chmod = null;
+ private String umask = null;
+ private FTPSystemType systemTypeKey = FTPSystemType.getDefault();
+ private String defaultDateFormatConfig = null;
+ private String recentDateFormatConfig = null;
+ private String serverLanguageCodeConfig = null;
+ private String serverTimeZoneConfig = null;
+ private String shortMonthNamesConfig = null;
+ private Granularity timestampGranularity = Granularity.getDefault();
+ private boolean isConfigurationSet = false;
+ private int retriesAllowed = 0;
+ private String siteCommand = null;
+ private String initialSiteCommand = null;
+ private boolean enableRemoteVerification = true;
+
+ private Path classpath;
+ private ClassLoader mirrorLoader;
+ private FTPTaskMirror delegate = null;
+
+ public static final String[] ACTION_STRS = {
+ "sending",
+ "getting",
+ "deleting",
+ "listing",
+ "making directory",
+ "chmod",
+ "removing",
+ "site"
+ };
+
+ public static final String[] COMPLETED_ACTION_STRS = {
+ "sent",
+ "retrieved",
+ "deleted",
+ "listed",
+ "created directory",
+ "mode changed",
+ "removed",
+ "site command executed"
+ };
+
+ public static final String[] ACTION_TARGET_STRS = {
+ "files",
+ "files",
+ "files",
+ "files",
+ "directory",
+ "files",
+ "directories",
+ "site command"
+ };
+
+ /**
+ * Sets the remote directory where files will be placed. This may be a
+ * relative or absolute path, and must be in the path syntax expected by
+ * the remote server. No correction of path syntax will be performed.
+ *
+ * @param dir the remote directory name.
+ */
+ public void setRemotedir(String dir) {
+ this.remotedir = dir;
+ }
+
+ public String getRemotedir() {
+ return remotedir;
+ }
+
+ /**
+ * Sets the FTP server to send files to.
+ *
+ * @param server the remote server name.
+ */
+ public void setServer(String server) {
+ this.server = server;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ /**
+ * Sets the FTP port used by the remote server.
+ *
+ * @param port the port on which the remote server is listening.
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * Sets the login user id to use on the specified server.
+ *
+ * @param userid remote system userid.
+ */
+ public void setUserid(String userid) {
+ this.userid = userid;
+ }
+
+ public String getUserid() {
+ return userid;
+ }
+
+ /**
+ * Sets the login password for the given user id.
+ *
+ * @param password the password on the remote system.
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * Sets the login account to use on the specified server.
+ *
+ * @param pAccount the account name on remote system
+ * @since Ant 1.7
+ */
+ public void setAccount(String pAccount) {
+ this.account = pAccount;
+ }
+
+ public String getAccount() {
+ return account;
+ }
+
+ /**
+ * If true, uses binary mode, otherwise text mode (default is binary).
+ *
+ * @param binary if true use binary mode in transfers.
+ */
+ public void setBinary(boolean binary) {
+ this.binary = binary;
+ }
+
+ public boolean isBinary() {
+ return binary;
+ }
+
+ /**
+ * Specifies whether to use passive mode. Set to true if you are behind a
+ * firewall and cannot connect without it. Passive mode is disabled by
+ * default.
+ *
+ * @param passive true is passive mode should be used.
+ */
+ public void setPassive(boolean passive) {
+ this.passive = passive;
+ }
+
+ public boolean isPassive() {
+ return passive;
+ }
+
+ /**
+ * Set to true to receive notification about each file as it is
+ * transferred.
+ *
+ * @param verbose true if verbose notifications are required.
+ */
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ /**
+ * A synonym for <tt>depends</tt>. Set to true to transmit only new
+ * or changed files.
+ *
+ * See the related attributes timediffmillis and timediffauto.
+ *
+ * @param newer if true only transfer newer files.
+ */
+ public void setNewer(boolean newer) {
+ this.newerOnly = newer;
+ }
+
+ public boolean isNewer() {
+ return newerOnly;
+ }
+
+ /**
+ * number of milliseconds to add to the time on the remote machine
+ * to get the time on the local machine.
+ *
+ * use in conjunction with <code>newer</code>
+ *
+ * @param timeDiffMillis number of milliseconds
+ *
+ * @since ant 1.6
+ */
+ public void setTimeDiffMillis(long timeDiffMillis) {
+ this.timeDiffMillis = timeDiffMillis;
+ }
+
+ public long getTimeDiffMillis() {
+ return timeDiffMillis;
+ }
+
+ /**
+ * &quot;true&quot; to find out automatically the time difference
+ * between local and remote machine.
+ *
+ * This requires right to create
+ * and delete a temporary file in the remote directory.
+ *
+ * @param timeDiffAuto true = find automatically the time diff
+ *
+ * @since ant 1.6
+ */
+ public void setTimeDiffAuto(boolean timeDiffAuto) {
+ this.timeDiffAuto = timeDiffAuto;
+ }
+
+ public boolean isTimeDiffAuto() {
+ return timeDiffAuto;
+ }
+
+ /**
+ * Set to true to preserve modification times for "gotten" files.
+ *
+ * @param preserveLastModified if true preserver modification times.
+ */
+ public void setPreserveLastModified(boolean preserveLastModified) {
+ this.preserveLastModified = preserveLastModified;
+ }
+
+ public boolean isPreserveLastModified() {
+ return preserveLastModified;
+ }
+
+ /**
+ * Set to true to transmit only files that are new or changed from their
+ * remote counterparts. The default is to transmit all files.
+ *
+ * @param depends if true only transfer newer files.
+ */
+ public void setDepends(boolean depends) {
+ this.newerOnly = depends;
+ }
+
+
+ /**
+ * Sets the remote file separator character. This normally defaults to the
+ * Unix standard forward slash, but can be manually overridden using this
+ * call if the remote server requires some other separator. Only the first
+ * character of the string is used.
+ *
+ * @param separator the file separator on the remote system.
+ */
+ public void setSeparator(String separator) {
+ remoteFileSep = separator;
+ }
+
+
+ public String getSeparator() {
+ return remoteFileSep;
+ }
+
+ /**
+ * Sets the file permission mode (Unix only) for files sent to the
+ * server.
+ *
+ * @param theMode unix style file mode for the files sent to the remote
+ * system.
+ */
+ public void setChmod(String theMode) {
+ this.chmod = theMode;
+ }
+
+ public String getChmod() {
+ return chmod;
+ }
+
+ /**
+ * Sets the default mask for file creation on a unix server.
+ *
+ * @param theUmask unix style umask for files created on the remote server.
+ */
+ public void setUmask(String theUmask) {
+ this.umask = theUmask;
+ }
+
+ public String getUmask() {
+ return umask;
+ }
+
+ /**
+ * A set of files to upload or download
+ *
+ * @param set the set of files to be added to the list of files to be
+ * transferred.
+ */
+ public void addFileset(FileSet set) {
+ filesets.addElement(set);
+ }
+
+ public Vector getFilesets() {
+ return filesets;
+ }
+
+ /**
+ * Sets the FTP action to be taken. Currently accepts "put", "get", "del",
+ * "mkdir", "chmod", "list", and "site".
+ *
+ * @deprecated since 1.5.x.
+ * setAction(String) is deprecated and is replaced with
+ * setAction(FTP.Action) to make Ant's Introspection mechanism do the
+ * work and also to encapsulate operations on the type in its own
+ * class.
+ * @ant.attribute ignore="true"
+ *
+ * @param action the FTP action to be performed.
+ *
+ * @throws BuildException if the action is not a valid action.
+ */
+ public void setAction(String action) throws BuildException {
+ log("DEPRECATED - The setAction(String) method has been deprecated."
+ + " Use setAction(FTP.Action) instead.");
+
+ Action a = new Action();
+
+ a.setValue(action);
+ this.action = a.getAction();
+ }
+
+
+ /**
+ * Sets the FTP action to be taken. Currently accepts "put", "get", "del",
+ * "mkdir", "chmod", "list", and "site".
+ *
+ * @param action the FTP action to be performed.
+ *
+ * @throws BuildException if the action is not a valid action.
+ */
+ public void setAction(Action action) throws BuildException {
+ this.action = action.getAction();
+ }
+
+ public int getAction() {
+ return this.action;
+ }
+
+ /**
+ * The output file for the "list" action. This attribute is ignored for
+ * any other actions.
+ *
+ * @param listing file in which to store the listing.
+ */
+ public void setListing(File listing) {
+ this.listing = listing;
+ }
+
+ public File getListing() {
+ return listing;
+ }
+
+ /**
+ * If true, enables unsuccessful file put, delete and get
+ * operations to be skipped with a warning and the remainder
+ * of the files still transferred.
+ *
+ * @param skipFailedTransfers true if failures in transfers are ignored.
+ */
+ public void setSkipFailedTransfers(boolean skipFailedTransfers) {
+ this.skipFailedTransfers = skipFailedTransfers;
+ }
+
+ public boolean isSkipFailedTransfers() {
+ return skipFailedTransfers;
+ }
+
+ /**
+ * set the flag to skip errors on directory creation.
+ * (and maybe later other server specific errors)
+ *
+ * @param ignoreNoncriticalErrors true if non-critical errors should not
+ * cause a failure.
+ */
+ public void setIgnoreNoncriticalErrors(boolean ignoreNoncriticalErrors) {
+ this.ignoreNoncriticalErrors = ignoreNoncriticalErrors;
+ }
+
+ public boolean isIgnoreNoncriticalErrors() {
+ return ignoreNoncriticalErrors;
+ }
+
+ private void configurationHasBeenSet() {
+ this.isConfigurationSet = true;
+ }
+
+ public boolean isConfigurationSet() {
+ return this.isConfigurationSet;
+ }
+
+ /**
+ * Sets the systemTypeKey attribute.
+ * Method for setting <code>FTPClientConfig</code> remote system key.
+ *
+ * @param systemKey the key to be set - BUT if blank
+ * the default value of null (which signifies "autodetect") will be kept.
+ * @see org.apache.commons.net.ftp.FTPClientConfig
+ */
+ public void setSystemTypeKey(FTPSystemType systemKey) {
+ if (systemKey != null && !systemKey.getValue().equals("")) {
+ this.systemTypeKey = systemKey;
+ configurationHasBeenSet();
+ }
+ }
+
+ /**
+ * Sets the defaultDateFormatConfig attribute.
+ * @param defaultDateFormat configuration to be set, unless it is
+ * null or empty string, in which case ignored.
+ * @see org.apache.commons.net.ftp.FTPClientConfig
+ */
+ public void setDefaultDateFormatConfig(String defaultDateFormat) {
+ if (defaultDateFormat != null && !defaultDateFormat.equals("")) {
+ this.defaultDateFormatConfig = defaultDateFormat;
+ configurationHasBeenSet();
+ }
+ }
+
+ /**
+ * Sets the recentDateFormatConfig attribute.
+ * @param recentDateFormat configuration to be set, unless it is
+ * null or empty string, in which case ignored.
+ * @see org.apache.commons.net.ftp.FTPClientConfig
+ */
+ public void setRecentDateFormatConfig(String recentDateFormat) {
+ if (recentDateFormat != null && !recentDateFormat.equals("")) {
+ this.recentDateFormatConfig = recentDateFormat;
+ configurationHasBeenSet();
+ }
+ }
+
+ /**
+ * Sets the serverLanguageCode attribute.
+ * @param serverLanguageCode configuration to be set, unless it is
+ * null or empty string, in which case ignored.
+ * @see org.apache.commons.net.ftp.FTPClientConfig
+ */
+ public void setServerLanguageCodeConfig(String serverLanguageCode) {
+ if (serverLanguageCode != null && !"".equals(serverLanguageCode)) {
+ this.serverLanguageCodeConfig = serverLanguageCode;
+ configurationHasBeenSet();
+ }
+ }
+
+ /**
+ * Sets the serverTimeZoneConfig attribute.
+ * @param serverTimeZoneId configuration to be set, unless it is
+ * null or empty string, in which case ignored.
+ * @see org.apache.commons.net.ftp.FTPClientConfig
+ */
+ public void setServerTimeZoneConfig(String serverTimeZoneId) {
+ if (serverTimeZoneId != null && !serverTimeZoneId.equals("")) {
+ this.serverTimeZoneConfig = serverTimeZoneId;
+ configurationHasBeenSet();
+ }
+ }
+
+ /**
+ * Sets the shortMonthNamesConfig attribute
+ *
+ * @param shortMonthNames configuration to be set, unless it is
+ * null or empty string, in which case ignored.
+ * @see org.apache.commons.net.ftp.FTPClientConfig
+ */
+ public void setShortMonthNamesConfig(String shortMonthNames) {
+ if (shortMonthNames != null && !shortMonthNames.equals("")) {
+ this.shortMonthNamesConfig = shortMonthNames;
+ configurationHasBeenSet();
+ }
+ }
+
+
+
+ /**
+ * Defines how many times to retry executing FTP command before giving up.
+ * Default is 0 - try once and if failure then give up.
+ *
+ * @param retriesAllowed number of retries to allow. -1 means
+ * keep trying forever. "forever" may also be specified as a
+ * synonym for -1.
+ */
+ public void setRetriesAllowed(String retriesAllowed) {
+ if ("FOREVER".equalsIgnoreCase(retriesAllowed)) {
+ this.retriesAllowed = Retryable.RETRY_FOREVER;
+ } else {
+ try {
+ int retries = Integer.parseInt(retriesAllowed);
+ if (retries < Retryable.RETRY_FOREVER) {
+ throw new BuildException(
+ "Invalid value for retriesAllowed attribute: "
+ + retriesAllowed);
+
+ }
+ this.retriesAllowed = retries;
+ } catch (NumberFormatException px) {
+ throw new BuildException(
+ "Invalid value for retriesAllowed attribute: "
+ + retriesAllowed);
+
+ }
+
+ }
+ }
+
+ public int getRetriesAllowed() {
+ return retriesAllowed;
+ }
+
+ /**
+ * @return Returns the systemTypeKey.
+ */
+ public String getSystemTypeKey() {
+ return systemTypeKey.getValue();
+ }
+ /**
+ * @return Returns the defaultDateFormatConfig.
+ */
+ public String getDefaultDateFormatConfig() {
+ return defaultDateFormatConfig;
+ }
+ /**
+ * @return Returns the recentDateFormatConfig.
+ */
+ public String getRecentDateFormatConfig() {
+ return recentDateFormatConfig;
+ }
+ /**
+ * @return Returns the serverLanguageCodeConfig.
+ */
+ public String getServerLanguageCodeConfig() {
+ return serverLanguageCodeConfig;
+ }
+ /**
+ * @return Returns the serverTimeZoneConfig.
+ */
+ public String getServerTimeZoneConfig() {
+ return serverTimeZoneConfig;
+ }
+ /**
+ * @return Returns the shortMonthNamesConfig.
+ */
+ public String getShortMonthNamesConfig() {
+ return shortMonthNamesConfig;
+ }
+ /**
+ * @return Returns the timestampGranularity.
+ */
+ public Granularity getTimestampGranularity() {
+ return timestampGranularity;
+ }
+ /**
+ * Sets the timestampGranularity attribute
+ * @param timestampGranularity The timestampGranularity to set.
+ */
+ public void setTimestampGranularity(Granularity timestampGranularity) {
+ if (null == timestampGranularity || "".equals(timestampGranularity.getValue())) {
+ return;
+ }
+ this.timestampGranularity = timestampGranularity;
+ }
+ /**
+ * Sets the siteCommand attribute. This attribute
+ * names the command that will be executed if the action
+ * is "site".
+ * @param siteCommand The siteCommand to set.
+ */
+ public void setSiteCommand(String siteCommand) {
+ this.siteCommand = siteCommand;
+ }
+
+ public String getSiteCommand() {
+ return siteCommand;
+ }
+
+ /**
+ * Sets the initialSiteCommand attribute. This attribute
+ * names a site command that will be executed immediately
+ * after connection.
+ * @param initialCommand The initialSiteCommand to set.
+ */
+ public void setInitialSiteCommand(String initialCommand) {
+ this.initialSiteCommand = initialCommand;
+ }
+
+ public String getInitialSiteCommand() {
+ return initialSiteCommand;
+ }
+
+ public long getGranularityMillis() {
+ return this.granularityMillis;
+ }
+
+ public void setGranularityMillis(long granularity) {
+ this.granularityMillis = granularity;
+ }
+
+ /**
+ * Whether to verify that data and control connections are
+ * connected to the same remote host.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setEnableRemoteVerification(boolean b) {
+ enableRemoteVerification = b;
+ }
+
+ public boolean getEnableRemoteVerification() {
+ return enableRemoteVerification;
+ }
+
+ /**
+ * Checks to see that all required parameters are set.
+ *
+ * @throws BuildException if the configuration is not valid.
+ */
+ protected void checkAttributes() throws BuildException {
+ if (server == null) {
+ throw new BuildException("server attribute must be set!");
+ }
+ if (userid == null) {
+ throw new BuildException("userid attribute must be set!");
+ }
+ if (password == null) {
+ throw new BuildException("password attribute must be set!");
+ }
+
+ if ((action == LIST_FILES) && (listing == null)) {
+ throw new BuildException("listing attribute must be set for list "
+ + "action!");
+ }
+
+ if (action == MK_DIR && remotedir == null) {
+ throw new BuildException("remotedir attribute must be set for "
+ + "mkdir action!");
+ }
+
+ if (action == CHMOD && chmod == null) {
+ throw new BuildException("chmod attribute must be set for chmod "
+ + "action!");
+ }
+ if (action == SITE_CMD && siteCommand == null) {
+ throw new BuildException("sitecommand attribute must be set for site "
+ + "action!");
+ }
+
+
+ if (this.isConfigurationSet) {
+ try {
+ Class.forName("org.apache.commons.net.ftp.FTPClientConfig");
+ } catch (ClassNotFoundException e) {
+ throw new BuildException(
+ "commons-net.jar >= 1.4.0 is required for at least one"
+ + " of the attributes specified.");
+ }
+ }
+ }
+
+ /**
+ * Runs the task.
+ *
+ * @throws BuildException if the task fails or is not configured
+ * correctly.
+ */
+ public void execute() throws BuildException {
+ checkAttributes();
+ try {
+ setupFTPDelegate();
+ delegate.doFTP();
+ } finally {
+ if (mirrorLoader instanceof SplitClassLoader) {
+ ((SplitClassLoader) mirrorLoader).cleanup();
+ }
+ mirrorLoader = null;
+ }
+ }
+
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(getProject());
+ }
+ return classpath;
+ }
+
+ protected void setupFTPDelegate() {
+ ClassLoader myLoader = FTPTask.class.getClassLoader();
+ if (mustSplit()) {
+ mirrorLoader =
+ new SplitClassLoader(myLoader, classpath, getProject(),
+ new String[] {
+ "FTPTaskMirrorImpl",
+ "FTPConfigurator"
+ });
+ } else {
+ mirrorLoader = myLoader;
+ }
+ delegate = createMirror(this, mirrorLoader);
+ }
+
+ private static boolean mustSplit() {
+ return LoaderUtils.getResourceSource(FTPTask.class.getClassLoader(),
+ "/org/apache/commons/net/"
+ + "ftp/FTP.class")
+ == null;
+ }
+
+ private static FTPTaskMirror createMirror(FTPTask task,
+ ClassLoader loader) {
+ try {
+ loader.loadClass("org.apache.commons.net.ftp.FTP"); // sanity check
+ } catch (ClassNotFoundException e) {
+ throw new BuildException("The <classpath> for <ftp> must include"
+ + " commons-net.jar if not in Ant's own "
+ + " classpath", e, task.getLocation());
+ }
+ try {
+ Class c = loader.loadClass(FTPTaskMirror.class.getName() + "Impl");
+ if (c.getClassLoader() != loader) {
+ throw new BuildException("Overdelegating loader",
+ task.getLocation());
+ }
+ Constructor cons = c.getConstructor(new Class[] {FTPTask.class});
+ return (FTPTaskMirror) cons.newInstance(new Object[] {task});
+ } catch (Exception e) {
+ throw new BuildException(e, task.getLocation());
+ }
+ }
+
+ /**
+ * an action to perform, one of
+ * "send", "put", "recv", "get", "del", "delete", "list", "mkdir", "chmod",
+ * "rmdir"
+ */
+ public static class Action extends EnumeratedAttribute {
+
+ private static final String[] VALID_ACTIONS = {
+ "send", "put", "recv", "get", "del", "delete", "list", "mkdir",
+ "chmod", "rmdir", "site"
+ };
+
+
+ /**
+ * Get the valid values
+ *
+ * @return an array of the valid FTP actions.
+ */
+ public String[] getValues() {
+ return VALID_ACTIONS;
+ }
+
+
+ /**
+ * Get the symbolic equivalent of the action value.
+ *
+ * @return the SYMBOL representing the given action.
+ */
+ public int getAction() {
+ String actionL = getValue().toLowerCase(Locale.ENGLISH);
+ if (actionL.equals("send") || actionL.equals("put")) {
+ return SEND_FILES;
+ } else if (actionL.equals("recv") || actionL.equals("get")) {
+ return GET_FILES;
+ } else if (actionL.equals("del") || actionL.equals("delete")) {
+ return DEL_FILES;
+ } else if (actionL.equals("list")) {
+ return LIST_FILES;
+ } else if (actionL.equals("chmod")) {
+ return CHMOD;
+ } else if (actionL.equals("mkdir")) {
+ return MK_DIR;
+ } else if (actionL.equals("rmdir")) {
+ return RM_DIR;
+ } else if (actionL.equals("site")) {
+ return SITE_CMD;
+ }
+ return SEND_FILES;
+ }
+ }
+ /**
+ * represents one of the valid timestamp adjustment values
+ * recognized by the <code>timestampGranularity</code> attribute.<p>
+
+ * A timestamp adjustment may be used in file transfers for checking
+ * uptodateness. MINUTE means to add one minute to the server
+ * timestamp. This is done because FTP servers typically list
+ * timestamps HH:mm and client FileSystems typically use HH:mm:ss.
+ *
+ * The default is to use MINUTE for PUT actions and NONE for GET
+ * actions, since GETs have the <code>preserveLastModified</code>
+ * option, which takes care of the problem in most use cases where
+ * this level of granularity is an issue.
+ *
+ */
+ public static class Granularity extends EnumeratedAttribute {
+
+ private static final String[] VALID_GRANULARITIES = {
+ "", "MINUTE", "NONE"
+ };
+
+ /**
+ * Get the valid values.
+ * @return the list of valid Granularity values
+ */
+ public String[] getValues() {
+ return VALID_GRANULARITIES;
+ }
+ /**
+ * returns the number of milliseconds associated with
+ * the attribute, which can vary in some cases depending
+ * on the value of the action parameter.
+ * @param action SEND_FILES or GET_FILES
+ * @return the number of milliseconds associated with
+ * the attribute, in the context of the supplied action
+ */
+ public long getMilliseconds(int action) {
+ String granularityU = getValue().toUpperCase(Locale.ENGLISH);
+ if ("".equals(granularityU)) {
+ if (action == SEND_FILES) {
+ return GRANULARITY_MINUTE;
+ }
+ } else if ("MINUTE".equals(granularityU)) {
+ return GRANULARITY_MINUTE;
+ }
+ return 0L;
+ }
+ static final Granularity getDefault() {
+ Granularity g = new Granularity();
+ g.setValue("");
+ return g;
+ }
+
+ }
+ /**
+ * one of the valid system type keys recognized by the systemTypeKey
+ * attribute.
+ */
+ public static class FTPSystemType extends EnumeratedAttribute {
+
+ private static final String[] VALID_SYSTEM_TYPES = {
+ "", "UNIX", "VMS", "WINDOWS", "OS/2", "OS/400",
+ "MVS"
+ };
+
+
+ /**
+ * Get the valid values.
+ * @return the list of valid system types.
+ */
+ public String[] getValues() {
+ return VALID_SYSTEM_TYPES;
+ }
+
+ static final FTPSystemType getDefault() {
+ FTPSystemType ftpst = new FTPSystemType();
+ ftpst.setValue("");
+ return ftpst;
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskConfig.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskConfig.java
new file mode 100644
index 00000000..006df61f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskConfig.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.tools.ant.taskdefs.optional.net;
+
+interface FTPTaskConfig {
+ void log(String msg, int level);
+ String getSystemTypeKey();
+ String getDefaultDateFormatConfig();
+ String getRecentDateFormatConfig();
+ String getServerLanguageCodeConfig();
+ String getServerTimeZoneConfig();
+ String getShortMonthNamesConfig();
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirror.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirror.java
new file mode 100644
index 00000000..5d09e6f6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirror.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.tools.ant.taskdefs.optional.net;
+
+import org.apache.tools.ant.BuildException;
+
+public interface FTPTaskMirror {
+ void doFTP() throws BuildException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirrorImpl.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirrorImpl.java
new file mode 100644
index 00000000..a4f24130
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/FTPTaskMirrorImpl.java
@@ -0,0 +1,1951 @@
+/*
+ * 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.tools.ant.taskdefs.optional.net;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.commons.net.ftp.FTPReply;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Delete;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.RetryHandler;
+import org.apache.tools.ant.util.Retryable;
+import org.apache.tools.ant.util.VectorSet;
+
+public class FTPTaskMirrorImpl implements FTPTaskMirror {
+
+ /** return code of ftp */
+ private static final int CODE_521 = 521;
+ private static final int CODE_550 = 550;
+ private static final int CODE_553 = 553;
+
+ /** Date formatter used in logging, note not thread safe! */
+ private static final SimpleDateFormat TIMESTAMP_LOGGING_SDF =
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private final FTPTask task;
+ private Set dirCache = new HashSet();
+ private int transferred = 0;
+ private int skipped = 0;
+
+ /**
+ * Constructor.
+ * @param task the FTPTask that uses this mirror.
+ */
+ public FTPTaskMirrorImpl(FTPTask task) {
+ this.task = task;
+ }
+
+ /**
+ * internal class providing a File-like interface to some of the information
+ * available from the FTP server
+ *
+ */
+ protected static class FTPFileProxy extends File {
+
+ private final FTPFile file;
+ private final String[] parts;
+ private final String name;
+
+ /**
+ * creates a proxy to a FTP file
+ * @param file
+ */
+ public FTPFileProxy(FTPFile file) {
+ super(file.getName());
+ name = file.getName();
+ this.file = file;
+ parts = FileUtils.getPathStack(name);
+ }
+
+ /**
+ * creates a proxy to a FTP directory
+ * @param completePath the remote directory.
+ */
+ public FTPFileProxy(String completePath) {
+ super(completePath);
+ file = null;
+ name = completePath;
+ parts = FileUtils.getPathStack(completePath);
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#exists()
+ */
+ public boolean exists() {
+ return true;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#getAbsolutePath()
+ */
+ public String getAbsolutePath() {
+ return name;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#getName()
+ */
+ public String getName() {
+ return parts.length > 0 ? parts[parts.length - 1] : name;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#getParent()
+ */
+ public String getParent() {
+ String result = "";
+ for(int i = 0; i < parts.length - 1; i++){
+ result += File.separatorChar + parts[i];
+ }
+ return result;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#getPath()
+ */
+ public String getPath() {
+ return name;
+ }
+
+
+ /**
+ * FTP files are stored as absolute paths
+ * @return true
+ */
+ public boolean isAbsolute() {
+ return true;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#isDirectory()
+ */
+ public boolean isDirectory() {
+ return file == null;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#isFile()
+ */
+ public boolean isFile() {
+ return file != null;
+ }
+
+
+ /**
+ * FTP files cannot be hidden
+ *
+ * @return false
+ */
+ public boolean isHidden() {
+ return false;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#lastModified()
+ */
+ public long lastModified() {
+ if (file != null) {
+ return file.getTimestamp().getTimeInMillis();
+ }
+ return 0;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.io.File#length()
+ */
+ public long length() {
+ if (file != null) {
+ return file.getSize();
+ }
+ return 0;
+ }
+ }
+
+ /**
+ * internal class allowing to read the contents of a remote file system
+ * using the FTP protocol
+ * used in particular for ftp get operations
+ * differences with DirectoryScanner
+ * "" (the root of the fileset) is never included in the included directories
+ * followSymlinks defaults to false
+ */
+ protected class FTPDirectoryScanner extends DirectoryScanner {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected FTPClient ftp = null;
+ // CheckStyle:VisibilityModifier ON
+
+ private String rootPath = null;
+
+ /**
+ * since ant 1.6
+ * this flag should be set to true on UNIX and can save scanning time
+ */
+ private boolean remoteSystemCaseSensitive = false;
+ private boolean remoteSensitivityChecked = false;
+
+ /**
+ * constructor
+ * @param ftp ftpclient object
+ */
+ public FTPDirectoryScanner(FTPClient ftp) {
+ super();
+ this.ftp = ftp;
+ this.setFollowSymlinks(false);
+ }
+
+
+ /**
+ * scans the remote directory,
+ * storing internally the included files, directories, ...
+ */
+ public void scan() {
+ if (includes == null) {
+ // No includes supplied, so set it to 'matches all'
+ includes = new String[1];
+ includes[0] = "**";
+ }
+ if (excludes == null) {
+ excludes = new String[0];
+ }
+
+ filesIncluded = new VectorSet();
+ filesNotIncluded = new Vector();
+ filesExcluded = new VectorSet();
+ dirsIncluded = new VectorSet();
+ dirsNotIncluded = new Vector();
+ dirsExcluded = new VectorSet();
+
+ try {
+ String cwd = ftp.printWorkingDirectory();
+ // always start from the current ftp working dir
+ forceRemoteSensitivityCheck();
+
+ checkIncludePatterns();
+ clearCaches();
+ ftp.changeWorkingDirectory(cwd);
+ } catch (IOException e) {
+ throw new BuildException("Unable to scan FTP server: ", e);
+ }
+ }
+
+
+ /**
+ * this routine is actually checking all the include patterns in
+ * order to avoid scanning everything under base dir
+ * @since ant1.6
+ */
+ private void checkIncludePatterns() {
+
+ Hashtable newroots = new Hashtable();
+ // put in the newroots vector the include patterns without
+ // wildcard tokens
+ for (int icounter = 0; icounter < includes.length; icounter++) {
+ String newpattern =
+ SelectorUtils.rtrimWildcardTokens(includes[icounter]);
+ newroots.put(newpattern, includes[icounter]);
+ }
+ if (task.getRemotedir() == null) {
+ try {
+ task.setRemotedir(ftp.printWorkingDirectory());
+ } catch (IOException e) {
+ throw new BuildException("could not read current ftp directory",
+ task.getLocation());
+ }
+ }
+ AntFTPFile baseFTPFile = new AntFTPRootFile(ftp, task.getRemotedir());
+ rootPath = baseFTPFile.getAbsolutePath();
+ // construct it
+ if (newroots.containsKey("")) {
+ // we are going to scan everything anyway
+ scandir(rootPath, "", true);
+ } else {
+ // only scan directories that can include matched files or
+ // directories
+ Enumeration enum2 = newroots.keys();
+
+ while (enum2.hasMoreElements()) {
+ String currentelement = (String) enum2.nextElement();
+ String originalpattern = (String) newroots.get(currentelement);
+ AntFTPFile myfile = new AntFTPFile(baseFTPFile, currentelement);
+ boolean isOK = true;
+ boolean traversesSymlinks = false;
+ String path = null;
+
+ if (myfile.exists()) {
+ forceRemoteSensitivityCheck();
+ if (remoteSensitivityChecked
+ && remoteSystemCaseSensitive && isFollowSymlinks()) {
+ // cool case,
+ //we do not need to scan all the subdirs in the relative path
+ path = myfile.getFastRelativePath();
+ } else {
+ // may be on a case insensitive file system. We want
+ // the results to show what's really on the disk, so
+ // we need to double check.
+ try {
+ path = myfile.getRelativePath();
+ traversesSymlinks = myfile.isTraverseSymlinks();
+ } catch (IOException be) {
+ throw new BuildException(be, task.getLocation());
+ } catch (BuildException be) {
+ isOK = false;
+ }
+ }
+ } else {
+ isOK = false;
+ }
+ if (isOK) {
+ currentelement = path.replace(task.getSeparator().charAt(0), File.separatorChar);
+ if (!isFollowSymlinks()
+ && traversesSymlinks) {
+ continue;
+ }
+
+ if (myfile.isDirectory()) {
+ if (isIncluded(currentelement)
+ && currentelement.length() > 0) {
+ accountForIncludedDir(currentelement, myfile, true);
+ } else {
+ if (currentelement.length() > 0) {
+ if (currentelement.charAt(currentelement
+ .length() - 1)
+ != File.separatorChar) {
+ currentelement =
+ currentelement + File.separatorChar;
+ }
+ }
+ scandir(myfile.getAbsolutePath(), currentelement, true);
+ }
+ } else {
+ if (isCaseSensitive
+ && originalpattern.equals(currentelement)) {
+ accountForIncludedFile(currentelement);
+ } else if (!isCaseSensitive
+ && originalpattern
+ .equalsIgnoreCase(currentelement)) {
+ accountForIncludedFile(currentelement);
+ }
+ }
+ }
+ }
+ }
+ }
+ /**
+ * scans a particular directory. populates the scannedDirs cache.
+ *
+ * @param dir directory to scan
+ * @param vpath relative path to the base directory of the remote fileset
+ * always ended with a File.separator
+ * @param fast seems to be always true in practice
+ */
+ protected void scandir(String dir, String vpath, boolean fast) {
+ // avoid double scanning of directories, can only happen in fast mode
+ if (fast && hasBeenScanned(vpath)) {
+ return;
+ }
+ try {
+ if (!ftp.changeWorkingDirectory(dir)) {
+ return;
+ }
+ String completePath = null;
+ if (!vpath.equals("")) {
+ completePath = rootPath + task.getSeparator()
+ + vpath.replace(File.separatorChar, task.getSeparator().charAt(0));
+ } else {
+ completePath = rootPath;
+ }
+ FTPFile[] newfiles = listFiles(completePath, false);
+
+ if (newfiles == null) {
+ ftp.changeToParentDirectory();
+ return;
+ }
+ for (int i = 0; i < newfiles.length; i++) {
+ FTPFile file = newfiles[i];
+ if (file != null
+ && !file.getName().equals(".")
+ && !file.getName().equals("..")) {
+ String name = vpath + file.getName();
+ scannedDirs.put(name, new FTPFileProxy(file));
+ if (isFunctioningAsDirectory(ftp, dir, file)) {
+ boolean slowScanAllowed = true;
+ if (!isFollowSymlinks() && file.isSymbolicLink()) {
+ dirsExcluded.addElement(name);
+ slowScanAllowed = false;
+ } else if (isIncluded(name)) {
+ accountForIncludedDir(name,
+ new AntFTPFile(ftp, file, completePath) , fast);
+ } else {
+ dirsNotIncluded.addElement(name);
+ if (fast && couldHoldIncluded(name)) {
+ scandir(file.getName(),
+ name + File.separator, fast);
+ }
+ }
+ if (!fast && slowScanAllowed) {
+ scandir(file.getName(),
+ name + File.separator, fast);
+ }
+ } else {
+ if (!isFollowSymlinks() && file.isSymbolicLink()) {
+ filesExcluded.addElement(name);
+ } else if (isFunctioningAsFile(ftp, dir, file)) {
+ accountForIncludedFile(name);
+ }
+ }
+ }
+ }
+ ftp.changeToParentDirectory();
+ } catch (IOException e) {
+ throw new BuildException("Error while communicating with FTP "
+ + "server: ", e);
+ }
+ }
+ /**
+ * process included file
+ * @param name path of the file relative to the directory of the fileset
+ */
+ private void accountForIncludedFile(String name) {
+ if (!filesIncluded.contains(name)
+ && !filesExcluded.contains(name)) {
+
+ if (isIncluded(name)) {
+ if (!isExcluded(name)
+ && isSelected(name, (File) scannedDirs.get(name))) {
+ filesIncluded.addElement(name);
+ } else {
+ filesExcluded.addElement(name);
+ }
+ } else {
+ filesNotIncluded.addElement(name);
+ }
+ }
+ }
+
+ /**
+ *
+ * @param name path of the directory relative to the directory of
+ * the fileset
+ * @param file directory as file
+ * @param fast
+ */
+ private void accountForIncludedDir(String name, AntFTPFile file, boolean fast) {
+ if (!dirsIncluded.contains(name)
+ && !dirsExcluded.contains(name)) {
+
+ if (!isExcluded(name)) {
+ if (fast) {
+ if (file.isSymbolicLink()) {
+ try {
+ file.getClient().changeWorkingDirectory(file.curpwd);
+ } catch (IOException ioe) {
+ throw new BuildException("could not change directory to curpwd");
+ }
+ scandir(file.getLink(),
+ name + File.separator, fast);
+ } else {
+ try {
+ file.getClient().changeWorkingDirectory(file.curpwd);
+ } catch (IOException ioe) {
+ throw new BuildException("could not change directory to curpwd");
+ }
+ scandir(file.getName(),
+ name + File.separator, fast);
+ }
+ }
+ dirsIncluded.addElement(name);
+ } else {
+ dirsExcluded.addElement(name);
+ if (fast && couldHoldIncluded(name)) {
+ try {
+ file.getClient().changeWorkingDirectory(file.curpwd);
+ } catch (IOException ioe) {
+ throw new BuildException("could not change directory to curpwd");
+ }
+ scandir(file.getName(),
+ name + File.separator, fast);
+ }
+ }
+ }
+ }
+ /**
+ * temporary table to speed up the various scanning methods below
+ *
+ * @since Ant 1.6
+ */
+ private Map fileListMap = new HashMap();
+ /**
+ * List of all scanned directories.
+ *
+ * @since Ant 1.6
+ */
+
+ private Map scannedDirs = new HashMap();
+
+ /**
+ * Has the directory with the given path relative to the base
+ * directory already been scanned?
+ *
+ * @since Ant 1.6
+ */
+ private boolean hasBeenScanned(String vpath) {
+ return scannedDirs.containsKey(vpath);
+ }
+
+ /**
+ * Clear internal caches.
+ *
+ * @since Ant 1.6
+ */
+ private void clearCaches() {
+ fileListMap.clear();
+ scannedDirs.clear();
+ }
+ /**
+ * list the files present in one directory.
+ * @param directory full path on the remote side
+ * @param changedir if true change to directory directory before listing
+ * @return array of FTPFile
+ */
+ public FTPFile[] listFiles(String directory, boolean changedir) {
+ //task.log("listing files in directory " + directory, Project.MSG_DEBUG);
+ String currentPath = directory;
+ if (changedir) {
+ try {
+ boolean result = ftp.changeWorkingDirectory(directory);
+ if (!result) {
+ return null;
+ }
+ currentPath = ftp.printWorkingDirectory();
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, task.getLocation());
+ }
+ }
+ if (fileListMap.containsKey(currentPath)) {
+ task.log("filelist map used in listing files", Project.MSG_DEBUG);
+ return ((FTPFile[]) fileListMap.get(currentPath));
+ }
+ FTPFile[] result = null;
+ try {
+ result = ftp.listFiles();
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, task.getLocation());
+ }
+ fileListMap.put(currentPath, result);
+ if (!remoteSensitivityChecked) {
+ checkRemoteSensitivity(result, directory);
+ }
+ return result;
+ }
+
+ private void forceRemoteSensitivityCheck() {
+ if (!remoteSensitivityChecked) {
+ try {
+ checkRemoteSensitivity(ftp.listFiles(), ftp.printWorkingDirectory());
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, task.getLocation());
+ }
+ }
+ }
+ /**
+ * cd into one directory and
+ * list the files present in one directory.
+ * @param directory full path on the remote side
+ * @return array of FTPFile
+ */
+ public FTPFile[] listFiles(String directory) {
+ return listFiles(directory, true);
+ }
+ private void checkRemoteSensitivity(FTPFile[] array, String directory) {
+ if (array == null) {
+ return;
+ }
+ boolean candidateFound = false;
+ String target = null;
+ for (int icounter = 0; icounter < array.length; icounter++) {
+ if (array[icounter] != null && array[icounter].isDirectory()) {
+ if (!array[icounter].getName().equals(".")
+ && !array[icounter].getName().equals("..")) {
+ candidateFound = true;
+ target = fiddleName(array[icounter].getName());
+ task.log("will try to cd to "
+ + target + " where a directory called " + array[icounter].getName()
+ + " exists", Project.MSG_DEBUG);
+ for (int pcounter = 0; pcounter < array.length; pcounter++) {
+ if (array[pcounter] != null
+ && pcounter != icounter
+ && target.equals(array[pcounter].getName())) {
+ candidateFound = false;
+ break;
+ }
+ }
+ if (candidateFound) {
+ break;
+ }
+ }
+ }
+ }
+ if (candidateFound) {
+ try {
+ task.log("testing case sensitivity, attempting to cd to "
+ + target, Project.MSG_DEBUG);
+ remoteSystemCaseSensitive = !ftp.changeWorkingDirectory(target);
+ } catch (IOException ioe) {
+ remoteSystemCaseSensitive = true;
+ } finally {
+ try {
+ ftp.changeWorkingDirectory(directory);
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, task.getLocation());
+ }
+ }
+ task.log("remote system is case sensitive : "
+ + remoteSystemCaseSensitive,
+ Project.MSG_VERBOSE);
+ remoteSensitivityChecked = true;
+ }
+ }
+ private String fiddleName(String origin) {
+ StringBuffer result = new StringBuffer();
+ for (int icounter = 0; icounter < origin.length(); icounter++) {
+ if (Character.isLowerCase(origin.charAt(icounter))) {
+ result.append(Character.toUpperCase(origin.charAt(icounter)));
+ } else if (Character.isUpperCase(origin.charAt(icounter))) {
+ result.append(Character.toLowerCase(origin.charAt(icounter)));
+ } else {
+ result.append(origin.charAt(icounter));
+ }
+ }
+ return result.toString();
+ }
+ /**
+ * an AntFTPFile is a representation of a remote file
+ * @since Ant 1.6
+ */
+ protected class AntFTPFile {
+ /**
+ * ftp client
+ */
+ private FTPClient client;
+ /**
+ * parent directory of the file
+ */
+ private String curpwd;
+ /**
+ * the file itself
+ */
+ private FTPFile ftpFile;
+ /**
+ *
+ */
+ private AntFTPFile parent = null;
+ private boolean relativePathCalculated = false;
+ private boolean traversesSymlinks = false;
+ private String relativePath = "";
+ /**
+ * constructor
+ * @param client ftp client variable
+ * @param ftpFile the file
+ * @param curpwd absolute remote path where the file is found
+ */
+ public AntFTPFile(FTPClient client, FTPFile ftpFile, String curpwd) {
+ this.client = client;
+ this.ftpFile = ftpFile;
+ this.curpwd = curpwd;
+ }
+ /**
+ * other constructor
+ * @param parent the parent file
+ * @param path a relative path to the parent file
+ */
+ public AntFTPFile(AntFTPFile parent, String path) {
+ this.parent = parent;
+ this.client = parent.client;
+ Vector pathElements = SelectorUtils.tokenizePath(path);
+ try {
+ boolean result = this.client.changeWorkingDirectory(parent.getAbsolutePath());
+ //this should not happen, except if parent has been deleted by another process
+ if (!result) {
+ return;
+ }
+ this.curpwd = parent.getAbsolutePath();
+ } catch (IOException ioe) {
+ throw new BuildException("could not change working dir to "
+ + parent.curpwd);
+ }
+ final int size = pathElements.size();
+ for (int fcount = 0; fcount < size - 1; fcount++) {
+ String currentPathElement = (String) pathElements.elementAt(fcount);
+ try {
+ boolean result = this.client.changeWorkingDirectory(currentPathElement);
+ if (!result && !isCaseSensitive()
+ && (remoteSystemCaseSensitive || !remoteSensitivityChecked)) {
+ currentPathElement = findPathElementCaseUnsensitive(this.curpwd,
+ currentPathElement);
+ if (currentPathElement == null) {
+ return;
+ }
+ } else if (!result) {
+ return;
+ }
+ this.curpwd = getCurpwdPlusFileSep()
+ + currentPathElement;
+ } catch (IOException ioe) {
+ throw new BuildException("could not change working dir to "
+ + (String) pathElements.elementAt(fcount)
+ + " from " + this.curpwd);
+ }
+
+ }
+ String lastpathelement = (String) pathElements.elementAt(size - 1);
+ FTPFile [] theFiles = listFiles(this.curpwd);
+ this.ftpFile = getFile(theFiles, lastpathelement);
+ }
+ /**
+ * find a file in a directory in case unsensitive way
+ * @param parentPath where we are
+ * @param soughtPathElement what is being sought
+ * @return the first file found or null if not found
+ */
+ private String findPathElementCaseUnsensitive(String parentPath,
+ String soughtPathElement) {
+ // we are already in the right path, so the second parameter
+ // is false
+ FTPFile[] theFiles = listFiles(parentPath, false);
+ if (theFiles == null) {
+ return null;
+ }
+ for (int icounter = 0; icounter < theFiles.length; icounter++) {
+ if (theFiles[icounter] != null
+ && theFiles[icounter].getName().equalsIgnoreCase(soughtPathElement)) {
+ return theFiles[icounter].getName();
+ }
+ }
+ return null;
+ }
+ /**
+ * find out if the file exists
+ * @return true if the file exists
+ */
+ public boolean exists() {
+ return (ftpFile != null);
+ }
+ /**
+ * if the file is a symbolic link, find out to what it is pointing
+ * @return the target of the symbolic link
+ */
+ public String getLink() {
+ return ftpFile.getLink();
+ }
+ /**
+ * get the name of the file
+ * @return the name of the file
+ */
+ public String getName() {
+ return ftpFile.getName();
+ }
+ /**
+ * find out the absolute path of the file
+ * @return absolute path as string
+ */
+ public String getAbsolutePath() {
+ return getCurpwdPlusFileSep() + ftpFile.getName();
+ }
+ /**
+ * find out the relative path assuming that the path used to construct
+ * this AntFTPFile was spelled properly with regards to case.
+ * This is OK on a case sensitive system such as UNIX
+ * @return relative path
+ */
+ public String getFastRelativePath() {
+ String absPath = getAbsolutePath();
+ if (absPath.startsWith(rootPath + task.getSeparator())) {
+ return absPath.substring(rootPath.length()
+ + task.getSeparator().length());
+ }
+ return null;
+ }
+ /**
+ * find out the relative path to the rootPath of the enclosing scanner.
+ * this relative path is spelled exactly like on disk,
+ * for instance if the AntFTPFile has been instantiated as ALPHA,
+ * but the file is really called alpha, this method will return alpha.
+ * If a symbolic link is encountered, it is followed, but the name of the link
+ * rather than the name of the target is returned.
+ * (ie does not behave like File.getCanonicalPath())
+ * @return relative path, separated by remoteFileSep
+ * @throws IOException if a change directory fails, ...
+ * @throws BuildException if one of the components of the relative path cannot
+ * be found.
+ */
+ public String getRelativePath() throws IOException, BuildException {
+ if (!relativePathCalculated) {
+ if (parent != null) {
+ traversesSymlinks = parent.isTraverseSymlinks();
+ relativePath = getRelativePath(parent.getAbsolutePath(),
+ parent.getRelativePath());
+ } else {
+ relativePath = getRelativePath(rootPath, "");
+ relativePathCalculated = true;
+ }
+ }
+ return relativePath;
+ }
+ /**
+ * get the relative path of this file
+ * @param currentPath base path
+ * @param currentRelativePath relative path of the base path with regards to remote dir
+ * @return relative path
+ */
+ private String getRelativePath(String currentPath, String currentRelativePath) {
+ Vector pathElements = SelectorUtils.tokenizePath(getAbsolutePath(), task.getSeparator());
+ Vector pathElements2 = SelectorUtils.tokenizePath(currentPath,
+ task.getSeparator());
+ String relPath = currentRelativePath;
+ final int size = pathElements.size();
+ for (int pcount = pathElements2.size(); pcount < size; pcount++) {
+ String currentElement = (String) pathElements.elementAt(pcount);
+ FTPFile[] theFiles = listFiles(currentPath);
+ FTPFile theFile = null;
+ if (theFiles != null) {
+ theFile = getFile(theFiles, currentElement);
+ }
+ if (!relPath.equals("")) {
+ relPath = relPath + task.getSeparator();
+ }
+ if (theFile == null) {
+ // hit a hidden file assume not a symlink
+ relPath = relPath + currentElement;
+ currentPath = currentPath + task.getSeparator()
+ + currentElement;
+ task.log("Hidden file " + relPath
+ + " assumed to not be a symlink.",
+ Project.MSG_VERBOSE);
+ } else {
+ traversesSymlinks = traversesSymlinks || theFile.isSymbolicLink();
+ relPath = relPath + theFile.getName();
+ currentPath = currentPath + task.getSeparator()
+ + theFile.getName();
+ }
+ }
+ return relPath;
+ }
+ /**
+ * find a file matching a string in an array of FTPFile.
+ * This method will find "alpha" when requested for "ALPHA"
+ * if and only if the caseSensitive attribute is set to false.
+ * When caseSensitive is set to true, only the exact match is returned.
+ * @param theFiles array of files
+ * @param lastpathelement the file name being sought
+ * @return null if the file cannot be found, otherwise return the matching file.
+ */
+ public FTPFile getFile(FTPFile[] theFiles, String lastpathelement) {
+ if (theFiles == null) {
+ return null;
+ }
+ for (int fcount = 0; fcount < theFiles.length; fcount++) {
+ if (theFiles[fcount] != null) {
+ if (theFiles[fcount].getName().equals(lastpathelement)) {
+ return theFiles[fcount];
+ } else if (!isCaseSensitive()
+ && theFiles[fcount].getName().equalsIgnoreCase(
+ lastpathelement)) {
+ return theFiles[fcount];
+ }
+ }
+ }
+ return null;
+ }
+ /**
+ * tell if a file is a directory.
+ * note that it will return false for symbolic links pointing to directories.
+ * @return <code>true</code> for directories
+ */
+ public boolean isDirectory() {
+ return ftpFile.isDirectory();
+ }
+ /**
+ * tell if a file is a symbolic link
+ * @return <code>true</code> for symbolic links
+ */
+ public boolean isSymbolicLink() {
+ return ftpFile.isSymbolicLink();
+ }
+ /**
+ * return the attached FTP client object.
+ * Warning : this instance is really shared with the enclosing class.
+ * @return FTP client
+ */
+ protected FTPClient getClient() {
+ return client;
+ }
+
+ /**
+ * sets the current path of an AntFTPFile
+ * @param curpwd the current path one wants to set
+ */
+ protected void setCurpwd(String curpwd) {
+ this.curpwd = curpwd;
+ }
+ /**
+ * returns the path of the directory containing the AntFTPFile.
+ * of the full path of the file itself in case of AntFTPRootFile
+ * @return parent directory of the AntFTPFile
+ */
+ public String getCurpwd() {
+ return curpwd;
+ }
+ /**
+ * returns the path of the directory containing the AntFTPFile.
+ * of the full path of the file itself in case of AntFTPRootFile
+ * and appends the remote file separator if necessary.
+ * @return parent directory of the AntFTPFile
+ * @since Ant 1.8.2
+ */
+ public String getCurpwdPlusFileSep() {
+ String sep = task.getSeparator();
+ return curpwd.endsWith(sep) ? curpwd : curpwd + sep;
+ }
+ /**
+ * find out if a symbolic link is encountered in the relative path of this file
+ * from rootPath.
+ * @return <code>true</code> if a symbolic link is encountered in the relative path.
+ * @throws IOException if one of the change directory or directory listing operations
+ * fails
+ * @throws BuildException if a path component in the relative path cannot be found.
+ */
+ public boolean isTraverseSymlinks() throws IOException, BuildException {
+ if (!relativePathCalculated) {
+ // getRelativePath also finds about symlinks
+ getRelativePath();
+ }
+ return traversesSymlinks;
+ }
+
+ /**
+ * Get a string rep of this object.
+ * @return a string containing the pwd and the file.
+ */
+ public String toString() {
+ return "AntFtpFile: " + curpwd + "%" + ftpFile;
+ }
+ }
+ /**
+ * special class to represent the remote directory itself
+ * @since Ant 1.6
+ */
+ protected class AntFTPRootFile extends AntFTPFile {
+ private String remotedir;
+ /**
+ * constructor
+ * @param aclient FTP client
+ * @param remotedir remote directory
+ */
+ public AntFTPRootFile(FTPClient aclient, String remotedir) {
+ super(aclient, null, remotedir);
+ this.remotedir = remotedir;
+ try {
+ this.getClient().changeWorkingDirectory(this.remotedir);
+ this.setCurpwd(this.getClient().printWorkingDirectory());
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, task.getLocation());
+ }
+ }
+ /**
+ * find the absolute path
+ * @return absolute path
+ */
+ public String getAbsolutePath() {
+ return this.getCurpwd();
+ }
+ /**
+ * find out the relative path to root
+ * @return empty string
+ * @throws BuildException actually never
+ * @throws IOException actually never
+ */
+ public String getRelativePath() throws BuildException, IOException {
+ return "";
+ }
+ }
+ }
+ /**
+ * check FTPFiles to check whether they function as directories too
+ * the FTPFile API seem to make directory and symbolic links incompatible
+ * we want to find out if we can cd to a symbolic link
+ * @param dir the parent directory of the file to test
+ * @param file the file to test
+ * @return true if it is possible to cd to this directory
+ * @since ant 1.6
+ */
+ private boolean isFunctioningAsDirectory(FTPClient ftp, String dir, FTPFile file) {
+ boolean result = false;
+ String currentWorkingDir = null;
+ if (file.isDirectory()) {
+ return true;
+ } else if (file.isFile()) {
+ return false;
+ }
+ try {
+ currentWorkingDir = ftp.printWorkingDirectory();
+ } catch (IOException ioe) {
+ task.log("could not find current working directory " + dir
+ + " while checking a symlink", Project.MSG_DEBUG);
+ }
+ if (currentWorkingDir != null) {
+ try {
+ result = ftp.changeWorkingDirectory(file.getLink());
+ } catch (IOException ioe) {
+ task.log("could not cd to " + file.getLink()
+ + " while checking a symlink",
+ Project.MSG_DEBUG);
+ }
+ if (result) {
+ boolean comeback = false;
+ try {
+ comeback = ftp.changeWorkingDirectory(currentWorkingDir);
+ } catch (IOException ioe) {
+ task.log("could not cd back to " + dir + " while checking a symlink",
+ Project.MSG_ERR);
+ } finally {
+ if (!comeback) {
+ throw new BuildException("could not cd back to " + dir
+ + " while checking a symlink");
+ }
+ }
+ }
+ }
+ return result;
+ }
+ /**
+ * check FTPFiles to check whether they function as directories too
+ * the FTPFile API seem to make directory and symbolic links incompatible
+ * we want to find out if we can cd to a symbolic link
+ * @param dir the parent directory of the file to test
+ * @param file the file to test
+ * @return true if it is possible to cd to this directory
+ * @since ant 1.6
+ */
+ private boolean isFunctioningAsFile(FTPClient ftp, String dir, FTPFile file) {
+ if (file.isDirectory()) {
+ return false;
+ } else if (file.isFile()) {
+ return true;
+ }
+ return !isFunctioningAsDirectory(ftp, dir, file);
+ }
+
+ /**
+ * Executable a retryable object.
+ * @param h the retry handler.
+ * @param r the object that should be retried until it succeeds
+ * or the number of retrys is reached.
+ * @param descr a description of the command that is being run.
+ * @throws IOException if there is a problem.
+ */
+ protected void executeRetryable(RetryHandler h, Retryable r, String descr)
+ throws IOException {
+ h.execute(r, descr);
+ }
+
+
+ /**
+ * For each file in the fileset, do the appropriate action: send, get,
+ * delete, or list.
+ *
+ * @param ftp the FTPClient instance used to perform FTP actions
+ * @param fs the fileset on which the actions are performed.
+ *
+ * @return the number of files to be transferred.
+ *
+ * @throws IOException if there is a problem reading a file
+ * @throws BuildException if there is a problem in the configuration.
+ */
+ protected int transferFiles(final FTPClient ftp, FileSet fs)
+ throws IOException, BuildException {
+ DirectoryScanner ds;
+ if (task.getAction() == FTPTask.SEND_FILES) {
+ ds = fs.getDirectoryScanner(task.getProject());
+ } else {
+ ds = new FTPDirectoryScanner(ftp);
+ fs.setupDirectoryScanner(ds, task.getProject());
+ ds.setFollowSymlinks(fs.isFollowSymlinks());
+ ds.scan();
+ }
+
+ String[] dsfiles = null;
+ if (task.getAction() == FTPTask.RM_DIR) {
+ dsfiles = ds.getIncludedDirectories();
+ } else {
+ dsfiles = ds.getIncludedFiles();
+ }
+ String dir = null;
+
+ if ((ds.getBasedir() == null)
+ && ((task.getAction() == FTPTask.SEND_FILES) || (task.getAction() == FTPTask.GET_FILES))) {
+ throw new BuildException("the dir attribute must be set for send "
+ + "and get actions");
+ } else {
+ if ((task.getAction() == FTPTask.SEND_FILES) || (task.getAction() == FTPTask.GET_FILES)) {
+ dir = ds.getBasedir().getAbsolutePath();
+ }
+ }
+
+ // If we are doing a listing, we need the output stream created now.
+ BufferedWriter bw = null;
+
+ try {
+ if (task.getAction() == FTPTask.LIST_FILES) {
+ File pd = task.getListing().getParentFile();
+
+ if (!pd.exists()) {
+ pd.mkdirs();
+ }
+ bw = new BufferedWriter(new FileWriter(task.getListing()));
+ }
+ RetryHandler h = new RetryHandler(task.getRetriesAllowed(), task);
+ if (task.getAction() == FTPTask.RM_DIR) {
+ // to remove directories, start by the end of the list
+ // the trunk does not let itself be removed before the leaves
+ for (int i = dsfiles.length - 1; i >= 0; i--) {
+ final String dsfile = dsfiles[i];
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ rmDir(ftp, dsfile);
+ }
+ }, dsfile);
+ }
+ } else {
+ final BufferedWriter fbw = bw;
+ final String fdir = dir;
+ if (task.isNewer()) {
+ task.setGranularityMillis(task.getTimestampGranularity()
+ .getMilliseconds(task.getAction()));
+ }
+ for (int i = 0; i < dsfiles.length; i++) {
+ final String dsfile = dsfiles[i];
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ switch (task.getAction()) {
+ case FTPTask.SEND_FILES:
+ sendFile(ftp, fdir, dsfile);
+ break;
+ case FTPTask.GET_FILES:
+ getFile(ftp, fdir, dsfile);
+ break;
+ case FTPTask.DEL_FILES:
+ delFile(ftp, dsfile);
+ break;
+ case FTPTask.LIST_FILES:
+ listFile(ftp, fbw, dsfile);
+ break;
+ case FTPTask.CHMOD:
+ doSiteCommand(ftp, "chmod " + task.getChmod() + " "
+ + resolveFile(dsfile));
+ transferred++;
+ break;
+ default:
+ throw new BuildException("unknown ftp action "
+ + task.getAction());
+ }
+ }
+ }, dsfile);
+ }
+ }
+ } finally {
+ if (bw != null) {
+ bw.close();
+ }
+ }
+
+ return dsfiles.length;
+ }
+
+
+ /**
+ * Sends all files specified by the configured filesets to the remote
+ * server.
+ *
+ * @param ftp the FTPClient instance used to perform FTP actions
+ *
+ * @throws IOException if there is a problem reading a file
+ * @throws BuildException if there is a problem in the configuration.
+ */
+ protected void transferFiles(FTPClient ftp)
+ throws IOException, BuildException {
+ transferred = 0;
+ skipped = 0;
+
+ if (task.getFilesets().size() == 0) {
+ throw new BuildException("at least one fileset must be specified.");
+ } else {
+ // get files from filesets
+ final int size = task.getFilesets().size();
+ for (int i = 0; i < size; i++) {
+ FileSet fs = (FileSet) task.getFilesets().elementAt(i);
+
+ if (fs != null) {
+ transferFiles(ftp, fs);
+ }
+ }
+ }
+
+ task.log(transferred + " " + FTPTask.ACTION_TARGET_STRS[task.getAction()] + " "
+ + FTPTask.COMPLETED_ACTION_STRS[task.getAction()]);
+ if (skipped != 0) {
+ task.log(skipped + " " + FTPTask.ACTION_TARGET_STRS[task.getAction()]
+ + " were not successfully " + FTPTask.COMPLETED_ACTION_STRS[task.getAction()]);
+ }
+ }
+
+
+ /**
+ * Correct a file path to correspond to the remote host requirements. This
+ * implementation currently assumes that the remote end can handle
+ * Unix-style paths with forward-slash separators. This can be overridden
+ * with the <code>separator</code> task parameter. No attempt is made to
+ * determine what syntax is appropriate for the remote host.
+ *
+ * @param file the remote file name to be resolved
+ *
+ * @return the filename as it will appear on the server.
+ */
+ protected String resolveFile(String file) {
+ return file.replace(System.getProperty("file.separator").charAt(0),
+ task.getSeparator().charAt(0));
+ }
+
+
+ /**
+ * Creates all parent directories specified in a complete relative
+ * pathname. Attempts to create existing directories will not cause
+ * errors.
+ *
+ * @param ftp the FTP client instance to use to execute FTP actions on
+ * the remote server.
+ * @param filename the name of the file whose parents should be created.
+ * @throws IOException under non documented circumstances
+ * @throws BuildException if it is impossible to cd to a remote directory
+ *
+ */
+ protected void createParents(FTPClient ftp, String filename)
+ throws IOException, BuildException {
+
+ File dir = new File(filename);
+ if (dirCache.contains(dir)) {
+ return;
+ }
+
+ Vector parents = new Vector();
+ String dirname;
+
+ while ((dirname = dir.getParent()) != null) {
+ File checkDir = new File(dirname);
+ if (dirCache.contains(checkDir)) {
+ break;
+ }
+ dir = checkDir;
+ parents.addElement(dir);
+ }
+
+ // find first non cached dir
+ int i = parents.size() - 1;
+
+ if (i >= 0) {
+ String cwd = ftp.printWorkingDirectory();
+ String parent = dir.getParent();
+ if (parent != null) {
+ if (!ftp.changeWorkingDirectory(resolveFile(parent))) {
+ throw new BuildException("could not change to "
+ + "directory: " + ftp.getReplyString());
+ }
+ }
+
+ while (i >= 0) {
+ dir = (File) parents.elementAt(i--);
+ // check if dir exists by trying to change into it.
+ if (!ftp.changeWorkingDirectory(dir.getName())) {
+ // could not change to it - try to create it
+ task.log("creating remote directory "
+ + resolveFile(dir.getPath()), Project.MSG_VERBOSE);
+ if (!ftp.makeDirectory(dir.getName())) {
+ handleMkDirFailure(ftp);
+ }
+ if (!ftp.changeWorkingDirectory(dir.getName())) {
+ throw new BuildException("could not change to "
+ + "directory: " + ftp.getReplyString());
+ }
+ }
+ dirCache.add(dir);
+ }
+ ftp.changeWorkingDirectory(cwd);
+ }
+ }
+ /**
+ * auto find the time difference between local and remote
+ * @param ftp handle to ftp client
+ * @return number of millis to add to remote time to make it comparable to local time
+ * @since ant 1.6
+ */
+ private long getTimeDiff(FTPClient ftp) {
+ long returnValue = 0;
+ File tempFile = findFileName(ftp);
+ try {
+ // create a local temporary file
+ FILE_UTILS.createNewFile(tempFile);
+ long localTimeStamp = tempFile.lastModified();
+ BufferedInputStream instream = new BufferedInputStream(new FileInputStream(tempFile));
+ ftp.storeFile(tempFile.getName(), instream);
+ instream.close();
+ boolean success = FTPReply.isPositiveCompletion(ftp.getReplyCode());
+ if (success) {
+ FTPFile [] ftpFiles = ftp.listFiles(tempFile.getName());
+ if (ftpFiles.length == 1) {
+ long remoteTimeStamp = ftpFiles[0].getTimestamp().getTime().getTime();
+ returnValue = localTimeStamp - remoteTimeStamp;
+ }
+ ftp.deleteFile(ftpFiles[0].getName());
+ }
+ // delegate the deletion of the local temp file to the delete task
+ // because of race conditions occurring on Windows
+ Delete mydelete = new Delete();
+ mydelete.bindToOwner(task);
+ mydelete.setFile(tempFile.getCanonicalFile());
+ mydelete.execute();
+ } catch (Exception e) {
+ throw new BuildException(e, task.getLocation());
+ }
+ return returnValue;
+ }
+ /**
+ * find a suitable name for local and remote temporary file
+ */
+ private File findFileName(FTPClient ftp) {
+ FTPFile [] theFiles = null;
+ final int maxIterations = 1000;
+ for (int counter = 1; counter < maxIterations; counter++) {
+ File localFile = FILE_UTILS.createTempFile(
+ "ant" + Integer.toString(counter), ".tmp",
+ null, false, false);
+ String fileName = localFile.getName();
+ boolean found = false;
+ try {
+ if (theFiles == null) {
+ theFiles = ftp.listFiles();
+ }
+ for (int counter2 = 0; counter2 < theFiles.length; counter2++) {
+ if (theFiles[counter2] != null
+ && theFiles[counter2].getName().equals(fileName)) {
+ found = true;
+ break;
+ }
+ }
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, task.getLocation());
+ }
+ if (!found) {
+ localFile.deleteOnExit();
+ return localFile;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Checks to see if the remote file is current as compared with the local
+ * file. Returns true if the target file is up to date.
+ * @param ftp ftpclient
+ * @param localFile local file
+ * @param remoteFile remote file
+ * @return true if the target file is up to date
+ * @throws IOException in unknown circumstances
+ * @throws BuildException if the date of the remote files cannot be found and the action is
+ * GET_FILES
+ */
+ protected boolean isUpToDate(FTPClient ftp, File localFile,
+ String remoteFile)
+ throws IOException, BuildException {
+ task.log("checking date for " + remoteFile, Project.MSG_VERBOSE);
+
+ FTPFile[] files = ftp.listFiles(remoteFile);
+
+ // For Microsoft's Ftp-Service an Array with length 0 is
+ // returned if configured to return listings in "MS-DOS"-Format
+ if (files == null || files.length == 0) {
+ // If we are sending files, then assume out of date.
+ // If we are getting files, then throw an error
+
+ if (task.getAction() == FTPTask.SEND_FILES) {
+ task.log("Could not date test remote file: " + remoteFile
+ + "assuming out of date.", Project.MSG_VERBOSE);
+ return false;
+ } else {
+ throw new BuildException("could not date test remote file: "
+ + ftp.getReplyString());
+ }
+ }
+
+ long remoteTimestamp = files[0].getTimestamp().getTime().getTime();
+ long localTimestamp = localFile.lastModified();
+ long adjustedRemoteTimestamp = remoteTimestamp + task.getTimeDiffMillis()
+ + task.getGranularityMillis();
+
+ StringBuffer msg;
+ synchronized(TIMESTAMP_LOGGING_SDF) {
+ msg = new StringBuffer(" [")
+ .append(TIMESTAMP_LOGGING_SDF.format(new Date(localTimestamp)))
+ .append("] local");
+ }
+ task.log(msg.toString(), Project.MSG_VERBOSE);
+
+ synchronized(TIMESTAMP_LOGGING_SDF) {
+ msg = new StringBuffer(" [")
+ .append(TIMESTAMP_LOGGING_SDF.format(new Date(adjustedRemoteTimestamp)))
+ .append("] remote");
+ }
+ if (remoteTimestamp != adjustedRemoteTimestamp) {
+ synchronized(TIMESTAMP_LOGGING_SDF) {
+ msg.append(" - (raw: ")
+ .append(TIMESTAMP_LOGGING_SDF.format(new Date(remoteTimestamp)))
+ .append(")");
+ }
+ }
+ task.log(msg.toString(), Project.MSG_VERBOSE);
+
+ if (task.getAction() == FTPTask.SEND_FILES) {
+ return adjustedRemoteTimestamp >= localTimestamp;
+ } else {
+ return localTimestamp >= adjustedRemoteTimestamp;
+ }
+ }
+
+
+ /**
+ * Sends a site command to the ftp server
+ * @param ftp ftp client
+ * @param theCMD command to execute
+ * @throws IOException in unknown circumstances
+ * @throws BuildException in unknown circumstances
+ */
+ protected void doSiteCommand(FTPClient ftp, String theCMD)
+ throws IOException, BuildException {
+ boolean rc;
+ String[] myReply = null;
+
+ task.log("Doing Site Command: " + theCMD, Project.MSG_VERBOSE);
+
+ rc = ftp.sendSiteCommand(theCMD);
+
+ if (!rc) {
+ task.log("Failed to issue Site Command: " + theCMD, Project.MSG_WARN);
+ } else {
+
+ myReply = ftp.getReplyStrings();
+
+ for (int x = 0; x < myReply.length; x++) {
+ if (myReply[x].indexOf("200") == -1) {
+ task.log(myReply[x], Project.MSG_WARN);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Sends a single file to the remote host. <code>filename</code> may
+ * contain a relative path specification. When this is the case, <code>sendFile</code>
+ * will attempt to create any necessary parent directories before sending
+ * the file. The file will then be sent using the entire relative path
+ * spec - no attempt is made to change directories. It is anticipated that
+ * this may eventually cause problems with some FTP servers, but it
+ * simplifies the coding.
+ * @param ftp ftp client
+ * @param dir base directory of the file to be sent (local)
+ * @param filename relative path of the file to be send
+ * locally relative to dir
+ * remotely relative to the remotedir attribute
+ * @throws IOException in unknown circumstances
+ * @throws BuildException in unknown circumstances
+ */
+ protected void sendFile(FTPClient ftp, String dir, String filename)
+ throws IOException, BuildException {
+ InputStream instream = null;
+
+ try {
+ // TODO - why not simply new File(dir, filename)?
+ File file = task.getProject().resolveFile(new File(dir, filename).getPath());
+
+ if (task.isNewer() && isUpToDate(ftp, file, resolveFile(filename))) {
+ return;
+ }
+
+ if (task.isVerbose()) {
+ task.log("transferring " + file.getAbsolutePath());
+ }
+
+ instream = new BufferedInputStream(new FileInputStream(file));
+
+ createParents(ftp, filename);
+
+ ftp.storeFile(resolveFile(filename), instream);
+
+ boolean success = FTPReply.isPositiveCompletion(ftp.getReplyCode());
+
+ if (!success) {
+ String s = "could not put file: " + ftp.getReplyString();
+
+ if (task.isSkipFailedTransfers()) {
+ task.log(s, Project.MSG_WARN);
+ skipped++;
+ } else {
+ throw new BuildException(s);
+ }
+
+ } else {
+ // see if we should issue a chmod command
+ if (task.getChmod() != null) {
+ doSiteCommand(ftp, "chmod " + task.getChmod() + " "
+ + resolveFile(filename));
+ }
+ task.log("File " + file.getAbsolutePath() + " copied to " + task.getServer(),
+ Project.MSG_VERBOSE);
+ transferred++;
+ }
+ } finally {
+ if (instream != null) {
+ try {
+ instream.close();
+ } catch (IOException ex) {
+ // ignore it
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Delete a file from the remote host.
+ * @param ftp ftp client
+ * @param filename file to delete
+ * @throws IOException in unknown circumstances
+ * @throws BuildException if skipFailedTransfers is set to false
+ * and the deletion could not be done
+ */
+ protected void delFile(FTPClient ftp, String filename)
+ throws IOException, BuildException {
+ if (task.isVerbose()) {
+ task.log("deleting " + filename);
+ }
+
+ if (!ftp.deleteFile(resolveFile(filename))) {
+ String s = "could not delete file: " + ftp.getReplyString();
+
+ if (task.isSkipFailedTransfers()) {
+ task.log(s, Project.MSG_WARN);
+ skipped++;
+ } else {
+ throw new BuildException(s);
+ }
+ } else {
+ task.log("File " + filename + " deleted from " + task.getServer(),
+ Project.MSG_VERBOSE);
+ transferred++;
+ }
+ }
+
+ /**
+ * Delete a directory, if empty, from the remote host.
+ * @param ftp ftp client
+ * @param dirname directory to delete
+ * @throws IOException in unknown circumstances
+ * @throws BuildException if skipFailedTransfers is set to false
+ * and the deletion could not be done
+ */
+ protected void rmDir(FTPClient ftp, String dirname)
+ throws IOException, BuildException {
+ if (task.isVerbose()) {
+ task.log("removing " + dirname);
+ }
+
+ if (!ftp.removeDirectory(resolveFile(dirname))) {
+ String s = "could not remove directory: " + ftp.getReplyString();
+
+ if (task.isSkipFailedTransfers()) {
+ task.log(s, Project.MSG_WARN);
+ skipped++;
+ } else {
+ throw new BuildException(s);
+ }
+ } else {
+ task.log("Directory " + dirname + " removed from " + task.getServer(),
+ Project.MSG_VERBOSE);
+ transferred++;
+ }
+ }
+
+
+ /**
+ * Retrieve a single file from the remote host. <code>filename</code> may
+ * contain a relative path specification. <p>
+ *
+ * The file will then be retreived using the entire relative path spec -
+ * no attempt is made to change directories. It is anticipated that this
+ * may eventually cause problems with some FTP servers, but it simplifies
+ * the coding.</p>
+ * @param ftp the ftp client
+ * @param dir local base directory to which the file should go back
+ * @param filename relative path of the file based upon the ftp remote directory
+ * and/or the local base directory (dir)
+ * @throws IOException in unknown circumstances
+ * @throws BuildException if skipFailedTransfers is false
+ * and the file cannot be retrieved.
+ */
+ protected void getFile(FTPClient ftp, String dir, String filename)
+ throws IOException, BuildException {
+ OutputStream outstream = null;
+ try {
+ File file = task.getProject().resolveFile(new File(dir, filename).getPath());
+
+ if (task.isNewer() && isUpToDate(ftp, file, resolveFile(filename))) {
+ return;
+ }
+
+ if (task.isVerbose()) {
+ task.log("transferring " + filename + " to "
+ + file.getAbsolutePath());
+ }
+
+ File pdir = file.getParentFile();
+
+ if (!pdir.exists()) {
+ pdir.mkdirs();
+ }
+ outstream = new BufferedOutputStream(new FileOutputStream(file));
+ ftp.retrieveFile(resolveFile(filename), outstream);
+
+ if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+ String s = "could not get file: " + ftp.getReplyString();
+
+ if (task.isSkipFailedTransfers()) {
+ task.log(s, Project.MSG_WARN);
+ skipped++;
+ } else {
+ throw new BuildException(s);
+ }
+
+ } else {
+ task.log(
+ "File " + file.getAbsolutePath() + " copied from "
+ + task.getServer(), Project.MSG_VERBOSE);
+ transferred++;
+ if (task.isPreserveLastModified()) {
+ outstream.close();
+ outstream = null;
+ FTPFile[] remote = ftp.listFiles(resolveFile(filename));
+ if (remote.length > 0) {
+ FILE_UTILS.setFileLastModified(file,
+ remote[0].getTimestamp()
+ .getTime().getTime());
+ }
+ }
+ }
+ } finally {
+ if (outstream != null) {
+ try {
+ outstream.close();
+ } catch (IOException ex) {
+ // ignore it
+ }
+ }
+ }
+ }
+
+
+ /**
+ * List information about a single file from the remote host. <code>filename</code>
+ * may contain a relative path specification. <p>
+ *
+ * The file listing will then be retrieved using the entire relative path
+ * spec - no attempt is made to change directories. It is anticipated that
+ * this may eventually cause problems with some FTP servers, but it
+ * simplifies the coding.</p>
+ * @param ftp ftp client
+ * @param bw buffered writer
+ * @param filename the directory one wants to list
+ * @throws IOException in unknown circumstances
+ * @throws BuildException in unknown circumstances
+ */
+ protected void listFile(FTPClient ftp, BufferedWriter bw, String filename)
+ throws IOException, BuildException {
+ if (task.isVerbose()) {
+ task.log("listing " + filename);
+ }
+ FTPFile[] ftpfiles = ftp.listFiles(resolveFile(filename));
+
+ if (ftpfiles != null && ftpfiles.length > 0) {
+ bw.write(ftpfiles[0].toString());
+ bw.newLine();
+ transferred++;
+ }
+ }
+
+
+ /**
+ * Create the specified directory on the remote host.
+ *
+ * @param ftp The FTP client connection
+ * @param dir The directory to create (format must be correct for host
+ * type)
+ * @throws IOException in unknown circumstances
+ * @throws BuildException if ignoreNoncriticalErrors has not been set to true
+ * and a directory could not be created, for instance because it was
+ * already existing. Precisely, the codes 521, 550 and 553 will trigger
+ * a BuildException
+ */
+ protected void makeRemoteDir(FTPClient ftp, String dir)
+ throws IOException, BuildException {
+ String workingDirectory = ftp.printWorkingDirectory();
+ if (task.isVerbose()) {
+ if (dir.startsWith("/") || workingDirectory == null) {
+ task.log("Creating directory: " + dir + " in /");
+ } else {
+ task.log("Creating directory: " + dir + " in " + workingDirectory);
+ }
+ }
+ if (dir.startsWith("/")) {
+ ftp.changeWorkingDirectory("/");
+ }
+ String subdir = "";
+ StringTokenizer st = new StringTokenizer(dir, "/");
+ while (st.hasMoreTokens()) {
+ subdir = st.nextToken();
+ task.log("Checking " + subdir, Project.MSG_DEBUG);
+ if (!ftp.changeWorkingDirectory(subdir)) {
+ if (!ftp.makeDirectory(subdir)) {
+ // codes 521, 550 and 553 can be produced by FTP Servers
+ // to indicate that an attempt to create a directory has
+ // failed because the directory already exists.
+ int rc = ftp.getReplyCode();
+ if (!(task.isIgnoreNoncriticalErrors() && (rc == CODE_550
+ || rc == CODE_553
+ || rc == CODE_521))) {
+ throw new BuildException("could not create directory: "
+ + ftp.getReplyString());
+ }
+ if (task.isVerbose()) {
+ task.log("Directory already exists");
+ }
+ } else {
+ if (task.isVerbose()) {
+ task.log("Directory created OK");
+ }
+ ftp.changeWorkingDirectory(subdir);
+ }
+ }
+ }
+ if (workingDirectory != null) {
+ ftp.changeWorkingDirectory(workingDirectory);
+ }
+ }
+
+ /**
+ * look at the response for a failed mkdir action, decide whether
+ * it matters or not. If it does, we throw an exception
+ * @param ftp current ftp connection
+ * @throws BuildException if this is an error to signal
+ */
+ private void handleMkDirFailure(FTPClient ftp)
+ throws BuildException {
+ int rc = ftp.getReplyCode();
+ if (!(task.isIgnoreNoncriticalErrors() && (rc == CODE_550
+ || rc == CODE_553
+ || rc == CODE_521))) {
+ throw new BuildException("could not create directory: "
+ + ftp.getReplyString());
+ }
+ }
+
+ public void doFTP() throws BuildException {
+ FTPClient ftp = null;
+
+ try {
+ task.log("Opening FTP connection to " + task.getServer(), Project.MSG_VERBOSE);
+
+ ftp = new FTPClient();
+ if (task.isConfigurationSet()) {
+ ftp = FTPConfigurator.configure(ftp, task);
+ }
+
+ ftp.setRemoteVerificationEnabled(task.getEnableRemoteVerification());
+ ftp.connect(task.getServer(), task.getPort());
+ if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+ throw new BuildException("FTP connection failed: "
+ + ftp.getReplyString());
+ }
+
+ task.log("connected", Project.MSG_VERBOSE);
+ task.log("logging in to FTP server", Project.MSG_VERBOSE);
+
+ if ((task.getAccount() != null && !ftp.login(task.getUserid(), task.getPassword(), task.getAccount()))
+ || (task.getAccount() == null && !ftp.login(task.getUserid(), task.getPassword()))) {
+ throw new BuildException("Could not login to FTP server");
+ }
+
+ task.log("login succeeded", Project.MSG_VERBOSE);
+
+ if (task.isBinary()) {
+ ftp.setFileType(org.apache.commons.net.ftp.FTP.BINARY_FILE_TYPE);
+ if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+ throw new BuildException("could not set transfer type: "
+ + ftp.getReplyString());
+ }
+ } else {
+ ftp.setFileType(org.apache.commons.net.ftp.FTP.ASCII_FILE_TYPE);
+ if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+ throw new BuildException("could not set transfer type: "
+ + ftp.getReplyString());
+ }
+ }
+
+ if (task.isPassive()) {
+ task.log("entering passive mode", Project.MSG_VERBOSE);
+ ftp.enterLocalPassiveMode();
+ if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+ throw new BuildException("could not enter into passive "
+ + "mode: " + ftp.getReplyString());
+ }
+ }
+
+ // If an initial command was configured then send it.
+ // Some FTP servers offer different modes of operation,
+ // E.G. switching between a UNIX file system mode and
+ // a legacy file system.
+ if (task.getInitialSiteCommand() != null) {
+ RetryHandler h = new RetryHandler(task.getRetriesAllowed(), task);
+ final FTPClient lftp = ftp;
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ doSiteCommand(lftp, task.getInitialSiteCommand());
+ }
+ }, "initial site command: " + task.getInitialSiteCommand());
+ }
+
+
+ // For a unix ftp server you can set the default mask for all files
+ // created.
+
+ if (task.getUmask() != null) {
+ RetryHandler h = new RetryHandler(task.getRetriesAllowed(), task);
+ final FTPClient lftp = ftp;
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ doSiteCommand(lftp, "umask " + task.getUmask());
+ }
+ }, "umask " + task.getUmask());
+ }
+
+ // If the action is MK_DIR, then the specified remote
+ // directory is the directory to create.
+
+ if (task.getAction() == FTPTask.MK_DIR) {
+ RetryHandler h = new RetryHandler(task.getRetriesAllowed(), task);
+ final FTPClient lftp = ftp;
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ makeRemoteDir(lftp, task.getRemotedir());
+ }
+ }, task.getRemotedir());
+ } else if (task.getAction() == FTPTask.SITE_CMD) {
+ RetryHandler h = new RetryHandler(task.getRetriesAllowed(), task);
+ final FTPClient lftp = ftp;
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ doSiteCommand(lftp, task.getSiteCommand());
+ }
+ }, "Site Command: " + task.getSiteCommand());
+ } else {
+ if (task.getRemotedir() != null) {
+ task.log("changing the remote directory", Project.MSG_VERBOSE);
+ ftp.changeWorkingDirectory(task.getRemotedir());
+ if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+ throw new BuildException("could not change remote "
+ + "directory: " + ftp.getReplyString());
+ }
+ }
+ if (task.isNewer() && task.isTimeDiffAuto()) {
+ // in this case we want to find how much time span there is between local
+ // and remote
+ task.setTimeDiffMillis(getTimeDiff(ftp));
+ }
+ task.log(FTPTask.ACTION_STRS[task.getAction()] + " " + FTPTask.ACTION_TARGET_STRS[task.getAction()]);
+ transferFiles(ftp);
+ }
+
+ } catch (IOException ex) {
+ throw new BuildException("error during FTP transfer: " + ex, ex);
+ } finally {
+ if (ftp != null && ftp.isConnected()) {
+ try {
+ task.log("disconnecting", Project.MSG_VERBOSE);
+ ftp.logout();
+ ftp.disconnect();
+ } catch (IOException ex) {
+ // ignore it
+ }
+ }
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/MimeMail.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/MimeMail.java
new file mode 100644
index 00000000..fca4215e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/MimeMail.java
@@ -0,0 +1,43 @@
+/*
+ * 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.tools.ant.taskdefs.optional.net;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.email.EmailTask;
+
+/**
+ * A task to send SMTP email; Use <tt>mail</tt> instead
+ *
+ * @deprecated since 1.6.x.
+ * Use {@link EmailTask} instead.
+ *
+ * @since Ant1.4
+ */
+public class MimeMail extends EmailTask {
+ /**
+ * Executes this build task.
+ *
+ * @exception BuildException On error.
+ */
+ public void execute()
+ throws BuildException {
+ log("DEPRECATED - The " + getTaskName() + " task is deprecated. "
+ + "Use the mail task instead.");
+ super.execute();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/RExecTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/RExecTask.java
new file mode 100644
index 00000000..01cb4ba9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/RExecTask.java
@@ -0,0 +1,479 @@
+/*
+ * 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.tools.ant.taskdefs.optional.net;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Calendar;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.commons.net.bsd.RExecClient;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Automates the rexec protocol.
+ *
+ * @since Ant 1.6
+ */
+
+public class RExecTask extends Task {
+
+ private static final int PAUSE_TIME = 250;
+
+ /**
+ * The userid to login with, if automated login is used
+ */
+ private String userid = null;
+
+ /**
+ * The password to login with, if automated login is used
+ */
+ private String password = null;
+
+ /**
+ * The command to execute
+ */
+ private String command = null;
+
+ /**
+ * The server to connect to.
+ */
+ private String server = null;
+
+ /**
+ * The tcp port to connect to.
+ */
+ private int port = RExecClient.DEFAULT_PORT;
+
+ /**
+ * The list of read/write commands for this session
+ */
+ private Vector rexecTasks = new Vector();
+
+ /**
+ * If true, adds a CR to beginning of login script
+ */
+ private boolean addCarriageReturn = false;
+
+ /**
+ * Default time allowed for waiting for a valid response
+ * for all child reads. A value of 0 means no limit.
+ */
+ private Integer defaultTimeout = null;
+
+ /**
+ * This class is the parent of the Read and Write tasks.
+ * It handles the common attributes for both.
+ */
+ public class RExecSubTask {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected String taskString = "";
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Execute the subtask.
+ * @param rexec the client
+ * @throws BuildException always as it is not allowed to instantiate this object
+ */
+ public void execute(AntRExecClient rexec)
+ throws BuildException {
+ throw new BuildException("Shouldn't be able instantiate a SubTask directly");
+ }
+
+ /**
+ * the message as nested text
+ * @param s the nested text
+ */
+ public void addText(String s) {
+ setString(getProject().replaceProperties(s));
+ }
+
+ /**
+ * the message as an attribute
+ * @param s a <code>String</code> value
+ */
+ public void setString(String s) {
+ taskString += s;
+ }
+ }
+
+ /**
+ * Sends text to the connected server
+ */
+ public class RExecWrite extends RExecSubTask {
+ private boolean echoString = true;
+ /**
+ * Execute the write exec task.
+ * @param rexec the task to use
+ * @throws BuildException on error
+ */
+ public void execute(AntRExecClient rexec)
+ throws BuildException {
+ rexec.sendString(taskString, echoString);
+ }
+
+ /**
+ * Whether or not the message should be echoed to the log.
+ * Defaults to <code>true</code>.
+ * @param b a <code>boolean</code> value
+ */
+ public void setEcho(boolean b) {
+ echoString = b;
+ }
+ }
+
+ /**
+ * Reads the output from the connected server
+ * until the required string is found or we time out.
+ */
+ public class RExecRead extends RExecSubTask {
+ private Integer timeout = null;
+ /**
+ * Execute the read exec task.
+ * @param rexec the task to use
+ * @throws BuildException on error
+ */
+ public void execute(AntRExecClient rexec)
+ throws BuildException {
+ rexec.waitForString(taskString, timeout);
+ }
+ /**
+ * a timeout value that overrides any task wide timeout.
+ * @param i an <code>Integer</code> value
+ */
+ public void setTimeout(Integer i) {
+ this.timeout = i;
+ }
+
+ /**
+ * Sets the default timeout if none has been set already
+ * @param defaultTimeout an <code>Integer</code> value
+ * @ant.attribute ignore="true"
+ */
+ public void setDefaultTimeout(Integer defaultTimeout) {
+ if (timeout == null) {
+ timeout = defaultTimeout;
+ }
+ }
+ }
+
+ /**
+ * This class handles the abstraction of the rexec protocol.
+ * Currently it is a wrapper around <a
+ * href="http://jakarta.apache.org/commons/net/index.html">Jakarta
+ * Commons Net</a>.
+ */
+ public class AntRExecClient extends RExecClient {
+ /**
+ * Read from the rexec session until the string we are
+ * waiting for is found
+ * @param s The string to wait on
+ */
+ public void waitForString(String s) {
+ waitForString(s, null);
+ }
+
+ /**
+ * Read from the rexec session until the string we are
+ * waiting for is found or the timeout has been reached
+ * @param s The string to wait on
+ * @param timeout The maximum number of seconds to wait
+ */
+ public void waitForString(String s, Integer timeout) {
+ InputStream is = this.getInputStream();
+ try {
+ StringBuffer sb = new StringBuffer();
+ int windowStart = -s.length();
+ if (timeout == null || timeout.intValue() == 0) {
+ while (windowStart < 0
+ || !sb.substring(windowStart).equals(s)) {
+ sb.append((char) is.read());
+ windowStart++;
+ }
+ } else {
+ Calendar endTime = Calendar.getInstance();
+ endTime.add(Calendar.SECOND, timeout.intValue());
+ while (windowStart < 0
+ || !sb.substring(windowStart).equals(s)) {
+ while (Calendar.getInstance().before(endTime)
+ && is.available() == 0) {
+ Thread.sleep(PAUSE_TIME);
+ }
+ if (is.available() == 0) {
+ throw new BuildException(
+ "Response timed-out waiting for \"" + s + '\"',
+ getLocation());
+ }
+ sb.append((char) is.read());
+ windowStart++;
+ }
+ }
+ log(sb.toString(), Project.MSG_INFO);
+ } catch (BuildException be) {
+ throw be;
+ } catch (Exception e) {
+ throw new BuildException(e, getLocation());
+ }
+ }
+
+ /**
+ * Write this string to the rexec session.
+ * @param s the string to write
+ * @param echoString if true log the string sent
+ */
+ public void sendString(String s, boolean echoString) {
+ OutputStream os = this.getOutputStream();
+ try {
+ os.write((s + "\n").getBytes());
+ if (echoString) {
+ log(s, Project.MSG_INFO);
+ }
+ os.flush();
+ } catch (Exception e) {
+ throw new BuildException(e, getLocation());
+ }
+ }
+ /**
+ * Read from the rexec session until the EOF is found or
+ * the timeout has been reached
+ * @param timeout The maximum number of seconds to wait
+ */
+ public void waitForEOF(Integer timeout) {
+ InputStream is = this.getInputStream();
+ try {
+ StringBuffer sb = new StringBuffer();
+ if (timeout == null || timeout.intValue() == 0) {
+ int read;
+ while ((read = is.read()) != -1) {
+ char c = (char) read;
+ sb.append(c);
+ if (c == '\n') {
+ log(sb.toString(), Project.MSG_INFO);
+ sb.delete(0, sb.length());
+ }
+ }
+ } else {
+ Calendar endTime = Calendar.getInstance();
+ endTime.add(Calendar.SECOND, timeout.intValue());
+ int read = 0;
+ while (read != -1) {
+ while (Calendar.getInstance().before(endTime) && is.available() == 0) {
+ Thread.sleep(PAUSE_TIME);
+ }
+ if (is.available() == 0) {
+ log(sb.toString(), Project.MSG_INFO);
+ throw new BuildException(
+ "Response timed-out waiting for EOF",
+ getLocation());
+ }
+ read = is.read();
+ if (read != -1) {
+ char c = (char) read;
+ sb.append(c);
+ if (c == '\n') {
+ log(sb.toString(), Project.MSG_INFO);
+ sb.delete(0, sb.length());
+ }
+ }
+ }
+ }
+ if (sb.length() > 0) {
+ log(sb.toString(), Project.MSG_INFO);
+ }
+ } catch (BuildException be) {
+ throw be;
+ } catch (Exception e) {
+ throw new BuildException(e, getLocation());
+ }
+ }
+
+ }
+ /**
+ * A string to wait for from the server.
+ * A subTask &lt;read&gt; tag was found. Create the object,
+ * Save it in our list, and return it.
+ * @return a read sub task
+ */
+
+ public RExecSubTask createRead() {
+ RExecSubTask task = (RExecSubTask) new RExecRead();
+ rexecTasks.addElement(task);
+ return task;
+ }
+ /**
+ * Add text to send to the server
+ * A subTask &lt;write&gt; tag was found. Create the object,
+ * Save it in our list, and return it.
+ * @return a write sub task
+ */
+ public RExecSubTask createWrite() {
+ RExecSubTask task = (RExecSubTask) new RExecWrite();
+ rexecTasks.addElement(task);
+ return task;
+ }
+ /**
+ * Verify that all parameters are included.
+ * Connect and possibly login.
+ * Iterate through the list of Reads and writes.
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ /** A server name is required to continue */
+ if (server == null) {
+ throw new BuildException("No Server Specified");
+ }
+ /** A userid and password must appear together
+ * if they appear. They are not required.
+ */
+ if (userid == null && password != null) {
+ throw new BuildException("No Userid Specified");
+ }
+ if (password == null && userid != null) {
+ throw new BuildException("No Password Specified");
+ }
+
+ /** Create the telnet client object */
+ AntRExecClient rexec = null;
+ try {
+ rexec = new AntRExecClient();
+ try {
+ rexec.connect(server, port);
+ } catch (IOException e) {
+ throw new BuildException("Can't connect to " + server);
+ }
+ if (userid != null && password != null && command != null
+ && rexecTasks.size() == 0) {
+ // simple one-shot execution
+ rexec.rexec(userid, password, command);
+ } else {
+ // need nested read/write elements
+ handleMultipleTasks(rexec);
+ }
+
+ /** Keep reading input stream until end of it or time-out */
+ rexec.waitForEOF(defaultTimeout);
+ } catch (IOException e) {
+ throw new BuildException("Error r-executing command", e);
+ } finally {
+ if (rexec != null && rexec.isConnected()) {
+ try {
+ rexec.disconnect();
+ } catch (IOException e) {
+ throw new BuildException("Error disconnecting from "
+ + server);
+ }
+ }
+ }
+ }
+ /**
+ * Process a 'typical' login. If it differs, use the read
+ * and write tasks explicitly
+ */
+ private void login(AntRExecClient rexec) {
+ if (addCarriageReturn) {
+ rexec.sendString("\n", true);
+ }
+ rexec.waitForString("ogin:");
+ rexec.sendString(userid, true);
+ rexec.waitForString("assword:");
+ rexec.sendString(password, false);
+ }
+ /**
+ * Set the the command to execute on the server;
+ * @param c a <code>String</code> value
+ */
+ public void setCommand(String c) {
+ this.command = c;
+ }
+
+ /**
+ * send a carriage return after connecting; optional, defaults to false.
+ * @param b a <code>boolean</code> value
+ */
+ public void setInitialCR(boolean b) {
+ this.addCarriageReturn = b;
+ }
+ /**
+ * Set the the login password to use
+ * required if <tt>userid</tt> is set.
+ * @param p a <code>String</code> value
+ */
+ public void setPassword(String p) {
+ this.password = p;
+ }
+
+ /**
+ * Set the tcp port to connect to; default is 23.
+ * @param p an <code>int</code> value
+ */
+ public void setPort(int p) {
+ this.port = p;
+ }
+
+ /**
+ * Set the hostname or address of the remote server.
+ * @param m a <code>String</code> value
+ */
+ public void setServer(String m) {
+ this.server = m;
+ }
+
+ /**
+ * set a default timeout in seconds to wait for a response,
+ * zero means forever (the default)
+ * @param i an <code>Integer</code> value
+ */
+ public void setTimeout(Integer i) {
+ this.defaultTimeout = i;
+ }
+ /**
+ * Set the the login id to use on the server;
+ * required if <tt>password</tt> is set.
+ * @param u a <code>String</code> value
+ */
+ public void setUserid(String u) {
+ this.userid = u;
+ }
+
+ /**
+ * Deals with multiple read/write calls.
+ *
+ * @since Ant 1.6.3
+ */
+ private void handleMultipleTasks(AntRExecClient rexec) {
+
+ /** Login if userid and password were specified */
+ if (userid != null && password != null) {
+ login(rexec);
+ }
+ /** Process each sub command */
+ Enumeration tasksToRun = rexecTasks.elements();
+ while (tasksToRun != null && tasksToRun.hasMoreElements()) {
+ RExecSubTask task = (RExecSubTask) tasksToRun.nextElement();
+ if (task instanceof RExecRead && defaultTimeout != null) {
+ ((RExecRead) task).setDefaultTimeout(defaultTimeout);
+ }
+ task.execute(rexec);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/SetProxy.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/SetProxy.java
new file mode 100644
index 00000000..1e1f6591
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/SetProxy.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.tools.ant.taskdefs.optional.net;
+
+import java.net.Authenticator;
+import java.net.PasswordAuthentication;
+import java.util.Properties;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.ProxySetup;
+
+/**
+ * Sets Java's web proxy properties, so that tasks and code run in
+ * the same JVM can have through-the-firewall access to remote web sites,
+ * and remote ftp sites.
+ * You can nominate an http and ftp proxy, or a socks server, reset the server
+ * settings, or do nothing at all.
+ * <p>
+ * Examples
+ * <pre>&lt;setproxy/&gt;</pre>
+ * do nothing
+ * <pre>&lt;setproxy proxyhost="firewall"/&gt;</pre>
+ * set the proxy to firewall:80
+ * <pre>&lt;setproxy proxyhost="firewall" proxyport="81"/&gt;</pre>
+ * set the proxy to firewall:81
+ * <pre>&lt;setproxy proxyhost=""/&gt;</pre>
+ * stop using the http proxy; don't change the socks settings
+ * <pre>&lt;setproxy socksproxyhost="socksy"/&gt;</pre>
+ * use socks via socksy:1080
+ * <pre>&lt;setproxy socksproxyhost=""/&gt;</pre>
+ * stop using the socks server.
+ * <p>
+ * You can set a username and password for http with the <tt>proxyHost</tt>
+ * and <tt>proxyPassword</tt> attributes. These can also be
+ * used against SOCKS5 servers.
+ * </p>
+ * @see <a href="http://java.sun.com/j2se/1.5.0/docs/guide/net/properties.html">
+ * java 1.5 network property list</a>
+ *@since Ant 1.5
+ * @ant.task category="network"
+ */
+public class SetProxy extends Task {
+ private static final int HTTP_PORT = 80;
+ private static final int SOCKS_PORT = 1080;
+ // CheckStyle:VisibilityModifier OFF - bc
+ /**
+ * proxy details
+ */
+ protected String proxyHost = null;
+
+ /**
+ * name of proxy port
+ */
+ protected int proxyPort = HTTP_PORT;
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * socks host.
+ */
+ private String socksProxyHost = null;
+
+ /**
+ * Socks proxy port. Default is 1080.
+ */
+ private int socksProxyPort = SOCKS_PORT;
+
+
+ /**
+ * list of non proxy hosts
+ */
+ private String nonProxyHosts = null;
+
+ /**
+ * user for http only
+ */
+ private String proxyUser = null;
+
+ /**
+ * password for http only
+ */
+ private String proxyPassword = null;
+
+ /**
+ * the HTTP/ftp proxy host. Set this to "" for the http proxy
+ * option to be disabled
+ *
+ * @param hostname the new proxy hostname
+ */
+ public void setProxyHost(String hostname) {
+ proxyHost = hostname;
+ }
+
+
+ /**
+ * the HTTP/ftp proxy port number; default is 80
+ *
+ * @param port port number of the proxy
+ */
+ public void setProxyPort(int port) {
+ proxyPort = port;
+ }
+
+ /**
+ * The name of a Socks server. Set to "" to turn socks
+ * proxying off.
+ *
+ * @param host The new SocksProxyHost value
+ */
+ public void setSocksProxyHost(String host) {
+ this.socksProxyHost = host;
+ }
+
+
+ /**
+ * Set the ProxyPort for socks connections. The default value is 1080
+ *
+ * @param port The new SocksProxyPort value
+ */
+ public void setSocksProxyPort(int port) {
+ this.socksProxyPort = port;
+ }
+
+
+ /**
+ * A list of hosts to bypass the proxy on. These should be separated
+ * with the vertical bar character '|'. Only in Java 1.4 does ftp use
+ * this list.
+ * e.g. fozbot.corp.sun.com|*.eng.sun.com
+ * @param nonProxyHosts lists of hosts to talk direct to
+ */
+ public void setNonProxyHosts(String nonProxyHosts) {
+ this.nonProxyHosts = nonProxyHosts;
+ }
+
+ /**
+ * set the proxy user. Probably requires a password to accompany this
+ * setting. Default=""
+ * @param proxyUser username
+ * @since Ant1.6
+ */
+ public void setProxyUser(String proxyUser) {
+ this.proxyUser = proxyUser;
+ }
+
+ /**
+ * Set the password for the proxy. Used only if the proxyUser is set.
+ * @param proxyPassword password to go with the username
+ * @since Ant1.6
+ */
+ public void setProxyPassword(String proxyPassword) {
+ this.proxyPassword = proxyPassword;
+ }
+
+ /**
+ * if the proxy port and host settings are not null, then the settings
+ * get applied these settings last beyond the life of the object and
+ * apply to all network connections
+ * Relevant docs: buglist #4183340
+ */
+
+ public void applyWebProxySettings() {
+ boolean settingsChanged = false;
+ boolean enablingProxy = false;
+ Properties sysprops = System.getProperties();
+ if (proxyHost != null) {
+ settingsChanged = true;
+ if (proxyHost.length() != 0) {
+ traceSettingInfo();
+ enablingProxy = true;
+ sysprops.put(ProxySetup.HTTP_PROXY_HOST, proxyHost);
+ String portString = Integer.toString(proxyPort);
+ sysprops.put(ProxySetup.HTTP_PROXY_PORT, portString);
+ sysprops.put(ProxySetup.HTTPS_PROXY_HOST, proxyHost);
+ sysprops.put(ProxySetup.HTTPS_PROXY_PORT, portString);
+ sysprops.put(ProxySetup.FTP_PROXY_HOST, proxyHost);
+ sysprops.put(ProxySetup.FTP_PROXY_PORT, portString);
+ if (nonProxyHosts != null) {
+ sysprops.put(ProxySetup.HTTP_NON_PROXY_HOSTS, nonProxyHosts);
+ sysprops.put(ProxySetup.HTTPS_NON_PROXY_HOSTS, nonProxyHosts);
+ sysprops.put(ProxySetup.FTP_NON_PROXY_HOSTS, nonProxyHosts);
+ }
+ if (proxyUser != null) {
+ sysprops.put(ProxySetup.HTTP_PROXY_USERNAME, proxyUser);
+ sysprops.put(ProxySetup.HTTP_PROXY_PASSWORD, proxyPassword);
+ }
+ } else {
+ log("resetting http proxy", Project.MSG_VERBOSE);
+ sysprops.remove(ProxySetup.HTTP_PROXY_HOST);
+ sysprops.remove(ProxySetup.HTTP_PROXY_PORT);
+ sysprops.remove(ProxySetup.HTTP_PROXY_USERNAME);
+ sysprops.remove(ProxySetup.HTTP_PROXY_PASSWORD);
+ sysprops.remove(ProxySetup.HTTPS_PROXY_HOST);
+ sysprops.remove(ProxySetup.HTTPS_PROXY_PORT);
+ sysprops.remove(ProxySetup.FTP_PROXY_HOST);
+ sysprops.remove(ProxySetup.FTP_PROXY_PORT);
+ }
+ }
+
+ //socks
+ if (socksProxyHost != null) {
+ settingsChanged = true;
+ if (socksProxyHost.length() != 0) {
+ enablingProxy = true;
+ sysprops.put(ProxySetup.SOCKS_PROXY_HOST, socksProxyHost);
+ sysprops.put(ProxySetup.SOCKS_PROXY_PORT, Integer.toString(socksProxyPort));
+ if (proxyUser != null) {
+ //this may be a java1.4 thingy only
+ sysprops.put(ProxySetup.SOCKS_PROXY_USERNAME, proxyUser);
+ sysprops.put(ProxySetup.SOCKS_PROXY_PASSWORD, proxyPassword);
+ }
+
+ } else {
+ log("resetting socks proxy", Project.MSG_VERBOSE);
+ sysprops.remove(ProxySetup.SOCKS_PROXY_HOST);
+ sysprops.remove(ProxySetup.SOCKS_PROXY_PORT);
+ sysprops.remove(ProxySetup.SOCKS_PROXY_USERNAME);
+ sysprops.remove(ProxySetup.SOCKS_PROXY_PASSWORD);
+ }
+ }
+
+ if (proxyUser != null) {
+ if (enablingProxy) {
+ Authenticator.setDefault(new ProxyAuth(proxyUser,
+ proxyPassword));
+ } else if (settingsChanged) {
+ Authenticator.setDefault(new ProxyAuth("", ""));
+ }
+ }
+ }
+
+ /**
+ * list out what is going on
+ */
+ private void traceSettingInfo() {
+ log("Setting proxy to "
+ + (proxyHost != null ? proxyHost : "''")
+ + ":" + proxyPort,
+ Project.MSG_VERBOSE);
+ }
+
+ /**
+ * Does the work.
+ *
+ * @exception BuildException thrown in unrecoverable error.
+ */
+ public void execute() throws BuildException {
+ applyWebProxySettings();
+ }
+
+ /**
+ * @since 1.6.3
+ */
+ private static final class ProxyAuth extends Authenticator {
+ private PasswordAuthentication auth;
+
+ private ProxyAuth(String user, String pass) {
+ auth = new PasswordAuthentication(user, pass.toCharArray());
+ }
+
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return auth;
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/TelnetTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/TelnetTask.java
new file mode 100644
index 00000000..82625fa7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/net/TelnetTask.java
@@ -0,0 +1,397 @@
+/*
+ * 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.tools.ant.taskdefs.optional.net;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Calendar;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.commons.net.telnet.TelnetClient;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Automates the telnet protocol.
+ *
+ */
+
+public class TelnetTask extends Task {
+ private static final int WAIT_INTERVAL = 250;
+ private static final int TELNET_PORT = 23;
+
+ /**
+ * The userid to login with, if automated login is used
+ */
+ private String userid = null;
+
+ /**
+ * The password to login with, if automated login is used
+ */
+ private String password = null;
+
+ /**
+ * The server to connect to.
+ */
+ private String server = null;
+
+ /**
+ * The tcp port to connect to.
+ */
+ private int port = TELNET_PORT;
+
+ /**
+ * The list of read/write commands for this session
+ */
+ private Vector telnetTasks = new Vector();
+
+ /**
+ * If true, adds a CR to beginning of login script
+ */
+ private boolean addCarriageReturn = false;
+
+ /**
+ * Default time allowed for waiting for a valid response
+ * for all child reads. A value of 0 means no limit.
+ */
+ private Integer defaultTimeout = null;
+
+ /**
+ * Verify that all parameters are included.
+ * Connect and possibly login
+ * Iterate through the list of Reads and writes
+ * @throws BuildException on error
+ */
+ public void execute() throws BuildException {
+ /** A server name is required to continue */
+ if (server == null) {
+ throw new BuildException("No Server Specified");
+ }
+ /** A userid and password must appear together
+ * if they appear. They are not required.
+ */
+ if (userid == null && password != null) {
+ throw new BuildException("No Userid Specified");
+ }
+ if (password == null && userid != null) {
+ throw new BuildException("No Password Specified");
+ }
+
+ /** Create the telnet client object */
+ AntTelnetClient telnet = null;
+ try {
+ telnet = new AntTelnetClient();
+ try {
+ telnet.connect(server, port);
+ } catch (IOException e) {
+ throw new BuildException("Can't connect to " + server);
+ }
+ /** Login if userid and password were specified */
+ if (userid != null && password != null) {
+ login(telnet);
+ }
+ /** Process each sub command */
+ Enumeration tasksToRun = telnetTasks.elements();
+ while (tasksToRun != null && tasksToRun.hasMoreElements()) {
+ TelnetSubTask task = (TelnetSubTask) tasksToRun.nextElement();
+ if (task instanceof TelnetRead && defaultTimeout != null) {
+ ((TelnetRead) task).setDefaultTimeout(defaultTimeout);
+ }
+ task.execute(telnet);
+ }
+ } finally {
+ if (telnet != null && telnet.isConnected()) {
+ try {
+ telnet.disconnect();
+ } catch (IOException e) {
+ throw new BuildException("Error disconnecting from "
+ + server);
+ }
+ }
+ }
+ }
+
+ /**
+ * Process a 'typical' login. If it differs, use the read
+ * and write tasks explicitly
+ */
+ private void login(AntTelnetClient telnet) {
+ if (addCarriageReturn) {
+ telnet.sendString("\n", true);
+ }
+ telnet.waitForString("ogin:");
+ telnet.sendString(userid, true);
+ telnet.waitForString("assword:");
+ telnet.sendString(password, false);
+ }
+
+ /**
+ * Set the the login id to use on the server;
+ * required if <tt>password</tt> is set.
+ * @param u a <code>String</code> value
+ */
+ public void setUserid(String u) {
+ this.userid = u;
+ }
+
+ /**
+ * Set the the login password to use
+ * required if <tt>userid</tt> is set.
+ * @param p a <code>String</code> value
+ */
+ public void setPassword(String p) {
+ this.password = p;
+ }
+
+ /**
+ * Set the hostname or address of the remote server.
+ * @param m a <code>String</code> value
+ */
+ public void setServer(String m) {
+ this.server = m;
+ }
+
+ /**
+ * Set the tcp port to connect to; default is 23.
+ * @param p an <code>int</code> value
+ */
+ public void setPort(int p) {
+ this.port = p;
+ }
+
+ /**
+ * send a carriage return after connecting; optional, defaults to false.
+ * @param b a <code>boolean</code> value
+ */
+ public void setInitialCR(boolean b) {
+ this.addCarriageReturn = b;
+ }
+
+ /**
+ * set a default timeout in seconds to wait for a response,
+ * zero means forever (the default)
+ * @param i an <code>Integer</code> value
+ */
+ public void setTimeout(Integer i) {
+ this.defaultTimeout = i;
+ }
+
+ /**
+ * A string to wait for from the server.
+ * A subTask &lt;read&gt; tag was found. Create the object,
+ * Save it in our list, and return it.
+ * @return a read telnet sub task
+ */
+
+ public TelnetSubTask createRead() {
+ TelnetSubTask task = (TelnetSubTask) new TelnetRead();
+ telnetTasks.addElement(task);
+ return task;
+ }
+
+ /**
+ * Add text to send to the server
+ * A subTask &lt;write&gt; tag was found. Create the object,
+ * Save it in our list, and return it.
+ * @return a write telnet sub task
+ */
+ public TelnetSubTask createWrite() {
+ TelnetSubTask task = (TelnetSubTask) new TelnetWrite();
+ telnetTasks.addElement(task);
+ return task;
+ }
+
+ /**
+ * This class is the parent of the Read and Write tasks.
+ * It handles the common attributes for both.
+ */
+ public class TelnetSubTask {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected String taskString = "";
+ // CheckStyle:VisibilityModifier ON
+ /**
+ * Execute the subtask.
+ * @param telnet the client
+ * @throws BuildException always as it is not allowed to instantiate this object
+ */
+ public void execute(AntTelnetClient telnet)
+ throws BuildException {
+ throw new BuildException("Shouldn't be able instantiate a SubTask directly");
+ }
+
+ /**
+ * the message as nested text
+ * @param s the nested text
+ */
+ public void addText(String s) {
+ setString(getProject().replaceProperties(s));
+ }
+
+ /**
+ * the message as an attribute
+ * @param s a <code>String</code> value
+ */
+ public void setString(String s) {
+ taskString += s;
+ }
+ }
+
+ /**
+ * Sends text to the connected server
+ */
+ public class TelnetWrite extends TelnetSubTask {
+ private boolean echoString = true;
+ /**
+ * Execute the write task.
+ * @param telnet the task to use
+ * @throws BuildException on error
+ */
+ public void execute(AntTelnetClient telnet)
+ throws BuildException {
+ telnet.sendString(taskString, echoString);
+ }
+
+ /**
+ * Whether or not the message should be echoed to the log.
+ * Defaults to <code>true</code>.
+ * @param b a <code>boolean</code> value
+ */
+ public void setEcho(boolean b) {
+ echoString = b;
+ }
+ }
+
+ /**
+ * Reads the output from the connected server
+ * until the required string is found or we time out.
+ */
+ public class TelnetRead extends TelnetSubTask {
+ private Integer timeout = null;
+ /**
+ * Execute the read task.
+ * @param telnet the task to use
+ * @throws BuildException on error
+ */
+ public void execute(AntTelnetClient telnet)
+ throws BuildException {
+ telnet.waitForString(taskString, timeout);
+ }
+ /**
+ * a timeout value that overrides any task wide timeout.
+ * @param i an <code>Integer</code> value
+ */
+ public void setTimeout(Integer i) {
+ this.timeout = i;
+ }
+
+ /**
+ * Sets the default timeout if none has been set already
+ * @param defaultTimeout an <code>Integer</code> value
+ * @ant.attribute ignore="true"
+ */
+ public void setDefaultTimeout(Integer defaultTimeout) {
+ if (timeout == null) {
+ timeout = defaultTimeout;
+ }
+ }
+ }
+
+ /**
+ * This class handles the abstraction of the telnet protocol.
+ * Currently it is a wrapper around <a
+ * href="http://jakarta.apache.org/commons/net/index.html">Jakarta
+ * Commons Net</a>.
+ */
+ public class AntTelnetClient extends TelnetClient {
+ /**
+ * Read from the telnet session until the string we are
+ * waiting for is found
+ * @param s The string to wait on
+ */
+ public void waitForString(String s) {
+ waitForString(s, null);
+ }
+
+ /**
+ * Read from the telnet session until the string we are
+ * waiting for is found or the timeout has been reached
+ * @param s The string to wait on
+ * @param timeout The maximum number of seconds to wait
+ */
+ public void waitForString(String s, Integer timeout) {
+ InputStream is = this.getInputStream();
+ try {
+ StringBuffer sb = new StringBuffer();
+ int windowStart = -s.length();
+ if (timeout == null || timeout.intValue() == 0) {
+ while (windowStart < 0
+ || !sb.substring(windowStart).equals(s)) {
+ sb.append((char) is.read());
+ windowStart++;
+ }
+ } else {
+ Calendar endTime = Calendar.getInstance();
+ endTime.add(Calendar.SECOND, timeout.intValue());
+ while (windowStart < 0
+ || !sb.substring(windowStart).equals(s)) {
+ while (Calendar.getInstance().before(endTime)
+ && is.available() == 0) {
+ Thread.sleep(WAIT_INTERVAL);
+ }
+ if (is.available() == 0) {
+ log("Read before running into timeout: "
+ + sb.toString(), Project.MSG_DEBUG);
+ throw new BuildException(
+ "Response timed-out waiting for \"" + s + '\"',
+ getLocation());
+ }
+ sb.append((char) is.read());
+ windowStart++;
+ }
+ }
+ log(sb.toString(), Project.MSG_INFO);
+ } catch (BuildException be) {
+ throw be;
+ } catch (Exception e) {
+ throw new BuildException(e, getLocation());
+ }
+ }
+
+ /**
+ * Write this string to the telnet session.
+ * @param s the string to write
+ * @param echoString if true log the string sent
+ */
+ public void sendString(String s, boolean echoString) {
+ OutputStream os = this.getOutputStream();
+ try {
+ os.write((s + "\n").getBytes());
+ if (echoString) {
+ log(s, Project.MSG_INFO);
+ }
+ os.flush();
+ } catch (Exception e) {
+ throw new BuildException(e, getLocation());
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/Pvcs.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/Pvcs.java
new file mode 100644
index 00000000..dd6016a8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/Pvcs.java
@@ -0,0 +1,675 @@
+/*
+ * 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.tools.ant.taskdefs.optional.pvcs;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.text.ParseException;
+import java.util.Enumeration;
+import java.util.Random;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.taskdefs.PumpStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ *
+ * Extracts the latest edition of the source code from a PVCS repository.
+ * PVCS is a version control system
+ * developed by <a href="http://www.merant.com/products/pvcs">Merant</a>.
+ * <br>
+ * Before using this tag, the user running ant must have access to the commands
+ * of PVCS (get and pcli) and must have access to the repository. Note that the way to specify
+ * the repository is platform dependent so use property to specify location of repository.
+ * <br>
+ * This version has been tested against PVCS version 6.5 and 6.6 under Windows and Solaris.
+
+ *
+ * <b>19-04-2001</b> <p>The task now has a more robust
+ * parser. It allows for platform independent file paths
+ * and supports file names with <i>()</i>. Thanks to Erik Husby for
+ * bringing the bug to my attention.
+ *
+ * <b>27-04-2001</b> <p>UNC paths are now handled properly.
+ * Fix provided by Don Jeffery. He also added an <i>UpdateOnly</i> flag
+ * that, when true, conditions the PVCS get using the -U option to only
+ * update those files that have a modification time (in PVCS) that is newer
+ * than the existing workfile.
+ *
+ * <b>25-10-2002</b> <p>Added a revision attribute that currently is a
+ * synonym for label, but in a future release the behavior of the label
+ * attribute will change to use the -v option of GET. See bug #13847 for
+ * discussion.
+ *
+ */
+public class Pvcs extends org.apache.tools.ant.Task {
+ // CheckStyle - magic numbers
+ // checking for "X:\ 0=dquote,1=letter,2=:,3=\
+ private static final int POS_1 = 1;
+ private static final int POS_2 = 2;
+ private static final int POS_3 = 3;
+
+ private String pvcsbin;
+ private String repository;
+ private String pvcsProject;
+ private Vector pvcsProjects;
+ private String workspace;
+ private String force;
+ private String promotiongroup;
+ private String label;
+ private String revision;
+ private boolean ignorerc;
+ private boolean updateOnly;
+ private String filenameFormat;
+ private String lineStart;
+ private String userId;
+ private String config;
+ /**
+ * Constant for the thing to execute
+ */
+ private static final String PCLI_EXE = "pcli";
+
+ /*
+ * Constant for the PCLI listversionedfiles recursive i a format "get" understands
+ */
+ // private static final String PCLI_LVF_ARGS = "lvf -z -aw";
+
+ /**
+ * Constant for the thing to execute
+ */
+ private static final String GET_EXE = "get";
+
+
+ /**
+ * Run the command.
+ * @param cmd the command line to use.
+ * @param out the output stream handler to use.
+ * @return the exit code of the command.
+ */
+ protected int runCmd(Commandline cmd, ExecuteStreamHandler out) {
+ try {
+ Project aProj = getProject();
+ Execute exe = new Execute(out);
+ exe.setAntRun(aProj);
+ exe.setWorkingDirectory(aProj.getBaseDir());
+ exe.setCommandline(cmd.getCommandline());
+ return exe.execute();
+ } catch (java.io.IOException e) {
+ String msg = "Failed executing: " + cmd.toString()
+ + ". Exception: " + e.getMessage();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+ private String getExecutable(String exe) {
+ StringBuffer correctedExe = new StringBuffer();
+ if (getPvcsbin() != null) {
+ if (pvcsbin.endsWith(File.separator)) {
+ correctedExe.append(pvcsbin);
+ } else {
+ correctedExe.append(pvcsbin).append(File.separator);
+ }
+ }
+ return correctedExe.append(exe).toString();
+ }
+
+ /**
+ * @exception org.apache.tools.ant.BuildException Something is stopping the build...
+ */
+ public void execute() throws org.apache.tools.ant.BuildException {
+ int result = 0;
+
+ if (repository == null || repository.trim().equals("")) {
+ throw new BuildException("Required argument repository not specified");
+ }
+
+ // Check workspace exists
+ // Launch PCLI listversionedfiles -z -aw
+ // Capture output
+ // build the command line from what we got the format is
+ Commandline commandLine = new Commandline();
+ commandLine.setExecutable(getExecutable(PCLI_EXE));
+
+ commandLine.createArgument().setValue("lvf");
+ commandLine.createArgument().setValue("-z");
+ commandLine.createArgument().setValue("-aw");
+ if (getWorkspace() != null) {
+ commandLine.createArgument().setValue("-sp" + getWorkspace());
+ }
+ commandLine.createArgument().setValue("-pr" + getRepository());
+
+ String uid = getUserId();
+
+ if (uid != null) {
+ commandLine.createArgument().setValue("-id" + uid);
+ }
+
+ // default pvcs project is "/"
+ if (getPvcsproject() == null && getPvcsprojects().isEmpty()) {
+ pvcsProject = "/";
+ }
+
+ if (getPvcsproject() != null) {
+ commandLine.createArgument().setValue(getPvcsproject());
+ }
+ if (!getPvcsprojects().isEmpty()) {
+ Enumeration e = getPvcsprojects().elements();
+ while (e.hasMoreElements()) {
+ String projectName = ((PvcsProject) e.nextElement()).getName();
+ if (projectName == null || (projectName.trim()).equals("")) {
+ throw new BuildException("name is a required attribute "
+ + "of pvcsproject");
+ }
+ commandLine.createArgument().setValue(projectName);
+ }
+ }
+
+ File tmp = null;
+ File tmp2 = null;
+ try {
+ Random rand = new Random(System.currentTimeMillis());
+ tmp = new File("pvcs_ant_" + rand.nextLong() + ".log");
+ FileOutputStream fos = new FileOutputStream(tmp);
+ tmp2 = new File("pvcs_ant_" + rand.nextLong() + ".log");
+ log(commandLine.describeCommand(), Project.MSG_VERBOSE);
+ try {
+ result = runCmd(commandLine,
+ new PumpStreamHandler(fos,
+ new LogOutputStream(this,
+ Project.MSG_WARN)));
+ } finally {
+ FileUtils.close(fos);
+ }
+
+ if (Execute.isFailure(result) && !ignorerc) {
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+
+ if (!tmp.exists()) {
+ throw new BuildException("Communication between ant and pvcs "
+ + "failed. No output generated from executing PVCS "
+ + "commandline interface \"pcli\" and \"get\"");
+ }
+
+ // Create folders in workspace
+ log("Creating folders", Project.MSG_INFO);
+ createFolders(tmp);
+
+ // Massage PCLI lvf output transforming '\' to '/' so get command works appropriately
+ massagePCLI(tmp, tmp2);
+
+ // Launch get on output captured from PCLI lvf
+ commandLine.clearArgs();
+ commandLine.setExecutable(getExecutable(GET_EXE));
+
+ if (getConfig() != null && getConfig().length() > 0) {
+ commandLine.createArgument().setValue("-c" + getConfig());
+ }
+
+ if (getForce() != null && getForce().equals("yes")) {
+ commandLine.createArgument().setValue("-Y");
+ } else {
+ commandLine.createArgument().setValue("-N");
+ }
+
+ if (getPromotiongroup() != null) {
+ commandLine.createArgument().setValue("-G"
+ + getPromotiongroup());
+ } else {
+ if (getLabel() != null) {
+ commandLine.createArgument().setValue("-v" + getLabel());
+ } else {
+ if (getRevision() != null) {
+ commandLine.createArgument().setValue("-r"
+ + getRevision());
+ }
+ }
+ }
+
+ if (updateOnly) {
+ commandLine.createArgument().setValue("-U");
+ }
+
+ commandLine.createArgument().setValue("@" + tmp2.getAbsolutePath());
+ log("Getting files", Project.MSG_INFO);
+ log("Executing " + commandLine.toString(), Project.MSG_VERBOSE);
+ result = runCmd(commandLine,
+ new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN));
+ if (result != 0 && !ignorerc) {
+ String msg = "Failed executing: " + commandLine.toString()
+ + ". Return code was " + result;
+ throw new BuildException(msg, getLocation());
+ }
+
+ } catch (FileNotFoundException e) {
+ String msg = "Failed executing: " + commandLine.toString()
+ + ". Exception: " + e.getMessage();
+ throw new BuildException(msg, getLocation());
+ } catch (IOException e) {
+ String msg = "Failed executing: " + commandLine.toString()
+ + ". Exception: " + e.getMessage();
+ throw new BuildException(msg, getLocation());
+ } catch (ParseException e) {
+ String msg = "Failed executing: " + commandLine.toString()
+ + ". Exception: " + e.getMessage();
+ throw new BuildException(msg, getLocation());
+ } finally {
+ if (tmp != null) {
+ tmp.delete();
+ }
+ if (tmp2 != null) {
+ tmp2.delete();
+ }
+ }
+ }
+
+ /**
+ * Parses the file and creates the folders specified in the output section
+ */
+ private void createFolders(File file) throws IOException, ParseException {
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(new FileReader(file));
+ MessageFormat mf = new MessageFormat(getFilenameFormat());
+ String line = in.readLine();
+ while (line != null) {
+ log("Considering \"" + line + "\"", Project.MSG_VERBOSE);
+ if (line.startsWith("\"\\") // Checking for "\
+ || line.startsWith("\"/") // or "/
+ // or "X:\...
+ || (line.length() > POS_3 && line.startsWith("\"")
+ && Character.isLetter(line.charAt(POS_1))
+ && String.valueOf(line.charAt(POS_2)).equals(":")
+ && String.valueOf(line.charAt(POS_3)).equals("\\"))) {
+ Object[] objs = mf.parse(line);
+ String f = (String) objs[1];
+ // Extract the name of the directory from the filename
+ int index = f.lastIndexOf(File.separator);
+ if (index > -1) {
+ File dir = new File(f.substring(0, index));
+ if (!dir.exists()) {
+ log("Creating " + dir.getAbsolutePath(),
+ Project.MSG_VERBOSE);
+ if (dir.mkdirs() || dir.isDirectory()) {
+ log("Created " + dir.getAbsolutePath(),
+ Project.MSG_INFO);
+ } else {
+ log("Failed to create "
+ + dir.getAbsolutePath(),
+ Project.MSG_INFO);
+ }
+ } else {
+ log(dir.getAbsolutePath() + " exists. Skipping",
+ Project.MSG_VERBOSE);
+ }
+ } else {
+ log("File separator problem with " + line,
+ Project.MSG_WARN);
+ }
+ } else {
+ log("Skipped \"" + line + "\"", Project.MSG_VERBOSE);
+ }
+ line = in.readLine();
+ }
+ } finally {
+ FileUtils.close(in);
+ }
+ }
+
+
+ /**
+ * Simple hack to handle the PVCS command-line tools botch when
+ * handling UNC notation.
+ * @throws IOException if there is an error.
+ */
+ private void massagePCLI(File in, File out)
+ throws IOException {
+ BufferedReader inReader = null;
+ BufferedWriter outWriter = null;
+ try {
+ inReader = new BufferedReader(new FileReader(in));
+ outWriter = new BufferedWriter(new FileWriter(out));
+ String s = null;
+ while ((s = inReader.readLine()) != null) {
+ String sNormal = s.replace('\\', '/');
+ outWriter.write(sNormal);
+ outWriter.newLine();
+ }
+ } finally {
+ FileUtils.close(inReader);
+ FileUtils.close(outWriter);
+ }
+ }
+
+ /**
+ * Get network name of the PVCS repository
+ * @return String
+ */
+ public String getRepository() {
+ return repository;
+ }
+
+ /**
+ * The filenameFormat attribute defines a MessageFormat string used
+ * to parse the output of the pcli command. It defaults to
+ * <code>{0}-arc({1})</code>. Repositories where the archive
+ * extension is not -arc should set this.
+ * @return the filename format attribute.
+ */
+ public String getFilenameFormat() {
+ return filenameFormat;
+ }
+
+ /**
+ * The format of the folder names; optional.
+ * This must be in a format suitable for
+ * <code>java.text.MessageFormat</code>.
+ * Index 1 of the format will be used as the file name.
+ * Defaults to <code>{0}-arc({1})</code>
+ * @param f the format to use.
+ */
+ public void setFilenameFormat(String f) {
+ filenameFormat = f;
+ }
+
+ /**
+
+ * The lineStart attribute is used to parse the output of the pcli
+ * command. It defaults to <code>&quot;P:</code>. The parser already
+ * knows about / and \\, this property is useful in cases where the
+ * repository is accessed on a Windows platform via a drive letter
+ * mapping.
+ * @return the lineStart attribute.
+ */
+ public String getLineStart() {
+ return lineStart;
+ }
+
+ /**
+ * What a valid return value from PVCS looks like
+ * when it describes a file. Defaults to <code>&quot;P:</code>.
+ * If you are not using an UNC name for your repository and the
+ * drive letter <code>P</code> is incorrect for your setup, you may
+ * need to change this value, UNC names will always be
+ * accepted.
+ * @param l the value to use.
+ */
+ public void setLineStart(String l) {
+ lineStart = l;
+ }
+
+ /**
+ * The network name of the PVCS repository; required.
+ * @param repo String
+ */
+ public void setRepository(String repo) {
+ repository = repo;
+ }
+
+ /**
+ * Get name of the project in the PVCS repository
+ * @return String
+ */
+ public String getPvcsproject() {
+ return pvcsProject;
+ }
+
+ /**
+ * The project within the PVCS repository to extract files from;
+ * optional, default &quot;/&quot;
+ * @param prj String
+ */
+ public void setPvcsproject(String prj) {
+ pvcsProject = prj;
+ }
+
+ /**
+ * Get name of the project in the PVCS repository
+ * @return Vector
+ */
+ public Vector getPvcsprojects() {
+ return pvcsProjects;
+ }
+
+ /**
+ * Get name of the workspace to store the retrieved files
+ * @return String
+ */
+ public String getWorkspace() {
+ return workspace;
+ }
+
+ /**
+ * Workspace to use; optional.
+ * By specifying a workspace, the files are extracted to that location.
+ * A PVCS workspace is a name for a location of the workfiles and
+ * isn't as such the location itself.
+ * You define the location for a workspace using the PVCS GUI clients.
+ * If this isn't specified the default workspace for the current user is used.
+ * @param ws String
+ */
+ public void setWorkspace(String ws) {
+ workspace = ws;
+ }
+
+ /**
+ * Get name of the PVCS bin directory
+ * @return String
+ */
+ public String getPvcsbin() {
+ return pvcsbin;
+ }
+
+ /**
+ * Specifies the location of the PVCS bin directory; optional if on the PATH.
+ * On some systems the PVCS executables <i>pcli</i>
+ * and <i>get</i> are not found in the PATH. In such cases this attribute
+ * should be set to the bin directory of the PVCS installation containing
+ * the executables mentioned before. If this attribute isn't specified the
+ * tag expects the executables to be found using the PATH environment variable.
+ * @param bin PVCS bin directory
+ * @todo use a File setter and resolve paths.
+ */
+ public void setPvcsbin(String bin) {
+ pvcsbin = bin;
+ }
+
+ /**
+ * Get value of force
+ * @return String
+ */
+ public String getForce() {
+ return force;
+ }
+
+ /**
+ * Specifies the value of the force argument; optional.
+ * If set to <i>yes</i> all files that exists and are
+ * writable are overwritten. Default <i>no</i> causes the files
+ * that are writable to be ignored. This stops the PVCS command
+ * <i>get</i> to stop asking questions!
+ * @todo make a boolean setter
+ * @param f String (yes/no)
+ */
+ public void setForce(String f) {
+ if (f != null && f.equalsIgnoreCase("yes")) {
+ force = "yes";
+ } else {
+ force = "no";
+ }
+ }
+
+ /**
+ * Get value of promotiongroup
+ * @return String
+ */
+ public String getPromotiongroup() {
+ return promotiongroup;
+ }
+
+ /**
+ * Specifies the name of the promotiongroup argument
+ * @param w String
+ */
+ public void setPromotiongroup(String w) {
+ promotiongroup = w;
+ }
+
+ /**
+ * Get value of label
+ * @return String
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * Only files marked with this label are extracted; optional.
+ * @param l String
+ */
+ public void setLabel(String l) {
+ label = l;
+ }
+
+ /**
+ * Get value of revision
+ * @return String
+ */
+ public String getRevision() {
+ return revision;
+ }
+
+ /**
+ * Only files with this revision are extract; optional.
+ * @param r String
+ */
+ public void setRevision(String r) {
+ revision = r;
+ }
+
+ /**
+ * Get value of ignorereturncode
+ * @return String
+ */
+ public boolean getIgnoreReturnCode() {
+ return ignorerc;
+ }
+
+ /**
+ * If set to true the return value from executing the pvcs
+ * commands are ignored; optional, default false.
+ * @param b a <code>boolean</code> value.
+ */
+ public void setIgnoreReturnCode(boolean b) {
+ ignorerc = b;
+ }
+
+ /**
+ * Specify a project within the PVCS repository to extract files from.
+ * @param p the pvcs project to use.
+ */
+ public void addPvcsproject(PvcsProject p) {
+ pvcsProjects.addElement(p);
+ }
+
+ /**
+ * get the updateOnly attribute.
+ * @return the updateOnly attribute.
+ */
+ public boolean getUpdateOnly() {
+ return updateOnly;
+ }
+
+ /**
+ * If set to <i>true</i> files are fetched only if
+ * newer than existing local files; optional, default false.
+ * @param l a <code>boolean</code> value.
+ */
+ public void setUpdateOnly(boolean l) {
+ updateOnly = l;
+ }
+
+ /**
+ * returns the path of the configuration file to be used
+ * @return the path of the config file
+ */
+ public String getConfig() {
+ return config;
+ }
+
+ /**
+ * Sets a configuration file other than the default to be used.
+ * These files have a .cfg extension and are often found in archive or pvcsprop folders.
+ * @param f config file - can be given absolute or relative to ant basedir
+ */
+ public void setConfig(File f) {
+ config = f.toString();
+ }
+
+
+ /**
+ * Get the userid.
+ * @return the userid.
+ */
+ public String getUserId() {
+ return userId;
+ }
+
+ /**
+ * User ID
+ * @param u the value to use.
+ */
+ public void setUserId(String u) {
+ userId = u;
+ }
+
+ /**
+ * Creates a Pvcs object
+ */
+ public Pvcs() {
+ super();
+ pvcsProject = null;
+ pvcsProjects = new Vector();
+ workspace = null;
+ repository = null;
+ pvcsbin = null;
+ force = null;
+ promotiongroup = null;
+ label = null;
+ ignorerc = false;
+ updateOnly = false;
+ lineStart = "\"P:";
+ filenameFormat = "{0}-arc({1})";
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/PvcsProject.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/PvcsProject.java
new file mode 100644
index 00000000..a8c6a2a1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/PvcsProject.java
@@ -0,0 +1,49 @@
+/*
+ * 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.tools.ant.taskdefs.optional.pvcs;
+
+
+
+/**
+ * represents a project within the PVCS repository to extract files from.
+ */
+public class PvcsProject {
+ private String name;
+
+ /** no arg constructor */
+ public PvcsProject() {
+ super();
+ }
+
+ /**
+ * Set the name of the project
+ * @param name the value to use.
+ */
+ public void setName(String name) {
+ PvcsProject.this.name = name;
+ }
+
+ /**
+ * Get the name of the project
+ * @return the name of the project.
+ */
+ public String getName() {
+ return name;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java
new file mode 100644
index 00000000..ac9eb88e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java
@@ -0,0 +1,397 @@
+/*
+ * 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.tools.ant.taskdefs.optional.script;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tools.ant.AntTypeDefinition;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.taskdefs.DefBase;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.util.ClasspathUtils;
+import org.apache.tools.ant.util.ScriptRunnerBase;
+import org.apache.tools.ant.util.ScriptRunnerHelper;
+
+/**
+ * Define a task using a script
+ *
+ * @since Ant 1.6
+ */
+public class ScriptDef extends DefBase {
+ /**
+ * script runner helper
+ */
+ private ScriptRunnerHelper helper = new ScriptRunnerHelper();
+
+ /** the name by which this script will be activated */
+ private String name;
+
+ /** Attributes definitions of this script */
+ private List attributes = new ArrayList();
+
+ /** Nested Element definitions of this script */
+ private List nestedElements = new ArrayList();
+
+ /** The attribute names as a set */
+ private Set attributeSet;
+
+ /** The nested element definitions indexed by their names */
+ private Map nestedElementMap;
+
+ /**
+ * Set the project.
+ * @param project the project that this def belows to.
+ */
+ public void setProject(Project project) {
+ super.setProject(project);
+ helper.setProjectComponent(this);
+ helper.setSetBeans(false);
+ }
+
+ /**
+ * set the name under which this script will be activated in a build
+ * file
+ *
+ * @param name the name of the script
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Indicates whether the task supports a given attribute name
+ *
+ * @param attributeName the name of the attribute.
+ *
+ * @return true if the attribute is supported by the script.
+ */
+ public boolean isAttributeSupported(String attributeName) {
+ return attributeSet.contains(attributeName);
+ }
+
+ /**
+ * Class representing an attribute definition
+ */
+ public static class Attribute {
+ /** The attribute name */
+ private String name;
+
+ /**
+ * Set the attribute name
+ *
+ * @param name the attribute name
+ */
+ public void setName(String name) {
+ this.name = name.toLowerCase(Locale.ENGLISH);
+ }
+ }
+
+ /**
+ * Add an attribute definition to this script.
+ *
+ * @param attribute the attribute definition.
+ */
+ public void addAttribute(Attribute attribute) {
+ attributes.add(attribute);
+ }
+
+ /**
+ * Class to represent a nested element definition
+ */
+ public static class NestedElement {
+ /** The name of the nested element */
+ private String name;
+
+ /** The Ant type to which this nested element corresponds. */
+ private String type;
+
+ /** The class to be created for this nested element */
+ private String className;
+
+ /**
+ * set the tag name for this nested element
+ *
+ * @param name the name of this nested element
+ */
+ public void setName(String name) {
+ this.name = name.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Set the type of this element. This is the name of an
+ * Ant task or type which is to be used when this element is to be
+ * created. This is an alternative to specifying the class name directly
+ *
+ * @param type the name of an Ant type, or task, to use for this nested
+ * element.
+ */
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ /**
+ * Set the classname of the class to be used for the nested element.
+ * This specifies the class directly and is an alternative to specifying
+ * the Ant type name.
+ *
+ * @param className the name of the class to use for this nested
+ * element.
+ */
+ public void setClassName(String className) {
+ this.className = className;
+ }
+ }
+
+ /**
+ * Add a nested element definition.
+ *
+ * @param nestedElement the nested element definition.
+ */
+ public void addElement(NestedElement nestedElement) {
+ nestedElements.add(nestedElement);
+ }
+
+ /**
+ * Define the script.
+ */
+ public void execute() {
+ if (name == null) {
+ throw new BuildException("scriptdef requires a name attribute to "
+ + "name the script");
+ }
+
+ if (helper.getLanguage() == null) {
+ throw new BuildException("<scriptdef> requires a language attribute "
+ + "to specify the script language");
+ }
+
+ // Check if need to set the loader
+ if (getAntlibClassLoader() != null || hasCpDelegate()) {
+ helper.setClassLoader(createLoader());
+ }
+
+ attributeSet = new HashSet();
+ for (Iterator i = attributes.iterator(); i.hasNext();) {
+ Attribute attribute = (Attribute) i.next();
+ if (attribute.name == null) {
+ throw new BuildException("scriptdef <attribute> elements "
+ + "must specify an attribute name");
+ }
+
+ if (attributeSet.contains(attribute.name)) {
+ throw new BuildException("scriptdef <" + name + "> declares "
+ + "the " + attribute.name + " attribute more than once");
+ }
+ attributeSet.add(attribute.name);
+ }
+
+ nestedElementMap = new HashMap();
+ for (Iterator i = nestedElements.iterator(); i.hasNext();) {
+ NestedElement nestedElement = (NestedElement) i.next();
+ if (nestedElement.name == null) {
+ throw new BuildException("scriptdef <element> elements "
+ + "must specify an element name");
+ }
+ if (nestedElementMap.containsKey(nestedElement.name)) {
+ throw new BuildException("scriptdef <" + name + "> declares "
+ + "the " + nestedElement.name + " nested element more "
+ + "than once");
+ }
+
+ if (nestedElement.className == null
+ && nestedElement.type == null) {
+ throw new BuildException("scriptdef <element> elements "
+ + "must specify either a classname or type attribute");
+ }
+ if (nestedElement.className != null
+ && nestedElement.type != null) {
+ throw new BuildException("scriptdef <element> elements "
+ + "must specify only one of the classname and type "
+ + "attributes");
+ }
+
+
+ nestedElementMap.put(nestedElement.name, nestedElement);
+ }
+
+ // find the script repository - it is stored in the project
+ Map scriptRepository = lookupScriptRepository();
+ name = ProjectHelper.genComponentName(getURI(), name);
+ scriptRepository.put(name, this);
+ AntTypeDefinition def = new AntTypeDefinition();
+ def.setName(name);
+ def.setClass(ScriptDefBase.class);
+ ComponentHelper.getComponentHelper(
+ getProject()).addDataTypeDefinition(def);
+ }
+
+ /**
+ * Find or create the script repository - it is stored in the project.
+ * This method is synchronized on the project under {@link MagicNames#SCRIPT_REPOSITORY}
+ * @return the current script repository registered as a reference.
+ */
+ private Map lookupScriptRepository() {
+ Map scriptRepository = null;
+ Project p = getProject();
+ synchronized (p) {
+ scriptRepository =
+ (Map) p.getReference(MagicNames.SCRIPT_REPOSITORY);
+ if (scriptRepository == null) {
+ scriptRepository = new HashMap();
+ p.addReference(MagicNames.SCRIPT_REPOSITORY,
+ scriptRepository);
+ }
+ }
+ return scriptRepository;
+ }
+
+ /**
+ * Create a nested element to be configured.
+ *
+ * @param elementName the name of the nested element.
+ * @return object representing the element name.
+ */
+ public Object createNestedElement(String elementName) {
+ NestedElement definition
+ = (NestedElement) nestedElementMap.get(elementName);
+ if (definition == null) {
+ throw new BuildException("<" + name + "> does not support "
+ + "the <" + elementName + "> nested element");
+ }
+
+ Object instance = null;
+ String classname = definition.className;
+ if (classname == null) {
+ instance = getProject().createTask(definition.type);
+ if (instance == null) {
+ instance = getProject().createDataType(definition.type);
+ }
+ } else {
+ /*
+ // try the context classloader
+ ClassLoader loader
+ = Thread.currentThread().getContextClassLoader();
+ */
+ ClassLoader loader = createLoader();
+
+ try {
+ instance = ClasspathUtils.newInstance(classname, loader);
+ } catch (BuildException e) {
+ instance = ClasspathUtils.newInstance(classname, ScriptDef.class.getClassLoader());
+ }
+
+ getProject().setProjectReference(instance);
+ }
+
+ if (instance == null) {
+ throw new BuildException("<" + name + "> is unable to create "
+ + "the <" + elementName + "> nested element");
+ }
+ return instance;
+ }
+
+ /**
+ * Execute the script.
+ *
+ * @param attributes collection of attributes
+ * @param elements a list of nested element values.
+ * @deprecated since 1.7.
+ * Use executeScript(attribute, elements, instance) instead.
+ */
+ public void executeScript(Map attributes, Map elements) {
+ executeScript(attributes, elements, null);
+ }
+
+ /**
+ * Execute the script.
+ * This is called by the script instance to execute the script for this
+ * definition.
+ *
+ * @param attributes collection of attributes
+ * @param elements a list of nested element values.
+ * @param instance the script instance; can be null
+ */
+ public void executeScript(Map attributes, Map elements, ScriptDefBase instance) {
+ ScriptRunnerBase runner = helper.getScriptRunner();
+ runner.addBean("attributes", attributes);
+ runner.addBean("elements", elements);
+ runner.addBean("project", getProject());
+ if (instance != null) {
+ runner.addBean("self", instance);
+ }
+ runner.executeScript("scriptdef_" + name);
+ }
+
+ /**
+ * Defines the manager.
+ *
+ * @param manager the scripting manager.
+ */
+ public void setManager(String manager) {
+ helper.setManager(manager);
+ }
+
+ /**
+ * Defines the language (required).
+ *
+ * @param language the scripting language name for the script.
+ */
+ public void setLanguage(String language) {
+ helper.setLanguage(language);
+ }
+
+ /**
+ * Load the script from an external file ; optional.
+ *
+ * @param file the file containing the script source.
+ */
+ public void setSrc(File file) {
+ helper.setSrc(file);
+ }
+
+ /**
+ * Set the script text.
+ *
+ * @param text a component of the script text to be added.
+ */
+ public void addText(String text) {
+ helper.addText(text);
+ }
+
+ /**
+ * Add any source resource.
+ * @since Ant1.7.1
+ * @param resource source of script
+ */
+ public void add(ResourceCollection resource) {
+ helper.add(resource);
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDefBase.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDefBase.java
new file mode 100644
index 00000000..95d15aa6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDefBase.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.tools.ant.taskdefs.optional.script;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DynamicConfigurator;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Task;
+
+/**
+ * The script execution class. This class finds the defining script task
+ * and passes control to that task's executeScript method.
+ *
+ * @since Ant 1.6
+ */
+public class ScriptDefBase extends Task implements DynamicConfigurator {
+
+ /** Nested elements */
+ private Map nestedElementMap = new HashMap();
+
+ /** Attributes */
+ private Map attributes = new HashMap();
+
+ private String text;
+
+ /**
+ * Locate the script defining task and execute the script by passing
+ * control to it
+ */
+ public void execute() {
+ getScript().executeScript(attributes, nestedElementMap, this);
+ }
+
+ private ScriptDef getScript() {
+ String name = getTaskType();
+ Map scriptRepository
+ = (Map) getProject().getReference(MagicNames.SCRIPT_REPOSITORY);
+ if (scriptRepository == null) {
+ throw new BuildException("Script repository not found for " + name);
+ }
+
+ ScriptDef definition = (ScriptDef) scriptRepository.get(getTaskType());
+ if (definition == null) {
+ throw new BuildException("Script definition not found for " + name);
+ }
+ return definition;
+ }
+
+ /**
+ * Create a nested element
+ *
+ * @param name the nested element name
+ * @return the element to be configured
+ */
+ public Object createDynamicElement(String name) {
+ List nestedElementList = (List) nestedElementMap.get(name);
+ if (nestedElementList == null) {
+ nestedElementList = new ArrayList();
+ nestedElementMap.put(name, nestedElementList);
+ }
+ Object element = getScript().createNestedElement(name);
+ nestedElementList.add(element);
+ return element;
+ }
+
+ /**
+ * Set a task attribute
+ *
+ * @param name the attribute name.
+ * @param value the attribute's string value
+ */
+ public void setDynamicAttribute(String name, String value) {
+ ScriptDef definition = getScript();
+ if (!definition.isAttributeSupported(name)) {
+ throw new BuildException("<" + getTaskType()
+ + "> does not support the \"" + name + "\" attribute");
+ }
+
+ attributes.put(name, value);
+ }
+
+ /**
+ * Set the script text.
+ *
+ * @param text a component of the script text to be added.
+ * @since ant1.7
+ */
+ public void addText(String text) {
+ this.text = getProject().replaceProperties(text);
+ }
+
+ /**
+ * get the text of this element; may be null
+ * @return text or null for no nested text
+ * @since ant1.7
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * Utility method for nested scripts; throws a BuildException
+ * with the given message.
+ * @param message text to pass to the BuildException
+ * @throws BuildException always.
+ * @since ant1.7
+ */
+ public void fail(String message) {
+ throw new BuildException(message);
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOS.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOS.java
new file mode 100644
index 00000000..03031d5e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOS.java
@@ -0,0 +1,479 @@
+/*
+ * 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.tools.ant.taskdefs.optional.sos;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * A base class for creating tasks for executing commands on SourceOffSite.
+ *
+ * These tasks were inspired by the VSS tasks.
+ *
+ */
+
+public abstract class SOS extends Task implements SOSCmd {
+
+ private static final int ERROR_EXIT_STATUS = 255;
+
+ private String sosCmdDir = null;
+ private String sosUsername = null;
+ private String sosPassword = "";
+ private String projectPath = null;
+ private String vssServerPath = null;
+ private String sosServerPath = null;
+ private String sosHome = null;
+ private String localPath = null;
+ private String version = null;
+ private String label = null;
+ private String comment = null;
+ private String filename = null;
+
+ private boolean noCompress = false;
+ private boolean noCache = false;
+ private boolean recursive = false;
+ private boolean verbose = false;
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ /** Commandline to be executed. */
+ protected Commandline commandLine;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Flag to disable the cache when set.
+ * Required if SOSHOME is set as an environment variable.
+ * Defaults to false.
+ *
+ * @param nocache True to disable caching.
+ */
+ public final void setNoCache(boolean nocache) {
+ noCache = nocache;
+ }
+
+ /**
+ * Flag to disable compression when set. Defaults to false.
+ *
+ * @param nocompress True to disable compression.
+ */
+ public final void setNoCompress(boolean nocompress) {
+ noCompress = nocompress;
+ }
+
+ /**
+ * The directory where soscmd(.exe) is located.
+ * soscmd must be on the path if omitted.
+ *
+ * @param dir The new sosCmd value.
+ */
+ public final void setSosCmd(String dir) {
+ sosCmdDir = FileUtils.translatePath(dir);
+ }
+
+ /**
+ * The SourceSafe username.
+ *
+ * @param username The new username value.
+ *
+ * @ant.attribute group="required"
+ */
+ public final void setUsername(String username) {
+ sosUsername = username;
+ }
+
+ /**
+ * The SourceSafe password.
+ *
+ * @param password The new password value.
+ */
+ public final void setPassword(String password) {
+ sosPassword = password;
+ }
+
+ /**
+ * The SourceSafe project path.
+ *
+ * @param projectpath The new projectpath value.
+ *
+ * @ant.attribute group="required"
+ */
+ public final void setProjectPath(String projectpath) {
+ if (projectpath.startsWith(SOSCmd.PROJECT_PREFIX)) {
+ projectPath = projectpath;
+ } else {
+ projectPath = SOSCmd.PROJECT_PREFIX + projectpath;
+ }
+ }
+
+ /**
+ * The path to the location of the ss.ini file.
+ *
+ * @param vssServerPath The new vssServerPath value.
+ *
+ * @ant.attribute group="required"
+ */
+ public final void setVssServerPath(String vssServerPath) {
+ this.vssServerPath = vssServerPath;
+ }
+
+ /**
+ * Path to the SourceOffSite home directory.
+ *
+ * @param sosHome The new sosHome value.
+ */
+ public final void setSosHome(String sosHome) {
+ this.sosHome = sosHome;
+ }
+
+ /**
+ * The address and port of SourceOffSite Server,
+ * for example 192.168.0.1:8888.
+ *
+ * @param sosServerPath The new sosServerPath value.
+ *
+ * @ant.attribute group="required"
+ */
+ public final void setSosServerPath(String sosServerPath) {
+ this.sosServerPath = sosServerPath;
+ }
+
+ /**
+ * Override the working directory and get to the specified path.
+ *
+ * @param path The new localPath value.
+ */
+ public final void setLocalPath(Path path) {
+ localPath = path.toString();
+ }
+
+ /**
+ * Enable verbose output. Defaults to false.
+ *
+ * @param verbose True for verbose output.
+ */
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ // Special setters for the sub-classes
+
+ /**
+ * Set the file name.
+ * @param file the filename to use.
+ */
+ protected void setInternalFilename(String file) {
+ filename = file;
+ }
+
+ /**
+ * Set the recursive flag.
+ * @param recurse if true use the recursive flag on the command line.
+ */
+ protected void setInternalRecursive(boolean recurse) {
+ recursive = recurse;
+ }
+
+ /**
+ * Set the comment text.
+ * @param text the comment text to use.
+ */
+ protected void setInternalComment(String text) {
+ comment = text;
+ }
+
+ /**
+ * Set the label.
+ * @param text the label to use.
+ */
+ protected void setInternalLabel(String text) {
+ label = text;
+ }
+
+ /**
+ * Set the version.
+ * @param text the version to use.
+ */
+ protected void setInternalVersion(String text) {
+ version = text;
+ }
+
+ /**
+ * Get the executable to run. Add the path if it was specified in the build file
+ *
+ * @return the executable to run.
+ */
+ protected String getSosCommand() {
+ if (sosCmdDir == null) {
+ return COMMAND_SOS_EXE;
+ } else {
+ return sosCmdDir + File.separator + COMMAND_SOS_EXE;
+ }
+ }
+
+ /**
+ * Get the comment
+ * @return if it was set, null if not.
+ */
+ protected String getComment() {
+ return comment;
+ }
+
+ /**
+ * Get the version
+ * @return if it was set, null if not.
+ */
+ protected String getVersion() {
+ return version;
+ }
+
+ /**
+ * Get the label
+ * @return if it was set, null if not.
+ */
+ protected String getLabel() {
+ return label;
+ }
+
+ /**
+ * Get the username
+ * @return if it was set, null if not.
+ */
+ protected String getUsername() {
+ return sosUsername;
+ }
+
+ /**
+ * Get the password
+ * @return empty string if it wasn't set.
+ */
+ protected String getPassword() {
+ return sosPassword;
+ }
+
+ /**
+ * Get the project path
+ * @return if it was set, null if not.
+ */
+ protected String getProjectPath() {
+ return projectPath;
+ }
+
+ /**
+ * Get the VSS server path
+ * @return if it was set, null if not.
+ */
+ protected String getVssServerPath() {
+ return vssServerPath;
+ }
+
+ /**
+ * Get the SOS home directory.
+ * @return if it was set, null if not.
+ */
+ protected String getSosHome() {
+ return sosHome;
+ }
+
+ /**
+ * Get the SOS serve path.
+ * @return if it was set, null if not.
+ */
+ protected String getSosServerPath() {
+ return sosServerPath;
+ }
+
+ /**
+ * Get the filename to be acted upon.
+ * @return if it was set, null if not.
+ */
+ protected String getFilename() {
+ return filename;
+ }
+
+ /**
+ * Get the NoCompress flag.
+ *
+ * @return the 'nocompress' Flag if the attribute was 'true',
+ * otherwise an empty string.
+ */
+ protected String getNoCompress() {
+ return noCompress ? FLAG_NO_COMPRESSION : "";
+ }
+
+ /**
+ * Get the NoCache flag.
+ *
+ * @return the 'nocache' Flag if the attribute was 'true', otherwise an empty string.
+ */
+ protected String getNoCache() {
+ return noCache ? FLAG_NO_CACHE : "";
+ }
+
+ /**
+ * Get the 'verbose' Flag.
+ *
+ * @return the 'verbose' Flag if the attribute was 'true', otherwise an empty string.
+ */
+ protected String getVerbose() {
+ return verbose ? FLAG_VERBOSE : "";
+ }
+
+ /**
+ * Get the 'recursive' Flag.
+ *
+ * @return the 'recursive' Flag if the attribute was 'true', otherwise an empty string.
+ */
+ protected String getRecursive() {
+ return recursive ? FLAG_RECURSION : "";
+ }
+
+ /**
+ * Builds and returns the working directory.
+ * <p>
+ * The localpath is created if it didn't exist.
+ *
+ * @return the absolute path of the working directory.
+ */
+ protected String getLocalPath() {
+ if (localPath == null) {
+ return getProject().getBaseDir().getAbsolutePath();
+ } else {
+ // make sure localDir exists, create it if it doesn't
+ File dir = getProject().resolveFile(localPath);
+ if (!dir.exists()) {
+ boolean done = dir.mkdirs() || dir.isDirectory();
+ if (!done) {
+ String msg = "Directory " + localPath + " creation was not "
+ + "successful for an unknown reason";
+ throw new BuildException(msg, getLocation());
+ }
+ getProject().log("Created dir: " + dir.getAbsolutePath());
+ }
+ return dir.getAbsolutePath();
+ }
+ }
+
+ /**
+ * Subclasses implement the logic required to construct the command line.
+ *
+ * @return The command line to execute.
+ */
+ abstract Commandline buildCmdLine();
+
+
+ /**
+ * Execute the created command line.
+ *
+ * @throws BuildException on error.
+ */
+ public void execute()
+ throws BuildException {
+ int result = 0;
+ buildCmdLine();
+ result = run(commandLine);
+ if (result == ERROR_EXIT_STATUS) { // This is the exit status
+ String msg = "Failed executing: " + commandLine.toString();
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+ /**
+ * Execute the created command line.
+ *
+ * @param cmd The command line to run.
+ * @return int the exit code.
+ * @throws BuildException
+ */
+ protected int run(Commandline cmd) {
+ try {
+ Execute exe = new Execute(new LogStreamHandler(this,
+ Project.MSG_INFO,
+ Project.MSG_WARN));
+
+ exe.setAntRun(getProject());
+ exe.setWorkingDirectory(getProject().getBaseDir());
+ exe.setCommandline(cmd.getCommandline());
+ exe.setVMLauncher(false); // Use the OS VM launcher so we get environment variables
+ return exe.execute();
+ } catch (java.io.IOException e) {
+ throw new BuildException(e, getLocation());
+ }
+ }
+
+ /** Sets the executable and add the required attributes to the command line. */
+ protected void getRequiredAttributes() {
+ // Get the path to the soscmd(.exe)
+ commandLine.setExecutable(getSosCommand());
+ // SOS server address is required
+ if (getSosServerPath() == null) {
+ throw new BuildException("sosserverpath attribute must be set!", getLocation());
+ }
+ commandLine.createArgument().setValue(FLAG_SOS_SERVER);
+ commandLine.createArgument().setValue(getSosServerPath());
+ // Login info is required
+ if (getUsername() == null) {
+ throw new BuildException("username attribute must be set!", getLocation());
+ }
+ commandLine.createArgument().setValue(FLAG_USERNAME);
+ commandLine.createArgument().setValue(getUsername());
+ // The SOS class knows that the SOS server needs the password flag,
+ // even if there is no password ,so we send a " "
+ commandLine.createArgument().setValue(FLAG_PASSWORD);
+ commandLine.createArgument().setValue(getPassword());
+ // VSS Info is required
+ if (getVssServerPath() == null) {
+ throw new BuildException("vssserverpath attribute must be set!", getLocation());
+ }
+ commandLine.createArgument().setValue(FLAG_VSS_SERVER);
+ commandLine.createArgument().setValue(getVssServerPath());
+ // VSS project is required
+ if (getProjectPath() == null) {
+ throw new BuildException("projectpath attribute must be set!", getLocation());
+ }
+ commandLine.createArgument().setValue(FLAG_PROJECT);
+ commandLine.createArgument().setValue(getProjectPath());
+ }
+
+ /** Adds the optional attributes to the command line. */
+ protected void getOptionalAttributes() {
+ // -verbose
+ commandLine.createArgument().setValue(getVerbose());
+ // Disable Compression
+ commandLine.createArgument().setValue(getNoCompress());
+ // Path to the SourceOffSite home directory /home/user/.sos
+ if (getSosHome() == null) {
+ // If -soshome was not specified then we can look for nocache
+ commandLine.createArgument().setValue(getNoCache());
+ } else {
+ commandLine.createArgument().setValue(FLAG_SOS_HOME);
+ commandLine.createArgument().setValue(getSosHome());
+ }
+ //If a working directory was specified then add it to the command line
+ if (getLocalPath() != null) {
+ commandLine.createArgument().setValue(FLAG_WORKING_DIR);
+ commandLine.createArgument().setValue(getLocalPath());
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckin.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckin.java
new file mode 100644
index 00000000..9095f075
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckin.java
@@ -0,0 +1,101 @@
+/*
+ * 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.tools.ant.taskdefs.optional.sos;
+
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Commits and unlocks files in Visual SourceSafe via a SourceOffSite server.
+ *
+ * @ant.task name="soscheckin" category="scm"
+ */
+public class SOSCheckin extends SOS {
+
+ /**
+ * The filename to act upon.
+ * If no file is specified then the task
+ * acts upon the project.
+ *
+ * @param filename The new file value
+ */
+ public final void setFile(String filename) {
+ super.setInternalFilename(filename);
+ }
+
+ /**
+ * Flag to recursively apply the action. Defaults to false.
+ *
+ * @param recursive True for recursive operation.
+ */
+ public void setRecursive(boolean recursive) {
+ super.setInternalRecursive(recursive);
+ }
+
+ /**
+ * The comment to apply to all files being labelled.
+ *
+ * @param comment The new comment value
+ */
+ public void setComment(String comment) {
+ super.setInternalComment(comment);
+ }
+
+ /**
+ * Build the command line. <p>
+ *
+ * CheckInFile required parameters: -server -name -password -database -project
+ * -file<br>
+ * CheckInFile optional parameters: -workdir -log -verbose -nocache -nocompression
+ * -soshome<br>
+ * CheckInProject required parameters: -server -name -password -database
+ * -project<br>
+ * CheckInProject optional parameters: workdir -recursive -log -verbose
+ * -nocache -nocompression -soshome<br>
+ *
+ * @return Commandline the generated command to be executed
+ */
+ protected Commandline buildCmdLine() {
+ commandLine = new Commandline();
+
+ // If we find a "file" attribute then act on a file otherwise act on a project
+ if (getFilename() != null) {
+ // add -command CheckInFile to the commandline
+ commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+ commandLine.createArgument().setValue(SOSCmd.COMMAND_CHECKIN_FILE);
+ // add -file xxxxx to the commandline
+ commandLine.createArgument().setValue(SOSCmd.FLAG_FILE);
+ commandLine.createArgument().setValue(getFilename());
+ } else {
+ // add -command CheckInProject to the commandline
+ commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+ commandLine.createArgument().setValue(SOSCmd.COMMAND_CHECKIN_PROJECT);
+ // look for a recursive option
+ commandLine.createArgument().setValue(getRecursive());
+ }
+
+ getRequiredAttributes();
+ getOptionalAttributes();
+
+ // Look for a comment
+ if (getComment() != null) {
+ commandLine.createArgument().setValue(SOSCmd.FLAG_COMMENT);
+ commandLine.createArgument().setValue(getComment());
+ }
+ return commandLine;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckout.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckout.java
new file mode 100644
index 00000000..fab6fb9f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckout.java
@@ -0,0 +1,85 @@
+/*
+ * 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.tools.ant.taskdefs.optional.sos;
+
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Retrieves and locks files in Visual SourceSafe via a SourceOffSite server.
+ *
+ * @ant.task name="soscheckout" category="scm"
+ */
+public class SOSCheckout extends SOS {
+
+ /**
+ * The filename to act upon.
+ * If no file is specified then the task
+ * acts upon the project.
+ *
+ * @param filename The new file value
+ */
+ public final void setFile(String filename) {
+ super.setInternalFilename(filename);
+ }
+
+ /**
+ * Flag to recursively apply the action. Defaults to false.
+ *
+ * @param recursive True for recursive operation.
+ */
+ public void setRecursive(boolean recursive) {
+ super.setInternalRecursive(recursive);
+ }
+
+ /**
+ * Build the command line <br>
+ *
+ * CheckOutFile required parameters: -server -name -password -database -project -file<br>
+ * CheckOutFile optional parameters: -workdir -verbose -nocache -nocompression -soshome<br>
+ *
+ * CheckOutProject required parameters: -server -name -password -database -project<br>
+ * CheckOutProject optional parameters:-workdir -recursive -verbose -nocache
+ * -nocompression -soshome<br>
+ *
+ * @return Commandline the generated command to be executed
+ */
+ protected Commandline buildCmdLine() {
+ commandLine = new Commandline();
+
+ // If we find a "file" attribute then act on a file otherwise act on a project
+ if (getFilename() != null) {
+ // add -command CheckOutFile to the commandline
+ commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+ commandLine.createArgument().setValue(SOSCmd.COMMAND_CHECKOUT_FILE);
+ // add -file xxxxx to the commandline
+ commandLine.createArgument().setValue(SOSCmd.FLAG_FILE);
+ commandLine.createArgument().setValue(getFilename());
+ } else {
+ // add -command CheckOutProject to the commandline
+ commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+ commandLine.createArgument().setValue(SOSCmd.COMMAND_CHECKOUT_PROJECT);
+ // look for a recursive option
+ commandLine.createArgument().setValue(getRecursive());
+ }
+
+ getRequiredAttributes();
+ getOptionalAttributes();
+
+ return commandLine;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCmd.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCmd.java
new file mode 100644
index 00000000..3543c414
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCmd.java
@@ -0,0 +1,81 @@
+/*
+ * 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.tools.ant.taskdefs.optional.sos;
+
+/**
+ * Interface to hold constants used by the SOS tasks
+ *
+ */
+// CheckStyle:InterfaceIsTypeCheck OFF (bc)
+public interface SOSCmd {
+ // soscmd Command options
+ /** The sos executable */
+ String COMMAND_SOS_EXE = "soscmd";
+ /** The get file command */
+ String COMMAND_GET_FILE = "GetFile";
+ /** The get project command */
+ String COMMAND_GET_PROJECT = "GetProject";
+ /** The checkout file command */
+ String COMMAND_CHECKOUT_FILE = "CheckOutFile";
+ /** The checkout project command */
+ String COMMAND_CHECKOUT_PROJECT = "CheckOutProject";
+ /** The checkin file command */
+ String COMMAND_CHECKIN_FILE = "CheckInFile";
+ /** The checkin project command */
+ String COMMAND_CHECKIN_PROJECT = "CheckInProject";
+ /** The get history command */
+ String COMMAND_HISTORY = "GetFileHistory";
+ /** The add label command */
+ String COMMAND_LABEL = "AddLabel";
+ /** The project prefix */
+ String PROJECT_PREFIX = "$";
+
+ // soscmd Option flags
+ /** The command option */
+ String FLAG_COMMAND = "-command";
+ /** The database (vss server) option */
+ String FLAG_VSS_SERVER = "-database";
+ /** The username option */
+ String FLAG_USERNAME = "-name";
+ /** The password option */
+ String FLAG_PASSWORD = "-password";
+ /** The log option */
+ String FLAG_COMMENT = "-log";
+ /** The workdir option */
+ String FLAG_WORKING_DIR = "-workdir";
+ /** The recursive option */
+ String FLAG_RECURSION = "-recursive";
+ /** The revision option */
+ String FLAG_VERSION = "-revision";
+ /** The label option */
+ String FLAG_LABEL = "-label";
+ /** The no compression option */
+ String FLAG_NO_COMPRESSION = "-nocompress";
+ /** The no cache option */
+ String FLAG_NO_CACHE = "-nocache";
+ /** The server option */
+ String FLAG_SOS_SERVER = "-server";
+ /** The sos home option */
+ String FLAG_SOS_HOME = "-soshome";
+ /** The project option */
+ String FLAG_PROJECT = "-project";
+ /** The file option */
+ String FLAG_FILE = "-file";
+ /** The verbose option */
+ String FLAG_VERBOSE = "-verbose";
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSGet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSGet.java
new file mode 100644
index 00000000..cdbf92bc
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSGet.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.tools.ant.taskdefs.optional.sos;
+
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Retrieves a read-only copy of the specified project or file
+ * from Visual SourceSafe via a SourceOffSite server.
+ *
+ * @ant.task name="sosget" category="scm"
+ */
+public class SOSGet extends SOS {
+
+ /**
+ * The Filename to act upon.
+ * If no file is specified then the tasks
+ * act upon the project.
+ *
+ * @param filename The new file value
+ */
+ public final void setFile(String filename) {
+ super.setInternalFilename(filename);
+ }
+
+ /**
+ * Flag to recursively apply the action. Defaults to false
+ *
+ * @param recursive True for recursive operation.
+ */
+ public void setRecursive(boolean recursive) {
+ super.setInternalRecursive(recursive);
+ }
+
+ /**
+ * Set the version number to get -
+ * only works with SOSGet on a file.
+ *
+ * @param version The new version value
+ */
+ public void setVersion(String version) {
+ super.setInternalVersion(version);
+ }
+
+ /**
+ * The labeled version to operate on in SourceSafe.
+ *
+ * @param label The new label value
+ */
+ public void setLabel(String label) {
+ super.setInternalLabel(label);
+ }
+
+ /**
+ * Build the command line <br>
+ *
+ * GetFile required parameters: -server -name -password -database -project -file<br>
+ * GetFile optional parameters: -workdir -revision -verbose -nocache -nocompression -soshome<br>
+ *
+ * GetProject required parameters: -server -name -password -database -project<br>
+ * GetProject optional parameters: -label -workdir -recursive -verbose -nocache
+ * -nocompression -soshome<br>
+ *
+ * @return Commandline the generated command to be executed
+ */
+ protected Commandline buildCmdLine() {
+ commandLine = new Commandline();
+
+ // If we find a "file" attribute then act on a file otherwise act on a project
+ if (getFilename() != null) {
+ // add -command GetFile to the commandline
+ commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+ commandLine.createArgument().setValue(SOSCmd.COMMAND_GET_FILE);
+ // add -file xxxxx to the commandline
+ commandLine.createArgument().setValue(SOSCmd.FLAG_FILE);
+ commandLine.createArgument().setValue(getFilename());
+ // look for a version attribute
+ if (getVersion() != null) {
+ //add -revision xxxxx to the commandline
+ commandLine.createArgument().setValue(SOSCmd.FLAG_VERSION);
+ commandLine.createArgument().setValue(getVersion());
+ }
+ } else {
+ // add -command GetProject to the commandline
+ commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+ commandLine.createArgument().setValue(SOSCmd.COMMAND_GET_PROJECT);
+ // look for a recursive option
+ commandLine.createArgument().setValue(getRecursive());
+ // look for a label option
+ if (getLabel() != null) {
+ commandLine.createArgument().setValue(SOSCmd.FLAG_LABEL);
+ commandLine.createArgument().setValue(getLabel());
+ }
+ }
+
+ getRequiredAttributes();
+ getOptionalAttributes();
+
+ return commandLine;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSLabel.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSLabel.java
new file mode 100644
index 00000000..dd6b13a2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSLabel.java
@@ -0,0 +1,91 @@
+/*
+ * 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.tools.ant.taskdefs.optional.sos;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Labels Visual SourceSafe files via a SourceOffSite server.
+ *
+ * @ant.task name="soslabel" category="scm"
+ */
+public class SOSLabel extends SOS {
+
+ /**
+ * The version number to label.
+ *
+ * @param version The new version value
+ */
+ public void setVersion(String version) {
+ super.setInternalVersion(version);
+ }
+
+ /**
+ * The label to apply the the files in SourceSafe.
+ *
+ * @param label The new label value
+ *
+ * @ant.attribute group="required"
+ */
+ public void setLabel(String label) {
+ super.setInternalLabel(label);
+ }
+
+ /**
+ * The comment to apply to all files being labelled.
+ *
+ * @param comment The new comment value
+ */
+ public void setComment(String comment) {
+ super.setInternalComment(comment);
+ }
+
+ /**
+ * Build the command line <br>
+ * AddLabel required parameters: -server -name -password -database -project -label<br>
+ * AddLabel optional parameters: -verbose -comment<br>
+ *
+ * @return Commandline the generated command to be executed
+ */
+ protected Commandline buildCmdLine() {
+ commandLine = new Commandline();
+
+ // add -command AddLabel to the commandline
+ commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+ commandLine.createArgument().setValue(SOSCmd.COMMAND_LABEL);
+
+ getRequiredAttributes();
+
+ // a label is required
+ if (getLabel() == null) {
+ throw new BuildException("label attribute must be set!", getLocation());
+ }
+ commandLine.createArgument().setValue(SOSCmd.FLAG_LABEL);
+ commandLine.createArgument().setValue(getLabel());
+
+ // -verbose
+ commandLine.createArgument().setValue(getVerbose());
+ // Look for a comment
+ if (getComment() != null) {
+ commandLine.createArgument().setValue(SOSCmd.FLAG_COMMENT);
+ commandLine.createArgument().setValue(getComment());
+ }
+ return commandLine;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/package.html b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/package.html
new file mode 100644
index 00000000..a0538c21
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sos/package.html
@@ -0,0 +1,29 @@
+<html>
+<!--
+ 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.
+-->
+<body>
+ <p>
+ Ant tasks for working with a SourceOffSite source control system.
+ </p>
+ <p>
+ The &lt;SOSGet&gt; Retreives file(s) from a SOS database<br>
+ The &lt;SOSCheckin&gt; Commits and unlocks file(s) in a SOS database<br>
+ The &lt;SOSCheckout&gt; Retreives and lock file(s) in a SOS database<br>
+ The &lt;SOSLabel&gt; Label a SOS database<br>
+ </p>
+</body>
+</html>
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/AntSoundPlayer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/AntSoundPlayer.java
new file mode 100644
index 00000000..7988bc60
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/AntSoundPlayer.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.tools.ant.taskdefs.optional.sound;
+
+// ant includes
+import java.io.File;
+import java.io.IOException;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.Clip;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.Line;
+import javax.sound.sampled.LineEvent;
+import javax.sound.sampled.LineListener;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.UnsupportedAudioFileException;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.Project;
+
+
+
+/**
+ * This class is designed to be used by any AntTask that requires audio output.
+ *
+ * It implements the BuildListener interface to listen for BuildEvents
+ * and could be easily extended to provide audio output upon any
+ * specific build events occurring.
+ *
+ * I have only tested this with .WAV and .AIFF sound file formats. Both seem to work fine.
+ *
+ */
+
+public class AntSoundPlayer implements LineListener, BuildListener {
+
+ private File fileSuccess = null;
+ private int loopsSuccess = 0;
+ private Long durationSuccess = null;
+
+ private File fileFail = null;
+ private int loopsFail = 0;
+ private Long durationFail = null;
+
+ /** Constructor for AntSoundPlayer. */
+ public AntSoundPlayer() {
+ }
+
+ /**
+ * @param file the location of the audio file to be played when the
+ * build is successful
+ * @param loops the number of times the file should be played when
+ * the build is successful
+ * @param duration the number of milliseconds the file should be
+ * played when the build is successful
+ */
+ public void addBuildSuccessfulSound(File file, int loops, Long duration) {
+ this.fileSuccess = file;
+ this.loopsSuccess = loops;
+ this.durationSuccess = duration;
+ }
+
+
+ /**
+ * @param fileFail the location of the audio file to be played
+ * when the build fails
+ * @param loopsFail the number of times the file should be played
+ * when the build is fails
+ * @param durationFail the number of milliseconds the file should be
+ * played when the build fails
+ */
+ public void addBuildFailedSound(File fileFail, int loopsFail, Long durationFail) {
+ this.fileFail = fileFail;
+ this.loopsFail = loopsFail;
+ this.durationFail = durationFail;
+ }
+
+ /**
+ * Plays the file for duration milliseconds or loops.
+ */
+ private void play(Project project, File file, int loops, Long duration) {
+
+ Clip audioClip = null;
+
+ AudioInputStream audioInputStream = null;
+
+
+ try {
+ audioInputStream = AudioSystem.getAudioInputStream(file);
+ } catch (UnsupportedAudioFileException uafe) {
+ project.log("Audio format is not yet supported: "
+ + uafe.getMessage());
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+
+ if (audioInputStream != null) {
+ AudioFormat format = audioInputStream.getFormat();
+ DataLine.Info info = new DataLine.Info(Clip.class, format,
+ AudioSystem.NOT_SPECIFIED);
+ try {
+ audioClip = (Clip) AudioSystem.getLine(info);
+ audioClip.addLineListener(this);
+ audioClip.open(audioInputStream);
+ } catch (LineUnavailableException e) {
+ project.log("The sound device is currently unavailable");
+ return;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ if (duration != null) {
+ playClip(audioClip, duration.longValue());
+ } else {
+ playClip(audioClip, loops);
+ }
+ audioClip.drain();
+ audioClip.close();
+ } else {
+ project.log("Can't get data from file " + file.getName());
+ }
+ }
+
+ private void playClip(Clip clip, int loops) {
+
+ clip.loop(loops);
+ do {
+ try {
+ long timeLeft =
+ (clip.getMicrosecondLength() - clip.getMicrosecondPosition())
+ / 1000;
+ if (timeLeft > 0) {
+ Thread.sleep(timeLeft);
+ }
+ } catch (InterruptedException e) {
+ break;
+ }
+ } while (clip.isRunning());
+
+ if (clip.isRunning()) {
+ clip.stop();
+ }
+ }
+
+ private void playClip(Clip clip, long duration) {
+ clip.loop(Clip.LOOP_CONTINUOUSLY);
+ try {
+ Thread.sleep(duration);
+ } catch (InterruptedException e) {
+ // Ignore Exception
+ }
+ clip.stop();
+ }
+
+ /**
+ * This is implemented to listen for any line events and closes the
+ * clip if required.
+ * @param event the line event to follow
+ */
+ public void update(LineEvent event) {
+ if (event.getType().equals(LineEvent.Type.STOP)) {
+ Line line = event.getLine();
+ line.close();
+ }
+ }
+
+
+ /**
+ * Fired before any targets are started.
+ * @param event ignored
+ */
+ public void buildStarted(BuildEvent event) {
+ }
+
+ /**
+ * Fired after the last target has finished. This event
+ * will still be thrown if an error occurred during the build.
+ * @param event the build finished event.
+ * @see BuildEvent#getException()
+ */
+ public void buildFinished(BuildEvent event) {
+ if (event.getException() == null && fileSuccess != null) {
+ // build successful!
+ play(event.getProject(), fileSuccess, loopsSuccess, durationSuccess);
+ } else if (event.getException() != null && fileFail != null) {
+ play(event.getProject(), fileFail, loopsFail, durationFail);
+ }
+ }
+
+ /**
+ * Fired when a target is started.
+ * @param event ignored.
+ * @see BuildEvent#getTarget()
+ */
+ public void targetStarted(BuildEvent event) {
+ }
+
+ /**
+ * Fired when a target has finished. This event will
+ * still be thrown if an error occurred during the build.
+ * @param event ignored.
+ * @see BuildEvent#getException()
+ */
+ public void targetFinished(BuildEvent event) {
+ }
+
+ /**
+ * Fired when a task is started.
+ * @param event ignored.
+ * @see BuildEvent#getTask()
+ */
+ public void taskStarted(BuildEvent event) {
+ }
+
+ /**
+ * Fired when a task has finished. This event will still
+ * be throw if an error occurred during the build.
+ * @param event ignored.
+ * @see BuildEvent#getException()
+ */
+ public void taskFinished(BuildEvent event) {
+ }
+
+ /**
+ * Fired whenever a message is logged.
+ * @param event the build event
+ * @see BuildEvent#getMessage()
+ * @see BuildEvent#getPriority()
+ */
+ public void messageLogged(BuildEvent event) {
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/SoundTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/SoundTask.java
new file mode 100644
index 00000000..dfb6e69a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/sound/SoundTask.java
@@ -0,0 +1,193 @@
+/*
+ * 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.tools.ant.taskdefs.optional.sound;
+
+import java.io.File;
+import java.util.Random;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Plays a sound file at the end of the build, according to whether the build failed or succeeded.
+ *
+ * There are three attributes to be set:
+ *
+ * <code>source</code>: the location of the audio file to be played
+ * <code>duration</code>: play the sound file continuously until "duration" milliseconds has expired
+ * <code>loops</code>: the number of times the sound file should be played until stopped
+ *
+ * I have only tested this with .WAV and .AIFF sound file formats. Both seem
+ * to work fine.
+ *
+ * plans for the future:
+ * - use the midi api to define sounds (or drum beat etc) in xml and have
+ * Ant play them back
+ *
+ */
+
+public class SoundTask extends Task {
+
+ private BuildAlert success = null;
+ private BuildAlert fail = null;
+
+ /**
+ * add a sound when the build succeeds
+ * @return a BuildAlert to be configured
+ */
+ public BuildAlert createSuccess() {
+ success = new BuildAlert();
+ return success;
+ }
+
+ /**
+ * add a sound when the build fails
+ * @return a BuildAlert to be configured
+ */
+ public BuildAlert createFail() {
+ fail = new BuildAlert();
+ return fail;
+ }
+
+ /** Constructor for SoundTask. */
+ public SoundTask() {
+ }
+
+ /**
+ * Initialize the task.
+ */
+ public void init() {
+ }
+
+ /**
+ * Execute the task.
+ */
+ public void execute() {
+
+ AntSoundPlayer soundPlayer = new AntSoundPlayer();
+
+ if (success == null) {
+ log("No nested success element found.", Project.MSG_WARN);
+ } else {
+ soundPlayer.addBuildSuccessfulSound(success.getSource(),
+ success.getLoops(), success.getDuration());
+ }
+
+ if (fail == null) {
+ log("No nested failure element found.", Project.MSG_WARN);
+ } else {
+ soundPlayer.addBuildFailedSound(fail.getSource(),
+ fail.getLoops(), fail.getDuration());
+ }
+
+ getProject().addBuildListener(soundPlayer);
+
+ }
+
+ /**
+ * A class to be extended by any BuildAlert's that require the output
+ * of sound.
+ */
+ public class BuildAlert {
+ private File source = null;
+ private int loops = 0;
+ private Long duration = null;
+
+ /**
+ * Sets the duration in milliseconds the file should be played; optional.
+ * @param duration the duration in milliseconds
+ */
+ public void setDuration(Long duration) {
+ this.duration = duration;
+ }
+
+ /**
+ * Sets the location of the file to get the audio; required.
+ *
+ * @param source the name of a sound-file directory or of the audio file
+ */
+ public void setSource(File source) {
+ this.source = source;
+ }
+
+ /**
+ * Sets the number of times the source file should be played; optional.
+ *
+ * @param loops the number of loops to play the source file
+ */
+ public void setLoops(int loops) {
+ this.loops = loops;
+ }
+
+ /**
+ * Gets the location of the file to get the audio.
+ * @return the file location
+ */
+ public File getSource() {
+ File nofile = null;
+ // Check if source is a directory
+ if (source.exists()) {
+ if (source.isDirectory()) {
+ // get the list of files in the dir
+ String[] entries = source.list();
+ Vector files = new Vector();
+ for (int i = 0; i < entries.length; i++) {
+ File f = new File(source, entries[i]);
+ if (f.isFile()) {
+ files.addElement(f);
+ }
+ }
+ if (files.size() < 1) {
+ throw new BuildException("No files found in directory " + source);
+ }
+ int numfiles = files.size();
+ // get a random number between 0 and the number of files
+ Random rn = new Random();
+ int x = rn.nextInt(numfiles);
+ // set the source to the file at that location
+ this.source = (File) files.elementAt(x);
+ }
+ } else {
+ log(source + ": invalid path.", Project.MSG_WARN);
+ this.source = nofile;
+ }
+ return this.source;
+ }
+
+ /**
+ * Sets the number of times the source file should be played.
+ *
+ * @return the number of loops to play the source file
+ */
+ public int getLoops() {
+ return this.loops;
+ }
+
+ /**
+ * Gets the duration in milliseconds the file should be played.
+ * @return the duration in milliseconds
+ */
+ public Long getDuration() {
+ return this.duration;
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashScreen.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashScreen.java
new file mode 100644
index 00000000..5de84cca
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashScreen.java
@@ -0,0 +1,181 @@
+/*
+ * 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.tools.ant.taskdefs.optional.splash;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.JWindow;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildListener;
+
+class SplashScreen extends JWindow implements ActionListener, BuildListener {
+ private static final int FONT_SIZE = 12;
+ private JLabel text;
+ private JProgressBar pb;
+ private int total;
+ private static final int MIN = 0;
+ private static final int MAX = 200;
+ private Pattern progressRegExpPattern;
+
+ public SplashScreen(String msg) {
+ this(msg, null, null);
+ }
+
+ public SplashScreen(ImageIcon img) {
+ this(img, null, null);
+ }
+
+ public SplashScreen(String msg, String progressRegExp, String displayText) {
+ init(null, progressRegExp, displayText);
+ setText(msg);
+ }
+
+ public SplashScreen(ImageIcon img, String progressRegExp,
+ String displayText) {
+ init(img, progressRegExp, displayText);
+ }
+
+ protected void init(ImageIcon img) {
+ init(img, null, null);
+ }
+
+ protected void init(ImageIcon img, String progressRegExp,
+ String displayText) {
+ if (progressRegExp != null) {
+ progressRegExpPattern = Pattern.compile(progressRegExp);
+ }
+
+ JPanel pan = (JPanel) getContentPane();
+ JLabel piccy;
+ if (img == null) {
+ piccy = new JLabel();
+ } else {
+ piccy = new JLabel(img);
+ }
+
+ piccy.setBorder(BorderFactory.createLineBorder(Color.black, 1));
+ if (displayText == null) {
+ displayText = "Building....";
+ }
+ text = new JLabel(displayText, JLabel.CENTER);
+ text.setFont(new Font("Sans-Serif", Font.BOLD, FONT_SIZE));
+ text.setBorder(BorderFactory.createEtchedBorder());
+
+ pb = new JProgressBar(MIN, MAX);
+ pb.setBorder(BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
+ JPanel pan2 = new JPanel();
+ pan2.setLayout(new BorderLayout());
+
+ pan2.add(text, BorderLayout.NORTH);
+ pan2.add(pb, BorderLayout.SOUTH);
+
+ pan.setLayout(new BorderLayout());
+ pan.add(piccy, BorderLayout.CENTER);
+ pan.add(pan2, BorderLayout.SOUTH);
+
+ pan.setBorder(BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
+
+ pack();
+
+ Dimension size = getSize();
+ Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
+ int x = (scr.width - size.width) / 2;
+ int y = (scr.height - size.height) / 2;
+ setBounds(x, y, size.width, size.height);
+ }
+
+ public void setText(String txt) {
+ text.setText(txt);
+ }
+
+ public void actionPerformed(ActionEvent a) {
+ if (!hasProgressPattern()) {
+ if (total < MAX) {
+ total++;
+ } else {
+ total = MIN;
+ }
+ pb.setValue(total);
+ }
+ }
+
+ public void buildStarted(BuildEvent event) {
+ actionPerformed(null);
+ }
+
+ public void buildFinished(BuildEvent event) {
+ pb.setValue(MAX);
+ setVisible(false);
+ dispose();
+ }
+ public void targetStarted(BuildEvent event) {
+ actionPerformed(null);
+ }
+
+ public void targetFinished(BuildEvent event) {
+ actionPerformed(null);
+ }
+
+ public void taskStarted(BuildEvent event) {
+ actionPerformed(null);
+ }
+
+ public void taskFinished(BuildEvent event) {
+ actionPerformed(null);
+ }
+
+ public void messageLogged(BuildEvent event) {
+ actionPerformed(null);
+ if (hasProgressPattern()) {
+ String message = event.getMessage();
+ Matcher matcher = progressRegExpPattern.matcher(message);
+ if (matcher != null && matcher.matches()) {
+ String gr = matcher.group(1);
+ try {
+ int i = Math.min(new Integer(gr).intValue() * 2, MAX);
+ pb.setValue(i);
+ } catch (NumberFormatException e) {
+ //TODO: how to reach logger?!?
+ //log("Number parsing error in progressRegExp", Project.MSG_VERBOSE);
+
+ }
+ }
+ }
+ }
+
+ protected boolean hasProgressPattern() {
+ return progressRegExpPattern != null;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashTask.java
new file mode 100644
index 00000000..3a73bdf7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashTask.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.tools.ant.taskdefs.optional.splash;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+import javax.swing.ImageIcon;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.optional.net.SetProxy;
+import org.apache.tools.ant.util.Base64Converter;
+
+/**
+ * Creates a splash screen. The splash screen is displayed
+ * for the duration of the build and includes a handy progress bar as
+ * well. Use in conjunction with the sound task to provide interest
+ * whilst waiting for your builds to complete...
+ * @since Ant1.5
+ */
+public class SplashTask extends Task {
+ private static final int DEFAULT_SHOW_DURATION = 5000;
+
+ private String imgurl = null;
+ private String proxy = null;
+ private String user = null;
+ private String password = null;
+ private String port = "80";
+ private int showDuration = DEFAULT_SHOW_DURATION;
+ private boolean useProxy = false;
+ private String progressRegExp = null;
+ private String displayText = null;
+
+ private static SplashScreen splash = null;
+
+ /**
+ * A URL pointing to an image to display; optional, default antlogo.gif
+ * from the classpath.
+ * @param imgurl the url string pointing to the image
+ */
+ public void setImageURL(String imgurl) {
+ this.imgurl = imgurl;
+ }
+
+ /**
+ * flag to enable proxy settings; optional, deprecated : consider
+ * using &lt;setproxy&gt; instead
+ * @param useProxy if ture, enable proxy settings
+ * @deprecated since 1.5.x.
+ * Use org.apache.tools.ant.taskdefs.optional.net.SetProxy
+ */
+ @Deprecated
+ public void setUseproxy(boolean useProxy) {
+ this.useProxy = useProxy;
+ }
+
+ /**
+ * name of proxy; optional.
+ * @param proxy the name of the proxy host
+ * @deprecated since 1.5.x.
+ * Use org.apache.tools.ant.taskdefs.optional.net.SetProxy
+ */
+ @Deprecated
+ public void setProxy(String proxy) {
+ this.proxy = proxy;
+ }
+
+ /**
+ * Proxy port; optional, default 80.
+ * @param port the proxy port
+ * @deprecated since 1.5.x.
+ * Use org.apache.tools.ant.taskdefs.optional.net.SetProxy
+ */
+ @Deprecated
+ public void setPort(String port) {
+ this.port = port;
+ }
+
+ /**
+ * Proxy user; optional, default =none.
+ * @param user the proxy user
+ * @deprecated since 1.5.x.
+ * Use org.apache.tools.ant.taskdefs.optional.net.SetProxy
+ */
+ @Deprecated
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ /**
+ * Proxy password; required if <tt>user</tt> is set.
+ * @param password the proxy password
+ * @deprecated since 1.5.x.
+ * Use org.apache.tools.ant.taskdefs.optional.net.SetProxy
+ */
+ @Deprecated
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ /**
+ * how long to show the splash screen in milliseconds,
+ * optional; default 5000 ms.
+ * @param duration the splash duration in milliseconds
+ */
+ public void setShowduration(int duration) {
+ this.showDuration = duration;
+ }
+
+
+ /**
+ * Progress regular expression which is used to parse the output
+ * and dig out current progress optional; if not provided,
+ * progress is increased every action and log output line
+ * @param progressRegExp Progress regular expression, exactly one
+ * group pattern must exists, and it represents the progress
+ * number (0-100) (i.e "Progress: (.*)%")
+ * @since Ant 1.8.0
+ */
+ public void setProgressRegExp(String progressRegExp) {
+ this.progressRegExp = progressRegExp;
+ }
+
+ /**
+ * Sets the display text presented in the splash window.
+ * optional; defaults to "Building ..."
+ * @param displayText the display text presented the splash window
+ * @since Ant 1.8.0
+ */
+ public void setDisplayText(String displayText) {
+ this.displayText = displayText;
+ }
+
+ /**
+ * Execute the task.
+ * @throws BuildException on error
+ */
+ @Override
+ public void execute() throws BuildException {
+ if (splash != null) {
+ splash.setVisible(false);
+ getProject().removeBuildListener(splash);
+ splash.dispose();
+ splash = null;
+ }
+
+ log("Creating new SplashScreen", Project.MSG_VERBOSE);
+ InputStream in = null;
+
+ if (imgurl != null) {
+ try {
+ URLConnection conn = null;
+
+ SetProxy sp = new SetProxy();
+ sp.setProxyHost(proxy);
+ if (port != null) {
+ sp.setProxyPort(Integer.parseInt(port));
+ }
+ sp.setProxyUser(user);
+ sp.setProxyPassword(password);
+ sp.applyWebProxySettings();
+
+ if (useProxy && (proxy != null && proxy.length() > 0)
+ && (port != null && port.length() > 0)) {
+
+ log("Using proxied Connection", Project.MSG_DEBUG);
+ System.getProperties().put("http.proxySet", "true");
+
+ URL url = new URL(imgurl);
+
+ conn = url.openConnection();
+ if (user != null && user.length() > 0) {
+ // converted from sun internal classes to
+ // new Base64Converter
+ // utility class extracted from Get task
+ String encodedcreds =
+ new Base64Converter().encode(user + ":" + password);
+ conn.setRequestProperty("Proxy-Authorization",
+ encodedcreds);
+ }
+
+ } else {
+ System.getProperties().put("http.proxySet", "false");
+ log("Using Direction HTTP Connection", Project.MSG_DEBUG);
+ URL url = new URL(imgurl);
+ conn = url.openConnection();
+ }
+ conn.setDoInput(true);
+ conn.setDoOutput(false);
+
+ in = conn.getInputStream();
+
+ // Catch everything - some of the above return nulls,
+ // throw exceptions or generally misbehave
+ // in the event of a problem etc
+
+ } catch (Throwable ioe) {
+ log("Unable to download image, trying default Ant Logo",
+ Project.MSG_DEBUG);
+ log("(Exception was \"" + ioe.getMessage() + "\"",
+ Project.MSG_DEBUG);
+ }
+ }
+
+ if (in == null) {
+ ClassLoader cl = SplashTask.class.getClassLoader();
+ if (cl != null) {
+ in = cl.getResourceAsStream("images/ant_logo_large.gif");
+ } else {
+ in = ClassLoader
+ .getSystemResourceAsStream("images/ant_logo_large.gif");
+ }
+ }
+
+ boolean success = false;
+ if (in != null) {
+ DataInputStream din = new DataInputStream(in);
+ try {
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ int data;
+ while ((data = din.read()) != -1) {
+ bout.write((byte) data);
+ }
+
+ log("Got ByteArray, creating splash", Project.MSG_DEBUG);
+
+ try {
+ ImageIcon img = new ImageIcon(bout.toByteArray());
+ splash = new SplashScreen(img, progressRegExp, displayText);
+ success = true;
+ } catch (Throwable e) {
+ logHeadless(e);
+ }
+ } catch (Exception e) {
+ throw new BuildException(e);
+ } finally {
+ try {
+ din.close();
+ } catch (IOException ioe) {
+ // swallow if there was an error before so that
+ // original error will be passed up
+ if (success) {
+ throw new BuildException(ioe);
+ }
+ }
+ }
+ } else {
+ try {
+ splash = new SplashScreen("Image Unavailable.", progressRegExp,
+ displayText);
+ success = true;
+ } catch (Throwable e) {
+ logHeadless(e);
+ }
+ }
+
+ if (success) {
+ splash.setVisible(true);
+ splash.toFront();
+ getProject().addBuildListener(splash);
+ try {
+ Thread.sleep(showDuration);
+ } catch (InterruptedException e) {
+ // Ignore Exception
+ }
+ }
+ }
+
+ private void logHeadless(Throwable e) {
+ log("failed to display SplashScreen, caught "
+ + e.getClass().getName() + " with message: " + e.getMessage(),
+ Project.MSG_WARN);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java
new file mode 100644
index 00000000..9365e8cd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java
@@ -0,0 +1,272 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ssh;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.NumberFormat;
+
+import org.apache.tools.ant.BuildException;
+
+import com.jcraft.jsch.Channel;
+import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpProgressMonitor;
+
+/**
+ * Abstract class for ssh upload and download
+ */
+public abstract class AbstractSshMessage {
+ private static final double ONE_SECOND = 1000.0;
+
+ private final Session session;
+ private final boolean verbose;
+ private LogListener listener = new LogListener() {
+ public void log(final String message) {
+ // do nothing;
+ }
+ };
+
+ /**
+ * Constructor for AbstractSshMessage
+ * @param session the ssh session to use
+ */
+ public AbstractSshMessage(final Session session) {
+ this(false, session);
+ }
+
+ /**
+ * Constructor for AbstractSshMessage
+ * @param verbose if true do verbose logging
+ * @param session the ssh session to use
+ * @since Ant 1.6.2
+ */
+ public AbstractSshMessage(final boolean verbose, final Session session) {
+ this.verbose = verbose;
+ this.session = session;
+ }
+
+ /**
+ * Open an ssh channel.
+ * @param command the command to use
+ * @return the channel
+ * @throws JSchException on error
+ */
+ protected Channel openExecChannel(final String command) throws JSchException {
+ final ChannelExec channel = (ChannelExec) session.openChannel("exec");
+ channel.setCommand(command);
+
+ return channel;
+ }
+
+ /**
+ * Open an ssh sftp channel.
+ * @return the channel
+ * @throws JSchException on error
+ */
+ protected ChannelSftp openSftpChannel() throws JSchException {
+ final ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
+
+ return channel;
+ }
+
+ /**
+ * Send an ack.
+ * @param out the output stream to use
+ * @throws IOException on error
+ */
+ protected void sendAck(final OutputStream out) throws IOException {
+ final byte[] buf = new byte[1];
+ buf[0] = 0;
+ out.write(buf);
+ out.flush();
+ }
+
+ /**
+ * Reads the response, throws a BuildException if the response
+ * indicates an error.
+ * @param in the input stream to use
+ * @throws IOException on I/O error
+ * @throws BuildException on other errors
+ */
+ protected void waitForAck(final InputStream in)
+ throws IOException, BuildException {
+ final int b = in.read();
+
+ // b may be 0 for success,
+ // 1 for error,
+ // 2 for fatal error,
+
+ if (b == -1) {
+ // didn't receive any response
+ throw new BuildException("No response from server");
+ } else if (b != 0) {
+ final StringBuffer sb = new StringBuffer();
+
+ int c = in.read();
+ while (c > 0 && c != '\n') {
+ sb.append((char) c);
+ c = in.read();
+ }
+
+ if (b == 1) {
+ throw new BuildException("server indicated an error: "
+ + sb.toString());
+ } else if (b == 2) {
+ throw new BuildException("server indicated a fatal error: "
+ + sb.toString());
+ } else {
+ throw new BuildException("unknown response, code " + b
+ + " message: " + sb.toString());
+ }
+ }
+ }
+
+ /**
+ * Carry out the transfer.
+ * @throws IOException on I/O errors
+ * @throws JSchException on ssh errors
+ */
+ public abstract void execute() throws IOException, JSchException;
+
+ /**
+ * Set a log listener.
+ * @param aListener the log listener
+ */
+ public void setLogListener(final LogListener aListener) {
+ listener = aListener;
+ }
+
+ /**
+ * Log a message to the log listener.
+ * @param message the message to log
+ */
+ protected void log(final String message) {
+ listener.log(message);
+ }
+
+ /**
+ * Log transfer stats to the log listener.
+ * @param timeStarted the time started
+ * @param timeEnded the finishing time
+ * @param totalLength the total length
+ */
+ protected void logStats(final long timeStarted,
+ final long timeEnded,
+ final long totalLength) {
+ final double duration = (timeEnded - timeStarted) / ONE_SECOND;
+ final NumberFormat format = NumberFormat.getNumberInstance();
+ format.setMaximumFractionDigits(2);
+ format.setMinimumFractionDigits(1);
+ listener.log("File transfer time: " + format.format(duration)
+ + " Average Rate: " + format.format(totalLength / duration)
+ + " B/s");
+ }
+
+ /**
+ * Is the verbose attribute set.
+ * @return true if the verbose attribute is set
+ * @since Ant 1.6.2
+ */
+ protected final boolean getVerbose() {
+ return verbose;
+ }
+
+ /**
+ * Track progress every 10% if 100kb < filesize < 1mb. For larger
+ * files track progress for every percent transmitted.
+ * @param filesize the size of the file been transmitted
+ * @param totalLength the total transmission size
+ * @param percentTransmitted the current percent transmitted
+ * @return the percent that the file is of the total
+ */
+ protected final int trackProgress(final long filesize, final long totalLength,
+ final int percentTransmitted) {
+
+ // CheckStyle:MagicNumber OFF
+ final int percent = (int) Math.round(Math.floor((totalLength
+ / (double) filesize) * 100));
+
+ if (percent > percentTransmitted) {
+ if (filesize < 1048576) {
+ if (percent % 10 == 0) {
+ if (percent == 100) {
+ System.out.println(" 100%");
+ } else {
+ System.out.print("*");
+ }
+ }
+ } else {
+ if (percent == 50) {
+ System.out.println(" 50%");
+ } else if (percent == 100) {
+ System.out.println(" 100%");
+ } else {
+ System.out.print(".");
+ }
+ }
+ }
+ // CheckStyle:MagicNumber ON
+
+ return percent;
+ }
+
+ private ProgressMonitor monitor = null;
+
+ /**
+ * Get the progress monitor.
+ * @return the progress monitor.
+ */
+ protected SftpProgressMonitor getProgressMonitor() {
+ if (monitor == null) {
+ monitor = new ProgressMonitor();
+ }
+ return monitor;
+ }
+
+ private class ProgressMonitor implements SftpProgressMonitor {
+ private long initFileSize = 0;
+ private long totalLength = 0;
+ private int percentTransmitted = 0;
+
+ public void init(final int op, final String src, final String dest, final long max) {
+ initFileSize = max;
+ totalLength = 0;
+ percentTransmitted = 0;
+ }
+
+ public boolean count(final long len) {
+ totalLength += len;
+ percentTransmitted = trackProgress(initFileSize,
+ totalLength,
+ percentTransmitted);
+ return true;
+ }
+
+ public void end() {
+ }
+
+ public long getTotalLength() {
+ return totalLength;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Directory.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Directory.java
new file mode 100644
index 00000000..b5088a7d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Directory.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.tools.ant.taskdefs.optional.ssh;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+/**
+ * A helper object for Scp representing a directory in a file system.
+ */
+public class Directory {
+
+ private File directory;
+ private Set childDirectories;
+ private ArrayList files;
+ private Directory parent;
+
+ /**
+ * Constructor for a Directory.
+ * @param directory a directory.
+ */
+ public Directory(File directory) {
+ this(directory, null);
+ }
+
+ /**
+ * Constructor for a Directory.
+ * @param directory a directory
+ * @param parent a parent Directory
+ */
+ public Directory(File directory , Directory parent) {
+ this.parent = parent;
+ this.childDirectories = new LinkedHashSet();
+ this.files = new ArrayList();
+ this.directory = directory;
+ }
+
+ /**
+ * Add a directory to the child directories.
+ * @param directory a Directory
+ */
+ public void addDirectory(Directory directory) {
+ if (!childDirectories.contains(directory)) {
+ childDirectories.add(directory);
+ }
+ }
+
+ /**
+ * Add a file to the list of files.
+ * @param file a file to add
+ */
+ public void addFile(File file) {
+ files.add(file);
+ }
+
+ /**
+ * Get an iterator over the child Directories.
+ * @return an iterator
+ */
+ public Iterator directoryIterator() {
+ return childDirectories.iterator();
+ }
+
+ /**
+ * Get an iterator over the files.
+ * @return an iterator
+ */
+ public Iterator filesIterator() {
+ return files.iterator();
+ }
+
+ /**
+ * Get the parent Directory.
+ * @return the parent Directory.
+ */
+ public Directory getParent() {
+ return parent;
+ }
+
+ /**
+ * Is this a root Directory?
+ * @return true if there is no parent Directory
+ */
+ public boolean isRoot() {
+ return parent == null;
+ }
+
+ /**
+ * Get the directory file.
+ * @return the directory file
+ */
+ public File getDirectory() {
+ return directory;
+ }
+
+ /**
+ * Get a child directory of this directory.
+ * @param dir the directory to look for
+ * @return the child directory, or null if not found
+ */
+ public Directory getChild(File dir) {
+ for (Iterator i = childDirectories.iterator(); i.hasNext();) {
+ Directory current = (Directory) i.next();
+ if (current.getDirectory().equals(dir)) {
+ return current;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * The equality method.
+ * This checks if the directory field is the same.
+ * @param obj the object to compare to
+ * @return true if this object has an equal directory field as the other object
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (!(obj instanceof Directory)) {
+ return false;
+ }
+
+ Directory d = (Directory) obj;
+
+ return this.directory.equals(d.directory);
+ }
+
+ /**
+ * The hashcode method.
+ * @return the hash code of the directory field
+ */
+ @Override
+ public int hashCode() {
+ return directory.hashCode();
+ }
+
+ /**
+ * Get the path components of this directory.
+ * @return the path components as an array of strings.
+ */
+ public String[] getPath() {
+ return getPath(directory.getAbsolutePath());
+ }
+
+ /**
+ * Convert a file path to an array of path components.
+ * This uses File.separator to split the file path string.
+ * @param thePath the file path string to convert
+ * @return an array of path components
+ */
+ public static String[] getPath(String thePath) {
+ StringTokenizer tokenizer = new StringTokenizer(thePath,
+ File.separator);
+ String[] path = new String[ tokenizer.countTokens() ];
+
+ int i = 0;
+ while (tokenizer.hasMoreTokens()) {
+ path[i] = tokenizer.nextToken();
+ i++;
+ }
+
+ return path;
+ }
+
+ /**
+ * Get the number of files in the files attribute.
+ * @return the number of files
+ */
+ public int fileSize() {
+ return files.size();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/LogListener.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/LogListener.java
new file mode 100644
index 00000000..41209ceb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/LogListener.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.tools.ant.taskdefs.optional.ssh;
+
+/**
+ * Interface for ssh log listeners to implement.
+ */
+public interface LogListener {
+ /**
+ * Method for the loglistener to implement to receive log messages.
+ * @param message the message to log
+ */
+ void log(String message);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHBase.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHBase.java
new file mode 100644
index 00000000..68419a85
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHBase.java
@@ -0,0 +1,236 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ssh;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+/**
+ * Base class for Ant tasks using jsch.
+ *
+ * @since Ant 1.6
+ */
+public abstract class SSHBase extends Task implements LogListener {
+
+ /** Default listen port for SSH daemon */
+ private static final int SSH_PORT = 22;
+
+ private String host;
+ private String knownHosts;
+ private int port = SSH_PORT;
+ private boolean failOnError = true;
+ private boolean verbose;
+ private final SSHUserInfo userInfo;
+
+ /**
+ * Constructor for SSHBase.
+ */
+ public SSHBase() {
+ super();
+ userInfo = new SSHUserInfo();
+ }
+
+ /**
+ * Remote host, either DNS name or IP.
+ *
+ * @param host The new host value
+ */
+ public void setHost(final String host) {
+ this.host = host;
+ }
+
+ /**
+ * Get the host.
+ * @return the host
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * Set the failonerror flag.
+ * Default is true
+ * @param failure if true throw a build exception when a failure occuries,
+ * otherwise just log the failure and continue
+ */
+ public void setFailonerror(final boolean failure) {
+ failOnError = failure;
+ }
+
+ /**
+ * Get the failonerror flag.
+ * @return the failonerror flag
+ */
+ public boolean getFailonerror() {
+ return failOnError;
+ }
+
+ /**
+ * Set the verbose flag.
+ * @param verbose if true output more verbose logging
+ * @since Ant 1.6.2
+ */
+ public void setVerbose(final boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ /**
+ * Get the verbose flag.
+ * @return the verbose flag
+ * @since Ant 1.6.2
+ */
+ public boolean getVerbose() {
+ return verbose;
+ }
+
+ /**
+ * Username known to remote host.
+ *
+ * @param username The new username value
+ */
+ public void setUsername(final String username) {
+ userInfo.setName(username);
+ }
+
+
+ /**
+ * Sets the password for the user.
+ *
+ * @param password The new password value
+ */
+ public void setPassword(final String password) {
+ userInfo.setPassword(password);
+ }
+
+ /**
+ * Sets the keyfile for the user.
+ *
+ * @param keyfile The new keyfile value
+ */
+ public void setKeyfile(final String keyfile) {
+ userInfo.setKeyfile(keyfile);
+ }
+
+ /**
+ * Sets the passphrase for the users key.
+ *
+ * @param passphrase The new passphrase value
+ */
+ public void setPassphrase(final String passphrase) {
+ userInfo.setPassphrase(passphrase);
+ }
+
+ /**
+ * Sets the path to the file that has the identities of
+ * all known hosts. This is used by SSH protocol to validate
+ * the identity of the host. The default is
+ * <i>${user.home}/.ssh/known_hosts</i>.
+ *
+ * @param knownHosts a path to the known hosts file.
+ */
+ public void setKnownhosts(final String knownHosts) {
+ this.knownHosts = knownHosts;
+ }
+
+ /**
+ * Setting this to true trusts hosts whose identity is unknown.
+ *
+ * @param yesOrNo if true trust the identity of unknown hosts.
+ */
+ public void setTrust(final boolean yesOrNo) {
+ userInfo.setTrust(yesOrNo);
+ }
+
+ /**
+ * Changes the port used to connect to the remote host.
+ *
+ * @param port port number of remote host.
+ */
+ public void setPort(final int port) {
+ this.port = port;
+ }
+
+ /**
+ * Get the port attribute.
+ * @return the port
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * Initialize the task.
+ * This initializizs the known hosts and sets the default port.
+ * @throws BuildException on error
+ */
+ public void init() throws BuildException {
+ super.init();
+ this.knownHosts = System.getProperty("user.home") + "/.ssh/known_hosts";
+ this.port = SSH_PORT;
+ }
+
+ /**
+ * Open an ssh session.
+ * @return the opened session
+ * @throws JSchException on error
+ */
+ protected Session openSession() throws JSchException {
+ final JSch jsch = new JSch();
+ final SSHBase base = this;
+ if (verbose) {
+ JSch.setLogger(new com.jcraft.jsch.Logger(){
+ public boolean isEnabled(final int level){
+ return true;
+ }
+ public void log(final int level, final String message){
+ base.log(message, Project.MSG_INFO);
+ }
+ });
+ }
+ if (null != userInfo.getKeyfile()) {
+ jsch.addIdentity(userInfo.getKeyfile());
+ }
+
+ if (!userInfo.getTrust() && knownHosts != null) {
+ log("Using known hosts: " + knownHosts, Project.MSG_DEBUG);
+ jsch.setKnownHosts(knownHosts);
+ }
+
+ final Session session = jsch.getSession(userInfo.getName(), host, port);
+ session.setConfig("PreferredAuthentications",
+ "publickey,keyboard-interactive,password");
+ session.setUserInfo(userInfo);
+ log("Connecting to " + host + ":" + port);
+ session.connect();
+ return session;
+ }
+
+ /**
+ * Get the user information.
+ * @return the user information
+ */
+ protected SSHUserInfo getUserInfo() {
+ return userInfo;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java
new file mode 100644
index 00000000..a04dfefa
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java
@@ -0,0 +1,519 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ssh;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.StringReader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.KeepAliveInputStream;
+import org.apache.tools.ant.util.KeepAliveOutputStream;
+import org.apache.tools.ant.util.TeeOutputStream;
+
+import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+/**
+ * Executes a command on a remote machine via ssh.
+ * @since Ant 1.6 (created February 2, 2003)
+ */
+public class SSHExec extends SSHBase {
+
+ private static final int BUFFER_SIZE = 8192;
+ private static final int RETRY_INTERVAL = 500;
+
+ /** the command to execute via ssh */
+ private String command = null;
+
+ /** units are milliseconds, default is 0=infinite */
+ private long maxwait = 0;
+
+ /** for waiting for the command to finish */
+ private Thread thread = null;
+
+ private String outputProperty = null; // like <exec>
+ private String errorProperty = null;
+ private String resultProperty = null;
+ private File outputFile = null; // like <exec>
+ private File errorFile = null;
+ private String inputProperty = null;
+ private String inputString = null; // like <exec>
+ private File inputFile = null; // like <exec>
+ private boolean append = false; // like <exec>
+ private boolean appenderr = false;
+ private boolean usePty = false;
+ private boolean useSystemIn = false;
+
+ private Resource commandResource = null;
+
+ private static final String TIMEOUT_MESSAGE =
+ "Timeout period exceeded, connection dropped.";
+
+ /**
+ * To suppress writing logs to System.out
+ */
+ private boolean suppressSystemOut = false;
+
+ /**
+ * To suppress writing logs to System.err
+ */
+ private boolean suppressSystemErr = false;
+
+ /**
+ * Constructor for SSHExecTask.
+ */
+ public SSHExec() {
+ super();
+ }
+
+ /**
+ * Sets the command to execute on the remote host.
+ *
+ * @param command The new command value
+ */
+ public void setCommand(final String command) {
+ this.command = command;
+ }
+
+ /**
+ * Sets a commandResource from a file
+ * @param f the value to use.
+ * @since Ant 1.7.1
+ */
+ public void setCommandResource(final String f) {
+ this.commandResource = new FileResource(new File(f));
+ }
+
+ /**
+ * The connection can be dropped after a specified number of
+ * milliseconds. This is sometimes useful when a connection may be
+ * flaky. Default is 0, which means &quot;wait forever&quot;.
+ *
+ * @param timeout The new timeout value in seconds
+ */
+ public void setTimeout(final long timeout) {
+ maxwait = timeout;
+ }
+
+ /**
+ * If used, stores the output of the command to the given file.
+ *
+ * @param output The file to write to.
+ */
+ public void setOutput(final File output) {
+ outputFile = output;
+ }
+
+ /**
+ * If used, stores the erroutput of the command to the given file.
+ *
+ * @param output The file to write to.
+ * @since Apache Ant 1.9.4
+ */
+ public void setErrorOutput(final File output) {
+ errorFile = output;
+ }
+
+ /**
+ * If used, the content of the file is piped to the remote command
+ *
+ * @param input The file which provides the input data for the remote command
+ *
+ * @since Ant 1.8.0
+ */
+ public void setInput(final File input) {
+ inputFile = input;
+ }
+
+ /**
+ * If used, the content of the property is piped to the remote command
+ *
+ * @param inputProperty The property which contains the input data
+ * for the remote command.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setInputProperty(final String inputProperty) {
+ this.inputProperty = inputProperty;
+ }
+
+ /**
+ * If used, the string is piped to the remote command.
+ *
+ * @param inputString the input data for the remote command.
+ *
+ * @since Ant 1.8.3
+ */
+ public void setInputString(final String inputString) {
+ this.inputString = inputString;
+ }
+
+ /**
+ * Determines if the output is appended to the file given in
+ * <code>setOutput</code>. Default is false, that is, overwrite
+ * the file.
+ *
+ * @param append True to append to an existing file, false to overwrite.
+ */
+ public void setAppend(final boolean append) {
+ this.append = append;
+ }
+
+ /**
+ * Determines if the output is appended to the file given in
+ * <code>setErrorOutput</code>. Default is false, that is, overwrite
+ * the file.
+ *
+ * @param appenderr True to append to an existing file, false to overwrite.
+ * @since Apache Ant 1.9.4
+ */
+ public void setErrAppend(final boolean appenderr) {
+ this.appenderr = appenderr;
+ }
+
+ /**
+ * If set, the output of the command will be stored in the given property.
+ *
+ * @param property The name of the property in which the command output
+ * will be stored.
+ */
+ public void setOutputproperty(final String property) {
+ outputProperty = property;
+ }
+
+ /**
+ * If set, the erroroutput of the command will be stored in the given property.
+ *
+ * @param property The name of the property in which the command erroroutput
+ * will be stored.
+ * @since Apache Ant 1.9.4
+ */
+ public void setErrorproperty (final String property) {
+ errorProperty = property;
+ }
+
+ /**
+ * If set, the exitcode of the command will be stored in the given property.
+ *
+ * @param property The name of the property in which the exitcode
+ * will be stored.
+ * @since Apache Ant 1.9.4
+ */
+ public void setResultproperty(final String property) {
+ resultProperty = property;
+ }
+
+ /**
+ * Whether a pseudo-tty should be allocated.
+ * @since Apache Ant 1.8.3
+ */
+ public void setUsePty(final boolean b) {
+ usePty = b;
+ }
+
+ /**
+ * If set, input will be taken from System.in
+ *
+ * @param useSystemIn True to use System.in as InputStream, false otherwise
+ * @since Apache Ant 1.9.4
+ */
+ public void setUseSystemIn(final boolean useSystemIn) {
+ this.useSystemIn = useSystemIn;
+ }
+
+ /**
+ * If suppressSystemOut is <code>true</code>, output will not be sent to System.out<br/>
+ * If suppressSystemOut is <code>false</code>, normal behavior
+ * @since Ant 1.9.0
+ */
+ public void setSuppressSystemOut(final boolean suppressSystemOut) {
+ this.suppressSystemOut = suppressSystemOut;
+ }
+
+ /**
+ * If suppressSystemErr is <code>true</code>, output will not be sent to System.err<br/>
+ * If suppressSystemErr is <code>false</code>, normal behavior
+ * @since Ant 1.9.4
+ */
+ public void setSuppressSystemErr(final boolean suppressSystemErr) {
+ this.suppressSystemErr = suppressSystemErr;
+ }
+
+ /**
+ * Execute the command on the remote host.
+ *
+ * @exception BuildException Most likely a network error or bad parameter.
+ */
+ @Override
+ public void execute() throws BuildException {
+
+ if (getHost() == null) {
+ throw new BuildException("Host is required.");
+ }
+ if (getUserInfo().getName() == null) {
+ throw new BuildException("Username is required.");
+ }
+ if (getUserInfo().getKeyfile() == null
+ && getUserInfo().getPassword() == null) {
+ throw new BuildException("Password or Keyfile is required.");
+ }
+ if (command == null && commandResource == null) {
+ throw new BuildException("Command or commandResource is required.");
+ }
+
+ final int numberOfInputs = (inputFile != null ? 1 : 0)
+ + (inputProperty != null ? 1 : 0)
+ + (inputString != null ? 1 : 0);
+ if (numberOfInputs > 1) {
+ throw new BuildException("You can't specify more than one of"
+ + " inputFile, inputProperty and"
+ + " inputString.");
+ }
+ if (inputFile != null && !inputFile.exists()) {
+ throw new BuildException("The input file "
+ + inputFile.getAbsolutePath()
+ + " does not exist.");
+ }
+
+ Session session = null;
+ final StringBuffer output = new StringBuffer();
+ try {
+ session = openSession();
+ /* called once */
+ if (command != null) {
+ log("cmd : " + command, Project.MSG_INFO);
+ executeCommand(session, command, output);
+ } else { // read command resource and execute for each command
+ try {
+ final BufferedReader br = new BufferedReader(
+ new InputStreamReader(commandResource.getInputStream()));
+ String cmd;
+ while ((cmd = br.readLine()) != null) {
+ log("cmd : " + cmd, Project.MSG_INFO);
+ output.append(cmd).append(" : ");
+ executeCommand(session, cmd, output);
+ output.append("\n");
+ }
+ FileUtils.close(br);
+ } catch (final IOException e) {
+ if (getFailonerror()) {
+ throw new BuildException(e);
+ } else {
+ log("Caught exception: " + e.getMessage(),
+ Project.MSG_ERR);
+ }
+ }
+ }
+ } catch (final JSchException e) {
+ if (getFailonerror()) {
+ throw new BuildException(e);
+ } else {
+ log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
+ }
+ } finally {
+ if (outputProperty != null) {
+ getProject().setNewProperty(outputProperty, output.toString());
+ }
+ if (session != null && session.isConnected()) {
+ session.disconnect();
+ }
+ }
+ }
+
+ private void executeCommand(final Session session, final String cmd, final StringBuffer sb)
+ throws BuildException {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ final ByteArrayOutputStream errout = new ByteArrayOutputStream();
+ final OutputStream teeErr = suppressSystemErr ? errout : new TeeOutputStream(errout, KeepAliveOutputStream.wrapSystemErr());
+ final OutputStream tee = suppressSystemOut ? out : new TeeOutputStream(out, KeepAliveOutputStream.wrapSystemOut());
+
+ InputStream istream = null;
+ if (inputFile != null) {
+ try {
+ istream = new FileInputStream(inputFile);
+ } catch (final IOException e) {
+ // because we checked the existence before, this one
+ // shouldn't happen What if the file exists, but there
+ // are no read permissions?
+ log("Failed to read " + inputFile + " because of: "
+ + e.getMessage(), Project.MSG_WARN);
+ }
+ }
+ if (inputProperty != null) {
+ final String inputData = getProject().getProperty(inputProperty);
+ if (inputData != null) {
+ istream = new ByteArrayInputStream(inputData.getBytes());
+ }
+ }
+ if (inputString != null) {
+ istream = new ByteArrayInputStream(inputString.getBytes());
+ }
+
+ if (useSystemIn) {
+ istream = KeepAliveInputStream.wrapSystemIn();
+ }
+
+ try {
+ final ChannelExec channel;
+ session.setTimeout((int) maxwait);
+ /* execute the command */
+ channel = (ChannelExec) session.openChannel("exec");
+ channel.setCommand(cmd);
+ channel.setOutputStream(tee);
+ channel.setExtOutputStream(tee);
+ channel.setErrStream(teeErr);
+ if (istream != null) {
+ channel.setInputStream(istream);
+ }
+ channel.setPty(usePty);
+ channel.connect();
+ // wait for it to finish
+ thread =
+ new Thread() {
+ @Override
+ public void run() {
+ while (!channel.isClosed()) {
+ if (thread == null) {
+ return;
+ }
+ try {
+ sleep(RETRY_INTERVAL);
+ } catch (final Exception e) {
+ // ignored
+ }
+ }
+ }
+ };
+
+ thread.start();
+ thread.join(maxwait);
+
+ if (thread.isAlive()) {
+ // ran out of time
+ thread = null;
+ if (getFailonerror()) {
+ throw new BuildException(TIMEOUT_MESSAGE);
+ } else {
+ log(TIMEOUT_MESSAGE, Project.MSG_ERR);
+ }
+ } else {
+ // stdout to outputFile
+ if (outputFile != null) {
+ writeToFile(out.toString(), append, outputFile);
+ }
+ // set errorProperty
+ if (errorProperty != null) {
+ getProject().setNewProperty(errorProperty, errout.toString());
+ }
+ // stderr to errorFile
+ if (errorFile != null) {
+ writeToFile(errout.toString(), appenderr, errorFile);
+ }
+ // this is the wrong test if the remote OS is OpenVMS,
+ // but there doesn't seem to be a way to detect it.
+ final int ec = channel.getExitStatus();
+ // set resultproperty
+ if (resultProperty != null) {
+ getProject().setNewProperty(resultProperty, Integer.toString(ec));
+ }
+ if (ec != 0) {
+ final String msg = "Remote command failed with exit status " + ec;
+ if (getFailonerror()) {
+ throw new BuildException(msg);
+ } else {
+ log(msg, Project.MSG_ERR);
+ }
+ }
+ }
+ } catch (final BuildException e) {
+ throw e;
+ } catch (final JSchException e) {
+ if (e.getMessage().indexOf("session is down") >= 0) {
+ if (getFailonerror()) {
+ throw new BuildException(TIMEOUT_MESSAGE, e);
+ } else {
+ log(TIMEOUT_MESSAGE, Project.MSG_ERR);
+ }
+ } else {
+ if (getFailonerror()) {
+ throw new BuildException(e);
+ } else {
+ log("Caught exception: " + e.getMessage(),
+ Project.MSG_ERR);
+ }
+ }
+ } catch (final Exception e) {
+ if (getFailonerror()) {
+ throw new BuildException(e);
+ } else {
+ log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
+ }
+ } finally {
+ sb.append(out.toString());
+ FileUtils.close(istream);
+ }
+ }
+
+ /**
+ * Writes a string to a file. If destination file exists, it may be
+ * overwritten depending on the "append" value.
+ *
+ * @param from string to write
+ * @param to file to write to
+ * @param append if true, append to existing file, else overwrite
+ * @exception Exception most likely an IOException
+ */
+ private void writeToFile(final String from, final boolean append, final File to)
+ throws IOException {
+ FileWriter out = null;
+ try {
+ out = new FileWriter(to.getAbsolutePath(), append);
+ final StringReader in = new StringReader(from);
+ final char[] buffer = new char[BUFFER_SIZE];
+ int bytesRead;
+ while (true) {
+ bytesRead = in.read(buffer);
+ if (bytesRead == -1) {
+ break;
+ }
+ out.write(buffer, 0, bytesRead);
+ }
+ out.flush();
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHSession.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHSession.java
new file mode 100644
index 00000000..e9f26757
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHSession.java
@@ -0,0 +1,333 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ssh;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+
+/**
+ * Establishes an ssh session with a remote machine, optionally
+ * establishing port forwarding, then executes any nested task(s)
+ * before closing the session.
+ * @since Ant 1.8.0
+ */
+public class SSHSession extends SSHBase {
+
+ /** units are milliseconds, default is 0=infinite */
+ private long maxwait = 0;
+
+ private final Vector localTunnels = new Vector();
+ private final Set localPortsUsed = new TreeSet();
+ private final Vector remoteTunnels = new Vector();
+ private final Set remotePortsUsed = new TreeSet();
+ private NestedSequential nestedSequential = null;
+
+ private static final String TIMEOUT_MESSAGE =
+ "Timeout period exceeded, connection dropped.";
+
+
+ /** Optional Vector holding the nested tasks */
+ private final Vector nestedTasks = new Vector();
+
+ /**
+ * Add a nested task to Sequential.
+ * <p>
+ * @param nestedTask Nested task to execute Sequential
+ * <p>
+ */
+ public void addTask(final Task nestedTask) {
+ nestedTasks.addElement(nestedTask);
+ }
+
+ /**
+ * The connection can be dropped after a specified number of
+ * milliseconds. This is sometimes useful when a connection may be
+ * flaky. Default is 0, which means &quot;wait forever&quot;.
+ *
+ * @param timeout The new timeout value in seconds
+ */
+ public void setTimeout(final long timeout) {
+ maxwait = timeout;
+ }
+
+ /**
+ * Changes the comma-delimited list of local tunnels to establish
+ * on the connection.
+ *
+ * @param tunnels a comma-delimited list of lport:rhost:rport
+ * tunnel specifications
+ */
+ public void setLocaltunnels(final String tunnels) {
+ final String[] specs = tunnels.split(", ");
+ for (int i = 0; i < specs.length; i++) {
+ if (specs[i].length() > 0) {
+ final String[] spec = specs[i].split(":", 3);
+ final int lport = Integer.parseInt(spec[0]);
+ final String rhost = spec[1];
+ final int rport = Integer.parseInt(spec[2]);
+ final LocalTunnel tunnel = createLocalTunnel();
+ tunnel.setLPort(lport);
+ tunnel.setRHost(rhost);
+ tunnel.setRPort(rport);
+ }
+ }
+ }
+
+ /**
+ * Changes the comma-delimited list of remote tunnels to establish
+ * on the connection.
+ *
+ * @param tunnels a comma-delimited list of rport:lhost:lport
+ * tunnel specifications
+ */
+ public void setRemotetunnels(final String tunnels) {
+ final String[] specs = tunnels.split(", ");
+ for (int i = 0; i < specs.length; i++) {
+ if (specs[i].length() > 0) {
+ final String[] spec = specs[i].split(":", 3);
+ final int rport = Integer.parseInt(spec[0]);
+ final String lhost = spec[1];
+ final int lport = Integer.parseInt(spec[2]);
+ final RemoteTunnel tunnel = createRemoteTunnel();
+ tunnel.setRPort(rport);
+ tunnel.setLHost(lhost);
+ tunnel.setLPort(lport);
+ }
+ }
+ }
+
+
+ /**
+ * Establish the ssh session and execute all nestedTasks
+ *
+ * @exception BuildException if one of the nested tasks fails, or
+ * network error or bad parameter.
+ */
+ @Override
+ public void execute() throws BuildException {
+ if (getHost() == null) {
+ throw new BuildException("Host is required.");
+ }
+ if (getUserInfo().getName() == null) {
+ throw new BuildException("Username is required.");
+ }
+ if (getUserInfo().getKeyfile() == null
+ && getUserInfo().getPassword() == null) {
+ throw new BuildException("Password or Keyfile is required.");
+ }
+ if (nestedSequential == null) {
+ throw new BuildException("Missing sequential element.");
+ }
+
+
+ Session session = null;
+ try {
+ // establish the session
+ session = openSession();
+ session.setTimeout((int) maxwait);
+
+ for (final Iterator i = localTunnels.iterator(); i.hasNext();) {
+ final LocalTunnel tunnel = (LocalTunnel) i.next();
+ session.setPortForwardingL(tunnel.getLPort(),
+ tunnel.getRHost(),
+ tunnel.getRPort());
+ }
+
+ for (final Iterator i = remoteTunnels.iterator(); i.hasNext();) {
+ final RemoteTunnel tunnel = (RemoteTunnel) i.next();
+ session.setPortForwardingR(tunnel.getRPort(),
+ tunnel.getLHost(),
+ tunnel.getLPort());
+ }
+
+ for (final Iterator i = nestedSequential.getNested().iterator();
+ i.hasNext();) {
+ final Task nestedTask = (Task) i.next();
+ nestedTask.perform();
+ }
+ // completed successfully
+
+ } catch (final JSchException e) {
+ if (e.getMessage().indexOf("session is down") >= 0) {
+ if (getFailonerror()) {
+ throw new BuildException(TIMEOUT_MESSAGE, e);
+ } else {
+ log(TIMEOUT_MESSAGE, Project.MSG_ERR);
+ }
+ } else {
+ if (getFailonerror()) {
+ throw new BuildException(e);
+ } else {
+ log("Caught exception: " + e.getMessage(),
+ Project.MSG_ERR);
+ }
+ }
+ } catch (final BuildException e) {
+ // avoid wrapping it into yet another BuildException further down
+ throw e;
+ } catch (final Exception e) {
+ if (getFailonerror()) {
+ throw new BuildException(e);
+ } else {
+ log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
+ }
+ } finally {
+ if (session != null && session.isConnected()) {
+ session.disconnect();
+ }
+ }
+ }
+
+ public LocalTunnel createLocalTunnel() {
+ final LocalTunnel tunnel = new LocalTunnel();
+ localTunnels.add(tunnel);
+ return tunnel;
+ }
+
+ public RemoteTunnel createRemoteTunnel() {
+ final RemoteTunnel tunnel = new RemoteTunnel();
+ remoteTunnels.add(tunnel);
+ return tunnel;
+ }
+
+ public class LocalTunnel {
+ public LocalTunnel() {}
+
+ int lport = 0;
+ String rhost = null;
+ int rport = 0;
+ public void setLPort(final int lport) {
+ final Integer portKey = new Integer(lport);
+ if (localPortsUsed.contains(portKey)) {
+ throw new BuildException("Multiple local tunnels defined to"
+ + " use same local port " + lport);
+ }
+ localPortsUsed.add(portKey);
+ this.lport = lport;
+ }
+ public void setRHost(final String rhost) { this.rhost = rhost; }
+ public void setRPort(final int rport) { this.rport = rport; }
+ public int getLPort() {
+ if (lport == 0) {
+ throw new BuildException("lport is required for LocalTunnel.");
+ }
+ return lport;
+ }
+ public String getRHost() {
+ if (rhost == null) {
+ throw new BuildException("rhost is required for LocalTunnel.");
+ }
+ return rhost;
+ }
+ public int getRPort() {
+ if (rport == 0) {
+ throw new BuildException("rport is required for LocalTunnel.");
+ }
+ return rport;
+ }
+ }
+
+ public class RemoteTunnel {
+ public RemoteTunnel() {}
+
+ int lport = 0;
+ String lhost = null;
+ int rport = 0;
+ public void setLPort(final int lport) { this.lport = lport; }
+ public void setLHost(final String lhost) { this.lhost = lhost; }
+ public void setRPort(final int rport) {
+ final Integer portKey = new Integer(rport);
+ if (remotePortsUsed.contains(portKey)) {
+ throw new BuildException("Multiple remote tunnels defined to"
+ + " use same remote port " + rport);
+ }
+ remotePortsUsed.add(portKey);
+ this.rport = rport;
+ }
+ public int getLPort() {
+ if (lport == 0) {
+ throw new BuildException("lport is required for RemoteTunnel.");
+ }
+ return lport;
+ }
+ public String getLHost() {
+ if (lhost == null) {
+ throw new BuildException("lhost is required for RemoteTunnel.");
+ }
+ return lhost;
+ }
+ public int getRPort() {
+ if (rport == 0) {
+ throw new BuildException("rport is required for RemoteTunnel.");
+ }
+ return rport;
+ }
+ }
+
+ /**
+ * This is the sequential nested element of the macrodef.
+ *
+ * @return a sequential element to be configured.
+ */
+ public NestedSequential createSequential() {
+ if (this.nestedSequential != null) {
+ throw new BuildException("Only one sequential allowed");
+ }
+ this.nestedSequential = new NestedSequential();
+ return this.nestedSequential;
+ }
+
+ /**
+ * The class corresponding to the sequential nested element.
+ * This is a simple task container.
+ */
+ public static class NestedSequential implements TaskContainer {
+ private final List<Task> nested = new ArrayList<Task>();
+
+ /**
+ * Add a task or type to the container.
+ *
+ * @param task an unknown element.
+ */
+ public void addTask(final Task task) {
+ nested.add(task);
+ }
+
+ /**
+ * @return the list of unknown elements
+ */
+ public List<Task> getNested() {
+ return nested;
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHUserInfo.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHUserInfo.java
new file mode 100644
index 00000000..54e70293
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHUserInfo.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.tools.ant.taskdefs.optional.ssh;
+
+import com.jcraft.jsch.UIKeyboardInteractive;
+import com.jcraft.jsch.UserInfo;
+
+
+/**
+ * Class containing information on an SSH user.
+ */
+public class SSHUserInfo implements UserInfo, UIKeyboardInteractive {
+
+ private String name;
+ private String password = null;
+ private String keyfile;
+ private String passphrase = null;
+ private boolean trustAllCertificates;
+
+ /** Constructor for SSHUserInfo. */
+ public SSHUserInfo() {
+ super();
+ this.trustAllCertificates = false;
+ }
+
+ /**
+ * Constructor for SSHUserInfo.
+ * @param password the user's password
+ * @param trustAllCertificates if true trust hosts whose identity is unknown
+ */
+ public SSHUserInfo(String password, boolean trustAllCertificates) {
+ super();
+ this.password = password;
+ this.trustAllCertificates = trustAllCertificates;
+ }
+
+ /**
+ * Gets the user name.
+ * @return the user name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Gets the pass phrase of the user.
+ * @param message a message
+ * @return the passphrase
+ */
+ public String getPassphrase(String message) {
+ return passphrase;
+ }
+
+ /**
+ * Gets the user's password.
+ * @return the user's password
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * Prompts a string.
+ * @param str the string
+ * @return whether the string was prompted
+ */
+ public boolean prompt(String str) {
+ return false;
+ }
+
+ /**
+ * Indicates whether a retry was done.
+ * @return whether a retry was done
+ */
+ public boolean retry() {
+ return false;
+ }
+
+ /**
+ * Sets the name.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Sets the passphrase.
+ * @param passphrase The passphrase to set
+ */
+ public void setPassphrase(String passphrase) {
+ this.passphrase = passphrase;
+ }
+
+ /**
+ * Sets the password.
+ * @param password The password to set
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ /**
+ * Sets the trust.
+ * @param trust whether to trust or not.
+ */
+ public void setTrust(boolean trust) {
+ this.trustAllCertificates = trust;
+ }
+
+ /**
+ * @return whether to trust or not.
+ */
+ public boolean getTrust() {
+ return this.trustAllCertificates;
+ }
+
+ /**
+ * Returns the passphrase.
+ * @return String
+ */
+ public String getPassphrase() {
+ return passphrase;
+ }
+
+ /**
+ * Returns the keyfile.
+ * @return String
+ */
+ public String getKeyfile() {
+ return keyfile;
+ }
+
+ /**
+ * Sets the keyfile.
+ * @param keyfile The keyfile to set
+ */
+ public void setKeyfile(String keyfile) {
+ this.keyfile = keyfile;
+ }
+
+ /**
+ * Implement the UserInfo interface.
+ * @param message ignored
+ * @return true always
+ */
+ public boolean promptPassphrase(String message) {
+ return true;
+ }
+
+ /**
+ * Implement the UserInfo interface.
+ * @param passwordPrompt ignored
+ * @return true the first time this is called, false otherwise
+ */
+ public boolean promptPassword(String passwordPrompt) {
+ return true;
+ }
+
+ /**
+ * Implement the UserInfo interface.
+ * @param message ignored
+ * @return the value of trustAllCertificates
+ */
+ public boolean promptYesNo(String message) {
+ return trustAllCertificates;
+ }
+
+//why do we do nothing?
+ /**
+ * Implement the UserInfo interface (noop).
+ * @param message ignored
+ */
+ public void showMessage(String message) {
+ //log(message, Project.MSG_DEBUG);
+ }
+
+ /**
+ * Implementation of UIKeyboardInteractive#promptKeyboardInteractive.
+ * @param destination not used.
+ * @param name not used.
+ * @param instruction not used.
+ * @param prompt the method checks if this is one in length.
+ * @param echo the method checks if the first element is false.
+ * @return the password in an size one array if there is a password
+ * and if the prompt and echo checks pass.
+ */
+ public String[] promptKeyboardInteractive(String destination,
+ String name,
+ String instruction,
+ String[] prompt,
+ boolean[] echo) {
+ if (prompt.length != 1 || echo[0] || this.password == null) {
+ return null;
+ }
+ String[] response = new String[1];
+ response[0] = this.password;
+ return response;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java
new file mode 100644
index 00000000..46e2ac64
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java
@@ -0,0 +1,486 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ssh;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+/**
+ * Ant task for sending files to remote machine over ssh/scp.
+ *
+ * @since Ant 1.6
+ */
+public class Scp extends SSHBase {
+
+ private static final String[] FROM_ATTRS = {
+ "file", "localfile", "remotefile" };
+
+ private static final String[] TO_ATTRS = {
+ "todir", "localtodir", "remotetodir", "localtofile", "remotetofile" };
+
+ private String fromUri;
+ private String toUri;
+ private boolean preserveLastModified = false;
+ private List fileSets = null;
+ private boolean isFromRemote, isToRemote;
+ private boolean isSftp = false;
+ private Integer fileMode, dirMode;
+
+ /**
+ * Sets the file to be transferred. This can either be a remote
+ * file or a local file. Remote files take the form:<br>
+ * <i>user:password@host:/directory/path/file.example</i><br>
+ * Files to transfer can also include a wildcard to include all
+ * files in a remote directory. For example:<br>
+ * <i>user:password@host:/directory/path/*</i><br>
+ * @param aFromUri a string representing the file to transfer.
+ */
+ public void setFile(final String aFromUri) {
+ setFromUri(aFromUri);
+ this.isFromRemote = isRemoteUri(this.fromUri);
+ }
+
+ /**
+ * Sets the location where files will be transferred to.
+ * This can either be a remote directory or a local directory.
+ * Remote directories take the form of:<br>
+ * <i>user:password@host:/directory/path/</i><br>
+ * This parameter is required.
+
+ * @param aToUri a string representing the target of the copy.
+ */
+ public void setTodir(final String aToUri) {
+ setToUri(aToUri);
+ this.isToRemote = isRemoteUri(this.toUri);
+ }
+
+ /**
+ * Similar to {@link #setFile setFile} but explicitly states that
+ * the file is a local file. This is the only way to specify a
+ * local file with a @ character.
+ * @param aFromUri a string representing the source of the copy.
+ * @since Ant 1.6.2
+ */
+ public void setLocalFile(final String aFromUri) {
+ setFromUri(aFromUri);
+ this.isFromRemote = false;
+ }
+
+ /**
+ * Similar to {@link #setFile setFile} but explicitly states that
+ * the file is a remote file.
+ * @param aFromUri a string representing the source of the copy.
+ * @since Ant 1.6.2
+ */
+ public void setRemoteFile(final String aFromUri) {
+ validateRemoteUri("remoteFile", aFromUri);
+ setFromUri(aFromUri);
+ this.isFromRemote = true;
+ }
+
+ /**
+ * Similar to {@link #setTodir setTodir} but explicitly states
+ * that the directory is a local. This is the only way to specify
+ * a local directory with a @ character.
+ * @param aToUri a string representing the target of the copy.
+ * @since Ant 1.6.2
+ */
+ public void setLocalTodir(final String aToUri) {
+ setToUri(aToUri);
+ this.isToRemote = false;
+ }
+
+ /**
+ * Sets flag to determine if file timestamp from
+ * remote system is to be preserved during copy.
+ * @since Ant 1.8.0
+ */
+ public void setPreservelastmodified(final boolean yesOrNo) {
+ this.preserveLastModified = yesOrNo;
+ }
+
+ /**
+ * Similar to {@link #setTodir setTodir} but explicitly states
+ * that the directory is a remote.
+ * @param aToUri a string representing the target of the copy.
+ * @since Ant 1.6.2
+ */
+ public void setRemoteTodir(final String aToUri) {
+ validateRemoteUri("remoteToDir", aToUri);
+ setToUri(aToUri);
+ this.isToRemote = true;
+ }
+
+ private static void validateRemoteUri(final String type, final String aToUri) {
+ if (!isRemoteUri(aToUri)) {
+ throw new BuildException(type + " '" + aToUri + "' is invalid. "
+ + "The 'remoteToDir' attribute must "
+ + "have syntax like the "
+ + "following: user:password@host:/path"
+ + " - the :password part is optional");
+ }
+ }
+
+ /**
+ * Changes the file name to the given name while receiving it,
+ * only useful if receiving a single file.
+ * @param aToUri a string representing the target of the copy.
+ * @since Ant 1.6.2
+ */
+ public void setLocalTofile(final String aToUri) {
+ setToUri(aToUri);
+ this.isToRemote = false;
+ }
+
+ /**
+ * Changes the file name to the given name while sending it,
+ * only useful if sending a single file.
+ * @param aToUri a string representing the target of the copy.
+ * @since Ant 1.6.2
+ */
+ public void setRemoteTofile(final String aToUri) {
+ validateRemoteUri("remoteToFile", aToUri);
+ setToUri(aToUri);
+ this.isToRemote = true;
+ }
+
+ /**
+ * Setting this to true to use sftp protocol.
+ *
+ * @param yesOrNo if true sftp protocol will be used.
+ */
+ public void setSftp(final boolean yesOrNo) {
+ isSftp = yesOrNo;
+ }
+
+ /**
+ * Set the file mode, defaults to "644".
+ * @since Ant 1.9.5
+ */
+ public void setFileMode(String fileMode) {
+ this.fileMode = Integer.parseInt(fileMode, 8);
+ }
+
+ /**
+ * Set the dir mode, defaults to "755".
+ * @since Ant 1.9.5
+ */
+ public void setDirMode(String dirMode) {
+ this.dirMode = Integer.parseInt(dirMode, 8);
+ }
+
+ /**
+ * Adds a FileSet transfer to remote host. NOTE: Either
+ * addFileSet() or setFile() are required. But, not both.
+ *
+ * @param set FileSet to send to remote host.
+ */
+ public void addFileset(final FileSet set) {
+ if (fileSets == null) {
+ fileSets = new LinkedList();
+ }
+ fileSets.add(set);
+ }
+
+ /**
+ * Initialize this task.
+ * @throws BuildException on error
+ */
+ @Override
+ public void init() throws BuildException {
+ super.init();
+ this.toUri = null;
+ this.fromUri = null;
+ this.fileSets = null;
+ }
+
+ /**
+ * Execute this task.
+ * @throws BuildException on error
+ */
+ @Override
+ public void execute() throws BuildException {
+ if (toUri == null) {
+ throw exactlyOne(TO_ATTRS);
+ }
+ if (fromUri == null && fileSets == null) {
+ throw exactlyOne(FROM_ATTRS, "one or more nested filesets");
+ }
+ try {
+ if (isFromRemote && !isToRemote) {
+ download(fromUri, toUri);
+ } else if (!isFromRemote && isToRemote) {
+ if (fileSets != null) {
+ upload(fileSets, toUri);
+ } else {
+ upload(fromUri, toUri);
+ }
+ } else if (isFromRemote && isToRemote) {
+ throw new BuildException(
+ "Copying from a remote server to a remote server is not supported.");
+ } else {
+ throw new BuildException("'todir' and 'file' attributes "
+ + "must have syntax like the following: "
+ + "user:password@host:/path");
+ }
+ } catch (final Exception e) {
+ if (getFailonerror()) {
+ if(e instanceof BuildException) {
+ final BuildException be = (BuildException) e;
+ if(be.getLocation() == null) {
+ be.setLocation(getLocation());
+ }
+ throw be;
+ } else {
+ throw new BuildException(e);
+ }
+ } else {
+ log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
+ }
+ }
+ }
+
+ private void download(final String fromSshUri, final String toPath)
+ throws JSchException, IOException {
+ final String file = parseUri(fromSshUri);
+
+ Session session = null;
+ try {
+ session = openSession();
+ ScpFromMessage message = null;
+ if (!isSftp) {
+ message =
+ new ScpFromMessage(getVerbose(), session, file,
+ getProject().resolveFile(toPath),
+ fromSshUri.endsWith("*"),
+ preserveLastModified);
+ } else {
+ message =
+ new ScpFromMessageBySftp(getVerbose(), session, file,
+ getProject().resolveFile(toPath),
+ fromSshUri.endsWith("*"),
+ preserveLastModified);
+ }
+ log("Receiving file: " + file);
+ message.setLogListener(this);
+ message.execute();
+ } finally {
+ if (session != null) {
+ session.disconnect();
+ }
+ }
+ }
+
+ private void upload(final List fileSet, final String toSshUri)
+ throws IOException, JSchException {
+ final String file = parseUri(toSshUri);
+
+ Session session = null;
+ try {
+ final List list = new ArrayList(fileSet.size());
+ for (final Iterator i = fileSet.iterator(); i.hasNext();) {
+ final FileSet set = (FileSet) i.next();
+ final Directory d = createDirectory(set);
+ if (d != null) {
+ list.add(d);
+ }
+ }
+ if (!list.isEmpty()) {
+ session = openSession();
+ ScpToMessage message = null;
+ if (!isSftp) {
+ message = new ScpToMessage(getVerbose(), session,
+ list, file);
+ } else {
+ message = new ScpToMessageBySftp(getVerbose(), session,
+ list, file);
+ }
+ message.setLogListener(this);
+ if (fileMode != null) {
+ message.setFileMode(fileMode.intValue());
+ }
+ if (dirMode != null) {
+ message.setDirMode(dirMode.intValue());
+ }
+ message.execute();
+ }
+ } finally {
+ if (session != null) {
+ session.disconnect();
+ }
+ }
+ }
+
+ private void upload(final String fromPath, final String toSshUri)
+ throws IOException, JSchException {
+ final String file = parseUri(toSshUri);
+
+ Session session = null;
+ try {
+ session = openSession();
+ ScpToMessage message = null;
+ if (!isSftp) {
+ message =
+ new ScpToMessage(getVerbose(), session,
+ getProject().resolveFile(fromPath), file);
+ } else {
+ message =
+ new ScpToMessageBySftp(getVerbose(), session,
+ getProject().resolveFile(fromPath),
+ file);
+ }
+ message.setLogListener(this);
+ if (fileMode != null) {
+ message.setFileMode(fileMode.intValue());
+ }
+ if (dirMode != null) {
+ message.setDirMode(dirMode.intValue());
+ }
+ message.execute();
+ } finally {
+ if (session != null) {
+ session.disconnect();
+ }
+ }
+ }
+
+ private String parseUri(final String uri) {
+
+ int indexOfAt = uri.indexOf('@');
+ final int indexOfColon = uri.indexOf(':');
+
+ if (indexOfColon > -1 && indexOfColon < indexOfAt) {
+ // user:password@host:/path notation
+ // everything upto the last @ before the last : is considered
+ // password. (so if the path contains an @ and a : it will not work)
+ int indexOfCurrentAt = indexOfAt;
+ final int indexOfLastColon = uri.lastIndexOf(':');
+ while (indexOfCurrentAt > -1 && indexOfCurrentAt < indexOfLastColon)
+ {
+ indexOfAt = indexOfCurrentAt;
+ indexOfCurrentAt = uri.indexOf('@', indexOfCurrentAt + 1);
+ }
+ setUsername(uri.substring(0, indexOfColon));
+ setPassword(uri.substring(indexOfColon + 1, indexOfAt));
+ } else if (indexOfAt > -1) {
+ // no password, will require keyfile
+ setUsername(uri.substring(0, indexOfAt));
+ } else {
+ throw new BuildException("no username was given. Can't authenticate.");
+ }
+
+ if (getUserInfo().getPassword() == null
+ && getUserInfo().getKeyfile() == null) {
+ throw new BuildException("neither password nor keyfile for user "
+ + getUserInfo().getName() + " has been "
+ + "given. Can't authenticate.");
+ }
+
+ final int indexOfPath = uri.indexOf(':', indexOfAt + 1);
+ if (indexOfPath == -1) {
+ throw new BuildException("no remote path in " + uri);
+ }
+
+ setHost(uri.substring(indexOfAt + 1, indexOfPath));
+ String remotePath = uri.substring(indexOfPath + 1);
+ if (remotePath.equals("")) {
+ remotePath = ".";
+ }
+ return remotePath;
+ }
+
+ private static boolean isRemoteUri(final String uri) {
+ boolean isRemote = true;
+ final int indexOfAt = uri.indexOf('@');
+ if (indexOfAt < 0) {
+ isRemote = false;
+ }
+ return isRemote;
+ }
+
+ private Directory createDirectory(final FileSet set) {
+ final DirectoryScanner scanner = set.getDirectoryScanner(getProject());
+ Directory root = new Directory(scanner.getBasedir());
+ final String[] files = scanner.getIncludedFiles();
+ if (files.length != 0) {
+ for (int j = 0; j < files.length; j++) {
+ final String[] path = Directory.getPath(files[j]);
+ Directory current = root;
+ File currentParent = scanner.getBasedir();
+ for (int i = 0; i < path.length; i++) {
+ final File file = new File(currentParent, path[i]);
+ if (file.isDirectory()) {
+ current.addDirectory(new Directory(file));
+ current = current.getChild(file);
+ currentParent = current.getDirectory();
+ } else if (file.isFile()) {
+ current.addFile(file);
+ }
+ }
+ }
+ } else {
+ // skip
+ root = null;
+ }
+ return root;
+ }
+
+ private void setFromUri(final String fromUri) {
+ if (this.fromUri != null) {
+ throw exactlyOne(FROM_ATTRS);
+ }
+ this.fromUri = fromUri;
+ }
+
+ private void setToUri(final String toUri) {
+ if (this.toUri != null) {
+ throw exactlyOne(TO_ATTRS);
+ }
+ this.toUri = toUri;
+ }
+
+ private BuildException exactlyOne(final String[] attrs) {
+ return exactlyOne(attrs, null);
+ }
+
+ private BuildException exactlyOne(final String[] attrs, final String alt) {
+ final StringBuffer buf = new StringBuffer("Exactly one of ").append(
+ '[').append(attrs[0]);
+ for (int i = 1; i < attrs.length; i++) {
+ buf.append('|').append(attrs[i]);
+ }
+ buf.append(']');
+ if (alt != null) {
+ buf.append(" or ").append(alt);
+ }
+ return new BuildException(buf.append(" is required.").toString());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java
new file mode 100644
index 00000000..b6d47379
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java
@@ -0,0 +1,311 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ssh;
+
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.util.FileUtils;
+
+import com.jcraft.jsch.Channel;
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpATTRS;
+import com.jcraft.jsch.SftpException;
+
+/**
+ * A helper object representing an scp download.
+ */
+public class ScpFromMessage extends AbstractSshMessage {
+
+ private static final int HUNDRED_KILOBYTES = 102400;
+ private static final byte LINE_FEED = 0x0a;
+ private static final int BUFFER_SIZE = 100*1024;
+
+ private String remoteFile;
+ private File localFile;
+ private boolean isRecursive = false;
+ private boolean preserveLastModified = false;
+
+ /**
+ * Constructor for ScpFromMessage
+ * @param session the ssh session to use
+ */
+ public ScpFromMessage(final Session session) {
+ super(session);
+ }
+
+ /**
+ * Constructor for ScpFromMessage
+ * @param verbose if true do verbose logging
+ * @param session the ssh session to use
+ * @since Ant 1.7
+ */
+ public ScpFromMessage(final boolean verbose, final Session session) {
+ super(verbose, session);
+ }
+
+ /**
+ * Constructor for ScpFromMessage.
+ * @param verbose if true log extra information
+ * @param session the Scp session to use
+ * @param aRemoteFile the remote file name
+ * @param aLocalFile the local file
+ * @param recursive if true use recursion (-r option to scp)
+ * @since Ant 1.6.2
+ */
+ public ScpFromMessage(final boolean verbose,
+ final Session session,
+ final String aRemoteFile,
+ final File aLocalFile,
+ final boolean recursive) {
+ this(false, session, aRemoteFile, aLocalFile, recursive, false);
+ }
+
+ /**
+ * Constructor for ScpFromMessage.
+ * @param session the Scp session to use
+ * @param aRemoteFile the remote file name
+ * @param aLocalFile the local file
+ * @param recursive if true use recursion (-r option to scp)
+ */
+ public ScpFromMessage(final Session session,
+ final String aRemoteFile,
+ final File aLocalFile,
+ final boolean recursive) {
+ this(false, session, aRemoteFile, aLocalFile, recursive);
+ }
+
+ /**
+ * Constructor for ScpFromMessage.
+ * @param verbose if true log extra information
+ * @param session the Scp session to use
+ * @param aRemoteFile the remote file name
+ * @param aLocalFile the local file
+ * @param recursive if true use recursion (-r option to scp)
+ * @param preserveLastModified whether to preserve file
+ * modification times
+ * @since Ant 1.8.0
+ */
+ public ScpFromMessage(final boolean verbose,
+ final Session session,
+ final String aRemoteFile,
+ final File aLocalFile,
+ final boolean recursive,
+ final boolean preserveLastModified) {
+ super(verbose, session);
+ this.remoteFile = aRemoteFile;
+ this.localFile = aLocalFile;
+ this.isRecursive = recursive;
+ this.preserveLastModified = preserveLastModified;
+ }
+
+ /**
+ * Carry out the transfer.
+ * @throws IOException on i/o errors
+ * @throws JSchException on errors detected by scp
+ */
+ public void execute() throws IOException, JSchException {
+ String command = "scp -f ";
+ if (isRecursive) {
+ command += "-r ";
+ }
+ command += remoteFile;
+ final Channel channel = openExecChannel(command);
+ try {
+ // get I/O streams for remote scp
+ final OutputStream out = channel.getOutputStream();
+ final InputStream in = channel.getInputStream();
+
+ channel.connect();
+
+ sendAck(out);
+ startRemoteCpProtocol(in, out, localFile);
+ } finally {
+ if (channel != null) {
+ channel.disconnect();
+ }
+ }
+ log("done\n");
+ }
+
+ protected boolean getPreserveLastModified() {
+ return preserveLastModified;
+ }
+
+ private void startRemoteCpProtocol(final InputStream in,
+ final OutputStream out,
+ final File localFile)
+ throws IOException, JSchException {
+ File startFile = localFile;
+ while (true) {
+ // C0644 filesize filename - header for a regular file
+ // T time 0 time 0\n - present if perserve time.
+ // D directory - this is the header for a directory.
+ final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ while (true) {
+ final int read = in.read();
+ if (read < 0) {
+ return;
+ }
+ if ((byte) read == LINE_FEED) {
+ break;
+ }
+ stream.write(read);
+ }
+ final String serverResponse = stream.toString("UTF-8");
+ if (serverResponse.charAt(0) == 'C') {
+ parseAndFetchFile(serverResponse, startFile, out, in);
+ } else if (serverResponse.charAt(0) == 'D') {
+ startFile = parseAndCreateDirectory(serverResponse,
+ startFile);
+ sendAck(out);
+ } else if (serverResponse.charAt(0) == 'E') {
+ startFile = startFile.getParentFile();
+ sendAck(out);
+ } else if (serverResponse.charAt(0) == '\01'
+ || serverResponse.charAt(0) == '\02') {
+ // this indicates an error.
+ throw new IOException(serverResponse.substring(1));
+ }
+ }
+ }
+
+ private File parseAndCreateDirectory(final String serverResponse,
+ final File localFile) {
+ int start = serverResponse.indexOf(" ");
+ // appears that the next token is not used and it's zero.
+ start = serverResponse.indexOf(" ", start + 1);
+ final String directoryName = serverResponse.substring(start + 1);
+ if (localFile.isDirectory()) {
+ final File dir = new File(localFile, directoryName);
+ dir.mkdir();
+ log("Creating: " + dir);
+ return dir;
+ }
+ return null;
+ }
+
+ private void parseAndFetchFile(final String serverResponse,
+ final File localFile,
+ final OutputStream out,
+ final InputStream in)
+ throws IOException, JSchException {
+ int start = 0;
+ int end = serverResponse.indexOf(" ", start + 1);
+ start = end + 1;
+ end = serverResponse.indexOf(" ", start + 1);
+ final long filesize = Long.parseLong(serverResponse.substring(start, end));
+ final String filename = serverResponse.substring(end + 1);
+ log("Receiving: " + filename + " : " + filesize);
+ final File transferFile = (localFile.isDirectory())
+ ? new File(localFile, filename)
+ : localFile;
+ fetchFile(transferFile, filesize, out, in);
+ waitForAck(in);
+ sendAck(out);
+ }
+
+ private void fetchFile(final File localFile,
+ long filesize,
+ final OutputStream out,
+ final InputStream in)
+ throws IOException, JSchException {
+ final byte[] buf = new byte[BUFFER_SIZE];
+ sendAck(out);
+
+ // read a content of lfile
+ final FileOutputStream fos = new FileOutputStream(localFile);
+ int length;
+ long totalLength = 0;
+ final long startTime = System.currentTimeMillis();
+
+ // only track progress for files larger than 100kb in verbose mode
+ final boolean trackProgress = getVerbose() && filesize > HUNDRED_KILOBYTES;
+ // since filesize keeps on decreasing we have to store the
+ // initial filesize
+ final long initFilesize = filesize;
+ int percentTransmitted = 0;
+
+ try {
+ while (true) {
+ length = in.read(buf, 0,
+ (BUFFER_SIZE < filesize) ? BUFFER_SIZE
+ : (int) filesize);
+ if (length < 0) {
+ throw new EOFException("Unexpected end of stream.");
+ }
+ fos.write(buf, 0, length);
+ filesize -= length;
+ totalLength += length;
+ if (filesize == 0) {
+ break;
+ }
+
+ if (trackProgress) {
+ percentTransmitted = trackProgress(initFilesize,
+ totalLength,
+ percentTransmitted);
+ }
+ }
+ } finally {
+ final long endTime = System.currentTimeMillis();
+ logStats(startTime, endTime, totalLength);
+ fos.flush();
+ fos.close();
+ }
+
+ if (getPreserveLastModified()) {
+ setLastModified(localFile);
+ }
+ }
+
+ private void setLastModified(final File localFile) throws JSchException {
+ SftpATTRS fileAttributes = null;
+ final ChannelSftp channel = openSftpChannel();
+ channel.connect();
+ try {
+ fileAttributes = channel.lstat(remoteDir(remoteFile)
+ + localFile.getName());
+ } catch (final SftpException e) {
+ throw new JSchException("failed to stat remote file", e);
+ }
+ FileUtils.getFileUtils().setFileLastModified(localFile,
+ ((long) fileAttributes
+ .getMTime())
+ * 1000);
+ }
+
+ /**
+ * returns the directory part of the remote file, if any.
+ */
+ private static String remoteDir(final String remoteFile) {
+ int index = remoteFile.lastIndexOf("/");
+ if (index < 0) {
+ index = remoteFile.lastIndexOf("\\");
+ }
+ return index > -1 ? remoteFile.substring(0, index + 1) : "";
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java
new file mode 100644
index 00000000..04f72d23
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java
@@ -0,0 +1,205 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ssh;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.util.FileUtils;
+
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpATTRS;
+import com.jcraft.jsch.SftpException;
+import com.jcraft.jsch.SftpProgressMonitor;
+
+/**
+ * A helper object representing an scp download.
+ */
+public class ScpFromMessageBySftp extends ScpFromMessage {
+
+ private static final int HUNDRED_KILOBYTES = 102400;
+
+ private String remoteFile;
+ private final File localFile;
+ private boolean isRecursive = false;
+ private boolean verbose = false;
+
+ /**
+ * Constructor for ScpFromMessageBySftp.
+ * @param verbose if true log extra information
+ * @param session the Scp session to use
+ * @param aRemoteFile the remote file name
+ * @param aLocalFile the local file
+ * @param recursive if true use recursion
+ * @since Ant 1.7
+ */
+ public ScpFromMessageBySftp(final boolean verbose,
+ final Session session,
+ final String aRemoteFile,
+ final File aLocalFile,
+ final boolean recursive) {
+ this(verbose, session, aRemoteFile, aLocalFile, recursive, false);
+ }
+
+ /**
+ * Constructor for ScpFromMessageBySftp.
+ * @param session the Scp session to use
+ * @param aRemoteFile the remote file name
+ * @param aLocalFile the local file
+ * @param recursive if true use recursion
+ */
+ public ScpFromMessageBySftp(final Session session,
+ final String aRemoteFile,
+ final File aLocalFile,
+ final boolean recursive) {
+ this(false, session, aRemoteFile, aLocalFile, recursive);
+ }
+
+ /**
+ * Constructor for ScpFromMessageBySftp.
+ * @param verbose if true log extra information
+ * @param session the Scp session to use
+ * @param aRemoteFile the remote file name
+ * @param aLocalFile the local file
+ * @param recursive if true use recursion
+ * @param preserveLastModified whether to preserve file
+ * modification times
+ * @since Ant 1.8.0
+ */
+ public ScpFromMessageBySftp(final boolean verbose,
+ final Session session,
+ final String aRemoteFile,
+ final File aLocalFile,
+ final boolean recursive,
+ final boolean preserveLastModified) {
+ super(verbose, session, aRemoteFile, aLocalFile, recursive,
+ preserveLastModified);
+ this.verbose = verbose;
+ this.remoteFile = aRemoteFile;
+ this.localFile = aLocalFile;
+ this.isRecursive = recursive;
+ }
+
+ /**
+ * Carry out the transfer.
+ * @throws IOException on i/o errors
+ * @throws JSchException on errors detected by scp
+ */
+ public void execute() throws IOException, JSchException {
+ final ChannelSftp channel = openSftpChannel();
+ try {
+ channel.connect();
+ try {
+ final SftpATTRS attrs = channel.stat(remoteFile);
+ if (attrs.isDir() && !remoteFile.endsWith("/")) {
+ remoteFile = remoteFile + "/";
+ }
+ } catch (final SftpException ee) {
+ // Ignored
+ }
+ getDir(channel, remoteFile, localFile);
+ } catch (final SftpException e) {
+ final JSchException schException = new JSchException("Could not get '"+ remoteFile
+ +"' to '"+localFile+"' - "
+ +e.toString());
+ schException.initCause(e);
+ throw schException;
+ } finally {
+ if (channel != null) {
+ channel.disconnect();
+ }
+ }
+ log("done\n");
+ }
+
+ private void getDir(final ChannelSftp channel,
+ final String remoteFile,
+ final File localFile) throws IOException, SftpException {
+ String pwd = remoteFile;
+ if (remoteFile.lastIndexOf('/') != -1) {
+ if (remoteFile.length() > 1) {
+ pwd = remoteFile.substring(0, remoteFile.lastIndexOf('/'));
+ }
+ }
+ channel.cd(pwd);
+ if (!localFile.exists()) {
+ localFile.mkdirs();
+ }
+ final java.util.Vector files = channel.ls(remoteFile);
+ final int size = files.size();
+ for (int i = 0; i < size; i++) {
+ final ChannelSftp.LsEntry le = (ChannelSftp.LsEntry) files.elementAt(i);
+ final String name = le.getFilename();
+ if (le.getAttrs().isDir()) {
+ if (name.equals(".") || name.equals("..")) {
+ continue;
+ }
+ getDir(channel,
+ channel.pwd() + "/" + name + "/",
+ new File(localFile, le.getFilename()));
+ } else {
+ getFile(channel, le, localFile);
+ }
+ }
+ channel.cd("..");
+ }
+
+ private void getFile(final ChannelSftp channel,
+ final ChannelSftp.LsEntry le,
+ File localFile) throws IOException, SftpException {
+ final String remoteFile = le.getFilename();
+ if (!localFile.exists()) {
+ final String path = localFile.getAbsolutePath();
+ final int i = path.lastIndexOf(File.pathSeparator);
+ if (i != -1) {
+ if (path.length() > File.pathSeparator.length()) {
+ new File(path.substring(0, i)).mkdirs();
+ }
+ }
+ }
+
+ if (localFile.isDirectory()) {
+ localFile = new File(localFile, remoteFile);
+ }
+
+ final long startTime = System.currentTimeMillis();
+ final long totalLength = le.getAttrs().getSize();
+
+ SftpProgressMonitor monitor = null;
+ final boolean trackProgress = getVerbose() && totalLength > HUNDRED_KILOBYTES;
+ if (trackProgress) {
+ monitor = getProgressMonitor();
+ }
+ try {
+ log("Receiving: " + remoteFile + " : " + le.getAttrs().getSize());
+ channel.get(remoteFile, localFile.getAbsolutePath(), monitor);
+ } finally {
+ final long endTime = System.currentTimeMillis();
+ logStats(startTime, endTime, (int) totalLength);
+ }
+ if (getPreserveLastModified()) {
+ FileUtils.getFileUtils().setFileLastModified(localFile,
+ ((long) le.getAttrs()
+ .getMTime())
+ * 1000);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java
new file mode 100644
index 00000000..5ba6b559
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java
@@ -0,0 +1,331 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ssh;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.List;
+
+import com.jcraft.jsch.Channel;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+/**
+ * Utility class to carry out an upload scp transfer.
+ */
+public class ScpToMessage extends AbstractSshMessage {
+
+ private static final int HUNDRED_KILOBYTES = 102400;
+ private static final int BUFFER_SIZE = 100*1024;
+ private static final int DEFAULT_DIR_MODE = 0755;
+ private static final int DEFAULT_FILE_MODE = 0644;
+
+ private File localFile;
+ private String remotePath;
+ private List directoryList;
+ private Integer fileMode, dirMode;
+
+ /**
+ * Constructor for ScpToMessage
+ * @param session the ssh session to use
+ */
+ public ScpToMessage(final Session session) {
+ super(session);
+ }
+
+ /**
+ * Constructor for ScpToMessage
+ * @param verbose if true do verbose logging
+ * @param session the ssh session to use
+ * @since Ant 1.7
+ */
+ public ScpToMessage(final boolean verbose, final Session session) {
+ super(verbose, session);
+ }
+
+ /**
+ * Constructor for a local file to remote.
+ * @param verbose if true do verbose logging
+ * @param session the scp session to use
+ * @param aLocalFile the local file
+ * @param aRemotePath the remote path
+ * @since Ant 1.6.2
+ */
+ public ScpToMessage(final boolean verbose,
+ final Session session,
+ final File aLocalFile,
+ final String aRemotePath) {
+ this(verbose, session, aRemotePath);
+
+ this.localFile = aLocalFile;
+ }
+
+ /**
+ * Constructor for a local directories to remote.
+ * @param verbose if true do verbose logging
+ * @param session the scp session to use
+ * @param aDirectoryList a list of directories
+ * @param aRemotePath the remote path
+ * @since Ant 1.6.2
+ */
+ public ScpToMessage(final boolean verbose,
+ final Session session,
+ final List aDirectoryList,
+ final String aRemotePath) {
+ this(verbose, session, aRemotePath);
+
+ this.directoryList = aDirectoryList;
+ }
+
+ /**
+ * Constructor for ScpToMessage.
+ * @param verbose if true do verbose logging
+ * @param session the scp session to use
+ * @param aRemotePath the remote path
+ * @since Ant 1.6.2
+ */
+ private ScpToMessage(final boolean verbose,
+ final Session session,
+ final String aRemotePath) {
+ super(verbose, session);
+ this.remotePath = aRemotePath;
+ }
+
+ /**
+ * Constructor for ScpToMessage.
+ * @param session the scp session to use
+ * @param aLocalFile the local file
+ * @param aRemotePath the remote path
+ */
+ public ScpToMessage(final Session session,
+ final File aLocalFile,
+ final String aRemotePath) {
+ this(false, session, aLocalFile, aRemotePath);
+ }
+
+ /**
+ * Constructor for ScpToMessage.
+ * @param session the scp session to use
+ * @param aDirectoryList a list of directories
+ * @param aRemotePath the remote path
+ */
+ public ScpToMessage(final Session session,
+ final List aDirectoryList,
+ final String aRemotePath) {
+ this(false, session, aDirectoryList, aRemotePath);
+ }
+
+ /**
+ * Carry out the transfer.
+ * @throws IOException on i/o errors
+ * @throws JSchException on errors detected by scp
+ */
+ @Override
+ public void execute() throws IOException, JSchException {
+ if (directoryList != null) {
+ doMultipleTransfer();
+ }
+ if (localFile != null) {
+ doSingleTransfer();
+ }
+ log("done.\n");
+ }
+
+ private void doSingleTransfer() throws IOException, JSchException {
+ final String cmd = "scp -t " + remotePath;
+ final Channel channel = openExecChannel(cmd);
+ try {
+
+ final OutputStream out = channel.getOutputStream();
+ final InputStream in = channel.getInputStream();
+
+ channel.connect();
+
+ waitForAck(in);
+ sendFileToRemote(localFile, in, out);
+ } finally {
+ if (channel != null) {
+ channel.disconnect();
+ }
+ }
+ }
+
+ private void doMultipleTransfer() throws IOException, JSchException {
+ final Channel channel = openExecChannel("scp -r -d -t " + remotePath);
+ try {
+ final OutputStream out = channel.getOutputStream();
+ final InputStream in = channel.getInputStream();
+
+ channel.connect();
+
+ waitForAck(in);
+ for (final Iterator i = directoryList.iterator(); i.hasNext();) {
+ final Directory current = (Directory) i.next();
+ sendDirectory(current, in, out);
+ }
+ } finally {
+ if (channel != null) {
+ channel.disconnect();
+ }
+ }
+ }
+
+ private void sendDirectory(final Directory current,
+ final InputStream in,
+ final OutputStream out) throws IOException {
+ for (final Iterator fileIt = current.filesIterator(); fileIt.hasNext();) {
+ sendFileToRemote((File) fileIt.next(), in, out);
+ }
+ for (final Iterator dirIt = current.directoryIterator(); dirIt.hasNext();) {
+ final Directory dir = (Directory) dirIt.next();
+ sendDirectoryToRemote(dir, in, out);
+ }
+ }
+
+ private void sendDirectoryToRemote(final Directory directory,
+ final InputStream in,
+ final OutputStream out) throws IOException {
+ String command = "D0";
+ command += Integer.toOctalString(getDirMode());
+ command += " 0 ";
+ command += directory.getDirectory().getName();
+ command += "\n";
+
+ out.write(command.getBytes());
+ out.flush();
+
+ waitForAck(in);
+ sendDirectory(directory, in, out);
+ out.write("E\n".getBytes());
+ out.flush();
+ waitForAck(in);
+ }
+
+ private void sendFileToRemote(final File localFile,
+ final InputStream in,
+ final OutputStream out) throws IOException {
+ // send "C0644 filesize filename", where filename should not include '/'
+ final long filesize = localFile.length();
+ String command = "C0";
+ command += Integer.toOctalString(getFileMode());
+ command += " " + filesize + " ";
+ command += localFile.getName();
+ command += "\n";
+
+ out.write(command.getBytes());
+ out.flush();
+
+ waitForAck(in);
+
+ // send a content of lfile
+ final FileInputStream fis = new FileInputStream(localFile);
+ final byte[] buf = new byte[BUFFER_SIZE];
+ final long startTime = System.currentTimeMillis();
+ long totalLength = 0;
+
+ // only track progress for files larger than 100kb in verbose mode
+ final boolean trackProgress = getVerbose() && filesize > HUNDRED_KILOBYTES;
+ // since filesize keeps on decreasing we have to store the
+ // initial filesize
+ final long initFilesize = filesize;
+ int percentTransmitted = 0;
+
+ try {
+ if (this.getVerbose()) {
+ log("Sending: " + localFile.getName() + " : " + localFile.length());
+ }
+ while (true) {
+ final int len = fis.read(buf, 0, buf.length);
+ if (len <= 0) {
+ break;
+ }
+ out.write(buf, 0, len);
+ totalLength += len;
+
+ if (trackProgress) {
+ percentTransmitted = trackProgress(initFilesize,
+ totalLength,
+ percentTransmitted);
+ }
+ }
+ out.flush();
+ sendAck(out);
+ waitForAck(in);
+ } finally {
+ if (this.getVerbose()) {
+ final long endTime = System.currentTimeMillis();
+ logStats(startTime, endTime, totalLength);
+ }
+ fis.close();
+ }
+ }
+
+ /**
+ * Get the local file
+ * @return the local file
+ */
+ public File getLocalFile() {
+ return localFile;
+ }
+
+ /**
+ * Get the remote path
+ * @return the remote path
+ */
+ public String getRemotePath() {
+ return remotePath;
+ }
+
+ /**
+ * Set the file mode, defaults to 0644.
+ * @since Ant 1.9.5
+ */
+ public void setFileMode(int fileMode) {
+ this.fileMode = fileMode;
+ }
+
+ /**
+ * Get the file mode.
+ * @since Ant 1.9.5
+ */
+ public int getFileMode() {
+ return fileMode != null ? fileMode.intValue() : DEFAULT_FILE_MODE;
+ }
+
+ /**
+ * Set the dir mode, defaults to 0755.
+ * @since Ant 1.9.5
+ */
+ public void setDirMode(int dirMode) {
+ this.dirMode = dirMode;
+ }
+
+ /**
+ * Get the dir mode.
+ * @since Ant 1.9.5
+ */
+ public int getDirMode() {
+ return dirMode != null ? dirMode.intValue() : DEFAULT_DIR_MODE;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessageBySftp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessageBySftp.java
new file mode 100644
index 00000000..2b32907d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessageBySftp.java
@@ -0,0 +1,277 @@
+/*
+ * 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.tools.ant.taskdefs.optional.ssh;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpException;
+import com.jcraft.jsch.SftpProgressMonitor;
+
+/**
+ * Utility class to carry out an upload by sftp.
+ */
+public class ScpToMessageBySftp extends ScpToMessage/*AbstractSshMessage*/ {
+
+ private static final int HUNDRED_KILOBYTES = 102400;
+
+ private File localFile;
+ private final String remotePath;
+ private List directoryList;
+
+ /**
+ * Constructor for a local file to remote.
+ * @param verbose if true do verbose logging
+ * @param session the scp session to use
+ * @param aLocalFile the local file
+ * @param aRemotePath the remote path
+ * @since Ant 1.7
+ */
+ public ScpToMessageBySftp(final boolean verbose,
+ final Session session,
+ final File aLocalFile,
+ final String aRemotePath) {
+ this(verbose, session, aRemotePath);
+
+ this.localFile = aLocalFile;
+ }
+
+ /**
+ * Constructor for a local directories to remote.
+ * @param verbose if true do verbose logging
+ * @param session the scp session to use
+ * @param aDirectoryList a list of directories
+ * @param aRemotePath the remote path
+ * @since Ant 1.7
+ */
+ public ScpToMessageBySftp(final boolean verbose,
+ final Session session,
+ final List aDirectoryList,
+ final String aRemotePath) {
+ this(verbose, session, aRemotePath);
+
+ this.directoryList = aDirectoryList;
+ }
+
+ /**
+ * Constructor for ScpToMessage.
+ * @param verbose if true do verbose logging
+ * @param session the scp session to use
+ * @param aRemotePath the remote path
+ * @since Ant 1.6.2
+ */
+ private ScpToMessageBySftp(final boolean verbose,
+ final Session session,
+ final String aRemotePath) {
+ super(verbose, session);
+ this.remotePath = aRemotePath;
+ }
+
+ /**
+ * Constructor for ScpToMessage.
+ * @param session the scp session to use
+ * @param aLocalFile the local file
+ * @param aRemotePath the remote path
+ */
+ public ScpToMessageBySftp(final Session session,
+ final File aLocalFile,
+ final String aRemotePath) {
+ this(false, session, aLocalFile, aRemotePath);
+ }
+
+ /**
+ * Constructor for ScpToMessage.
+ * @param session the scp session to use
+ * @param aDirectoryList a list of directories
+ * @param aRemotePath the remote path
+ */
+ public ScpToMessageBySftp(final Session session,
+ final List aDirectoryList,
+ final String aRemotePath) {
+ this(false, session, aDirectoryList, aRemotePath);
+ }
+
+ /**
+ * Carry out the transfer.
+ * @throws IOException on i/o errors
+ * @throws JSchException on errors detected by scp
+ */
+ @Override
+ public void execute() throws IOException, JSchException {
+ if (directoryList != null) {
+ doMultipleTransfer();
+ }
+ if (localFile != null) {
+ doSingleTransfer();
+ }
+ log("done.\n");
+ }
+
+ private void doSingleTransfer() throws IOException, JSchException {
+ final ChannelSftp channel = openSftpChannel();
+ try {
+ channel.connect();
+ try {
+ sendFileToRemote(channel, localFile, remotePath);
+ } catch (final SftpException e) {
+ final JSchException schException = new JSchException("Could not send '" + localFile
+ + "' to '" + remotePath + "' - "
+ + e.toString());
+ schException.initCause(e);
+ throw schException;
+ }
+ } finally {
+ if (channel != null) {
+ channel.disconnect();
+ }
+ }
+ }
+
+ private void doMultipleTransfer() throws IOException, JSchException {
+ final ChannelSftp channel = openSftpChannel();
+ try {
+ channel.connect();
+
+ try {
+ try {
+ channel.stat(remotePath);
+ } catch (final SftpException e) {
+ if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+ // dir does not exist.
+ channel.mkdir(remotePath);
+ channel.chmod(getDirMode(), remotePath);
+ } else {
+ throw new JSchException("failed to access remote dir '"
+ + remotePath + "'", e);
+ }
+ }
+ channel.cd(remotePath);
+ } catch (final SftpException e) {
+ throw new JSchException("Could not CD to '" + remotePath
+ + "' - " + e.toString(), e);
+ }
+ Directory current = null;
+ try {
+ for (final Iterator i = directoryList.iterator(); i.hasNext();) {
+ current = (Directory) i.next();
+ if (getVerbose()) {
+ log("Sending directory " + current);
+ }
+ sendDirectory(channel, current);
+ }
+ } catch (final SftpException e) {
+ String msg = "Error sending directory";
+ if (current != null && current.getDirectory() != null) {
+ msg += " '" + current.getDirectory().getName() + "'";
+ }
+ throw new JSchException(msg, e);
+ }
+ } finally {
+ if (channel != null) {
+ channel.disconnect();
+ }
+ }
+ }
+
+ private void sendDirectory(final ChannelSftp channel,
+ final Directory current)
+ throws IOException, SftpException {
+ for (final Iterator fileIt = current.filesIterator(); fileIt.hasNext();) {
+ sendFileToRemote(channel, (File) fileIt.next(), null);
+ }
+ for (final Iterator dirIt = current.directoryIterator(); dirIt.hasNext();) {
+ final Directory dir = (Directory) dirIt.next();
+ sendDirectoryToRemote(channel, dir);
+ }
+ }
+
+ private void sendDirectoryToRemote(final ChannelSftp channel,
+ final Directory directory)
+ throws IOException, SftpException {
+ final String dir = directory.getDirectory().getName();
+ try {
+ channel.stat(dir);
+ } catch (final SftpException e) {
+ // dir does not exist.
+ if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+ channel.mkdir(dir);
+ channel.chmod(getDirMode(), dir);
+ }
+ }
+ channel.cd(dir);
+ sendDirectory(channel, directory);
+ channel.cd("..");
+ }
+
+ private void sendFileToRemote(final ChannelSftp channel,
+ final File localFile,
+ String remotePath)
+ throws IOException, SftpException {
+ final long filesize = localFile.length();
+
+ if (remotePath == null) {
+ remotePath = localFile.getName();
+ }
+
+ final long startTime = System.currentTimeMillis();
+ final long totalLength = filesize;
+
+ // only track progress for files larger than 100kb in verbose mode
+ final boolean trackProgress = getVerbose() && filesize > HUNDRED_KILOBYTES;
+
+ SftpProgressMonitor monitor = null;
+ if (trackProgress) {
+ monitor = getProgressMonitor();
+ }
+
+ try {
+ if (this.getVerbose()) {
+ log("Sending: " + localFile.getName() + " : " + filesize);
+ }
+ channel.put(localFile.getAbsolutePath(), remotePath, monitor);
+ channel.chmod(getFileMode(), remotePath);
+ } finally {
+ if (this.getVerbose()) {
+ final long endTime = System.currentTimeMillis();
+ logStats(startTime, endTime, (int) totalLength);
+ }
+ }
+ }
+
+ /**
+ * Get the local file.
+ * @return the local file.
+ */
+ public File getLocalFile() {
+ return localFile;
+ }
+
+ /**
+ * Get the remote path.
+ * @return the remote path.
+ */
+ public String getRemotePath() {
+ return remotePath;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java
new file mode 100644
index 00000000..4b2978ab
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java
@@ -0,0 +1,71 @@
+/*
+ * 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.tools.ant.taskdefs.optional.testing;
+
+import org.apache.tools.ant.taskdefs.WaitFor;
+
+/**
+ * @since Ant 1.8
+ */
+
+public class BlockFor extends WaitFor {
+
+ /**
+ * Text to include in a message
+ */
+ private String text;
+
+
+ /**
+ * Constructor that takes the name of the task in the task name.
+ *
+ */
+ public BlockFor() {
+ super("blockfor");
+ text = getTaskName() + " timed out";
+ }
+
+ /**
+ * Constructor that takes the name of the task in the task name.
+ *
+ * @param taskName the name of the task.
+ */
+ public BlockFor(String taskName) {
+ super(taskName);
+ }
+
+ /**
+ * If the wait fails, a BuildException is thrown. All the superclasses actions are called first.
+ * @throws BuildTimeoutException on timeout, using the text in {@link #text}
+ *
+ */
+ protected void processTimeout() throws BuildTimeoutException {
+ super.processTimeout();
+ throw new BuildTimeoutException(text, getLocation());
+ }
+
+ /**
+ * Set the error text; all properties are expanded in the message.
+ *
+ * @param message the text to use in a failure message
+ */
+ public void addText(String message) {
+ text = getProject().replaceProperties(message);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java
new file mode 100644
index 00000000..143d08c9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java
@@ -0,0 +1,114 @@
+/*
+ * 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.tools.ant.taskdefs.optional.testing;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
+
+/**
+ *
+ * This exception is used to indicate timeouts.
+ * @since Ant1.8
+ *
+ */
+
+public class BuildTimeoutException extends BuildException {
+
+ private static final long serialVersionUID = -8057644603246297562L;
+
+ /**
+ * Constructs a build exception with no descriptive information.
+ */
+ public BuildTimeoutException() {
+ }
+
+ /**
+ * Constructs an exception with the given descriptive message.
+ *
+ * @param message A description of or information about the exception.
+ * Should not be <code>null</code>.
+ */
+ public BuildTimeoutException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs an exception with the given message and exception as
+ * a root cause.
+ *
+ * @param message A description of or information about the exception.
+ * Should not be <code>null</code> unless a cause is specified.
+ * @param cause The exception that might have caused this one.
+ * May be <code>null</code>.
+ */
+ public BuildTimeoutException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructs an exception with the given message and exception as
+ * a root cause and a location in a file.
+ *
+ * @param msg A description of or information about the exception.
+ * Should not be <code>null</code> unless a cause is specified.
+ * @param cause The exception that might have caused this one.
+ * May be <code>null</code>.
+ * @param location The location in the project file where the error
+ * occurred. Must not be <code>null</code>.
+ */
+ public BuildTimeoutException(String msg, Throwable cause, Location location) {
+ super(msg, cause, location);
+ }
+
+ /**
+ * Constructs an exception with the given exception as a root cause.
+ *
+ * @param cause The exception that might have caused this one.
+ * Should not be <code>null</code>.
+ */
+ public BuildTimeoutException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructs an exception with the given descriptive message and a
+ * location in a file.
+ *
+ * @param message A description of or information about the exception.
+ * Should not be <code>null</code>.
+ * @param location The location in the project file where the error
+ * occurred. Must not be <code>null</code>.
+ */
+ public BuildTimeoutException(String message, Location location) {
+ super(message, location);
+ }
+
+ /**
+ * Constructs an exception with the given exception as
+ * a root cause and a location in a file.
+ *
+ * @param cause The exception that might have caused this one.
+ * Should not be <code>null</code>.
+ * @param location The location in the project file where the error
+ * occurred. Must not be <code>null</code>.
+ */
+ public BuildTimeoutException(Throwable cause, Location location) {
+ super(cause, location);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java
new file mode 100644
index 00000000..2eb357a3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java
@@ -0,0 +1,577 @@
+/*
+ * 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.tools.ant.taskdefs.optional.testing;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskAdapter;
+import org.apache.tools.ant.taskdefs.Parallel;
+import org.apache.tools.ant.taskdefs.Sequential;
+import org.apache.tools.ant.taskdefs.WaitFor;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.taskdefs.condition.ConditionBase;
+import org.apache.tools.ant.util.WorkerAnt;
+
+/**
+ * Task to provide functional testing under Ant, with a fairly complex workflow of:
+ *
+ * <ul>
+ * <li>Conditional execution</li>
+ * <li>Application to start</li>
+ * <li>A probe to "waitfor" before running tests</li>
+ * <li>A tests sequence</li>
+ * <li>A reporting sequence that runs after the tests have finished</li>
+ * <li>A "teardown" clause that runs after the rest.</li>
+ * <li>Automated termination of the program it executes, if a timeout is not met</li>
+ * <li>Checking of a failure property and automatic raising of a fault
+ * (with the text in failureText)
+ * if test shutdown and reporting succeeded</li>
+ * </ul>
+ *
+ * The task is designed to be framework neutral; it will work with JUnit,
+ * TestNG and other test frameworks That can be
+ * executed from Ant. It bears a resemblance to the FunctionalTest task from
+ * SmartFrog, as the attribute names were
+ * chosen to make migration easier. However, this task benefits from the
+ * ability to tweak Ant's internals, and so
+ * simplify the workflow, and from the experience of using the SmartFrog task.
+ * No code has been shared.
+ *
+ * @since Ant 1.8
+ */
+
+public class Funtest extends Task {
+
+ /**
+ * A condition that must be true before the tests are run. This makes it
+ * easier to define complex tests that only
+ * run if certain conditions are met, such as OS or network state.
+ */
+
+ private NestedCondition condition;
+
+
+ /**
+ * Used internally to set the workflow up
+ */
+ private Parallel timedTests;
+
+ /**
+ * Setup runs if the condition is met. Once setup is complete, teardown
+ * will be run when the task finishes
+ */
+ private Sequential setup;
+
+ /**
+ * The application to run
+ */
+ private Sequential application;
+
+ /**
+ * A block that halts the tests until met.
+ */
+ private BlockFor block;
+
+ /**
+ * Tests to run
+ */
+ private Sequential tests;
+
+ /**
+ * Reporting only runs if the tests were executed. If the block stopped
+ * them, reporting is skipped.
+ */
+ private Sequential reporting;
+
+ /**
+ * Any teardown operations.
+ */
+ private Sequential teardown;
+
+ /**
+ * time for the tests to time out
+ */
+ private long timeout;
+
+ private long timeoutUnitMultiplier = WaitFor.ONE_MILLISECOND;
+
+ /**
+ * time for the execution to time out.
+ */
+ private long shutdownTime = 10 * WaitFor.ONE_SECOND;
+
+ private long shutdownUnitMultiplier = WaitFor.ONE_MILLISECOND;
+
+ /**
+ * Name of a property to look for
+ */
+ private String failureProperty;
+
+ /**
+ * Message to send when tests failed
+ */
+ private String failureMessage = "Tests failed";
+
+ /**
+ * Flag to set to true if you don't care about any shutdown errors.
+ * <p/>
+ * In that situation, errors raised during teardown are logged but not
+ * turned into BuildFault events. Similar to catching and ignoring
+ * <code>finally {}</code> clauses in Java/
+ */
+ private boolean failOnTeardownErrors = true;
+
+
+ /**
+ * What was thrown in the test run (including reporting)
+ */
+ private BuildException testException;
+ /**
+ * What got thrown during teardown
+ */
+ private BuildException teardownException;
+
+ /**
+ * Did the application throw an exception
+ */
+ private BuildException applicationException;
+
+ /**
+ * Did the task throw an exception
+ */
+ private BuildException taskException;
+
+ /** {@value} */
+ public static final String WARN_OVERRIDING = "Overriding previous definition of ";
+ /** {@value} */
+ public static final String APPLICATION_FORCIBLY_SHUT_DOWN = "Application forcibly shut down";
+ /** {@value} */
+ public static final String SHUTDOWN_INTERRUPTED = "Shutdown interrupted";
+ /** {@value} */
+ public static final String SKIPPING_TESTS
+ = "Condition failed -skipping tests";
+ /** Application exception : {@value} */
+ public static final String APPLICATION_EXCEPTION = "Application Exception";
+ /** Teardown exception : {@value} */
+ public static final String TEARDOWN_EXCEPTION = "Teardown Exception";
+
+ /**
+ * Log if the definition is overriding something
+ *
+ * @param name what is being defined
+ * @param definition what should be null if you don't want a warning
+ */
+ private void logOverride(String name, Object definition) {
+ if (definition != null) {
+ log(WARN_OVERRIDING + '<' + name + '>', Project.MSG_INFO);
+ }
+ }
+
+ /**
+ * Add a condition element.
+ * @return <code>ConditionBase</code>.
+ * @since Ant 1.6.2
+ */
+ public ConditionBase createCondition() {
+ logOverride("condition", condition);
+ condition = new NestedCondition();
+ return condition;
+ }
+
+ /**
+ * Add an application.
+ * @param sequence the application to add.
+ */
+ public void addApplication(Sequential sequence) {
+ logOverride("application", application);
+ application = sequence;
+ }
+
+ /**
+ * Add a setup sequence.
+ * @param sequence the setup sequence to add.
+ */
+ public void addSetup(Sequential sequence) {
+ logOverride("setup", setup);
+ setup = sequence;
+ }
+
+ /**
+ * Add a block.
+ * @param sequence the block for to add.
+ */
+ public void addBlock(BlockFor sequence) {
+ logOverride("block", block);
+ block = sequence;
+ }
+
+ /**
+ * add tests.
+ * @param sequence a sequence to add.
+ */
+ public void addTests(Sequential sequence) {
+ logOverride("tests", tests);
+ tests = sequence;
+ }
+
+ /**
+ * set reporting sequence of tasks.
+ * @param sequence a reporting sequence to use.
+ */
+ public void addReporting(Sequential sequence) {
+ logOverride("reporting", reporting);
+ reporting = sequence;
+ }
+
+ /**
+ * set teardown sequence of tasks.
+ * @param sequence a teardown sequence to use.
+ */
+ public void addTeardown(Sequential sequence) {
+ logOverride("teardown", teardown);
+ teardown = sequence;
+ }
+
+ /**
+ * Set the failOnTeardownErrors attribute.
+ * @param failOnTeardownErrors the value to use.
+ */
+ public void setFailOnTeardownErrors(boolean failOnTeardownErrors) {
+ this.failOnTeardownErrors = failOnTeardownErrors;
+ }
+
+ /**
+ * Set the failureMessage attribute.
+ * @param failureMessage the value to use.
+ */
+ public void setFailureMessage(String failureMessage) {
+ this.failureMessage = failureMessage;
+ }
+
+ /**
+ * Set the failureProperty attribute.
+ * @param failureProperty the value to use.
+ */
+ public void setFailureProperty(String failureProperty) {
+ this.failureProperty = failureProperty;
+ }
+
+ /**
+ * Set the shutdownTime attribute.
+ * @param shutdownTime the value to use.
+ */
+ public void setShutdownTime(long shutdownTime) {
+ this.shutdownTime = shutdownTime;
+ }
+
+ /**
+ * Set the timeout attribute.
+ * @param timeout the value to use.
+ */
+ public void setTimeout(long timeout) {
+ this.timeout = timeout;
+ }
+
+ /**
+ * Set the timeoutunit attribute.
+ * @param unit the value to use.
+ */
+ public void setTimeoutUnit(WaitFor.Unit unit) {
+ timeoutUnitMultiplier = unit.getMultiplier();
+ }
+
+ /**
+ * Set the shutdownunit attribute.
+ * @param unit the value to use.
+ */
+ public void setShutdownUnit(WaitFor.Unit unit) {
+ shutdownUnitMultiplier = unit.getMultiplier();
+ }
+
+
+ /**
+ * Get the application exception.
+ * @return the application exception.
+ */
+ public BuildException getApplicationException() {
+ return applicationException;
+ }
+
+ /**
+ * Get the teardown exception.
+ * @return the teardown exception.
+ */
+ public BuildException getTeardownException() {
+ return teardownException;
+ }
+
+ /**
+ * Get the test exception.
+ * @return the test exception.
+ */
+ public BuildException getTestException() {
+ return testException;
+ }
+
+ /**
+ * Get the task exception.
+ * @return the task exception.
+ */
+ public BuildException getTaskException() {
+ return taskException;
+ }
+
+ /**
+ * Bind and initialise a task
+ * @param task task to bind
+ */
+ private void bind(Task task) {
+ task.bindToOwner(this);
+ task.init();
+ }
+
+ /**
+ * Create a newly bound parallel instance
+ * @param parallelTimeout timeout
+ * @return a bound and initialised parallel instance.
+ */
+ private Parallel newParallel(long parallelTimeout) {
+ Parallel par = new Parallel();
+ bind(par);
+ par.setFailOnAny(true);
+ par.setTimeout(parallelTimeout);
+ return par;
+ }
+
+ /**
+ * Create a newly bound parallel instance with one child
+ * @param parallelTimeout timeout
+ * @param child task
+ * @return a bound and initialised parallel instance.
+ */
+ private Parallel newParallel(long parallelTimeout, Task child) {
+ Parallel par = newParallel(parallelTimeout);
+ par.addTask(child);
+ return par;
+ }
+
+ /**
+ * Add any task validation needed to ensure internal code quality
+ * @param task task
+ * @param role role of the task
+ */
+ private void validateTask(Task task, String role) {
+ if (task!=null && task.getProject() == null) {
+ throw new BuildException(role + " task is not bound to the project" + task);
+ }
+ }
+
+ /**
+ * Run the functional test sequence.
+ * <p>
+ * This is a fairly complex workflow -what is going on is that we try to clean up
+ * no matter how the run ended, and to retain the innermost exception that got thrown
+ * during cleanup. That is, if teardown fails after the tests themselves failed, it is the
+ * test failing that is more important.
+ * @throws BuildException if something was caught during the run or teardown.
+ */
+ public void execute() throws BuildException {
+
+ //validation
+ validateTask(setup, "setup");
+ validateTask(application, "application");
+ validateTask(tests, "tests");
+ validateTask(reporting, "reporting");
+ validateTask(teardown, "teardown");
+
+ //check the condition
+ //and bail out if it is defined but not true
+ if (condition != null && !condition.eval()) {
+ //we are skipping the test
+ log(SKIPPING_TESTS);
+ return;
+ }
+
+ long timeoutMillis = timeout * timeoutUnitMultiplier;
+
+ //set up the application to run in a separate thread
+ Parallel applicationRun = newParallel(timeoutMillis);
+ //with a worker which we can use to manage it
+ WorkerAnt worker = new WorkerAnt(applicationRun, null);
+ if (application != null) {
+ applicationRun.addTask(application);
+ }
+
+ //The test run consists of the block followed by the tests.
+ long testRunTimeout = 0;
+ Sequential testRun = new Sequential();
+ bind(testRun);
+ if (block != null) {
+ //waitfor is not a task, it needs to be adapted
+ TaskAdapter ta = new TaskAdapter(block);
+ ta.bindToOwner(this);
+ validateTask(ta, "block");
+ testRun.addTask(ta);
+ //add the block time to the total test run timeout
+ testRunTimeout = block.calculateMaxWaitMillis();
+ }
+
+ //add the tests and more delay
+ if (tests != null) {
+ testRun.addTask(tests);
+ testRunTimeout += timeoutMillis;
+ }
+ //add the reporting and more delay
+ if (reporting != null) {
+ testRun.addTask(reporting);
+ testRunTimeout += timeoutMillis;
+ }
+
+ //wrap this in a parallel purely to set up timeouts for the
+ //test run
+ timedTests = newParallel(testRunTimeout, testRun);
+
+ try {
+ //run any setup task
+ if (setup != null) {
+ Parallel setupRun = newParallel(timeoutMillis, setup);
+ setupRun.execute();
+ }
+ //start the worker thread and leave it running
+ worker.start();
+ //start the probe+test sequence
+ timedTests.execute();
+ } catch (BuildException e) {
+ //Record the exception and continue
+ testException = e;
+ } finally {
+ //teardown always runs; its faults are filed away
+ if (teardown != null) {
+ try {
+ Parallel teardownRun = newParallel(timeoutMillis, teardown);
+ teardownRun.execute();
+ } catch (BuildException e) {
+ teardownException = e;
+ }
+ }
+ }
+
+ //we get here whether or not the tests/teardown have thrown a BuildException.
+ //do a forced shutdown of the running application, before processing the faults
+
+ try {
+ //wait for the worker to have finished
+ long shutdownTimeMillis = shutdownTime * shutdownUnitMultiplier;
+ worker.waitUntilFinished(shutdownTimeMillis);
+ if (worker.isAlive()) {
+ //then, if it is still running, interrupt it a second time.
+ log(APPLICATION_FORCIBLY_SHUT_DOWN, Project.MSG_WARN);
+ worker.interrupt();
+ worker.waitUntilFinished(shutdownTimeMillis);
+ }
+ } catch (InterruptedException e) {
+ //success, something interrupted the shutdown. There may be a leaked
+ //worker;
+ log(SHUTDOWN_INTERRUPTED, e, Project.MSG_VERBOSE);
+ }
+ applicationException = worker.getBuildException();
+
+ //Now faults are analysed
+
+ processExceptions();
+ }
+
+ /**
+ * Now faults are analysed.
+ * <p> The priority is
+ * <ol>
+ * <li>testexceptions, except those indicating a build timeout when the application itself
+ failed.<br>
+ (because often it is the application fault that is more interesting than the probe
+ failure, which is usually triggered by the application not starting
+ </li><li>
+ Application exceptions (above test timeout exceptions)
+ </li><li>
+ Teardown exceptions -except when they are being ignored
+ </li><li>
+ Test failures as indicated by the failure property
+ </li></ol>
+
+ */
+ protected void processExceptions() {
+ taskException = testException;
+
+ //look for an application fault
+ if (applicationException != null) {
+ if (taskException == null || taskException instanceof BuildTimeoutException) {
+ taskException = applicationException;
+ } else {
+ ignoringThrowable(APPLICATION_EXCEPTION, applicationException);
+ }
+ }
+
+ //now look for teardown faults, which may be ignored
+ if (teardownException != null) {
+ if (taskException == null && failOnTeardownErrors) {
+ taskException = teardownException;
+ } else {
+ //don't let the cleanup exception get in the way of any other failure
+ ignoringThrowable(TEARDOWN_EXCEPTION, teardownException);
+ }
+ }
+
+ //now, analyse the tests
+ if (failureProperty != null
+ && getProject().getProperty(failureProperty) != null) {
+ //we've failed
+ log(failureMessage);
+ if (taskException == null) {
+ taskException = new BuildException(failureMessage);
+ }
+ }
+
+ //at this point taskException is null or not.
+ //if not, throw the exception
+ if (taskException != null) {
+ throw taskException;
+ }
+ }
+
+ /**
+ * log that we are ignoring something rather than rethrowing it.
+ * @param type name of exception
+ * @param thrown what was thrown
+ */
+ protected void ignoringThrowable(String type, Throwable thrown) {
+ log(type + ": " + thrown.toString(),
+ thrown,
+ Project.MSG_WARN);
+ }
+
+ private static class NestedCondition extends ConditionBase implements Condition {
+ public boolean eval() {
+ if (countConditions() != 1) {
+ throw new BuildException(
+ "A single nested condition is required.");
+ }
+ return ((Condition) (getConditions().nextElement())).eval();
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/AbstractAccessTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/AbstractAccessTask.java
new file mode 100644
index 00000000..d3385e6c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/AbstractAccessTask.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.
+ *
+ */
+
+/*
+ * Since the initial version of this file was deveolped on the clock on
+ * an NSF grant I should say the following boilerplate:
+ *
+ * This material is based upon work supported by the National Science
+ * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and
+ * conclusions or recommendations expressed in this material are those
+ * of the author and do not necessarily reflect the views of the
+ * National Science Foundation.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.unix;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * @since Ant 1.6
+ *
+ * @ant.task category="filesystem"
+ */
+
+public abstract class AbstractAccessTask
+ extends org.apache.tools.ant.taskdefs.ExecuteOn {
+
+ /**
+ * Chmod task for setting file and directory permissions.
+ */
+ public AbstractAccessTask() {
+ super.setParallel(true);
+ super.setSkipEmptyFilesets(true);
+ }
+
+ /**
+ * Set the file which should have its access attributes modified.
+ * @param src the file to modify
+ */
+ public void setFile(File src) {
+ FileSet fs = new FileSet();
+ fs.setFile(src);
+ addFileset(fs);
+ }
+
+ /**
+ * Prevent the user from specifying a different command.
+ *
+ * @ant.attribute ignore="true"
+ * @param cmdl A user supplied command line that we won't accept.
+ */
+ public void setCommand(Commandline cmdl) {
+ throw new BuildException(getTaskType()
+ + " doesn\'t support the command attribute",
+ getLocation());
+ }
+
+ /**
+ * Prevent the skipping of empty filesets
+ *
+ * @ant.attribute ignore="true"
+ * @param skip A user supplied boolean we won't accept.
+ */
+ public void setSkipEmptyFilesets(boolean skip) {
+ throw new BuildException(getTaskType() + " doesn\'t support the "
+ + "skipemptyfileset attribute",
+ getLocation());
+ }
+
+ /**
+ * Prevent the use of the addsourcefile attribute.
+ *
+ * @ant.attribute ignore="true"
+ * @param b A user supplied boolean we won't accept.
+ */
+ public void setAddsourcefile(boolean b) {
+ throw new BuildException(getTaskType()
+ + " doesn\'t support the addsourcefile attribute", getLocation());
+ }
+
+ /**
+ * Automatically approve Unix OS's.
+ * @return true if a valid OS, for unix this is always true, otherwise
+ * use the superclasses' test (user set).
+ */
+ protected boolean isValidOs() {
+ return getOs() == null && getOsFamily() == null
+ ? Os.isFamily(Os.FAMILY_UNIX) : super.isValidOs();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chgrp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chgrp.java
new file mode 100644
index 00000000..1279a2c8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chgrp.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * Since the initial version of this file was deveolped on the clock on
+ * an NSF grant I should say the following boilerplate:
+ *
+ * This material is based upon work supported by the National Science
+ * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and
+ * conclusions or recommendations expressed in this material are those
+ * of the author and do not necessarily reflect the views of the
+ * National Science Foundation.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.unix;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Chgrp equivalent for unix-like environments.
+ *
+ * @since Ant 1.6
+ *
+ * @ant.task category="filesystem"
+ */
+public class Chgrp extends AbstractAccessTask {
+
+ private boolean haveGroup = false;
+
+ /**
+ * Chgrp task for setting unix group of a file.
+ */
+ public Chgrp() {
+ super.setExecutable("chgrp");
+ }
+
+ /**
+ * Set the group attribute.
+ *
+ * @param group The new group for the file(s) or directory(ies)
+ */
+ public void setGroup(String group) {
+ createArg().setValue(group);
+ haveGroup = true;
+ }
+
+ /**
+ * Ensure that all the required arguments and other conditions have
+ * been set.
+ */
+ protected void checkConfiguration() {
+ if (!haveGroup) {
+ throw new BuildException("Required attribute group not set in "
+ + "chgrp", getLocation());
+ }
+ super.checkConfiguration();
+ }
+
+ /**
+ * We don't want to expose the executable attribute, so override it.
+ *
+ * @param e User supplied executable that we won't accept.
+ */
+ public void setExecutable(String e) {
+ throw new BuildException(getTaskType()
+ + " doesn\'t support the executable"
+ + " attribute", getLocation());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chown.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chown.java
new file mode 100644
index 00000000..53f72536
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chown.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * Since the initial version of this file was deveolped on the clock on
+ * an NSF grant I should say the following boilerplate:
+ *
+ * This material is based upon work supported by the National Science
+ * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and
+ * conclusions or recommendations expressed in this material are those
+ * of the author and do not necessarily reflect the views of the
+ * National Science Foundation.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.unix;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Chown equivalent for unix-like environments.
+ *
+ * @since Ant 1.6
+ *
+ * @ant.task category="filesystem"
+ */
+public class Chown extends AbstractAccessTask {
+
+ private boolean haveOwner = false;
+
+ /**
+ * Chown task for setting file and directory permissions.
+ */
+ public Chown() {
+ super.setExecutable("chown");
+ }
+
+ /**
+ * Set the owner attribute.
+ *
+ * @param owner The new owner for the file(s) or directory(ies)
+ */
+ public void setOwner(String owner) {
+ createArg().setValue(owner);
+ haveOwner = true;
+ }
+
+ /**
+ * Ensure that all the required arguments and other conditions have
+ * been set.
+ */
+ protected void checkConfiguration() {
+ if (!haveOwner) {
+ throw new BuildException("Required attribute owner not set in"
+ + " chown", getLocation());
+ }
+ super.checkConfiguration();
+ }
+
+ /**
+ * We don't want to expose the executable attribute, so override it.
+ *
+ * @param e User supplied executable that we won't accept.
+ */
+ public void setExecutable(String e) {
+ throw new BuildException(getTaskType()
+ + " doesn\'t support the executable"
+ + " attribute", getLocation());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java
new file mode 100644
index 00000000..ad9aee8b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java
@@ -0,0 +1,600 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * Since the initial version of this file was developed on the clock on
+ * an NSF grant I should say the following boilerplate:
+ *
+ * This material is based upon work supported by the National Science
+ * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and
+ * conclusions or recommendations expressed in this material are those
+ * of the author and do not necessarily reflect the views of the
+ * National Science Foundation.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.unix;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.dispatch.DispatchTask;
+import org.apache.tools.ant.dispatch.DispatchUtils;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.SymbolicLinkUtils;
+
+/**
+ * Creates, Deletes, Records and Restores Symlinks.
+ *
+ * <p> This task performs several related operations. In the most trivial
+ * and default usage, it creates a link specified in the link attribute to
+ * a resource specified in the resource attribute. The second usage of this
+ * task is to traverse a directory structure specified by a fileset,
+ * and write a properties file in each included directory describing the
+ * links found in that directory. The third usage is to traverse a
+ * directory structure specified by a fileset, looking for properties files
+ * (also specified as included in the fileset) and recreate the links
+ * that have been previously recorded for each directory. Finally, it can be
+ * used to remove a symlink without deleting the associated resource.
+ *
+ * <p> Usage examples:
+ *
+ * <p> Make a link named &quot;foo&quot; to a resource named
+ * &quot;bar.foo&quot; in subdir:
+ * <pre>
+ * &lt;symlink link=&quot;${dir.top}/foo&quot; resource=&quot;${dir.top}/subdir/bar.foo&quot;/&gt;
+ * </pre>
+ *
+ * <p> Record all links in subdir and its descendants in files named
+ * &quot;dir.links&quot;:
+ * <pre>
+ * &lt;symlink action=&quot;record&quot; linkfilename=&quot;dir.links&quot;&gt;
+ * &lt;fileset dir=&quot;${dir.top}&quot; includes=&quot;subdir&#47;**&quot; /&gt;
+ * &lt;/symlink&gt;
+ * </pre>
+ *
+ * <p> Recreate the links recorded in the previous example:
+ * <pre>
+ * &lt;symlink action=&quot;recreate&quot;&gt;
+ * &lt;fileset dir=&quot;${dir.top}&quot; includes=&quot;subdir&#47;**&#47;dir.links&quot; /&gt;
+ * &lt;/symlink&gt;
+ * </pre>
+ *
+ * <p> Delete a link named &quot;foo&quot; to a resource named
+ * &quot;bar.foo&quot; in subdir:
+ * <pre>
+ * &lt;symlink action=&quot;delete&quot; link=&quot;${dir.top}/foo&quot;/&gt;
+ * </pre>
+ *
+ * <p><strong>LIMITATIONS:</strong> Because Java has no direct support for
+ * handling symlinks this task divines them by comparing canonical and
+ * absolute paths. On non-unix systems this may cause false positives.
+ * Furthermore, any operating system on which the command
+ * <code>ln -s link resource</code> is not a valid command on the command line
+ * will not be able to use action=&quot;delete&quot;, action=&quot;single&quot;
+ * or action=&quot;recreate&quot;, but action=&quot;record&quot; should still
+ * work. Finally, the lack of support for symlinks in Java means that all links
+ * are recorded as links to the <strong>canonical</strong> resource name.
+ * Therefore the link: <code>link --> subdir/dir/../foo.bar</code> will be
+ * recorded as <code>link=subdir/foo.bar</code> and restored as
+ * <code>link --> subdir/foo.bar</code>.
+ *
+ */
+public class Symlink extends DispatchTask {
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+ private static final SymbolicLinkUtils SYMLINK_UTILS =
+ SymbolicLinkUtils.getSymbolicLinkUtils();
+
+ private String resource;
+ private String link;
+ private Vector fileSets = new Vector();
+ private String linkFileName;
+ private boolean overwrite;
+ private boolean failonerror;
+ private boolean executing = false;
+
+ /**
+ * Initialize the task.
+ * @throws BuildException on error.
+ */
+ @Override
+ public void init() throws BuildException {
+ super.init();
+ setDefaults();
+ }
+
+ /**
+ * The standard method for executing any task.
+ * @throws BuildException on error.
+ */
+ @Override
+ public synchronized void execute() throws BuildException {
+ if (executing) {
+ throw new BuildException(
+ "Infinite recursion detected in Symlink.execute()");
+ }
+ try {
+ executing = true;
+ DispatchUtils.execute(this);
+ } finally {
+ executing = false;
+ }
+ }
+
+ /**
+ * Create a symlink.
+ * @throws BuildException on error.
+ * @since Ant 1.7
+ */
+ public void single() throws BuildException {
+ try {
+ if (resource == null) {
+ handleError("Must define the resource to symlink to!");
+ return;
+ }
+ if (link == null) {
+ handleError("Must define the link name for symlink!");
+ return;
+ }
+ doLink(resource, link);
+ } finally {
+ setDefaults();
+ }
+ }
+
+ /**
+ * Delete a symlink.
+ * @throws BuildException on error.
+ * @since Ant 1.7
+ */
+ public void delete() throws BuildException {
+ try {
+ if (link == null) {
+ handleError("Must define the link name for symlink!");
+ return;
+ }
+ log("Removing symlink: " + link);
+ SYMLINK_UTILS.deleteSymbolicLink(FILE_UTILS
+ .resolveFile(new File("."), link),
+ this);
+ } catch (FileNotFoundException fnfe) {
+ handleError(fnfe.toString());
+ } catch (IOException ioe) {
+ handleError(ioe.toString());
+ } finally {
+ setDefaults();
+ }
+ }
+
+ /**
+ * Restore symlinks.
+ * @throws BuildException on error.
+ * @since Ant 1.7
+ */
+ public void recreate() throws BuildException {
+ try {
+ if (fileSets.isEmpty()) {
+ handleError("File set identifying link file(s) "
+ + "required for action recreate");
+ return;
+ }
+ Properties links = loadLinks(fileSets);
+
+ for (Iterator kitr = links.keySet().iterator(); kitr.hasNext();) {
+ String lnk = (String) kitr.next();
+ String res = links.getProperty(lnk);
+ // handle the case where lnk points to a directory (bug 25181)
+ try {
+ File test = new File(lnk);
+ if (!SYMLINK_UTILS.isSymbolicLink(lnk)) {
+ doLink(res, lnk);
+ } else if (!test.getCanonicalPath().equals(
+ new File(res).getCanonicalPath())) {
+ SYMLINK_UTILS.deleteSymbolicLink(test, this);
+ doLink(res, lnk);
+ } // else lnk exists, do nothing
+ } catch (IOException ioe) {
+ handleError("IO exception while creating link");
+ }
+ }
+ } finally {
+ setDefaults();
+ }
+ }
+
+ /**
+ * Record symlinks.
+ * @throws BuildException on error.
+ * @since Ant 1.7
+ */
+ public void record() throws BuildException {
+ try {
+ if (fileSets.isEmpty()) {
+ handleError("Fileset identifying links to record required");
+ return;
+ }
+ if (linkFileName == null) {
+ handleError("Name of file to record links in required");
+ return;
+ }
+ // create a hashtable to group them by parent directory:
+ Hashtable byDir = new Hashtable();
+
+ // get an Iterator of file objects representing links (canonical):
+ for (Iterator litr = findLinks(fileSets).iterator();
+ litr.hasNext();) {
+ File thisLink = (File) litr.next();
+ File parent = thisLink.getParentFile();
+ Vector v = (Vector) byDir.get(parent);
+ if (v == null) {
+ v = new Vector();
+ byDir.put(parent, v);
+ }
+ v.addElement(thisLink);
+ }
+ // write a Properties file in each directory:
+ for (Iterator dirs = byDir.keySet().iterator(); dirs.hasNext();) {
+ File dir = (File) dirs.next();
+ Vector linksInDir = (Vector) byDir.get(dir);
+ Properties linksToStore = new Properties();
+
+ // fill up a Properties object with link and resource names:
+ for (Iterator dlnk = linksInDir.iterator(); dlnk.hasNext();) {
+ File lnk = (File) dlnk.next();
+ try {
+ linksToStore.put(lnk.getName(), lnk.getCanonicalPath());
+ } catch (IOException ioe) {
+ handleError("Couldn't get canonical name of parent link");
+ }
+ }
+ writePropertyFile(linksToStore, dir);
+ }
+ } finally {
+ setDefaults();
+ }
+ }
+
+ /**
+ * Return all variables to their default state for the next invocation.
+ * @since Ant 1.7
+ */
+ private void setDefaults() {
+ resource = null;
+ link = null;
+ linkFileName = null;
+ failonerror = true; // default behavior is to fail on an error
+ overwrite = false; // default behavior is to not overwrite
+ setAction("single"); // default behavior is make a single link
+ fileSets.clear();
+ }
+
+ /**
+ * Set overwrite mode. If set to false (default)
+ * the task will not overwrite existing links, and may stop the build
+ * if a link already exists depending on the setting of failonerror.
+ *
+ * @param owrite If true overwrite existing links.
+ */
+ public void setOverwrite(boolean owrite) {
+ this.overwrite = owrite;
+ }
+
+ /**
+ * Set failonerror mode. If set to true (default) the entire build fails
+ * upon error; otherwise the error is logged and the build will continue.
+ *
+ * @param foe If true throw BuildException on error, else log it.
+ */
+ public void setFailOnError(boolean foe) {
+ this.failonerror = foe;
+ }
+
+ /**
+ * Set the action to be performed. May be &quot;single&quot;,
+ * &quot;delete&quot;, &quot;recreate&quot; or &quot;record&quot;.
+ *
+ * @param action The action to perform.
+ */
+ @Override
+ public void setAction(String action) {
+ super.setAction(action);
+ }
+
+ /**
+ * Set the name of the link. Used when action = &quot;single&quot;.
+ *
+ * @param lnk The name for the link.
+ */
+ public void setLink(String lnk) {
+ this.link = lnk;
+ }
+
+ /**
+ * Set the name of the resource to which a link should be created.
+ * Used when action = &quot;single&quot;.
+ *
+ * @param src The resource to be linked.
+ */
+ public void setResource(String src) {
+ this.resource = src;
+ }
+
+ /**
+ * Set the name of the file to which links will be written.
+ * Used when action = &quot;record&quot;.
+ *
+ * @param lf The name of the file to write links to.
+ */
+ public void setLinkfilename(String lf) {
+ this.linkFileName = lf;
+ }
+
+ /**
+ * Add a fileset to this task.
+ *
+ * @param set The fileset to add.
+ */
+ public void addFileset(FileSet set) {
+ fileSets.addElement(set);
+ }
+
+ /**
+ * Delete a symlink (without deleting the associated resource).
+ *
+ * <p>This is a convenience method that simply invokes
+ * <code>deleteSymlink(java.io.File)</code>.
+ *
+ * @param path A string containing the path of the symlink to delete.
+ *
+ * @throws FileNotFoundException When the path results in a
+ * <code>File</code> that doesn't exist.
+ * @throws IOException If calls to <code>File.rename</code>
+ * or <code>File.delete</code> fail.
+ * @deprecated use
+ * org.apache.tools.ant.util.SymbolicLinkUtils#deleteSymbolicLink
+ * instead
+ */
+ @Deprecated
+ public static void deleteSymlink(String path)
+ throws IOException, FileNotFoundException {
+ SYMLINK_UTILS.deleteSymbolicLink(new File(path), null);
+ }
+
+ /**
+ * Delete a symlink (without deleting the associated resource).
+ *
+ * <p>This is a utility method that removes a unix symlink without removing
+ * the resource that the symlink points to. If it is accidentally invoked
+ * on a real file, the real file will not be harmed.</p>
+ *
+ * <p>This method works by
+ * getting the canonical path of the link, using the canonical path to
+ * rename the resource (breaking the link) and then deleting the link.
+ * The resource is then returned to its original name inside a finally
+ * block to ensure that the resource is unharmed even in the event of
+ * an exception.</p>
+ *
+ * <p>Since Ant 1.8.0 this method will try to delete the File object if
+ * it reports it wouldn't exist (as symlinks pointing nowhere usually do).
+ * Prior version would throw a FileNotFoundException in that case.</p>
+ *
+ * @param linkfil A <code>File</code> object of the symlink to delete.
+ *
+ * @throws IOException If calls to <code>File.rename</code>,
+ * <code>File.delete</code> or
+ * <code>File.getCanonicalPath</code>
+ * fail.
+ * @deprecated use
+ * org.apache.tools.ant.util.SymbolicLinkUtils#deleteSymbolicLink
+ * instead
+ */
+ @Deprecated
+ public static void deleteSymlink(File linkfil)
+ throws IOException {
+ SYMLINK_UTILS.deleteSymbolicLink(linkfil, null);
+ }
+
+ /**
+ * Write a properties file. This method uses <code>Properties.store</code>
+ * and thus may throw exceptions that occur while writing the file.
+ *
+ * @param properties The properties object to be written.
+ * @param dir The directory for which we are writing the links.
+ * @throws BuildException if the property file could not be written
+ */
+ private void writePropertyFile(Properties properties, File dir)
+ throws BuildException {
+ BufferedOutputStream bos = null;
+ try {
+ bos = new BufferedOutputStream(
+ new FileOutputStream(new File(dir, linkFileName)));
+ properties.store(bos, "Symlinks from " + dir);
+ } catch (IOException ioe) {
+ throw new BuildException(ioe, getLocation());
+ } finally {
+ FileUtils.close(bos);
+ }
+ }
+
+ /**
+ * Handle errors based on the setting of failonerror.
+ *
+ * @param msg The message to log, or include in the
+ * <code>BuildException</code>.
+ * @throws BuildException with the message if failonerror=true
+ */
+ private void handleError(String msg) {
+ if (failonerror) {
+ throw new BuildException(msg);
+ }
+ log(msg);
+ }
+
+ /**
+ * Conduct the actual construction of a link.
+ *
+ * <p> The link is constructed by calling <code>Execute.runCommand</code>.
+ *
+ * @param res The path of the resource we are linking to.
+ * @param lnk The name of the link we wish to make.
+ * @throws BuildException when things go wrong
+ */
+ private void doLink(String res, String lnk) throws BuildException {
+ File linkfil = new File(lnk);
+ String options = "-s";
+ if (overwrite) {
+ options += "f";
+ if (linkfil.exists()) {
+ try {
+ SYMLINK_UTILS.deleteSymbolicLink(linkfil, this);
+ } catch (FileNotFoundException fnfe) {
+ log("Symlink disappeared before it was deleted: " + lnk);
+ } catch (IOException ioe) {
+ log("Unable to overwrite preexisting link or file: " + lnk,
+ ioe, Project.MSG_INFO);
+ }
+ }
+ }
+ String[] cmd = new String[] {"ln", options, res, lnk};
+ try {
+ Execute.runCommand(this, cmd);
+ } catch (BuildException failedToExecute) {
+ if (failonerror) {
+ throw failedToExecute;
+ } else {
+ //log at the info level, and keep going.
+ log(failedToExecute.getMessage(), failedToExecute, Project.MSG_INFO);
+ }
+ }
+ }
+
+ /**
+ * Find all the links in all supplied filesets.
+ *
+ * <p> This method is invoked when the action attribute is
+ * &quot;record&quot;. This means that filesets are interpreted
+ * as the directories in which links may be found.
+ *
+ * @param v The filesets specified by the user.
+ * @return A HashSet of <code>File</code> objects containing the
+ * links (with canonical parent directories).
+ */
+ private HashSet findLinks(Vector v) {
+ HashSet result = new HashSet();
+ final int size = v.size();
+ for (int i = 0; i < size; i++) {
+ FileSet fs = (FileSet) v.get(i);
+ DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+ String[][] fnd = new String[][]
+ {ds.getIncludedFiles(), ds.getIncludedDirectories()};
+ File dir = fs.getDir(getProject());
+ for (int j = 0; j < fnd.length; j++) {
+ for (int k = 0; k < fnd[j].length; k++) {
+ try {
+ File f = new File(dir, fnd[j][k]);
+ File pf = f.getParentFile();
+ String name = f.getName();
+ if (SYMLINK_UTILS.isSymbolicLink(pf, name)) {
+ result.add(new File(pf.getCanonicalFile(), name));
+ }
+ } catch (IOException e) {
+ handleError("IOException: " + fnd[j][k] + " omitted");
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Load links from properties files included in one or more FileSets.
+ *
+ * <p> This method is only invoked when the action attribute is set to
+ * &quot;recreate&quot;. The filesets passed in are assumed to specify the
+ * names of the property files with the link information and the
+ * subdirectories in which to look for them.
+ *
+ * @param v The <code>FileSet</code>s for this task.
+ * @return The links to be made.
+ */
+ private Properties loadLinks(Vector v) {
+ Properties finalList = new Properties();
+ // loop through the supplied file sets:
+ final int size = v.size();
+ for (int i = 0; i < size; i++) {
+ FileSet fs = (FileSet) v.elementAt(i);
+ DirectoryScanner ds = new DirectoryScanner();
+ fs.setupDirectoryScanner(ds, getProject());
+ ds.setFollowSymlinks(false);
+ ds.scan();
+ String[] incs = ds.getIncludedFiles();
+ File dir = fs.getDir(getProject());
+
+ // load included files as properties files:
+ for (int j = 0; j < incs.length; j++) {
+ File inc = new File(dir, incs[j]);
+ File pf = inc.getParentFile();
+ Properties lnks = new Properties();
+ InputStream is = null;
+ try {
+ is = new BufferedInputStream(new FileInputStream(inc));
+ lnks.load(is);
+ pf = pf.getCanonicalFile();
+ } catch (FileNotFoundException fnfe) {
+ handleError("Unable to find " + incs[j] + "; skipping it.");
+ continue;
+ } catch (IOException ioe) {
+ handleError("Unable to open " + incs[j]
+ + " or its parent dir; skipping it.");
+ continue;
+ } finally {
+ FileUtils.close(is);
+ }
+ lnks.list(new PrintStream(
+ new LogOutputStream(this, Project.MSG_INFO)));
+ // Write the contents to our master list of links
+ // This method assumes that all links are defined in
+ // terms of absolute paths, or paths relative to the
+ // working directory:
+ for (Iterator kitr = lnks.keySet().iterator(); kitr.hasNext();) {
+ String key = (String) kitr.next();
+ finalList.put(new File(pf, key).getAbsolutePath(),
+ lnks.getProperty(key));
+ }
+ }
+ }
+ return finalList;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSS.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSS.java
new file mode 100644
index 00000000..f514fc62
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSS.java
@@ -0,0 +1,784 @@
+/*
+ * 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.tools.ant.taskdefs.optional.vss;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * A base class for creating tasks for executing commands on Visual SourceSafe.
+ * <p>
+ * The class extends the 'exec' task as it operates by executing the ss.exe program
+ * supplied with SourceSafe. By default the task expects ss.exe to be in the path,
+ * you can override this be specifying the ssdir attribute.
+ * </p>
+ * <p>
+ * This class provides set and get methods for 'login' and 'vsspath' attributes. It
+ * also contains constants for the flags that can be passed to SS.
+ * </p>
+ *
+ */
+public abstract class MSVSS extends Task implements MSVSSConstants {
+
+ private String ssDir = null;
+ private String vssLogin = null;
+ private String vssPath = null;
+ private String serverPath = null;
+
+ /** Version */
+ private String version = null;
+ /** Date */
+ private String date = null;
+ /** Label */
+ private String label = null;
+ /** Auto response */
+ private String autoResponse = null;
+ /** Local path */
+ private String localPath = null;
+ /** Comment */
+ private String comment = null;
+ /** From label */
+ private String fromLabel = null;
+ /** To label */
+ private String toLabel = null;
+ /** Output file name */
+ private String outputFileName = null;
+ /** User */
+ private String user = null;
+ /** From date */
+ private String fromDate = null;
+ /** To date */
+ private String toDate = null;
+ /** History style */
+ private String style = null;
+ /** Quiet defaults to false */
+ private boolean quiet = false;
+ /** Recursive defaults to false */
+ private boolean recursive = false;
+ /** Writable defaults to false */
+ private boolean writable = false;
+ /** Fail on error defaults to true */
+ private boolean failOnError = true;
+ /** Get local copy for checkout defaults to true */
+ private boolean getLocalCopy = true;
+ /** Number of days offset for History */
+ private int numDays = Integer.MIN_VALUE;
+ /** Date format for History */
+ private DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT);
+ /** Timestamp for retreived files */
+ private CurrentModUpdated timestamp = null;
+ /** Behaviour for writable files */
+ private WritableFiles writableFiles = null;
+
+ /**
+ * Each sub-class must implemnt this method and return the constructed
+ * command line to be executed. It is up to the sub-task to determine the
+ * required attrubutes and their order.
+ * @return The Constructed command line.
+ */
+ abstract Commandline buildCmdLine();
+
+ /**
+ * Directory where <code>ss.exe</code> resides.
+ * By default the task expects it to be in the PATH.
+ * @param dir The directory containing ss.exe.
+ */
+ public final void setSsdir(String dir) {
+ this.ssDir = FileUtils.translatePath(dir);
+ }
+
+ /**
+ * Login to use when accessing VSS, formatted as "username,password".
+ * <p>
+ * You can omit the password if your database is not password protected.
+ * If you have a password and omit it, Ant will hang.
+ * @param vssLogin The login string to use.
+ */
+ public final void setLogin(final String vssLogin) {
+ this.vssLogin = vssLogin;
+ }
+
+ /**
+ * SourceSafe path which specifies the project/file(s) you wish to perform
+ * the action on.
+ * <p>
+ * A prefix of 'vss://' will be removed if specified.
+ * @param vssPath The VSS project path.
+ * @ant.attribute group="required"
+ */
+ public final void setVsspath(final String vssPath) {
+ String projectPath;
+ // CheckStyle:MagicNumber OFF
+ if (vssPath.startsWith("vss://")) { //$NON-NLS-1$
+ projectPath = vssPath.substring(5);
+ } else {
+ projectPath = vssPath;
+ }
+ // CheckStyle:MagicNumber ON
+
+ if (projectPath.startsWith(PROJECT_PREFIX)) {
+ this.vssPath = projectPath;
+ } else {
+ this.vssPath = PROJECT_PREFIX + projectPath;
+ }
+ }
+
+ /**
+ * Directory where <code>srssafe.ini</code> resides.
+ * @param serverPath The path to the VSS server.
+ */
+ public final void setServerpath(final String serverPath) {
+ this.serverPath = serverPath;
+ }
+
+ /**
+ * Indicates if the build should fail if the Sourcesafe command does. Defaults to true.
+ * @param failOnError True if task should fail on any error.
+ */
+ public final void setFailOnError(final boolean failOnError) {
+ this.failOnError = failOnError;
+ }
+
+ /**
+ * Executes the task. <br>
+ * Builds a command line to execute ss.exe and then calls Exec's run method
+ * to execute the command line.
+ * @throws BuildException if the command cannot execute.
+ */
+ public void execute() throws BuildException {
+ int result = 0;
+ Commandline commandLine = buildCmdLine();
+ result = run(commandLine);
+ if (Execute.isFailure(result) && getFailOnError()) {
+ String msg = "Failed executing: " + formatCommandLine(commandLine)
+ + " With a return code of " + result;
+ throw new BuildException(msg, getLocation());
+ }
+ }
+
+ // Special setters for the sub-classes
+
+ /**
+ * Set the internal comment attribute.
+ * @param comment the value to use.
+ */
+ protected void setInternalComment(final String comment) {
+ this.comment = comment;
+ }
+
+ /**
+ * Set the auto response attribute.
+ * @param autoResponse the value to use.
+ */
+ protected void setInternalAutoResponse(final String autoResponse) {
+ this.autoResponse = autoResponse;
+ }
+
+ /**
+ * Set the date attribute.
+ * @param date the value to use.
+ */
+ protected void setInternalDate(final String date) {
+ this.date = date;
+ }
+
+ /**
+ * Set the date format attribute.
+ * @param dateFormat the value to use.
+ */
+ protected void setInternalDateFormat(final DateFormat dateFormat) {
+ this.dateFormat = dateFormat;
+ }
+
+ /**
+ * Set the failOnError attribute.
+ * @param failOnError the value to use.
+ */
+ protected void setInternalFailOnError(final boolean failOnError) {
+ this.failOnError = failOnError;
+ }
+
+ /**
+ * Set the from date attribute.
+ * @param fromDate the value to use.
+ */
+ protected void setInternalFromDate(final String fromDate) {
+ this.fromDate = fromDate;
+ }
+
+ /**
+ * Set the from label attribute.
+ * @param fromLabel the value to use.
+ */
+ protected void setInternalFromLabel(final String fromLabel) {
+ this.fromLabel = fromLabel;
+ }
+
+ /**
+ * Set the label attribute.
+ * @param label the value to use.
+ */
+ protected void setInternalLabel(final String label) {
+ this.label = label;
+ }
+
+ /**
+ * Set the local path comment attribute.
+ * @param localPath the value to use.
+ */
+ protected void setInternalLocalPath(final String localPath) {
+ this.localPath = localPath;
+ }
+
+ /**
+ * Set the num days attribute.
+ * @param numDays the value to use.
+ */
+ protected void setInternalNumDays(final int numDays) {
+ this.numDays = numDays;
+ }
+
+ /**
+ * Set the outputFileName comment attribute.
+ * @param outputFileName the value to use.
+ */
+ protected void setInternalOutputFilename(final String outputFileName) {
+ this.outputFileName = outputFileName;
+ }
+
+ /**
+ * Set the quiet attribute.
+ * @param quiet the value to use.
+ */
+ protected void setInternalQuiet(final boolean quiet) {
+ this.quiet = quiet;
+ }
+
+ /**
+ * Set the recursive attribute.
+ * @param recursive the value to use.
+ */
+ protected void setInternalRecursive(final boolean recursive) {
+ this.recursive = recursive;
+ }
+
+ /**
+ * Set the style attribute.
+ * @param style the value to use.
+ */
+ protected void setInternalStyle(final String style) {
+ this.style = style;
+ }
+
+ /**
+ * Set the to date attribute.
+ * @param toDate the value to use.
+ */
+ protected void setInternalToDate(final String toDate) {
+ this.toDate = toDate;
+ }
+
+ /**
+ * Set the to label attribute.
+ * @param toLabel the value to use.
+ */
+ protected void setInternalToLabel(final String toLabel) {
+ this.toLabel = toLabel;
+ }
+
+ /**
+ * Set the user attribute.
+ * @param user the value to use.
+ */
+ protected void setInternalUser(final String user) {
+ this.user = user;
+ }
+
+ /**
+ * Set the version attribute.
+ * @param version the value to use.
+ */
+ protected void setInternalVersion(final String version) {
+ this.version = version;
+ }
+
+ /**
+ * Set the writable attribute.
+ * @param writable the value to use.
+ */
+ protected void setInternalWritable(final boolean writable) {
+ this.writable = writable;
+ }
+
+ /**
+ * Set the timestamp attribute.
+ * @param timestamp the value to use.
+ */
+ protected void setInternalFileTimeStamp(final CurrentModUpdated timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ /**
+ * Set the writableFiles attribute.
+ * @param writableFiles the value to use.
+ */
+ protected void setInternalWritableFiles(final WritableFiles writableFiles) {
+ this.writableFiles = writableFiles;
+ }
+
+ /**
+ * Set the getLocalCopy attribute.
+ * @param getLocalCopy the value to use.
+ */
+ protected void setInternalGetLocalCopy(final boolean getLocalCopy) {
+ this.getLocalCopy = getLocalCopy;
+ }
+
+ /**
+ * Gets the sscommand string. "ss" or "c:\path\to\ss"
+ * @return The path to ss.exe or just ss if sscommand is not set.
+ */
+ protected String getSSCommand() {
+ if (ssDir == null) {
+ return SS_EXE;
+ }
+ return ssDir.endsWith(File.separator) ? ssDir + SS_EXE : ssDir
+ + File.separator + SS_EXE;
+ }
+
+ /**
+ * Gets the vssserverpath string.
+ * @return null if vssserverpath is not set.
+ */
+ protected String getVsspath() {
+ return vssPath;
+ }
+
+ /**
+ * Gets the quiet string. -O-
+ * @return An empty string if quiet is not set or is false.
+ */
+ protected String getQuiet() {
+ return quiet ? FLAG_QUIET : "";
+ }
+
+ /**
+ * Gets the recursive string. "-R"
+ * @return An empty string if recursive is not set or is false.
+ */
+ protected String getRecursive() {
+ return recursive ? FLAG_RECURSION : "";
+ }
+
+ /**
+ * Gets the writable string. "-W"
+ * @return An empty string if writable is not set or is false.
+ */
+ protected String getWritable() {
+ return writable ? FLAG_WRITABLE : "";
+ }
+
+ /**
+ * Gets the label string. "-Lbuild1"
+ * Max label length is 32 chars
+ * @return An empty string if label is not set.
+ */
+ protected String getLabel() {
+ String shortLabel = "";
+ if (label != null && label.length() > 0) {
+ shortLabel = FLAG_LABEL + getShortLabel();
+ }
+ return shortLabel;
+ }
+ /**
+ * Return at most the 30 first chars of the label,
+ * logging a warning message about the truncation
+ * @return at most the 30 first chars of the label
+ */
+ private String getShortLabel() {
+ String shortLabel;
+ // CheckStyle:MagicNumber OFF
+ if (label != null && label.length() > 31) {
+ shortLabel = this.label.substring(0, 30);
+ log("Label is longer than 31 characters, truncated to: " + shortLabel,
+ Project.MSG_WARN);
+ } else {
+ shortLabel = label;
+ }
+ // CheckStyle:MagicNumber ON
+ return shortLabel;
+ }
+ /**
+ * Gets the style string. "-Lbuild1"
+ * @return An empty string if label is not set.
+ */
+ protected String getStyle() {
+ return style != null ? style : "";
+ }
+
+ /**
+ * Gets the version string. Returns the first specified of version "-V1.0",
+ * date "-Vd01.01.01", label "-Vlbuild1".
+ * @return An empty string if a version, date and label are not set.
+ */
+ protected String getVersionDateLabel() {
+ String versionDateLabel = "";
+ if (version != null) {
+ versionDateLabel = FLAG_VERSION + version;
+ } else if (date != null) {
+ versionDateLabel = FLAG_VERSION_DATE + date;
+ } else {
+ // Use getShortLabel() so labels longer then 30 char are truncated
+ // and the user is warned
+ String shortLabel = getShortLabel();
+ if (shortLabel != null && !shortLabel.equals("")) {
+ versionDateLabel = FLAG_VERSION_LABEL + shortLabel;
+ }
+ }
+ return versionDateLabel;
+ }
+
+ /**
+ * Gets the version string.
+ * @return An empty string if a version is not set.
+ */
+ protected String getVersion() {
+ return version != null ? FLAG_VERSION + version : "";
+ }
+
+ /**
+ * Gets the localpath string. "-GLc:\source" <p>
+ * The localpath is created if it didn't exist.
+ * @return An empty string if localpath is not set.
+ */
+ protected String getLocalpath() {
+ String lclPath = ""; //set to empty str if no local path return
+ if (localPath != null) {
+ //make sure m_LocalDir exists, create it if it doesn't
+ File dir = getProject().resolveFile(localPath);
+ if (!dir.exists()) {
+ boolean done = dir.mkdirs();
+ if (!done) {
+ String msg = "Directory " + localPath + " creation was not "
+ + "successful for an unknown reason";
+ throw new BuildException(msg, getLocation());
+ }
+ getProject().log("Created dir: " + dir.getAbsolutePath());
+ }
+ lclPath = FLAG_OVERRIDE_WORKING_DIR + localPath;
+ }
+ return lclPath;
+ }
+
+ /**
+ * Gets the comment string. "-Ccomment text"
+ * @return A comment of "-" if comment is not set.
+ */
+ protected String getComment() {
+ return comment != null ? FLAG_COMMENT + comment : FLAG_COMMENT + "-";
+ }
+
+ /**
+ * Gets the auto response string. This can be Y "-I-Y" or N "-I-N".
+ * @return The default value "-I-" if autoresponse is not set.
+ */
+ protected String getAutoresponse() {
+ if (autoResponse == null) {
+ return FLAG_AUTORESPONSE_DEF;
+ }
+ if (autoResponse.equalsIgnoreCase("Y")) {
+ return FLAG_AUTORESPONSE_YES;
+ } else if (autoResponse.equalsIgnoreCase("N")) {
+ return FLAG_AUTORESPONSE_NO;
+ } else {
+ return FLAG_AUTORESPONSE_DEF;
+ }
+ }
+
+ /**
+ * Gets the login string. This can be user and password, "-Yuser,password"
+ * or just user "-Yuser".
+ * @return An empty string if login is not set.
+ */
+ protected String getLogin() {
+ return vssLogin != null ? FLAG_LOGIN + vssLogin : "";
+ }
+
+ /**
+ * Gets the output file string. "-Ooutput.file"
+ * @return An empty string if user is not set.
+ */
+ protected String getOutput() {
+ return outputFileName != null ? FLAG_OUTPUT + outputFileName : "";
+ }
+
+ /**
+ * Gets the user string. "-Uusername"
+ * @return An empty string if user is not set.
+ */
+ protected String getUser() {
+ return user != null ? FLAG_USER + user : "";
+ }
+
+ /**
+ * Gets the version string. This can be to-from "-VLbuild2~Lbuild1", from
+ * "~Lbuild1" or to "-VLbuild2".
+ * @return An empty string if neither tolabel or fromlabel are set.
+ */
+ protected String getVersionLabel() {
+ if (fromLabel == null && toLabel == null) {
+ return "";
+ }
+ // CheckStyle:MagicNumber OFF
+ if (fromLabel != null && toLabel != null) {
+ if (fromLabel.length() > 31) {
+ fromLabel = fromLabel.substring(0, 30);
+ log("FromLabel is longer than 31 characters, truncated to: "
+ + fromLabel, Project.MSG_WARN);
+ }
+ if (toLabel.length() > 31) {
+ toLabel = toLabel.substring(0, 30);
+ log("ToLabel is longer than 31 characters, truncated to: "
+ + toLabel, Project.MSG_WARN);
+ }
+ return FLAG_VERSION_LABEL + toLabel + VALUE_FROMLABEL + fromLabel;
+ } else if (fromLabel != null) {
+ if (fromLabel.length() > 31) {
+ fromLabel = fromLabel.substring(0, 30);
+ log("FromLabel is longer than 31 characters, truncated to: "
+ + fromLabel, Project.MSG_WARN);
+ }
+ return FLAG_VERSION + VALUE_FROMLABEL + fromLabel;
+ } else {
+ if (toLabel.length() > 31) {
+ toLabel = toLabel.substring(0, 30);
+ log("ToLabel is longer than 31 characters, truncated to: "
+ + toLabel, Project.MSG_WARN);
+ }
+ return FLAG_VERSION_LABEL + toLabel;
+ }
+ // CheckStyle:MagicNumber ON
+ }
+
+ /**
+ * Gets the Version date string.
+ * @return An empty string if neither Todate or from date are set.
+ * @throws BuildException if there is an error.
+ */
+ protected String getVersionDate() throws BuildException {
+ if (fromDate == null && toDate == null
+ && numDays == Integer.MIN_VALUE) {
+ return "";
+ }
+ if (fromDate != null && toDate != null) {
+ return FLAG_VERSION_DATE + toDate + VALUE_FROMDATE + fromDate;
+ } else if (toDate != null && numDays != Integer.MIN_VALUE) {
+ try {
+ return FLAG_VERSION_DATE + toDate + VALUE_FROMDATE
+ + calcDate(toDate, numDays);
+ } catch (ParseException ex) {
+ String msg = "Error parsing date: " + toDate;
+ throw new BuildException(msg, getLocation());
+ }
+ } else if (fromDate != null && numDays != Integer.MIN_VALUE) {
+ try {
+ return FLAG_VERSION_DATE + calcDate(fromDate, numDays)
+ + VALUE_FROMDATE + fromDate;
+ } catch (ParseException ex) {
+ String msg = "Error parsing date: " + fromDate;
+ throw new BuildException(msg, getLocation());
+ }
+ } else {
+ return fromDate != null ? FLAG_VERSION + VALUE_FROMDATE
+ + fromDate : FLAG_VERSION_DATE + toDate;
+ }
+ }
+
+ /**
+ * Builds and returns the -G- flag if required.
+ * @return An empty string if get local copy is true.
+ */
+ protected String getGetLocalCopy() {
+ return (!getLocalCopy) ? FLAG_NO_GET : "";
+ }
+
+ /**
+ * Gets the value of the fail on error flag.
+ * @return True if the FailOnError flag has been set or if 'writablefiles=skip'.
+ */
+ private boolean getFailOnError() {
+ return getWritableFiles().equals(WRITABLE_SKIP) ? false : failOnError;
+ }
+
+
+ /**
+ * Gets the value set for the FileTimeStamp.
+ * if it equals "current" then we return -GTC
+ * if it equals "modified" then we return -GTM
+ * if it equals "updated" then we return -GTU
+ * otherwise we return -GTC
+ *
+ * @return The default file time flag, if not set.
+ */
+ public String getFileTimeStamp() {
+ if (timestamp == null) {
+ return "";
+ } else if (timestamp.getValue().equals(TIME_MODIFIED)) {
+ return FLAG_FILETIME_MODIFIED;
+ } else if (timestamp.getValue().equals(TIME_UPDATED)) {
+ return FLAG_FILETIME_UPDATED;
+ } else {
+ return FLAG_FILETIME_DEF;
+ }
+ }
+
+
+ /**
+ * Gets the value to determine the behaviour when encountering writable files.
+ * @return An empty String, if not set.
+ */
+ public String getWritableFiles() {
+ if (writableFiles == null) {
+ return "";
+ } else if (writableFiles.getValue().equals(WRITABLE_REPLACE)) {
+ return FLAG_REPLACE_WRITABLE;
+ } else if (writableFiles.getValue().equals(WRITABLE_SKIP)) {
+ // ss.exe exits with '100', when files have been skipped
+ // so we have to ignore the failure
+ failOnError = false;
+ return FLAG_SKIP_WRITABLE;
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * Sets up the required environment and executes the command line.
+ *
+ * @param cmd The command line to execute.
+ * @return The return code from the exec'd process.
+ */
+ private int run(Commandline cmd) {
+ try {
+ Execute exe = new Execute(new LogStreamHandler(this,
+ Project.MSG_INFO,
+ Project.MSG_WARN));
+
+ // If location of ss.ini is specified we need to set the
+ // environment-variable SSDIR to this value
+ if (serverPath != null) {
+ String[] env = exe.getEnvironment();
+ if (env == null) {
+ env = new String[0];
+ }
+ String[] newEnv = new String[env.length + 1];
+ System.arraycopy(env, 0, newEnv, 0, env.length);
+ newEnv[env.length] = "SSDIR=" + serverPath;
+
+ exe.setEnvironment(newEnv);
+ }
+
+ exe.setAntRun(getProject());
+ exe.setWorkingDirectory(getProject().getBaseDir());
+ exe.setCommandline(cmd.getCommandline());
+ // Use the OS launcher so we get environment variables
+ exe.setVMLauncher(false);
+ return exe.execute();
+ } catch (IOException e) {
+ throw new BuildException(e, getLocation());
+ }
+ }
+
+ /**
+ * Calculates the start date for version comparison.
+ * <p>
+ * Calculates the date numDay days earlier than startdate.
+ * @param startDate The start date.
+ * @param daysToAdd The number of days to add.
+ * @return The calculated date.
+ * @throws ParseException
+ */
+ private String calcDate(String startDate, int daysToAdd) throws ParseException {
+ Calendar calendar = new GregorianCalendar();
+ Date currentDate = dateFormat.parse(startDate);
+ calendar.setTime(currentDate);
+ calendar.add(Calendar.DATE, daysToAdd);
+ return dateFormat.format(calendar.getTime());
+ }
+
+ /**
+ * Changes the password to '***' so it isn't displayed on screen if the build fails
+ *
+ * @param cmd The command line to clean
+ * @return The command line as a string with out the password
+ */
+ private String formatCommandLine(Commandline cmd) {
+ StringBuffer sBuff = new StringBuffer(cmd.toString());
+ int indexUser = sBuff.substring(0).indexOf(FLAG_LOGIN);
+ if (indexUser > 0) {
+ int indexPass = sBuff.substring(0).indexOf(",", indexUser);
+ int indexAfterPass = sBuff.substring(0).indexOf(" ", indexPass);
+
+ for (int i = indexPass + 1; i < indexAfterPass; i++) {
+ sBuff.setCharAt(i, '*');
+ }
+ }
+ return sBuff.toString();
+ }
+
+ /**
+ * Extension of EnumeratedAttribute to hold the values for file time stamp.
+ */
+ public static class CurrentModUpdated extends EnumeratedAttribute {
+ /**
+ * Gets the list of allowable values.
+ * @return The values.
+ */
+ public String[] getValues() {
+ return new String[] {TIME_CURRENT, TIME_MODIFIED, TIME_UPDATED};
+ }
+ }
+
+ /**
+ * Extension of EnumeratedAttribute to hold the values for writable filess.
+ */
+ public static class WritableFiles extends EnumeratedAttribute {
+ /**
+ * Gets the list of allowable values.
+ * @return The values.
+ */
+ public String[] getValues() {
+ return new String[] {WRITABLE_REPLACE, WRITABLE_SKIP, WRITABLE_FAIL};
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSADD.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSADD.java
new file mode 100644
index 00000000..0241ee3c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSADD.java
@@ -0,0 +1,122 @@
+/*
+ * 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.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Performs Add commands to Microsoft Visual SourceSafe.
+ *
+ * @ant.task name="vssadd" category="scm"
+ */
+public class MSVSSADD extends MSVSS {
+
+ private String localPath = null;
+
+ /**
+ * Builds a command line to execute ss.
+ * @return The constructed commandline.
+ */
+ protected Commandline buildCmdLine() {
+ Commandline commandLine = new Commandline();
+
+ // first off, make sure that we've got a command and a localPath ...
+ if (getLocalpath() == null) {
+ String msg = "localPath attribute must be set!";
+ throw new BuildException(msg, getLocation());
+ }
+
+ // build the command line from what we got the format is
+ // ss Add VSS items [-B] [-C] [-D-] [-H] [-I-] [-K] [-N] [-O] [-R] [-W] [-Y] [-?]
+ // as specified in the SS.EXE help
+ commandLine.setExecutable(getSSCommand());
+ commandLine.createArgument().setValue(COMMAND_ADD);
+
+ // VSS items
+ commandLine.createArgument().setValue(getLocalpath());
+ // -I- or -I-Y or -I-N
+ commandLine.createArgument().setValue(getAutoresponse());
+ // -R
+ commandLine.createArgument().setValue(getRecursive());
+ // -W
+ commandLine.createArgument().setValue(getWritable());
+ // -Y
+ commandLine.createArgument().setValue(getLogin());
+ // -C
+ commandLine.createArgument().setValue(getComment());
+
+ return commandLine;
+ }
+
+ /**
+ * Returns the local path without the flag.; required
+ * @todo See why this returns the local path without the flag.
+ * @return The local path value.
+ */
+ protected String getLocalpath() {
+ return localPath;
+ }
+
+ /**
+ * Add files recursively. Defaults to false.
+ *
+ * @param recursive The boolean value for recursive.
+ */
+ public void setRecursive(boolean recursive) {
+ super.setInternalRecursive(recursive);
+ }
+
+ /**
+ * Unset the READ-ONLY flag on local copies of files added to VSS. Defaults to false.
+ *
+ * @param writable The boolean value for writable.
+ */
+ public final void setWritable(boolean writable) {
+ super.setInternalWritable(writable);
+ }
+
+ /**
+ * Autoresponce behaviour. Valid options are Y and N.
+ *
+ * @param response The auto response value.
+ */
+ public void setAutoresponse(String response) {
+ super.setInternalAutoResponse(response);
+ }
+
+ /**
+ * Comment to apply to files added to SourceSafe.
+ *
+ * @param comment The comment to apply in SourceSafe
+ */
+ public void setComment(String comment) {
+ super.setInternalComment(comment);
+ }
+
+ /**
+ * Override the project working directory.
+ *
+ * @param localPath The path on disk.
+ */
+ public void setLocalpath(Path localPath) {
+ this.localPath = localPath.toString();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKIN.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKIN.java
new file mode 100644
index 00000000..f9521aa0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKIN.java
@@ -0,0 +1,114 @@
+/*
+ * 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.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Performs CheckIn commands to Microsoft Visual SourceSafe.
+ *
+ * @ant.task name="vsscheckin" category="scm"
+ */
+public class MSVSSCHECKIN extends MSVSS {
+
+ /**
+ * Builds a command line to execute ss.
+ * @return The constructed commandline.
+ */
+ protected Commandline buildCmdLine() {
+ Commandline commandLine = new Commandline();
+
+ // first off, make sure that we've got a command and a vssdir ...
+ if (getVsspath() == null) {
+ String msg = "vsspath attribute must be set!";
+ throw new BuildException(msg, getLocation());
+ }
+
+ // build the command line from what we got the format is
+ // ss Checkin VSS items [-H] [-C] [-I-] [-N] [-O] [-R] [-W] [-Y] [-?]
+ // as specified in the SS.EXE help
+ commandLine.setExecutable(getSSCommand());
+ commandLine.createArgument().setValue(COMMAND_CHECKIN);
+
+ // VSS items
+ commandLine.createArgument().setValue(getVsspath());
+ // -GL
+ commandLine.createArgument().setValue(getLocalpath());
+ // -I- or -I-Y or -I-N
+ commandLine.createArgument().setValue(getAutoresponse());
+ // -R
+ commandLine.createArgument().setValue(getRecursive());
+ // -W
+ commandLine.createArgument().setValue(getWritable());
+ // -Y
+ commandLine.createArgument().setValue(getLogin());
+ // -C
+ commandLine.createArgument().setValue(getComment());
+
+ return commandLine;
+ }
+
+ /**
+ * Override the project working directory.
+ *
+ * @param localPath The path on disk.
+ */
+ public void setLocalpath(Path localPath) {
+ super.setInternalLocalPath(localPath.toString());
+ }
+
+ /**
+ * Check-in files recursively. Defaults to false.
+ *
+ * @param recursive The boolean value for recursive.
+ */
+ public void setRecursive(boolean recursive) {
+ super.setInternalRecursive(recursive);
+ }
+
+ /**
+ * Unset the READ-ONLY flag on local copies of files checked-in to VSS.
+ * Defaults to false.
+ *
+ * @param writable The boolean value for writable.
+ */
+ public final void setWritable(boolean writable) {
+ super.setInternalWritable(writable);
+ }
+
+ /**
+ * Autoresponce behaviour. Valid options are Y and N.
+ *
+ * @param response The auto response value.
+ */
+ public void setAutoresponse(String response) {
+ super.setInternalAutoResponse(response);
+ }
+
+ /**
+ * Comment to apply to files checked-in to SourceSafe.
+ *
+ * @param comment The comment to apply in SourceSafe
+ */
+ public void setComment(String comment) {
+ super.setInternalComment(comment);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKOUT.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKOUT.java
new file mode 100644
index 00000000..edbcde96
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKOUT.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.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Performs CheckOut commands to Microsoft Visual SourceSafe.
+ *
+ * @ant.task name="vsscheckout" category="scm"
+ * @ant.attribute.group name="vdl" description="Only one of version, date or label"
+ */
+public class MSVSSCHECKOUT extends MSVSS {
+
+ /**
+ * Builds a command line to execute ss.
+ * @return The constructed commandline.
+ */
+ protected Commandline buildCmdLine() {
+ Commandline commandLine = new Commandline();
+
+ // first off, make sure that we've got a command and a vssdir ...
+ if (getVsspath() == null) {
+ String msg = "vsspath attribute must be set!";
+ throw new BuildException(msg, getLocation());
+ }
+
+ // build the command line from what we got the format is
+ // ss Checkout VSS items [-G] [-C] [-H] [-I-] [-N] [-O] [-R] [-V] [-Y] [-?]
+ // as specified in the SS.EXE help
+ commandLine.setExecutable(getSSCommand());
+ commandLine.createArgument().setValue(COMMAND_CHECKOUT);
+
+ // VSS items
+ commandLine.createArgument().setValue(getVsspath());
+ // -GL
+ commandLine.createArgument().setValue(getLocalpath());
+ // -I- or -I-Y or -I-N
+ commandLine.createArgument().setValue(getAutoresponse());
+ // -R
+ commandLine.createArgument().setValue(getRecursive());
+ // -V
+ commandLine.createArgument().setValue(getVersionDateLabel());
+ // -Y
+ commandLine.createArgument().setValue(getLogin());
+ // -G
+ commandLine.createArgument().setValue(getFileTimeStamp());
+ // -GWS or -GWR
+ commandLine.createArgument().setValue(getWritableFiles());
+ // -G-
+ commandLine.createArgument().setValue(getGetLocalCopy());
+
+ return commandLine;
+ }
+
+ /**
+ * Override the project working directory.
+ *
+ * @param localPath The path on disk.
+ */
+ public void setLocalpath(Path localPath) {
+ super.setInternalLocalPath(localPath.toString());
+ }
+
+ /**
+ * Check-out files recursively. Defaults to false.
+ *
+ * @param recursive The boolean value for recursive.
+ */
+ public void setRecursive(boolean recursive) {
+ super.setInternalRecursive(recursive);
+ }
+
+ /**
+ * Version to check-out.
+ *
+ * @param version The version to check-out.
+ *
+ * @ant.attribute group="vdl"
+ */
+ public void setVersion(String version) {
+ super.setInternalVersion(version);
+ }
+
+ /**
+ * Date to check-out.
+ *
+ * @param date The date to check-out.
+ *
+ * @ant.attribute group="vdl"
+ */
+ public void setDate(String date) {
+ super.setInternalDate(date);
+ }
+
+ /**
+ * Label to check-out.
+ *
+ * @param label The label to check-out.
+ *
+ * @ant.attribute group="vdl"
+ */
+ public void setLabel(String label) {
+ super.setInternalLabel(label);
+ }
+
+ /**
+ * Autoresponce behaviour. Valid options are Y and N.
+ *
+ * @param response The auto response value.
+ */
+ public void setAutoresponse(String response) {
+ super.setInternalAutoResponse(response);
+ }
+
+ /**
+ * Date and time stamp given to the local copy. Defaults to <code>current</code>.
+ *
+ * @param timestamp The file time stamping behaviour.
+ */
+ public void setFileTimeStamp(CurrentModUpdated timestamp) {
+ super.setInternalFileTimeStamp(timestamp);
+ }
+
+ /**
+ * Action taken when local files are writable. Defaults to <code>fail</code>.
+ * <p>
+ * Due to ss.exe returning with an exit code of '100' for both errors and when
+ * a file has been skipped, <code>failonerror</code> is set to false when using
+ * the <code>skip</code> option.
+ * </p>
+ *
+ * @param files The writable files behaviour
+ */
+ public void setWritableFiles(WritableFiles files) {
+ super.setInternalWritableFiles(files);
+ }
+
+ /**
+ * Retrieve a local copy during a checkout. Defaults to true.
+ *
+ * @param get The get local copy behaviour
+ */
+ public void setGetLocalCopy(boolean get) {
+ super.setInternalGetLocalCopy(get);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCP.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCP.java
new file mode 100644
index 00000000..ae2fcb7c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCP.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.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs CP (Change Project) commands to Microsoft Visual SourceSafe.
+ * <p>This task is typically used before a VssAdd in order to set the target project</p>
+ *
+ * @ant.task name="vsscp" category="scm"
+ */
+public class MSVSSCP extends MSVSS {
+
+ /**
+ * Builds a command line to execute ss.
+ * @return The constructed commandline.
+ */
+ protected Commandline buildCmdLine() {
+ Commandline commandLine = new Commandline();
+
+ // first off, make sure that we've got a command and a vssdir ...
+ if (getVsspath() == null) {
+ String msg = "vsspath attribute must be set!";
+ throw new BuildException(msg, getLocation());
+ }
+
+ // build the command line from what we got the format is
+ // ss CP VSS items [-H] [-I-] [-Y] [-?]
+ // as specified in the SS.EXE help
+ commandLine.setExecutable(getSSCommand());
+ commandLine.createArgument().setValue(COMMAND_CP);
+
+ // VSS items
+ commandLine.createArgument().setValue(getVsspath());
+ // -I- or -I-Y or -I-N
+ commandLine.createArgument().setValue(getAutoresponse());
+ // -Y
+ commandLine.createArgument().setValue(getLogin());
+
+ return commandLine;
+ }
+
+ /**
+ * Autoresponce behaviour. Valid options are Y and N.
+ *
+ * @param response The auto response value.
+ */
+ public void setAutoresponse(String response) {
+ super.setInternalAutoResponse(response);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCREATE.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCREATE.java
new file mode 100644
index 00000000..4f298c03
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCREATE.java
@@ -0,0 +1,91 @@
+/*
+ * 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.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Creates a new project in Microsoft Visual SourceSafe.
+ *
+ * @ant.task name="vsscreate" category="scm"
+ */
+public class MSVSSCREATE extends MSVSS {
+
+ /**
+ * Builds a command line to execute ss.
+ * @return The constructed commandline.
+ */
+ Commandline buildCmdLine() {
+ Commandline commandLine = new Commandline();
+
+ // first off, make sure that we've got a command and a vssdir...
+ if (getVsspath() == null) {
+ String msg = "vsspath attribute must be set!";
+ throw new BuildException(msg, getLocation());
+ }
+
+ // build the command line from what we got
+ // the format is:
+ // ss Create VSS items [-C] [-H] [-I-] [-N] [-O] [-S] [-Y] [-?]
+ // as specified in the SS.EXE help
+ commandLine.setExecutable(getSSCommand());
+ commandLine.createArgument().setValue(COMMAND_CREATE);
+
+ // VSS items
+ commandLine.createArgument().setValue(getVsspath());
+ // -C
+ commandLine.createArgument().setValue(getComment());
+ // -I- or -I-Y or -I-N
+ commandLine.createArgument().setValue(getAutoresponse());
+ // -O-
+ commandLine.createArgument().setValue(getQuiet());
+ // -Y
+ commandLine.createArgument().setValue(getLogin());
+
+ return commandLine;
+ }
+
+ /**
+ * Comment to apply to the project created in SourceSafe.
+ *
+ * @param comment The comment to apply in SourceSafe
+ */
+ public void setComment(String comment) {
+ super.setInternalComment(comment);
+ }
+
+ /**
+ * Enable quiet mode. Defaults to false.
+ *
+ * @param quiet The boolean value for quiet.
+ */
+ public final void setQuiet (boolean quiet) {
+ super.setInternalQuiet(quiet);
+ }
+
+ /**
+ * Autoresponce behaviour. Valid options are Y and N.
+ *
+ * @param response The auto response value.
+ */
+ public void setAutoresponse(String response) {
+ super.setInternalAutoResponse(response);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSConstants.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSConstants.java
new file mode 100644
index 00000000..9e5ec606
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSConstants.java
@@ -0,0 +1,127 @@
+/*
+ * 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.tools.ant.taskdefs.optional.vss;
+
+/**
+ * Holds all the constants for the VSS tasks.
+ *
+ */
+// CheckStyle:InterfaceIsType OFF (bc)
+public interface MSVSSConstants {
+ /** Constant for the thing to execute */
+ String SS_EXE = "ss";
+ /** Dollar Sigh to prefix the project path */
+ String PROJECT_PREFIX = "$";
+
+ /** The 'CP' command */
+ String COMMAND_CP = "CP";
+ /** The 'Add' command */
+ String COMMAND_ADD = "Add";
+ /** The 'Get' command */
+ String COMMAND_GET = "Get";
+ /** The 'Checkout' command */
+ String COMMAND_CHECKOUT = "Checkout";
+ /** The 'Checkin' command */
+ String COMMAND_CHECKIN = "Checkin";
+ /** The 'Label' command */
+ String COMMAND_LABEL = "Label";
+ /** The 'History' command */
+ String COMMAND_HISTORY = "History";
+ /** The 'Create' command */
+ String COMMAND_CREATE = "Create";
+
+ /** The brief style flag */
+ String STYLE_BRIEF = "brief";
+ /** The codediff style flag */
+ String STYLE_CODEDIFF = "codediff";
+ /** The nofile style flag */
+ String STYLE_NOFILE = "nofile";
+ /** The default style flag */
+ String STYLE_DEFAULT = "default";
+
+ /** The text for current (default) timestamp */
+ String TIME_CURRENT = "current";
+ /** The text for modified timestamp */
+ String TIME_MODIFIED = "modified";
+ /** The text for updated timestamp */
+ String TIME_UPDATED = "updated";
+
+ /** The text for replacing writable files */
+ String WRITABLE_REPLACE = "replace";
+ /** The text for skipping writable files */
+ String WRITABLE_SKIP = "skip";
+ /** The text for failing on writable files */
+ String WRITABLE_FAIL = "fail";
+
+ /** -Y flag */
+ String FLAG_LOGIN = "-Y";
+ /** -GL flag */
+ String FLAG_OVERRIDE_WORKING_DIR = "-GL";
+ /** -I- flag */
+ String FLAG_AUTORESPONSE_DEF = "-I-";
+ /** -I-Y flag */
+ String FLAG_AUTORESPONSE_YES = "-I-Y";
+ /** -I-N flag */
+ String FLAG_AUTORESPONSE_NO = "-I-N";
+ /** -R flag */
+ String FLAG_RECURSION = "-R";
+ /** -V flag */
+ String FLAG_VERSION = "-V";
+ /** -Vd flag */
+ String FLAG_VERSION_DATE = "-Vd";
+ /** -VL flag */
+ String FLAG_VERSION_LABEL = "-VL";
+ /** -W flag */
+ String FLAG_WRITABLE = "-W";
+ /** -N flag */
+ String VALUE_NO = "-N";
+ /** -Y flag */
+ String VALUE_YES = "-Y";
+ /** -O- flag */
+ String FLAG_QUIET = "-O-";
+ /** -C flag */
+ String FLAG_COMMENT = "-C";
+ /** -L flag */
+ String FLAG_LABEL = "-L";
+ /** ~d flag */
+ String VALUE_FROMDATE = "~d";
+ /** ~L flag */
+ String VALUE_FROMLABEL = "~L";
+ /** -O flag */
+ String FLAG_OUTPUT = "-O";
+ /** -U flag */
+ String FLAG_USER = "-U";
+ /** -F- flag */
+ String FLAG_NO_FILE = "-F-";
+ /** -B flag */
+ String FLAG_BRIEF = "-B";
+ /** -D flag */
+ String FLAG_CODEDIFF = "-D";
+ /** -GTC flag */
+ String FLAG_FILETIME_DEF = "-GTC";
+ /** -GTM flag */
+ String FLAG_FILETIME_MODIFIED = "-GTM";
+ /** -GTU flag */
+ String FLAG_FILETIME_UPDATED = "-GTU";
+ /** -GWR flag */
+ String FLAG_REPLACE_WRITABLE = "-GWR";
+ /** -GWS flag */
+ String FLAG_SKIP_WRITABLE = "-GWS";
+ /** -G- flag */
+ String FLAG_NO_GET = "-G-";
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSGET.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSGET.java
new file mode 100644
index 00000000..fd5ed096
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSGET.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.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Perform Get commands from Microsoft Visual SourceSafe.
+ *
+ * @ant.task name="vssget" category="scm"
+ * @ant.attribute.group name="vdl" description="Only one of version, date or label"
+ */
+public class MSVSSGET extends MSVSS {
+
+ /**
+ * Builds a command line to execute ss.
+ * @return The constructed commandline.
+ */
+ Commandline buildCmdLine() {
+ Commandline commandLine = new Commandline();
+
+ // build the command line from what we got the format is
+ // ss Get VSS items [-G] [-H] [-I-] [-N] [-O] [-R] [-V] [-W] [-Y] [-?]
+ // as specified in the SS.EXE help
+ commandLine.setExecutable(getSSCommand());
+ commandLine.createArgument().setValue(COMMAND_GET);
+
+ if (getVsspath() == null) {
+ throw new BuildException("vsspath attribute must be set!", getLocation());
+ }
+ commandLine.createArgument().setValue(getVsspath());
+
+ // -GL
+ commandLine.createArgument().setValue(getLocalpath());
+ // -I- or -I-Y or -I-N
+ commandLine.createArgument().setValue(getAutoresponse());
+ // -O-
+ commandLine.createArgument().setValue(getQuiet());
+ // -R
+ commandLine.createArgument().setValue(getRecursive());
+ // -V
+ commandLine.createArgument().setValue(getVersionDateLabel());
+ // -W
+ commandLine.createArgument().setValue(getWritable());
+ // -Y
+ commandLine.createArgument().setValue(getLogin());
+ // -G
+ commandLine.createArgument().setValue(getFileTimeStamp());
+ // -GWS or -GWR
+ commandLine.createArgument().setValue(getWritableFiles());
+
+ return commandLine;
+ }
+
+ /**
+ * Override the project working directory.
+ *
+ * @param localPath The path on disk.
+ */
+ public void setLocalpath(Path localPath) {
+ super.setInternalLocalPath(localPath.toString());
+ }
+
+ /**
+ * Get files recursively. Defaults to false.
+ *
+ * @param recursive The boolean value for recursive.
+ */
+ public final void setRecursive(boolean recursive) {
+ super.setInternalRecursive(recursive);
+ }
+
+ /**
+ * Enable quiet mode. Defaults to false.
+ *
+ * @param quiet The boolean value for quiet.
+ */
+ public final void setQuiet (boolean quiet) {
+ super.setInternalQuiet(quiet);
+ }
+
+ /**
+ * Unset the READ-ONLY flag on files retrieved from VSS. Defaults to false.
+ *
+ * @param writable The boolean value for writable.
+ */
+ public final void setWritable(boolean writable) {
+ super.setInternalWritable(writable);
+ }
+
+ /**
+ * Version to get.
+ *
+ * @param version The version to get.
+ *
+ * @ant.attribute group="vdl"
+ */
+ public void setVersion(String version) {
+ super.setInternalVersion(version);
+ }
+
+ /**
+ * Date to get.
+ *
+ * @param date The date to get.
+ *
+ * @ant.attribute group="vdl"
+ */
+ public void setDate(String date) {
+ super.setInternalDate(date);
+ }
+
+ /**
+ * Label to get.
+ *
+ * @param label The label to get.
+ *
+ * @ant.attribute group="vdl"
+ */
+ public void setLabel(String label) {
+ super.setInternalLabel(label);
+ }
+
+ /**
+ * Autoresponce behaviour. Valid options are Y and N.
+ *
+ * @param response The auto response value.
+ */
+ public void setAutoresponse(String response) {
+ super.setInternalAutoResponse(response);
+ }
+
+ /**
+ * Date and time stamp given to the local copy. Defaults to <code>current</code>.
+ *
+ * @param timestamp The file time stamping behaviour.
+ */
+ public void setFileTimeStamp(CurrentModUpdated timestamp) {
+ super.setInternalFileTimeStamp(timestamp);
+ }
+
+ /**
+ * Action taken when local files are writable. Defaults to <code>fail</code>.
+ * <p>
+ * Due to ss.exe returning with an exit code of '100' for both errors and when
+ * a file has been skipped, <code>failonerror</code> is set to false when using
+ * the <code>skip</code> option.
+ *
+ * @param files The action to take.
+ */
+ public void setWritableFiles(WritableFiles files) {
+ super.setInternalWritableFiles(files);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSHISTORY.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSHISTORY.java
new file mode 100644
index 00000000..05ec91c2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSHISTORY.java
@@ -0,0 +1,199 @@
+/*
+ * 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.tools.ant.taskdefs.optional.vss;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ * Performs History commands to Microsoft Visual SourceSafe.
+ *
+ * @ant.task name="vsshistory" category="scm"
+ */
+public class MSVSSHISTORY extends MSVSS {
+
+ /**
+ * Builds a command line to execute ss.
+ * @return The constructed commandline.
+ */
+ Commandline buildCmdLine() {
+ Commandline commandLine = new Commandline();
+
+ // first off, make sure that we've got a command and a vssdir and a label ...
+ if (getVsspath() == null) {
+ String msg = "vsspath attribute must be set!";
+ throw new BuildException(msg, getLocation());
+ }
+
+ // build the command line from what we got the format is
+ // ss History elements [-H] [-L] [-N] [-O] [-V] [-Y] [-#] [-?]
+ // as specified in the SS.EXE help
+ commandLine.setExecutable(getSSCommand());
+ commandLine.createArgument().setValue(COMMAND_HISTORY);
+
+ // VSS items
+ commandLine.createArgument().setValue(getVsspath());
+ // -I-
+ commandLine.createArgument().setValue(FLAG_AUTORESPONSE_DEF); // ignore all errors
+ // -Vd
+ commandLine.createArgument().setValue(getVersionDate());
+ // -VL
+ commandLine.createArgument().setValue(getVersionLabel());
+ // -R
+ commandLine.createArgument().setValue(getRecursive());
+ // -B / -D / -F-
+ commandLine.createArgument().setValue(getStyle());
+ // -Y
+ commandLine.createArgument().setValue(getLogin());
+ // -O
+ commandLine.createArgument().setValue(getOutput());
+
+ return commandLine;
+ }
+
+ /**
+ * Retrieve history recursively. Defaults to false.
+ *
+ * @param recursive The boolean value for recursive.
+ */
+ public void setRecursive(boolean recursive) {
+ super.setInternalRecursive(recursive);
+ }
+
+ /**
+ * Name of the user whose change history is generated.
+ *
+ * @param user The username.
+ */
+ public void setUser(String user) {
+ super.setInternalUser(user);
+ }
+
+ /**
+ * Date representing the 'start' of the range.
+ *
+ * @param fromDate The start date.
+ */
+ public void setFromDate(String fromDate) {
+ super.setInternalFromDate(fromDate);
+ }
+
+ /**
+ * Date representing the 'end' of the range.
+ *
+ * @param toDate The end date.
+ */
+ public void setToDate(String toDate) {
+ super.setInternalToDate(toDate);
+ }
+
+ /**
+ * Label representing the 'start' of the range.
+ *
+ * @param fromLabel The start label.
+ */
+ public void setFromLabel(String fromLabel) {
+ super.setInternalFromLabel(fromLabel);
+ }
+
+ /**
+ * Label representing the 'end' of the range.
+ *
+ * @param toLabel The end label.
+ */
+ public void setToLabel(String toLabel) {
+ super.setInternalToLabel(toLabel);
+ }
+
+ /**
+ * Number of days for comparison.
+ * Defaults to 2 days.
+ *
+ * @param numd The number of days.
+ */
+ public void setNumdays(int numd) {
+ super.setInternalNumDays(numd);
+ }
+
+ /**
+ * Output file name for the history.
+ *
+ * @param outfile The output file name.
+ */
+ public void setOutput(File outfile) {
+ if (outfile != null) {
+ super.setInternalOutputFilename(outfile.getAbsolutePath());
+ }
+ }
+
+ /**
+ * Format of dates in <code>fromDate</code and <code>toDate</code>.
+ * Used when calculating dates with the numdays attribute.
+ * This string uses the formatting rules of <code>SimpleDateFormat</code>.
+ * Defaults to <code>DateFormat.SHORT</code>.
+ *
+ * @param dateFormat The date format.
+ */
+ public void setDateFormat(String dateFormat) {
+ super.setInternalDateFormat(new SimpleDateFormat(dateFormat));
+ }
+
+ /**
+ * Output style. Valid options are:
+ * <ul>
+ * <li>brief: -B Display a brief history.
+ * <li>codediff: -D Display line-by-line file changes.
+ * <li>nofile: -F- Do not display individual file updates in the project history.
+ * <li>default: No option specified. Display in Source Safe's default format.
+ * </ul>
+ *
+ * @param attr The history style:
+ */
+ public void setStyle(BriefCodediffNofile attr) {
+ String option = attr.getValue();
+ if (option.equals(STYLE_BRIEF)) {
+ super.setInternalStyle(FLAG_BRIEF);
+ } else if (option.equals(STYLE_CODEDIFF)) {
+ super.setInternalStyle(FLAG_CODEDIFF);
+ } else if (option.equals(STYLE_DEFAULT)) {
+ super.setInternalStyle("");
+ } else if (option.equals(STYLE_NOFILE)) {
+ super.setInternalStyle(FLAG_NO_FILE);
+ } else {
+ throw new BuildException("Style " + attr + " unknown.", getLocation());
+ }
+ }
+
+ /**
+ * Extension of EnumeratedAttribute to hold the values for style.
+ */
+ public static class BriefCodediffNofile extends EnumeratedAttribute {
+ /**
+ * Gets the list of allowable values.
+ * @return The values.
+ */
+ public String[] getValues() {
+ return new String[] {STYLE_BRIEF, STYLE_CODEDIFF, STYLE_NOFILE, STYLE_DEFAULT};
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSLABEL.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSLABEL.java
new file mode 100644
index 00000000..4ba9d7b3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSLABEL.java
@@ -0,0 +1,108 @@
+/*
+ * 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.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs Label commands to Microsoft Visual SourceSafe.
+ *
+ * @ant.task name="vsslabel" category="scm"
+ */
+public class MSVSSLABEL extends MSVSS {
+
+ /**
+ * Builds a command line to execute ss.
+ * @return The constructed commandline.
+ */
+ Commandline buildCmdLine() {
+ Commandline commandLine = new Commandline();
+
+ // first off, make sure that we've got a command and a vssdir and a label ...
+ if (getVsspath() == null) {
+ throw new BuildException("vsspath attribute must be set!", getLocation());
+ }
+
+ String label = getLabel();
+ if (label.equals("")) {
+ String msg = "label attribute must be set!";
+ throw new BuildException(msg, getLocation());
+ }
+
+ // build the command line from what we got the format is
+ // ss Label VSS items [-C] [-H] [-I-] [-Llabel] [-N] [-O] [-V] [-Y] [-?]
+ // as specified in the SS.EXE help
+ commandLine.setExecutable(getSSCommand());
+ commandLine.createArgument().setValue(COMMAND_LABEL);
+
+ // VSS items
+ commandLine.createArgument().setValue(getVsspath());
+ // -C
+ commandLine.createArgument().setValue(getComment());
+ // -I- or -I-Y or -I-N
+ commandLine.createArgument().setValue(getAutoresponse());
+ // -L Specify the new label on the command line (instead of being prompted)
+ commandLine.createArgument().setValue(label);
+ // -V Label an existing file or project version
+ commandLine.createArgument().setValue(getVersion());
+ // -Y
+ commandLine.createArgument().setValue(getLogin());
+
+ return commandLine;
+ }
+
+ /**
+ * Label to apply in SourceSafe.
+ *
+ * @param label The label to apply.
+ *
+ * @ant.attribute group="required"
+ */
+ public void setLabel(String label) {
+ super.setInternalLabel(label);
+ }
+
+ /**
+ * Version to label.
+ *
+ * @param version The version to label.
+ */
+ public void setVersion(String version) {
+ super.setInternalVersion(version);
+ }
+
+ /**
+ * Comment to apply to files labeled in SourceSafe.
+ *
+ * @param comment The comment to apply in SourceSafe
+ */
+ public void setComment(String comment) {
+ super.setInternalComment(comment);
+ }
+
+ /**
+ * Autoresponce behaviour. Valid options are Y and N.
+ *
+ * @param response The auto response value.
+ */
+ public void setAutoresponse(String response) {
+ super.setInternalAutoResponse(response);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/windows/Attrib.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/windows/Attrib.java
new file mode 100644
index 00000000..e0291914
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/windows/Attrib.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.tools.ant.taskdefs.optional.windows;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.ExecuteOn;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * Attrib equivalent for Win32 environments.
+ * Note: Attrib parameters /S and /D are not handled.
+ *
+ * @since Ant 1.6
+ */
+public class Attrib extends ExecuteOn {
+
+ private static final String ATTR_READONLY = "R";
+ private static final String ATTR_ARCHIVE = "A";
+ private static final String ATTR_SYSTEM = "S";
+ private static final String ATTR_HIDDEN = "H";
+ private static final String SET = "+";
+ private static final String UNSET = "-";
+
+ private boolean haveAttr = false;
+
+ /** Constructor for Attrib. */
+ public Attrib() {
+ super.setExecutable("attrib");
+ super.setParallel(false);
+ }
+
+ /**
+ * A file to be attribed.
+ * @param src a file
+ */
+ public void setFile(File src) {
+ FileSet fs = new FileSet();
+ fs.setFile(src);
+ addFileset(fs);
+ }
+
+ /**
+ * Set the ReadOnly file attribute.
+ * @param value a <code>boolean</code> value
+ */
+ public void setReadonly(boolean value) {
+ addArg(value, ATTR_READONLY);
+ }
+
+ /**
+ * Set the Archive file attribute.
+ * @param value a <code>boolean</code> value
+ */
+ public void setArchive(boolean value) {
+ addArg(value, ATTR_ARCHIVE);
+ }
+
+ /**
+ * Set the System file attribute.
+ * @param value a <code>boolean</code> value
+ */
+ public void setSystem(boolean value) {
+ addArg(value, ATTR_SYSTEM);
+ }
+
+ /**
+ * Set the Hidden file attribute.
+ * @param value a <code>boolean</code> value
+ */
+ public void setHidden(boolean value) {
+ addArg(value, ATTR_HIDDEN);
+ }
+
+ /**
+ * Check the attributes.
+ */
+ protected void checkConfiguration() {
+ if (!haveAttr()) {
+ throw new BuildException("Missing attribute parameter",
+ getLocation());
+ }
+ super.checkConfiguration();
+ }
+
+ /**
+ * Set the executable.
+ * This is not allowed, and it always throws a BuildException.
+ * @param e ignored
+ * @ant.attribute ignore="true"
+ */
+ public void setExecutable(String e) {
+ throw new BuildException(getTaskType()
+ + " doesn\'t support the executable attribute", getLocation());
+ }
+
+ /**
+ * Set the executable.
+ * This is not allowed, and it always throws a BuildException.
+ * @param e ignored
+ * @ant.attribute ignore="true"
+ */
+ public void setCommand(String e) {
+ throw new BuildException(getTaskType()
+ + " doesn\'t support the command attribute", getLocation());
+ }
+
+ /**
+ * Add source file.
+ * This is not allowed, and it always throws a BuildException.
+ * @param b ignored
+ * @ant.attribute ignore="true"
+ */
+ public void setAddsourcefile(boolean b) {
+ throw new BuildException(getTaskType()
+ + " doesn\'t support the addsourcefile attribute", getLocation());
+ }
+
+ /**
+ * Set skip empty file sets.
+ * This is not allowed, and it always throws a BuildException.
+ * @param skip ignored
+ * @ant.attribute ignore="true"
+ */
+ public void setSkipEmptyFilesets(boolean skip) {
+ throw new BuildException(getTaskType() + " doesn\'t support the "
+ + "skipemptyfileset attribute",
+ getLocation());
+ }
+
+ /**
+ * Set parallel.
+ * This is not allowed, and it always throws a BuildException.
+ * @param parallel ignored
+ * @ant.attribute ignore="true"
+ */
+ public void setParallel(boolean parallel) {
+ throw new BuildException(getTaskType()
+ + " doesn\'t support the parallel attribute",
+ getLocation());
+ }
+
+ /**
+ * Set max parallel.
+ * This is not allowed, and it always throws a BuildException.
+ * @param max ignored
+ * @ant.attribute ignore="true"
+ */
+ public void setMaxParallel(int max) {
+ throw new BuildException(getTaskType()
+ + " doesn\'t support the maxparallel attribute",
+ getLocation());
+ }
+
+ /**
+ * Check if the os is valid.
+ * Default is to allow windows
+ * @return true if the os is valid.
+ */
+ protected boolean isValidOs() {
+ return getOs() == null && getOsFamily() == null ?
+ Os.isFamily(Os.FAMILY_WINDOWS) : super.isValidOs();
+ }
+
+ private static String getSignString(boolean attr) {
+ return (attr ? SET : UNSET);
+ }
+
+ private void addArg(boolean sign, String attribute) {
+ createArg().setValue(getSignString(sign) + attribute);
+ haveAttr = true;
+ }
+
+ private boolean haveAttr() {
+ return haveAttr;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/DefaultRmicAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/DefaultRmicAdapter.java
new file mode 100644
index 00000000..bb2cfaab
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/DefaultRmicAdapter.java
@@ -0,0 +1,493 @@
+/*
+ * 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.tools.ant.taskdefs.rmic;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.Vector;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Rmic;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * This is the default implementation for the RmicAdapter interface.
+ * Currently, this is a cut-and-paste of the original rmic task and
+ * DefaultCompilerAdapter.
+ *
+ * @since Ant 1.4
+ */
+public abstract class DefaultRmicAdapter implements RmicAdapter {
+
+ private Rmic attributes;
+ private FileNameMapper mapper;
+ private static final Random RAND = new Random();
+ /** suffix denoting a stub file: {@value} */
+ public static final String RMI_STUB_SUFFIX = "_Stub";
+ /** suffix denoting a skel file: {@value} */
+ public static final String RMI_SKEL_SUFFIX = "_Skel";
+ /** suffix denoting a tie file: {@value} */
+ public static final String RMI_TIE_SUFFIX = "_Tie";
+ /** arg for compat: {@value} */
+ public static final String STUB_COMPAT = "-vcompat";
+ /** arg for 1.1: {@value} */
+ public static final String STUB_1_1 = "-v1.1";
+ /** arg for 1.2: {@value} */
+ public static final String STUB_1_2 = "-v1.2";
+
+ /**
+ * option for stub 1.1 in the rmic task: {@value}
+ */
+ public static final String STUB_OPTION_1_1 = "1.1";
+ /**
+ * option for stub 1.2 in the rmic task: {@value}
+ */
+ public static final String STUB_OPTION_1_2 = "1.2";
+ /**
+ * option for stub compat in the rmic task: {@value}
+ */
+ public static final String STUB_OPTION_COMPAT = "compat";
+
+ /**
+ * Default constructor
+ */
+ public DefaultRmicAdapter() {
+ }
+
+ /**
+ * Sets Rmic attributes
+ * @param attributes the rmic attributes
+ */
+ public void setRmic(final Rmic attributes) {
+ this.attributes = attributes;
+ mapper = new RmicFileNameMapper();
+ }
+
+ /**
+ * Get the Rmic attributes
+ * @return the attributes as a Rmic taskdef
+ */
+ public Rmic getRmic() {
+ return attributes;
+ }
+
+ /**
+ * Gets the stub class suffix
+ * @return the stub suffix &quot;_Stub&quot;
+ */
+ protected String getStubClassSuffix() {
+ return RMI_STUB_SUFFIX;
+ }
+
+ /**
+ * Gets the skeleton class suffix
+ * @return the skeleton suffix &quot;_Skel&quot;
+ */
+ protected String getSkelClassSuffix() {
+ return RMI_SKEL_SUFFIX;
+ }
+
+ /**
+ * Gets the tie class suffix
+ * @return the tie suffix &quot;_Tie&quot;
+ */
+ protected String getTieClassSuffix() {
+ return RMI_TIE_SUFFIX;
+ }
+
+ /**
+ * This implementation returns a mapper that may return up to two
+ * file names.
+ *
+ * <ul>
+ * <li>for JRMP it will return *_getStubClassSuffix (and
+ * *_getSkelClassSuffix if JDK 1.1 is used)</li>
+ *
+ * <li>for IDL it will return a random name, causing &lt;rmic&gt; to
+ * always recompile.</li>
+ *
+ * <li>for IIOP it will return _*_getStubClassSuffix for
+ * interfaces and _*_getStubClassSuffix for non-interfaces (and
+ * determine the interface and create _*_Stub from that).</li>
+ * </ul>
+ * @return a <code>FileNameMapper</code>
+ */
+ public FileNameMapper getMapper() {
+ return mapper;
+ }
+
+ /**
+ * Gets the CLASSPATH this rmic process will use.
+ * @return the classpath
+ */
+ public Path getClasspath() {
+ return getCompileClasspath();
+ }
+
+ /**
+ * Builds the compilation classpath.
+ * @return the classpath
+ */
+ protected Path getCompileClasspath() {
+ Path classpath = new Path(attributes.getProject());
+ // add dest dir to classpath so that previously compiled and
+ // untouched classes are on classpath
+ classpath.setLocation(attributes.getBase());
+
+ // Combine the build classpath with the system classpath, in an
+ // order determined by the value of build.sysclasspath
+
+ Path cp = attributes.getClasspath();
+ if (cp == null) {
+ cp = new Path(attributes.getProject());
+ }
+ if (attributes.getIncludeantruntime()) {
+ classpath.addExisting(cp.concatSystemClasspath("last"));
+ } else {
+ classpath.addExisting(cp.concatSystemClasspath("ignore"));
+ }
+
+ if (attributes.getIncludejavaruntime()) {
+ classpath.addJavaRuntime();
+ }
+ return classpath;
+ }
+
+ /**
+ * Setup rmic argument for rmic.
+ * @return the command line
+ */
+ protected Commandline setupRmicCommand() {
+ return setupRmicCommand(null);
+ }
+
+ /**
+ * Setup rmic argument for rmic.
+ * @param options additional parameters needed by a specific
+ * implementation.
+ * @return the command line
+ */
+ protected Commandline setupRmicCommand(String[] options) {
+ Commandline cmd = new Commandline();
+
+ if (options != null) {
+ for (int i = 0; i < options.length; i++) {
+ cmd.createArgument().setValue(options[i]);
+ }
+ }
+
+ Path classpath = getCompileClasspath();
+
+ cmd.createArgument().setValue("-d");
+ cmd.createArgument().setFile(attributes.getOutputDir());
+
+ if (attributes.getExtdirs() != null) {
+ cmd.createArgument().setValue("-extdirs");
+ cmd.createArgument().setPath(attributes.getExtdirs());
+ }
+
+ cmd.createArgument().setValue("-classpath");
+ cmd.createArgument().setPath(classpath);
+ String stubOption = addStubVersionOptions();
+ if (stubOption != null) {
+ //set the non-null stubOption
+ cmd.createArgument().setValue(stubOption);
+ }
+
+
+ if (null != attributes.getSourceBase()) {
+ cmd.createArgument().setValue("-keepgenerated");
+ }
+
+ if (attributes.getIiop()) {
+ attributes.log("IIOP has been turned on.", Project.MSG_INFO);
+ cmd.createArgument().setValue("-iiop");
+ if (attributes.getIiopopts() != null) {
+ attributes.log("IIOP Options: " + attributes.getIiopopts(),
+ Project.MSG_INFO);
+ cmd.createArgument().setValue(attributes.getIiopopts());
+ }
+ }
+
+ if (attributes.getIdl()) {
+ cmd.createArgument().setValue("-idl");
+ attributes.log("IDL has been turned on.", Project.MSG_INFO);
+ if (attributes.getIdlopts() != null) {
+ cmd.createArgument().setValue(attributes.getIdlopts());
+ attributes.log("IDL Options: " + attributes.getIdlopts(),
+ Project.MSG_INFO);
+ }
+ }
+
+ if (attributes.getDebug()) {
+ cmd.createArgument().setValue("-g");
+ }
+
+ String[] compilerArgs = attributes.getCurrentCompilerArgs();
+ compilerArgs = preprocessCompilerArgs(compilerArgs);
+ cmd.addArguments(compilerArgs);
+
+ logAndAddFilesToCompile(cmd);
+ return cmd;
+ }
+
+ /**
+ * This is an override point; get the stub version off the rmic command and
+ * translate that into a compiler-specific argument
+ * @return a string to use for the stub version; can be null
+ * @since Ant1.7.1
+ */
+ protected String addStubVersionOptions() {
+ //handle the many different stub options.
+ String stubVersion = attributes.getStubVersion();
+ //default is compatibility
+ String stubOption = null;
+ if (null != stubVersion) {
+ if (STUB_OPTION_1_1.equals(stubVersion)) {
+ stubOption = STUB_1_1;
+ } else if (STUB_OPTION_1_2.equals(stubVersion)) {
+ stubOption = STUB_1_2;
+ } else if (STUB_OPTION_COMPAT.equals(stubVersion)) {
+ stubOption = STUB_COMPAT;
+ } else {
+ //anything else
+ attributes.log("Unknown stub option " + stubVersion);
+ //do nothing with the value? or go -v+stubVersion??
+ }
+ }
+ //for java1.5+, we generate compatible stubs, that is, unless
+ //the caller asked for IDL or IIOP support.
+ if (stubOption == null
+ && !attributes.getIiop()
+ && !attributes.getIdl()) {
+ stubOption = STUB_COMPAT;
+ }
+ return stubOption;
+ }
+
+ /**
+ * Preprocess the compiler arguments in any way you see fit.
+ * This is to allow compiler adapters to validate or filter the arguments.
+ * The base implementation returns the original compiler arguments unchanged.
+ * @param compilerArgs the original compiler arguments
+ * @return the filtered set.
+ */
+ protected String[] preprocessCompilerArgs(String[] compilerArgs) {
+ return compilerArgs;
+ }
+
+
+ /**
+ * Strip out all -J args from the command list. Invoke this from
+ * {@link #preprocessCompilerArgs(String[])} if you have a non-forking
+ * compiler.
+ * @param compilerArgs the original compiler arguments
+ * @return the filtered set.
+ */
+ protected String[] filterJvmCompilerArgs(String[] compilerArgs) {
+ int len = compilerArgs.length;
+ List args = new ArrayList(len);
+ for (int i = 0; i < len; i++) {
+ String arg = compilerArgs[i];
+ if (!arg.startsWith("-J")) {
+ args.add(arg);
+ } else {
+ attributes.log("Dropping " + arg + " from compiler arguments");
+ }
+ }
+ int count = args.size();
+ return (String[]) args.toArray(new String[count]);
+ }
+
+
+ /**
+ * Logs the compilation parameters, adds the files to compile and logs the
+ * &quot;niceSourceList&quot;
+ * @param cmd the commandline args
+ */
+ protected void logAndAddFilesToCompile(Commandline cmd) {
+ Vector compileList = attributes.getCompileList();
+
+ attributes.log("Compilation " + cmd.describeArguments(),
+ Project.MSG_VERBOSE);
+
+ StringBuffer niceSourceList = new StringBuffer("File");
+ int cListSize = compileList.size();
+ if (cListSize != 1) {
+ niceSourceList.append("s");
+ }
+ niceSourceList.append(" to be compiled:");
+
+ for (int i = 0; i < cListSize; i++) {
+ String arg = (String) compileList.elementAt(i);
+ cmd.createArgument().setValue(arg);
+ niceSourceList.append(" ");
+ niceSourceList.append(arg);
+ }
+
+ attributes.log(niceSourceList.toString(), Project.MSG_VERBOSE);
+ }
+
+ /**
+ * Mapper that may return up to two file names.
+ *
+ * <ul>
+ * <li>for JRMP it will return *_getStubClassSuffix (and
+ * *_getSkelClassSuffix if JDK 1.1 is used)</li>
+ *
+ * <li>for IDL it will return a random name, causing <rmic> to
+ * always recompile.</li>
+ *
+ * <li>for IIOP it will return _*_getStubClassSuffix for
+ * interfaces and _*_getStubClassSuffix for non-interfaces (and
+ * determine the interface and create _*_Stub from that).</li>
+ * </ul>
+ */
+ private class RmicFileNameMapper implements FileNameMapper {
+
+ RmicFileNameMapper() {
+ }
+
+ /**
+ * Empty implementation.
+ */
+ public void setFrom(String s) {
+ }
+ /**
+ * Empty implementation.
+ */
+ public void setTo(String s) {
+ }
+
+ public String[] mapFileName(String name) {
+ if (name == null
+ || !name.endsWith(".class")
+ || name.endsWith(getStubClassSuffix() + ".class")
+ || name.endsWith(getSkelClassSuffix() + ".class")
+ || name.endsWith(getTieClassSuffix() + ".class")) {
+ // Not a .class file or the one we'd generate
+ return null;
+ }
+
+ // we know that name.endsWith(".class")
+ String base = StringUtils.removeSuffix(name, ".class");
+
+ String classname = base.replace(File.separatorChar, '.');
+ if (attributes.getVerify()
+ && !attributes.isValidRmiRemote(classname)) {
+ return null;
+ }
+
+ /*
+ * fallback in case we have trouble loading the class or
+ * don't know how to handle it (there is no easy way to
+ * know what IDL mode would generate.
+ *
+ * This is supposed to make Ant always recompile the
+ * class, as a file of that name should not exist.
+ */
+ String[] target = new String[] {name + ".tmp." + RAND.nextLong()};
+
+ if (!attributes.getIiop() && !attributes.getIdl()) {
+ // JRMP with simple naming convention
+ if (STUB_OPTION_1_2.equals(attributes.getStubVersion())) {
+ target = new String[] {
+ base + getStubClassSuffix() + ".class"
+ };
+ } else {
+ target = new String[] {
+ base + getStubClassSuffix() + ".class",
+ base + getSkelClassSuffix() + ".class",
+ };
+ }
+ } else if (!attributes.getIdl()) {
+ int lastSlash = base.lastIndexOf(File.separatorChar);
+
+ String dirname = "";
+ /*
+ * I know, this is not necessary, but I prefer it explicit (SB)
+ */
+ int index = -1;
+ if (lastSlash == -1) {
+ // no package
+ index = 0;
+ } else {
+ index = lastSlash + 1;
+ dirname = base.substring(0, index);
+ }
+
+ String filename = base.substring(index);
+
+ try {
+ Class c = attributes.getLoader().loadClass(classname);
+
+ if (c.isInterface()) {
+ // only stub, no tie
+ target = new String[] {
+ dirname + "_" + filename + getStubClassSuffix()
+ + ".class"
+ };
+ } else {
+ /*
+ * stub is derived from implementation,
+ * tie from interface name.
+ */
+ Class interf = attributes.getRemoteInterface(c);
+ String iName = interf.getName();
+ String iDir = "";
+ int iIndex = -1;
+ int lastDot = iName.lastIndexOf(".");
+ if (lastDot == -1) {
+ // no package
+ iIndex = 0;
+ } else {
+ iIndex = lastDot + 1;
+ iDir = iName.substring(0, iIndex);
+ iDir = iDir.replace('.', File.separatorChar);
+ }
+
+ target = new String[] {
+ dirname + "_" + filename + getTieClassSuffix()
+ + ".class",
+ iDir + "_" + iName.substring(iIndex)
+ + getStubClassSuffix() + ".class"
+ };
+ }
+ } catch (ClassNotFoundException e) {
+ attributes.log("Unable to verify class " + classname
+ + ". It could not be found.",
+ Project.MSG_WARN);
+ } catch (NoClassDefFoundError e) {
+ attributes.log("Unable to verify class " + classname
+ + ". It is not defined.", Project.MSG_WARN);
+ } catch (Throwable t) {
+ attributes.log("Unable to verify class " + classname
+ + ". Loading caused Exception: "
+ + t.getMessage(), Project.MSG_WARN);
+ }
+ }
+ return target;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/ForkingSunRmic.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/ForkingSunRmic.java
new file mode 100644
index 00000000..81bd7971
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/ForkingSunRmic.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.tools.ant.taskdefs.rmic;
+
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.taskdefs.Rmic;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * This is an extension of the sun rmic compiler, which forks rather than
+ * executes it inline. Why so? Because rmic is dog slow, but if you fork the
+ * compiler you can have multiple copies compiling different bits of your project
+ * at the same time. Which, on a multi-cpu system results in significant speedups.
+ *
+ * Also, Java1.6 behaves oddly with -XNew, so we switch it on here if needed.
+ * @since ant1.7
+ */
+public class ForkingSunRmic extends DefaultRmicAdapter {
+
+ /**
+ * the name of this adapter for users to select
+ */
+ public static final String COMPILER_NAME = "forking";
+
+ /**
+ * exec by creating a new command
+ * @return true if the command ran successfully
+ * @throws BuildException on error
+ */
+ public boolean execute() throws BuildException {
+ Rmic owner = getRmic();
+ Commandline cmd = setupRmicCommand();
+ Project project = owner.getProject();
+ String executable = owner.getExecutable();
+ if (executable == null) {
+ // no explicitly specified executable
+ // rely on RMIC being on the path
+ executable = JavaEnvUtils.getJdkExecutable(getExecutableName());
+ }
+ cmd.setExecutable(executable);
+
+ //set up the args
+ String[] args = cmd.getCommandline();
+
+ try {
+ Execute exe = new Execute(new LogStreamHandler(owner,
+ Project.MSG_INFO,
+ Project.MSG_WARN));
+ exe.setAntRun(project);
+ exe.setWorkingDirectory(project.getBaseDir());
+ exe.setCommandline(args);
+ exe.execute();
+ return !exe.isFailure();
+ } catch (IOException exception) {
+ throw new BuildException("Error running " + getExecutableName()
+ + " -maybe it is not on the path", exception);
+ }
+ }
+
+ /**
+ * Override point.
+ * @return the executable name.
+ */
+ protected String getExecutableName() {
+ return SunRmic.RMIC_EXECUTABLE;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/KaffeRmic.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/KaffeRmic.java
new file mode 100644
index 00000000..2108a68b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/KaffeRmic.java
@@ -0,0 +1,104 @@
+/*
+ * 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.tools.ant.taskdefs.rmic;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ExecuteJava;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * The implementation of the rmic for Kaffe
+ *
+ * @since Ant 1.4
+ */
+public class KaffeRmic extends DefaultRmicAdapter {
+ // sorted by newest Kaffe version first
+ private static final String[] RMIC_CLASSNAMES = new String[] {
+ "gnu.classpath.tools.rmi.rmic.RMIC",
+ // pre Kaffe 1.1.5
+ "gnu.java.rmi.rmic.RMIC",
+ // pre Kaffe 1.1.2
+ "kaffe.rmi.rmic.RMIC",
+ };
+
+ /**
+ * the name of this adapter for users to select
+ */
+ public static final String COMPILER_NAME = "kaffe";
+
+ /** {@inheritDoc} */
+ public boolean execute() throws BuildException {
+ getRmic().log("Using Kaffe rmic", Project.MSG_VERBOSE);
+ Commandline cmd = setupRmicCommand();
+
+ Class c = getRmicClass();
+ if (c == null) {
+ StringBuffer buf = new StringBuffer("Cannot use Kaffe rmic, as it"
+ + " is not available. None"
+ + " of ");
+ for (int i = 0; i < RMIC_CLASSNAMES.length; i++) {
+ if (i != 0) {
+ buf.append(", ");
+ }
+
+ buf.append(RMIC_CLASSNAMES[i]);
+ }
+ buf.append(" have been found. A common solution is to set the"
+ + " environment variable JAVA_HOME or CLASSPATH.");
+ throw new BuildException(buf.toString(),
+ getRmic().getLocation());
+ }
+
+ cmd.setExecutable(c.getName());
+ if (!c.getName().equals(RMIC_CLASSNAMES[RMIC_CLASSNAMES.length - 1])) {
+ // only supported since Kaffe 1.1.2
+ cmd.createArgument().setValue("-verbose");
+ getRmic().log(Commandline.describeCommand(cmd));
+ }
+ ExecuteJava ej = new ExecuteJava();
+ ej.setJavaCommand(cmd);
+ return ej.fork(getRmic()) == 0;
+ }
+
+ /**
+ * test for kaffe being on the system
+ * @return true if kaffe is on the current classpath
+ */
+ public static boolean isAvailable() {
+ return getRmicClass() != null;
+ }
+
+ /**
+ * tries to load Kaffe RMIC and falls back to the older class name
+ * if necessary.
+ *
+ * @return null if neither class can get loaded.
+ */
+ private static Class getRmicClass() {
+ for (int i = 0; i < RMIC_CLASSNAMES.length; i++) {
+ try {
+ return Class.forName(RMIC_CLASSNAMES[i]);
+ } catch (ClassNotFoundException cnfe) {
+ // Ignore
+ }
+ }
+ return null;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapter.java
new file mode 100644
index 00000000..2cabe2fa
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapter.java
@@ -0,0 +1,67 @@
+/*
+ * 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.tools.ant.taskdefs.rmic;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Rmic;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileNameMapper;
+
+/**
+ * The interface that all rmic adapters must adhere to.
+ *
+ * <p>A rmic adapter is an adapter that interprets the rmic's
+ * parameters in preparation to be passed off to the compiler this
+ * adapter represents. As all the necessary values are stored in the
+ * Rmic task itself, the only thing all adapters need is the rmic
+ * task, the execute command and a parameterless constructor (for
+ * reflection).</p>
+ *
+ * @since Ant 1.4
+ */
+
+public interface RmicAdapter {
+
+ /**
+ * Sets the rmic attributes, which are stored in the Rmic task.
+ * @param attributes the rmic attributes to use
+ */
+ void setRmic(Rmic attributes);
+
+ /**
+ * Call the rmic compiler.
+ *
+ * @return true if has the compilation been successful
+ * @throws BuildException on error
+ */
+ boolean execute() throws BuildException;
+
+ /**
+ * Maps source class files to the files generated by this rmic
+ * implementation.
+ * @return the filename mapper used by this implementation
+ */
+ FileNameMapper getMapper();
+
+ /**
+ * The CLASSPATH this rmic process will use.
+ * @return the classpath this rmic process will use
+ */
+ Path getClasspath();
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapterFactory.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapterFactory.java
new file mode 100644
index 00000000..4a2708c7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapterFactory.java
@@ -0,0 +1,135 @@
+/*
+ * 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.tools.ant.taskdefs.rmic;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.ClasspathUtils;
+
+/**
+ * Creates the necessary rmic adapter, given basic criteria.
+ *
+ * @since 1.4
+ */
+public final class RmicAdapterFactory {
+ /** The error message to be used when the compiler cannot be found. */
+ public static final String ERROR_UNKNOWN_COMPILER = "Class not found: ";
+
+ /** The error message to be used when the class is not an rmic adapter. */
+ public static final String ERROR_NOT_RMIC_ADAPTER = "Class of unexpected Type: ";
+
+ /** If the compiler has this name use a default compiler. */
+ public static final String DEFAULT_COMPILER = "default";
+
+ /** This is a singleton -- can't create instances!! */
+ private RmicAdapterFactory() {
+ }
+
+ /**
+ * Based on the parameter passed in, this method creates the necessary
+ * factory desired.
+ *
+ * <p>The current mapping for rmic names are as follows:</p>
+ * <ul><li>sun = SUN's rmic
+ * <li>kaffe = Kaffe's rmic
+ * <li><i>a fully qualified classname</i> = the name of a rmic
+ * adapter
+ * <li>weblogic = weblogic compiler
+ * <li>forking = Sun's RMIC by forking a new JVM
+ * </ul>
+ *
+ * @param rmicType either the name of the desired rmic, or the
+ * full classname of the rmic's adapter.
+ * @param task a task to log through.
+ * @return the compiler adapter
+ * @throws BuildException if the rmic type could not be resolved into
+ * a rmic adapter.
+ */
+ public static RmicAdapter getRmic(String rmicType, Task task)
+ throws BuildException {
+ return getRmic(rmicType, task, null);
+ }
+
+ /**
+ * Based on the parameter passed in, this method creates the necessary
+ * factory desired.
+ *
+ * <p>The current mapping for rmic names are as follows:</p>
+ * <ul><li>sun = SUN's rmic
+ * <li>kaffe = Kaffe's rmic
+ * <li><i>a fully qualified classname</i> = the name of a rmic
+ * adapter
+ * <li>weblogic = weblogic compiler
+ * <li>forking = Sun's RMIC by forking a new JVM
+ * </ul>
+ *
+ * @param rmicType either the name of the desired rmic, or the
+ * full classname of the rmic's adapter.
+ * @param task a task to log through.
+ * @param classpath the classpath to use when looking up an
+ * adapter class
+ * @return the compiler adapter
+ * @throws BuildException if the rmic type could not be resolved into
+ * a rmic adapter.
+ * @since Ant 1.8.0
+ */
+ public static RmicAdapter getRmic(String rmicType, Task task,
+ Path classpath)
+ throws BuildException {
+ //handle default specially by choosing the sun or kaffe compiler
+ if (DEFAULT_COMPILER.equalsIgnoreCase(rmicType) || rmicType.length() == 0) {
+ rmicType = KaffeRmic.isAvailable()
+ ? KaffeRmic.COMPILER_NAME
+ : SunRmic.COMPILER_NAME;
+ }
+ if (SunRmic.COMPILER_NAME.equalsIgnoreCase(rmicType)) {
+ return new SunRmic();
+ } else if (KaffeRmic.COMPILER_NAME.equalsIgnoreCase(rmicType)) {
+ return new KaffeRmic();
+ } else if (WLRmic.COMPILER_NAME.equalsIgnoreCase(rmicType)) {
+ return new WLRmic();
+ } else if (ForkingSunRmic.COMPILER_NAME.equalsIgnoreCase(rmicType)) {
+ return new ForkingSunRmic();
+ } else if (XNewRmic.COMPILER_NAME.equalsIgnoreCase(rmicType)) {
+ return new XNewRmic();
+ }
+ //no match?
+ return resolveClassName(rmicType,
+ // Memory leak in line below
+ task.getProject().createClassLoader(classpath));
+ }
+
+ /**
+ * Tries to resolve the given classname into a rmic adapter.
+ * Throws a fit if it can't.
+ *
+ * @param className The fully qualified classname to be created.
+ * @param loader the classloader to use
+ * @throws BuildException This is the fit that is thrown if className
+ * isn't an instance of RmicAdapter.
+ */
+ private static RmicAdapter resolveClassName(String className,
+ ClassLoader loader)
+ throws BuildException {
+ return (RmicAdapter) ClasspathUtils.newInstance(className,
+ loader != null ? loader :
+ RmicAdapterFactory.class.getClassLoader(), RmicAdapter.class);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/SunRmic.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/SunRmic.java
new file mode 100644
index 00000000..07cbd306
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/SunRmic.java
@@ -0,0 +1,114 @@
+/*
+ * 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.tools.ant.taskdefs.rmic;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * The implementation of the rmic for SUN's JDK.
+ *
+ * @since Ant 1.4
+ */
+public class SunRmic extends DefaultRmicAdapter {
+
+ /**
+ * name of the class
+ */
+ public static final String RMIC_CLASSNAME = "sun.rmi.rmic.Main";
+
+ /**
+ * the name of this adapter for users to select
+ */
+ public static final String COMPILER_NAME = "sun";
+
+ /**
+ * name of the executable
+ */
+ public static final String RMIC_EXECUTABLE = "rmic";
+ /** Error message to use with the sun rmic is not the classpath. */
+ public static final String ERROR_NO_RMIC_ON_CLASSPATH = "Cannot use SUN rmic, as it is not "
+ + "available. A common solution is to "
+ + "set the environment variable "
+ + "JAVA_HOME";
+ /** Error message to use when there is an error starting the sun rmic compiler */
+ public static final String ERROR_RMIC_FAILED = "Error starting SUN rmic: ";
+
+ /**
+ * Run the rmic compiler.
+ * @return true if the compilation succeeded
+ * @throws BuildException on error
+ */
+ public boolean execute() throws BuildException {
+ getRmic().log("Using SUN rmic compiler", Project.MSG_VERBOSE);
+ Commandline cmd = setupRmicCommand();
+
+ // Create an instance of the rmic, redirecting output to
+ // the project log
+ LogOutputStream logstr = new LogOutputStream(getRmic(),
+ Project.MSG_WARN);
+
+ try {
+ Class c = Class.forName(RMIC_CLASSNAME);
+ Constructor cons
+ = c.getConstructor(new Class[] {OutputStream.class, String.class});
+ Object rmic = cons.newInstance(new Object[] {logstr, "rmic"});
+
+ Method doRmic = c.getMethod("compile",
+ new Class [] {String[].class});
+ Boolean ok =
+ (Boolean) doRmic.invoke(rmic,
+ (new Object[] {cmd.getArguments()}));
+ return ok.booleanValue();
+ } catch (ClassNotFoundException ex) {
+ throw new BuildException(ERROR_NO_RMIC_ON_CLASSPATH,
+ getRmic().getLocation());
+ } catch (Exception ex) {
+ if (ex instanceof BuildException) {
+ throw (BuildException) ex;
+ } else {
+ throw new BuildException(ERROR_RMIC_FAILED,
+ ex, getRmic().getLocation());
+ }
+ } finally {
+ try {
+ logstr.close();
+ } catch (IOException e) {
+ throw new BuildException(e);
+ }
+ }
+ }
+
+
+ /**
+ * Strip out all -J args from the command list.
+ * @param compilerArgs the original compiler arguments
+ * @return the filtered set.
+ */
+ protected String[] preprocessCompilerArgs(String[] compilerArgs) {
+ return filterJvmCompilerArgs(compilerArgs);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/WLRmic.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/WLRmic.java
new file mode 100644
index 00000000..3b1f2a19
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/WLRmic.java
@@ -0,0 +1,136 @@
+/*
+ * 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.tools.ant.taskdefs.rmic;
+
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * The implementation of the rmic for WebLogic
+ *
+ * @since Ant 1.4
+ */
+public class WLRmic extends DefaultRmicAdapter {
+ /** The classname of the weblogic rmic */
+ public static final String WLRMIC_CLASSNAME = "weblogic.rmic";
+ /**
+ * the name of this adapter for users to select
+ */
+ public static final String COMPILER_NAME = "weblogic";
+
+ /** The error string to use if not able to find the weblogic rmic */
+ public static final String ERROR_NO_WLRMIC_ON_CLASSPATH =
+ "Cannot use WebLogic rmic, as it is not "
+ + "available. Add it to Ant's classpath with the -lib option";
+
+ /** The error string to use if not able to start the weblogic rmic */
+ public static final String ERROR_WLRMIC_FAILED = "Error starting WebLogic rmic: ";
+ /** The stub suffix */
+ public static final String WL_RMI_STUB_SUFFIX = "_WLStub";
+ /** The skeleton suffix */
+ public static final String WL_RMI_SKEL_SUFFIX = "_WLSkel";
+ /** unsupported error message */
+ public static final String UNSUPPORTED_STUB_OPTION = "Unsupported stub option: ";
+
+ /**
+ * Carry out the rmic compilation.
+ * @return true if the compilation succeeded
+ * @throws BuildException on error
+ */
+ public boolean execute() throws BuildException {
+ getRmic().log("Using WebLogic rmic", Project.MSG_VERBOSE);
+ Commandline cmd = setupRmicCommand(new String[] {"-noexit"});
+
+ AntClassLoader loader = null;
+ try {
+ // Create an instance of the rmic
+ Class c = null;
+ if (getRmic().getClasspath() == null) {
+ c = Class.forName(WLRMIC_CLASSNAME);
+ } else {
+ loader
+ = getRmic().getProject().createClassLoader(getRmic().getClasspath());
+ c = Class.forName(WLRMIC_CLASSNAME, true, loader);
+ }
+ Method doRmic = c.getMethod("main",
+ new Class [] {String[].class});
+ doRmic.invoke(null, new Object[] {cmd.getArguments()});
+ return true;
+ } catch (ClassNotFoundException ex) {
+ throw new BuildException(ERROR_NO_WLRMIC_ON_CLASSPATH, getRmic().getLocation());
+ } catch (Exception ex) {
+ if (ex instanceof BuildException) {
+ throw (BuildException) ex;
+ } else {
+ throw new BuildException(ERROR_WLRMIC_FAILED, ex,
+ getRmic().getLocation());
+ }
+ } finally {
+ if (loader != null) {
+ loader.cleanup();
+ }
+ }
+ }
+
+ /**
+ * Get the suffix for the rmic stub classes
+ * @return the stub suffix
+ */
+ public String getStubClassSuffix() {
+ return WL_RMI_STUB_SUFFIX;
+ }
+
+ /**
+ * Get the suffix for the rmic skeleton classes
+ * @return the skeleton suffix
+ */
+ public String getSkelClassSuffix() {
+ return WL_RMI_SKEL_SUFFIX;
+ }
+
+ /**
+ * Strip out all -J args from the command list.
+ *
+ * @param compilerArgs the original compiler arguments
+ * @return the filtered set.
+ */
+ protected String[] preprocessCompilerArgs(String[] compilerArgs) {
+ return filterJvmCompilerArgs(compilerArgs);
+ }
+
+ /**
+ * This is an override point; no stub version is returned. If any
+ * stub option is set, a warning is printed.
+ * @return null, for no stub version
+ */
+ protected String addStubVersionOptions() {
+ //handle the many different stub options.
+ String stubVersion = getRmic().getStubVersion();
+ if (null != stubVersion) {
+ getRmic().log(UNSUPPORTED_STUB_OPTION + stubVersion,
+ Project.MSG_WARN);
+ }
+ return null;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/XNewRmic.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/XNewRmic.java
new file mode 100644
index 00000000..559c7698
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/rmic/XNewRmic.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.tools.ant.taskdefs.rmic;
+
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Run rmic in a new process with -Xnew set.
+ * This switches rmic to use a new compiler, one that doesn't work in-process
+ * on ant on java1.6.
+ * see: <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=38732">
+ * http://issues.apache.org/bugzilla/show_bug.cgi?id=38732</a>
+ */
+public class XNewRmic extends ForkingSunRmic {
+
+ /**
+ * the name of this adapter for users to select
+ */
+ public static final String COMPILER_NAME = "xnew";
+
+ /** No-arg constructor. */
+ public XNewRmic() {
+ }
+
+ /**
+ * Create a normal command line, then with -Xnew at the front
+ * @return a command line that hands off to thw
+ */
+ protected Commandline setupRmicCommand() {
+ String[] options = new String[] {
+ "-Xnew"
+ };
+ Commandline commandline = super.setupRmicCommand(options);
+ return commandline;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/AbstractFileSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/AbstractFileSet.java
new file mode 100644
index 00000000..8f274a09
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/AbstractFileSet.java
@@ -0,0 +1,922 @@
+/*
+ * 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.tools.ant.types;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.FileScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.selectors.AndSelector;
+import org.apache.tools.ant.types.selectors.ContainsRegexpSelector;
+import org.apache.tools.ant.types.selectors.ContainsSelector;
+import org.apache.tools.ant.types.selectors.DateSelector;
+import org.apache.tools.ant.types.selectors.DependSelector;
+import org.apache.tools.ant.types.selectors.DepthSelector;
+import org.apache.tools.ant.types.selectors.DifferentSelector;
+import org.apache.tools.ant.types.selectors.ExtendSelector;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.FilenameSelector;
+import org.apache.tools.ant.types.selectors.MajoritySelector;
+import org.apache.tools.ant.types.selectors.NoneSelector;
+import org.apache.tools.ant.types.selectors.NotSelector;
+import org.apache.tools.ant.types.selectors.OrSelector;
+import org.apache.tools.ant.types.selectors.PresentSelector;
+import org.apache.tools.ant.types.selectors.ReadableSelector;
+import org.apache.tools.ant.types.selectors.SelectSelector;
+import org.apache.tools.ant.types.selectors.SelectorContainer;
+import org.apache.tools.ant.types.selectors.SelectorScanner;
+import org.apache.tools.ant.types.selectors.SizeSelector;
+import org.apache.tools.ant.types.selectors.TypeSelector;
+import org.apache.tools.ant.types.selectors.WritableSelector;
+import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector;
+
+/**
+ * Class that holds an implicit patternset and supports nested
+ * patternsets and creates a DirectoryScanner using these patterns.
+ *
+ * <p>Common base class for DirSet and FileSet.</p>
+ *
+ */
+public abstract class AbstractFileSet extends DataType
+ implements Cloneable, SelectorContainer {
+
+ private PatternSet defaultPatterns = new PatternSet();
+ private List<PatternSet> additionalPatterns = new ArrayList<PatternSet>();
+ private List<FileSelector> selectors = new ArrayList<FileSelector>();
+
+ private File dir;
+ private boolean useDefaultExcludes = true;
+ private boolean caseSensitive = true;
+ private boolean followSymlinks = true;
+ private boolean errorOnMissingDir = true;
+ private int maxLevelsOfSymlinks = DirectoryScanner.MAX_LEVELS_OF_SYMLINKS;
+
+ /* cached DirectoryScanner instance for our own Project only */
+ private DirectoryScanner directoryScanner = null;
+
+ /**
+ * Construct a new <code>AbstractFileSet</code>.
+ */
+ public AbstractFileSet() {
+ super();
+ }
+
+ /**
+ * Construct a new <code>AbstractFileSet</code>, shallowly cloned
+ * from the specified <code>AbstractFileSet</code>.
+ * @param fileset the <code>AbstractFileSet</code> to use as a template.
+ */
+ protected AbstractFileSet(AbstractFileSet fileset) {
+ this.dir = fileset.dir;
+ this.defaultPatterns = fileset.defaultPatterns;
+ this.additionalPatterns = fileset.additionalPatterns;
+ this.selectors = fileset.selectors;
+ this.useDefaultExcludes = fileset.useDefaultExcludes;
+ this.caseSensitive = fileset.caseSensitive;
+ this.followSymlinks = fileset.followSymlinks;
+ this.errorOnMissingDir = fileset.errorOnMissingDir;
+ this.maxLevelsOfSymlinks = fileset.maxLevelsOfSymlinks;
+ setProject(fileset.getProject());
+ }
+
+ /**
+ * Makes this instance in effect a reference to another instance.
+ *
+ * <p>You must not set another attribute or nest elements inside
+ * this element if you make it a reference.</p>
+ * @param r the <code>Reference</code> to use.
+ * @throws BuildException on error
+ */
+ public void setRefid(Reference r) throws BuildException {
+ if (dir != null || defaultPatterns.hasPatterns(getProject())) {
+ throw tooManyAttributes();
+ }
+ if (!additionalPatterns.isEmpty()) {
+ throw noChildrenAllowed();
+ }
+ if (!selectors.isEmpty()) {
+ throw noChildrenAllowed();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Sets the base-directory for this instance.
+ * @param dir the directory's <code>File</code> instance.
+ * @throws BuildException on error
+ */
+ public synchronized void setDir(File dir) throws BuildException {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.dir = dir;
+ directoryScanner = null;
+ }
+
+ /**
+ * Retrieves the base-directory for this instance.
+ * @return <code>File</code>.
+ */
+ public File getDir() {
+ return getDir(getProject());
+ }
+
+ /**
+ * Retrieves the base-directory for this instance.
+ * @param p the <code>Project</code> against which the
+ * reference is resolved, if set.
+ * @return <code>File</code>.
+ */
+ public synchronized File getDir(Project p) {
+ if (isReference()) {
+ return getRef(p).getDir(p);
+ }
+ dieOnCircularReference();
+ return dir;
+ }
+
+ /**
+ * Creates a nested patternset.
+ * @return <code>PatternSet</code>.
+ */
+ public synchronized PatternSet createPatternSet() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ PatternSet patterns = new PatternSet();
+ additionalPatterns.add(patterns);
+ directoryScanner = null;
+ return patterns;
+ }
+
+ /**
+ * Add a name entry to the include list.
+ * @return <code>PatternSet.NameEntry</code>.
+ */
+ public synchronized PatternSet.NameEntry createInclude() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ directoryScanner = null;
+ return defaultPatterns.createInclude();
+ }
+
+ /**
+ * Add a name entry to the include files list.
+ * @return <code>PatternSet.NameEntry</code>.
+ */
+ public synchronized PatternSet.NameEntry createIncludesFile() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ directoryScanner = null;
+ return defaultPatterns.createIncludesFile();
+ }
+
+ /**
+ * Add a name entry to the exclude list.
+ * @return <code>PatternSet.NameEntry</code>.
+ */
+ public synchronized PatternSet.NameEntry createExclude() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ directoryScanner = null;
+ return defaultPatterns.createExclude();
+ }
+
+ /**
+ * Add a name entry to the excludes files list.
+ * @return <code>PatternSet.NameEntry</code>.
+ */
+ public synchronized PatternSet.NameEntry createExcludesFile() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ directoryScanner = null;
+ return defaultPatterns.createExcludesFile();
+ }
+
+ /**
+ * Creates a single file fileset.
+ * @param file the single <code>File</code> included in this
+ * <code>AbstractFileSet</code>.
+ */
+ public synchronized void setFile(File file) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ setDir(file.getParentFile());
+ createInclude().setName(file.getName());
+ }
+
+ /**
+ * Appends <code>includes</code> to the current list of include
+ * patterns.
+ *
+ * <p>Patterns may be separated by a comma or a space.</p>
+ *
+ * @param includes the <code>String</code> containing the include patterns.
+ */
+ public synchronized void setIncludes(String includes) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ defaultPatterns.setIncludes(includes);
+ directoryScanner = null;
+ }
+
+ /**
+ * Appends <code>includes</code> to the current list of include
+ * patterns.
+ *
+ * @param includes array containing the include patterns.
+ * @since Ant 1.7
+ */
+ public synchronized void appendIncludes(String[] includes) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (includes != null) {
+ for (int i = 0; i < includes.length; i++) {
+ defaultPatterns.createInclude().setName(includes[i]);
+ }
+ directoryScanner = null;
+ }
+ }
+
+ /**
+ * Appends <code>excludes</code> to the current list of exclude
+ * patterns.
+ *
+ * <p>Patterns may be separated by a comma or a space.</p>
+ *
+ * @param excludes the <code>String</code> containing the exclude patterns.
+ */
+ public synchronized void setExcludes(String excludes) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ defaultPatterns.setExcludes(excludes);
+ directoryScanner = null;
+ }
+
+ /**
+ * Appends <code>excludes</code> to the current list of include
+ * patterns.
+ *
+ * @param excludes array containing the exclude patterns.
+ * @since Ant 1.7
+ */
+ public synchronized void appendExcludes(String[] excludes) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (excludes != null) {
+ for (int i = 0; i < excludes.length; i++) {
+ defaultPatterns.createExclude().setName(excludes[i]);
+ }
+ directoryScanner = null;
+ }
+ }
+
+ /**
+ * Sets the <code>File</code> containing the includes patterns.
+ *
+ * @param incl <code>File</code> instance.
+ * @throws BuildException on error
+ */
+ public synchronized void setIncludesfile(File incl) throws BuildException {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ defaultPatterns.setIncludesfile(incl);
+ directoryScanner = null;
+ }
+
+ /**
+ * Sets the <code>File</code> containing the excludes patterns.
+ *
+ * @param excl <code>File</code> instance.
+ * @throws BuildException on error
+ */
+ public synchronized void setExcludesfile(File excl) throws BuildException {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ defaultPatterns.setExcludesfile(excl);
+ directoryScanner = null;
+ }
+
+ /**
+ * Sets whether default exclusions should be used or not.
+ *
+ * @param useDefaultExcludes <code>boolean</code>.
+ */
+ public synchronized void setDefaultexcludes(boolean useDefaultExcludes) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.useDefaultExcludes = useDefaultExcludes;
+ directoryScanner = null;
+ }
+
+ /**
+ * Whether default exclusions should be used or not.
+ * @return the default exclusions value.
+ * @since Ant 1.6.3
+ */
+ public synchronized boolean getDefaultexcludes() {
+ if (isReference()) {
+ return getRef(getProject()).getDefaultexcludes();
+ }
+ dieOnCircularReference();
+ return useDefaultExcludes;
+ }
+
+ /**
+ * Sets case sensitivity of the file system.
+ *
+ * @param caseSensitive <code>boolean</code>.
+ */
+ public synchronized void setCaseSensitive(boolean caseSensitive) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.caseSensitive = caseSensitive;
+ directoryScanner = null;
+ }
+
+ /**
+ * Find out if the fileset is case sensitive.
+ *
+ * @return <code>boolean</code> indicating whether the fileset is
+ * case sensitive.
+ *
+ * @since Ant 1.7
+ */
+ public synchronized boolean isCaseSensitive() {
+ if (isReference()) {
+ return getRef(getProject()).isCaseSensitive();
+ }
+ dieOnCircularReference();
+ return caseSensitive;
+ }
+
+ /**
+ * Sets whether or not symbolic links should be followed.
+ *
+ * @param followSymlinks whether or not symbolic links should be followed.
+ */
+ public synchronized void setFollowSymlinks(boolean followSymlinks) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.followSymlinks = followSymlinks;
+ directoryScanner = null;
+ }
+
+ /**
+ * Find out if the fileset wants to follow symbolic links.
+ *
+ * @return <code>boolean</code> indicating whether symbolic links
+ * should be followed.
+ *
+ * @since Ant 1.6
+ */
+ public synchronized boolean isFollowSymlinks() {
+ if (isReference()) {
+ return getRef(getProject()).isCaseSensitive();
+ }
+ dieOnCircularReference();
+ return followSymlinks;
+ }
+
+ /**
+ * The maximum number of times a symbolic link may be followed
+ * during a scan.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setMaxLevelsOfSymlinks(int max) {
+ maxLevelsOfSymlinks = max;
+ }
+
+ /**
+ * The maximum number of times a symbolic link may be followed
+ * during a scan.
+ *
+ * @since Ant 1.8.0
+ */
+ public int getMaxLevelsOfSymlinks() {
+ return maxLevelsOfSymlinks;
+ }
+
+ /**
+ * Sets whether an error is thrown if a directory does not exist.
+ *
+ * @param errorOnMissingDir true if missing directories cause errors,
+ * false if not.
+ */
+ public void setErrorOnMissingDir(boolean errorOnMissingDir) {
+ this.errorOnMissingDir = errorOnMissingDir;
+ }
+
+ /**
+ * Gets whether an error is/should be thrown if the base directory
+ * does not exist.
+ * @since Ant 1.8.2
+ */
+ public boolean getErrorOnMissingDir() {
+ return errorOnMissingDir;
+ }
+
+ /**
+ * Returns the directory scanner needed to access the files to process.
+ * @return a <code>DirectoryScanner</code> instance.
+ */
+ public DirectoryScanner getDirectoryScanner() {
+ return getDirectoryScanner(getProject());
+ }
+
+ /**
+ * Returns the directory scanner needed to access the files to process.
+ * @param p the Project against which the DirectoryScanner should be configured.
+ * @return a <code>DirectoryScanner</code> instance.
+ */
+ public DirectoryScanner getDirectoryScanner(Project p) {
+ if (isReference()) {
+ return getRef(p).getDirectoryScanner(p);
+ }
+ dieOnCircularReference();
+ DirectoryScanner ds = null;
+ synchronized (this) {
+ if (directoryScanner != null && p == getProject()) {
+ ds = directoryScanner;
+ } else {
+ if (dir == null) {
+ throw new BuildException("No directory specified for "
+ + getDataTypeName() + ".");
+ }
+ if (!dir.exists() && errorOnMissingDir) {
+ throw new BuildException(dir.getAbsolutePath()
+ + DirectoryScanner
+ .DOES_NOT_EXIST_POSTFIX);
+ }
+ if (!dir.isDirectory() && dir.exists()) {
+ throw new BuildException(dir.getAbsolutePath()
+ + " is not a directory.");
+ }
+ ds = new DirectoryScanner();
+ setupDirectoryScanner(ds, p);
+ ds.setFollowSymlinks(followSymlinks);
+ ds.setErrorOnMissingDir(errorOnMissingDir);
+ ds.setMaxLevelsOfSymlinks(maxLevelsOfSymlinks);
+ directoryScanner = (p == getProject()) ? ds : directoryScanner;
+ }
+ }
+ ds.scan();
+ return ds;
+ }
+
+ /**
+ * Set up the specified directory scanner against this
+ * AbstractFileSet's Project.
+ * @param ds a <code>FileScanner</code> instance.
+ */
+ public void setupDirectoryScanner(FileScanner ds) {
+ setupDirectoryScanner(ds, getProject());
+ }
+
+ /**
+ * Set up the specified directory scanner against the specified project.
+ * @param ds a <code>FileScanner</code> instance.
+ * @param p an Ant <code>Project</code> instance.
+ */
+ public synchronized void setupDirectoryScanner(FileScanner ds, Project p) {
+ if (isReference()) {
+ getRef(p).setupDirectoryScanner(ds, p);
+ return;
+ }
+ dieOnCircularReference(p);
+ if (ds == null) {
+ throw new IllegalArgumentException("ds cannot be null");
+ }
+ ds.setBasedir(dir);
+
+ PatternSet ps = mergePatterns(p);
+ p.log(getDataTypeName() + ": Setup scanner in dir " + dir
+ + " with " + ps, Project.MSG_DEBUG);
+
+ ds.setIncludes(ps.getIncludePatterns(p));
+ ds.setExcludes(ps.getExcludePatterns(p));
+ if (ds instanceof SelectorScanner) {
+ SelectorScanner ss = (SelectorScanner) ds;
+ ss.setSelectors(getSelectors(p));
+ }
+ if (useDefaultExcludes) {
+ ds.addDefaultExcludes();
+ }
+ ds.setCaseSensitive(caseSensitive);
+ }
+
+ /**
+ * Performs the check for circular references and returns the
+ * referenced FileSet.
+ * @param p the current project
+ * @return the referenced FileSet
+ */
+ protected AbstractFileSet getRef(Project p) {
+ return (AbstractFileSet) getCheckedRef(p);
+ }
+
+ // SelectorContainer methods
+
+ /**
+ * Indicates whether there are any selectors here.
+ *
+ * @return whether any selectors are in this container.
+ */
+ public synchronized boolean hasSelectors() {
+ if (isReference()) {
+ return getRef(getProject()).hasSelectors();
+ }
+ dieOnCircularReference();
+ return !(selectors.isEmpty());
+ }
+
+ /**
+ * Indicates whether there are any patterns here.
+ *
+ * @return whether any patterns are in this container.
+ */
+ public synchronized boolean hasPatterns() {
+ if (isReference() && getProject() != null) {
+ return getRef(getProject()).hasPatterns();
+ }
+ dieOnCircularReference();
+ if (defaultPatterns.hasPatterns(getProject())) {
+ return true;
+ }
+ for (PatternSet ps : additionalPatterns) {
+ if (ps.hasPatterns(getProject())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gives the count of the number of selectors in this container.
+ *
+ * @return the number of selectors in this container as an <code>int</code>.
+ */
+ public synchronized int selectorCount() {
+ if (isReference()) {
+ return getRef(getProject()).selectorCount();
+ }
+ dieOnCircularReference();
+ return selectors.size();
+ }
+
+ /**
+ * Returns the set of selectors as an array.
+ * @param p the current project
+ * @return a <code>FileSelector[]</code> of the selectors in this container.
+ */
+ public synchronized FileSelector[] getSelectors(Project p) {
+ if (isReference()) {
+ return getRef(getProject()).getSelectors(p);
+ }
+ dieOnCircularReference(p);
+ return (FileSelector[]) (selectors.toArray(
+ new FileSelector[selectors.size()]));
+ }
+
+ /**
+ * Returns an enumerator for accessing the set of selectors.
+ *
+ * @return an <code>Enumeration</code> of selectors.
+ */
+ public synchronized Enumeration<FileSelector> selectorElements() {
+ if (isReference()) {
+ return getRef(getProject()).selectorElements();
+ }
+ dieOnCircularReference();
+ return Collections.enumeration(selectors);
+ }
+
+ /**
+ * Add a new selector into this container.
+ *
+ * @param selector the new <code>FileSelector</code> to add.
+ */
+ public synchronized void appendSelector(FileSelector selector) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ selectors.add(selector);
+ directoryScanner = null;
+ setChecked(false);
+ }
+
+ /* Methods below all add specific selectors */
+
+ /**
+ * Add a "Select" selector entry on the selector list.
+ * @param selector the <code>SelectSelector</code> to add.
+ */
+ public void addSelector(SelectSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add an "And" selector entry on the selector list.
+ * @param selector the <code>AndSelector</code> to add.
+ */
+ public void addAnd(AndSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add an "Or" selector entry on the selector list.
+ * @param selector the <code>OrSelector</code> to add.
+ */
+ public void addOr(OrSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add a "Not" selector entry on the selector list.
+ * @param selector the <code>NotSelector</code> to add.
+ */
+ public void addNot(NotSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add a "None" selector entry on the selector list.
+ * @param selector the <code>NoneSelector</code> to add.
+ */
+ public void addNone(NoneSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add a majority selector entry on the selector list.
+ * @param selector the <code>MajoritySelector</code> to add.
+ */
+ public void addMajority(MajoritySelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add a selector date entry on the selector list.
+ * @param selector the <code>DateSelector</code> to add.
+ */
+ public void addDate(DateSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add a selector size entry on the selector list.
+ * @param selector the <code>SizeSelector</code> to add.
+ */
+ public void addSize(SizeSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add a DifferentSelector entry on the selector list.
+ * @param selector the <code>DifferentSelector</code> to add.
+ */
+ public void addDifferent(DifferentSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add a selector filename entry on the selector list.
+ * @param selector the <code>FilenameSelector</code> to add.
+ */
+ public void addFilename(FilenameSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add a selector type entry on the selector list.
+ * @param selector the <code>TypeSelector</code> to add.
+ */
+ public void addType(TypeSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add an extended selector entry on the selector list.
+ * @param selector the <code>ExtendSelector</code> to add.
+ */
+ public void addCustom(ExtendSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add a contains selector entry on the selector list.
+ * @param selector the <code>ContainsSelector</code> to add.
+ */
+ public void addContains(ContainsSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add a present selector entry on the selector list.
+ * @param selector the <code>PresentSelector</code> to add.
+ */
+ public void addPresent(PresentSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add a depth selector entry on the selector list.
+ * @param selector the <code>DepthSelector</code> to add.
+ */
+ public void addDepth(DepthSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add a depends selector entry on the selector list.
+ * @param selector the <code>DependSelector</code> to add.
+ */
+ public void addDepend(DependSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add a regular expression selector entry on the selector list.
+ * @param selector the <code>ContainsRegexpSelector</code> to add.
+ */
+ public void addContainsRegexp(ContainsRegexpSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Add the modified selector.
+ * @param selector the <code>ModifiedSelector</code> to add.
+ * @since ant 1.6
+ */
+ public void addModified(ModifiedSelector selector) {
+ appendSelector(selector);
+ }
+
+ public void addReadable(ReadableSelector r) {
+ appendSelector(r);
+ }
+
+ public void addWritable(WritableSelector w) {
+ appendSelector(w);
+ }
+
+ /**
+ * Add an arbitrary selector.
+ * @param selector the <code>FileSelector</code> to add.
+ * @since Ant 1.6
+ */
+ public void add(FileSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * Returns included files as a list of semicolon-separated filenames.
+ *
+ * @return a <code>String</code> of included filenames.
+ */
+ public String toString() {
+ if (isReference()) {
+ return getRef(getProject()).toString();
+ }
+ dieOnCircularReference();
+ DirectoryScanner ds = getDirectoryScanner(getProject());
+ String[] files = ds.getIncludedFiles();
+ StringBuffer sb = new StringBuffer();
+
+ for (int i = 0; i < files.length; i++) {
+ if (i > 0) {
+ sb.append(';');
+ }
+ sb.append(files[i]);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Creates a deep clone of this instance, except for the nested
+ * selectors (the list of selectors is a shallow clone of this
+ * instance's list).
+ * @return the cloned object
+ * @since Ant 1.6
+ */
+ public synchronized Object clone() {
+ if (isReference()) {
+ return (getRef(getProject())).clone();
+ } else {
+ try {
+ AbstractFileSet fs = (AbstractFileSet) super.clone();
+ fs.defaultPatterns = (PatternSet) defaultPatterns.clone();
+ fs.additionalPatterns = new ArrayList<PatternSet>(additionalPatterns.size());
+ for (PatternSet ps : additionalPatterns) {
+ fs.additionalPatterns.add((PatternSet) ps.clone());
+ }
+ fs.selectors = new ArrayList<FileSelector>(selectors);
+ return fs;
+ } catch (CloneNotSupportedException e) {
+ throw new BuildException(e);
+ }
+ }
+ }
+
+ /**
+ * Get the merged include patterns for this AbstractFileSet.
+ * @param p the project to use.
+ * @return the include patterns of the default pattern set and all
+ * nested patternsets.
+ *
+ * @since Ant 1.7
+ */
+ public String[] mergeIncludes(Project p) {
+ return mergePatterns(p).getIncludePatterns(p);
+ }
+
+ /**
+ * Get the merged exclude patterns for this AbstractFileSet.
+ * @param p the project to use.
+ * @return the exclude patterns of the default pattern set and all
+ * nested patternsets.
+ *
+ * @since Ant 1.7
+ */
+ public String[] mergeExcludes(Project p) {
+ return mergePatterns(p).getExcludePatterns(p);
+ }
+
+ /**
+ * Get the merged patterns for this AbstractFileSet.
+ * @param p the project to use.
+ * @return the default patternset merged with the additional sets
+ * in a new PatternSet instance.
+ *
+ * @since Ant 1.7
+ */
+ public synchronized PatternSet mergePatterns(Project p) {
+ if (isReference()) {
+ return getRef(p).mergePatterns(p);
+ }
+ dieOnCircularReference();
+ PatternSet ps = (PatternSet) defaultPatterns.clone();
+ final int count = additionalPatterns.size();
+ for (int i = 0; i < count; i++) {
+ ps.append(additionalPatterns.get(i), p);
+ }
+ return ps;
+ }
+
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ for (FileSelector fileSelector : selectors) {
+ if (fileSelector instanceof DataType) {
+ pushAndInvokeCircularReferenceCheck((DataType) fileSelector, stk, p);
+ }
+ }
+ for (PatternSet ps : additionalPatterns) {
+ pushAndInvokeCircularReferenceCheck(ps, stk, p);
+ }
+ setChecked(true);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/AntFilterReader.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/AntFilterReader.java
new file mode 100644
index 00000000..20c41bc7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/AntFilterReader.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.tools.ant.types;
+
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * An AntFilterReader is a wrapper class that encloses the classname
+ * and configuration of a Configurable FilterReader.
+ */
+public final class AntFilterReader
+ extends DataType implements Cloneable {
+
+ private String className;
+
+ private final Vector<Parameter> parameters = new Vector<Parameter>();
+
+ private Path classpath;
+
+ /**
+ * Set the className attribute.
+ *
+ * @param className a <code>String</code> value
+ */
+ public void setClassName(final String className) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.className = className;
+ }
+
+ /**
+ * Get the className attribute.
+ *
+ * @return a <code>String</code> value
+ */
+ public String getClassName() {
+ if (isReference()) {
+ return ((AntFilterReader) getCheckedRef()).getClassName();
+ }
+ dieOnCircularReference();
+ return className;
+ }
+
+ /**
+ * Add a Parameter.
+ *
+ * @param param a <code>Parameter</code> value
+ */
+ public void addParam(final Parameter param) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ parameters.addElement(param);
+ }
+
+ /**
+ * Set the classpath to load the FilterReader through (attribute).
+ * @param classpath a classpath
+ */
+ public void setClasspath(Path classpath) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (this.classpath == null) {
+ this.classpath = classpath;
+ } else {
+ this.classpath.append(classpath);
+ }
+ setChecked(false);
+ }
+
+ /**
+ * Set the classpath to load the FilterReader through (nested element).
+ * @return a classpath to be configured
+ */
+ public Path createClasspath() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (this.classpath == null) {
+ this.classpath = new Path(getProject());
+ }
+ setChecked(false);
+ return this.classpath.createPath();
+ }
+
+ /**
+ * Get the classpath.
+ * @return the classpath
+ */
+ public Path getClasspath() {
+ if (isReference()) {
+ ((AntFilterReader) getCheckedRef()).getClasspath();
+ }
+ dieOnCircularReference();
+ return classpath;
+ }
+
+ /**
+ * Set the classpath to load the FilterReader through via
+ * reference (attribute).
+ * @param r a reference to a classpath
+ */
+ public void setClasspathRef(Reference r) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * The parameters for this filter.
+ *
+ * @return a <code>Parameter[]</code> value
+ */
+ public Parameter[] getParams() {
+ if (isReference()) {
+ ((AntFilterReader) getCheckedRef()).getParams();
+ }
+ dieOnCircularReference();
+ Parameter[] params = new Parameter[parameters.size()];
+ parameters.copyInto(params);
+ return params;
+ }
+
+ /**
+ * Makes this instance in effect a reference to another AntFilterReader
+ * instance.
+ *
+ * <p>You must not set another attribute or nest elements inside
+ * this element if you make it a reference.</p>
+ *
+ * @param r the reference to which this instance is associated
+ * @exception BuildException if this instance already has been configured.
+ */
+ public void setRefid(Reference r) throws BuildException {
+ if (!parameters.isEmpty() || className != null
+ || classpath != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ if (classpath != null) {
+ pushAndInvokeCircularReferenceCheck(classpath, stk, p);
+ }
+ setChecked(true);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ArchiveFileSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ArchiveFileSet.java
new file mode 100644
index 00000000..e9a07303
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ArchiveFileSet.java
@@ -0,0 +1,596 @@
+/*
+ * 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.tools.ant.types;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.zip.UnixStat;
+
+/**
+ * A ArchiveFileSet is a FileSet with extra attributes useful in the
+ * context of archiving tasks.
+ *
+ * It includes a prefix attribute which is prepended to each entry in
+ * the output archive file as well as a fullpath attribute. It also
+ * supports Unix file permissions for files and directories.
+ *
+ * @since Ant 1.7
+ */
+public abstract class ArchiveFileSet extends FileSet {
+
+ private static final int BASE_OCTAL = 8;
+
+ /**
+ * Default value for the dirmode attribute.
+ *
+ * @since Ant 1.5.2
+ */
+ public static final int DEFAULT_DIR_MODE =
+ UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM;
+
+ /**
+ * Default value for the filemode attribute.
+ *
+ * @since Ant 1.5.2
+ */
+ public static final int DEFAULT_FILE_MODE =
+ UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM;
+
+ private Resource src = null;
+ private String prefix = "";
+ private String fullpath = "";
+ private boolean hasDir = false;
+ private int fileMode = DEFAULT_FILE_MODE;
+ private int dirMode = DEFAULT_DIR_MODE;
+
+ private boolean fileModeHasBeenSet = false;
+ private boolean dirModeHasBeenSet = false;
+ private static final String ERROR_DIR_AND_SRC_ATTRIBUTES = "Cannot set both dir and src attributes";
+ private static final String ERROR_PATH_AND_PREFIX = "Cannot set both fullpath and prefix attributes";
+
+ private boolean errorOnMissingArchive = true;
+
+ private String encoding = null;
+
+ /** Constructor for ArchiveFileSet */
+ public ArchiveFileSet() {
+ super();
+ }
+
+ /**
+ * Constructor using a fileset argument.
+ * @param fileset the fileset to use
+ */
+ protected ArchiveFileSet(FileSet fileset) {
+ super(fileset);
+ }
+
+ /**
+ * Constructor using a archive fileset argument.
+ * @param fileset the archivefileset to use
+ */
+ protected ArchiveFileSet(ArchiveFileSet fileset) {
+ super(fileset);
+ src = fileset.src;
+ prefix = fileset.prefix;
+ fullpath = fileset.fullpath;
+ hasDir = fileset.hasDir;
+ fileMode = fileset.fileMode;
+ dirMode = fileset.dirMode;
+ fileModeHasBeenSet = fileset.fileModeHasBeenSet;
+ dirModeHasBeenSet = fileset.dirModeHasBeenSet;
+ errorOnMissingArchive = fileset.errorOnMissingArchive;
+ encoding = fileset.encoding;
+ }
+
+ /**
+ * Set the directory for the fileset.
+ * @param dir the directory for the fileset
+ * @throws BuildException on error
+ */
+ public void setDir(File dir) throws BuildException {
+ checkAttributesAllowed();
+ if (src != null) {
+ throw new BuildException(ERROR_DIR_AND_SRC_ATTRIBUTES);
+ }
+ super.setDir(dir);
+ hasDir = true;
+ }
+
+ /**
+ * Set the source Archive file for the archivefileset. Prevents both
+ * "dir" and "src" from being specified.
+ * @param a the archive as a single element Resource collection.
+ */
+ public void addConfigured(ResourceCollection a) {
+ checkChildrenAllowed();
+ if (a.size() != 1) {
+ throw new BuildException("only single argument resource collections"
+ + " are supported as archives");
+ }
+ setSrcResource(a.iterator().next());
+ }
+
+ /**
+ * Set the source Archive file for the archivefileset. Prevents both
+ * "dir" and "src" from being specified.
+ *
+ * @param srcFile The archive from which to extract entries.
+ */
+ public void setSrc(File srcFile) {
+ setSrcResource(new FileResource(srcFile));
+ }
+
+ /**
+ * Set the source Archive file for the archivefileset. Prevents both
+ * "dir" and "src" from being specified.
+ *
+ * @param src The archive from which to extract entries.
+ */
+ public void setSrcResource(Resource src) {
+ checkArchiveAttributesAllowed();
+ if (hasDir) {
+ throw new BuildException(ERROR_DIR_AND_SRC_ATTRIBUTES);
+ }
+ this.src = src;
+ setChecked(false);
+ }
+
+ /**
+ * Get the archive from which entries will be extracted.
+ * @param p the project to use
+ * @return the source file
+ */
+ public File getSrc(Project p) {
+ if (isReference()) {
+ return ((ArchiveFileSet) getRef(p)).getSrc(p);
+ }
+ return getSrc();
+ }
+
+ /**
+ * Sets whether an error is thrown if an archive does not exist.
+ *
+ * @param errorOnMissingArchive true if missing archives cause errors,
+ * false if not.
+ * @since Ant 1.8.0
+ */
+ public void setErrorOnMissingArchive(boolean errorOnMissingArchive) {
+ checkAttributesAllowed();
+ this.errorOnMissingArchive = errorOnMissingArchive;
+ }
+
+ /**
+ * Get the archive file from which entries will be extracted.
+ * @return the archive in case the archive is a file, null otherwise.
+ */
+ public File getSrc() {
+ if (isReference()) {
+ return ((ArchiveFileSet) getCheckedRef()).getSrc();
+ }
+ dieOnCircularReference();
+ if (src != null) {
+ FileProvider fp = src.as(FileProvider.class);
+ if (fp != null) {
+ return fp.getFile();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Performs the check for circular references and returns the
+ * referenced object.
+ * This is an override which does not delegate to the superclass; instead it invokes
+ * {@link #getRef(Project)}, because that contains the special support for fileset
+ * references, which can be handled by all ArchiveFileSets.
+ * @param p the Ant Project instance against which to resolve references.
+ * @return the dereferenced object.
+ * @throws BuildException if the reference is invalid (circular ref, wrong class, etc).
+ * @since Ant 1.8
+ */
+ // TODO is the above true? AFAICT the calls look circular :/
+ protected Object getCheckedRef(Project p) {
+ return getRef(p);
+ }
+
+ /**
+ * Prepend this prefix to the path for each archive entry.
+ * Prevents both prefix and fullpath from being specified
+ *
+ * @param prefix The prefix to prepend to entries in the archive file.
+ */
+ public void setPrefix(String prefix) {
+ checkArchiveAttributesAllowed();
+ if (!"".equals(prefix) && !"".equals(fullpath)) {
+ throw new BuildException(ERROR_PATH_AND_PREFIX);
+ }
+ this.prefix = prefix;
+ }
+
+ /**
+ * Return the prefix prepended to entries in the archive file.
+ * @param p the project to use
+ * @return the prefix
+ */
+ public String getPrefix(Project p) {
+ if (isReference()) {
+ return ((ArchiveFileSet) getRef(p)).getPrefix(p);
+ }
+ dieOnCircularReference(p);
+ return prefix;
+ }
+
+ /**
+ * Set the full pathname of the single entry in this fileset.
+ * Prevents both prefix and fullpath from being specified
+ *
+ * @param fullpath the full pathname of the single entry in this fileset.
+ */
+ public void setFullpath(String fullpath) {
+ checkArchiveAttributesAllowed();
+ if (!"".equals(prefix) && !"".equals(fullpath)) {
+ throw new BuildException(ERROR_PATH_AND_PREFIX);
+ }
+ this.fullpath = fullpath;
+ }
+
+ /**
+ * Return the full pathname of the single entry in this fileset.
+ * @param p the project to use
+ * @return the full path
+ */
+ public String getFullpath(Project p) {
+ if (isReference()) {
+ return ((ArchiveFileSet) getRef(p)).getFullpath(p);
+ }
+ dieOnCircularReference(p);
+ return fullpath;
+ }
+
+ /**
+ * Set the encoding used for this ZipFileSet.
+ * @param enc encoding as String.
+ * @since Ant 1.9.5
+ */
+ public void setEncoding(String enc) {
+ checkAttributesAllowed();
+ this.encoding = enc;
+ }
+
+ /**
+ * Get the encoding used for this ZipFileSet.
+ * @return String encoding.
+ * @since Ant 1.9.5
+ */
+ public String getEncoding() {
+ if (isReference()) {
+ AbstractFileSet ref = getRef(getProject());
+ if (ref instanceof ArchiveFileSet) {
+ return ((ArchiveFileSet) ref).getEncoding();
+ } else {
+ return null;
+ }
+ }
+ return encoding;
+ }
+
+ /**
+ * Creates a scanner for this type of archive.
+ * @return the scanner.
+ */
+ protected abstract ArchiveScanner newArchiveScanner();
+
+ /**
+ * Return the DirectoryScanner associated with this FileSet.
+ * If the ArchiveFileSet defines a source Archive file, then an ArchiveScanner
+ * is returned instead.
+ * @param p the project to use
+ * @return a directory scanner
+ */
+ public DirectoryScanner getDirectoryScanner(Project p) {
+ if (isReference()) {
+ return getRef(p).getDirectoryScanner(p);
+ }
+ dieOnCircularReference();
+ if (src == null) {
+ return super.getDirectoryScanner(p);
+ }
+ if (!src.isExists() && errorOnMissingArchive) {
+ throw new BuildException(
+ "The archive " + src.getName() + " doesn't exist");
+ }
+ if (src.isDirectory()) {
+ throw new BuildException("The archive " + src.getName()
+ + " can't be a directory");
+ }
+ ArchiveScanner as = newArchiveScanner();
+ as.setErrorOnMissingArchive(errorOnMissingArchive);
+ as.setSrc(src);
+ super.setDir(p.getBaseDir());
+ setupDirectoryScanner(as, p);
+ as.init();
+ return as;
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return Iterator of Resources.
+ * @since Ant 1.7
+ */
+ public Iterator<Resource> iterator() {
+ if (isReference()) {
+ return ((ResourceCollection) (getRef(getProject()))).iterator();
+ }
+ if (src == null) {
+ return super.iterator();
+ }
+ ArchiveScanner as = (ArchiveScanner) getDirectoryScanner(getProject());
+ return as.getResourceFiles(getProject());
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return size of the collection as int.
+ * @since Ant 1.7
+ */
+ public int size() {
+ if (isReference()) {
+ return ((ResourceCollection) (getRef(getProject()))).size();
+ }
+ if (src == null) {
+ return super.size();
+ }
+ ArchiveScanner as = (ArchiveScanner) getDirectoryScanner(getProject());
+ return as.getIncludedFilesCount();
+ }
+
+ /**
+ * Indicate whether this ResourceCollection is composed entirely of
+ * Resources accessible via local filesystem conventions. If true,
+ * all Resources returned from this ResourceCollection should be
+ * instances of FileResource.
+ * @return whether this is a filesystem-only resource collection.
+ * @since Ant 1.7
+ */
+ public boolean isFilesystemOnly() {
+ if (isReference()) {
+ return ((ArchiveFileSet) getCheckedRef()).isFilesystemOnly();
+ }
+ dieOnCircularReference();
+ return src == null;
+ }
+
+ /**
+ * A 3 digit octal string, specify the user, group and
+ * other modes in the standard Unix fashion;
+ * optional, default=0644
+ * @param octalString a <code>String</code> value
+ */
+ public void setFileMode(String octalString) {
+ checkArchiveAttributesAllowed();
+ integerSetFileMode(Integer.parseInt(octalString, BASE_OCTAL));
+ }
+
+ /**
+ * specify the user, group and
+ * other modes in the standard Unix fashion;
+ * optional, default=0644
+ *
+ * <p>We use the strange name so this method doesn't appear in
+ * IntrospectionHelpers list of attribute setters.</p>
+ * @param mode a <code>int</code> value
+ * @since Ant 1.7
+ */
+ public void integerSetFileMode(int mode) {
+ fileModeHasBeenSet = true;
+ this.fileMode = UnixStat.FILE_FLAG | mode;
+ }
+
+ /**
+ * Get the mode of the archive fileset
+ * @param p the project to use
+ * @return the mode
+ */
+ public int getFileMode(Project p) {
+ if (isReference()) {
+ return ((ArchiveFileSet) getRef(p)).getFileMode(p);
+ }
+ dieOnCircularReference();
+ return fileMode;
+ }
+
+ /**
+ * Whether the user has specified the mode explicitly.
+ * @return true if it has been set
+ */
+ public boolean hasFileModeBeenSet() {
+ if (isReference()) {
+ return ((ArchiveFileSet) getRef(getProject())).hasFileModeBeenSet();
+ }
+ dieOnCircularReference();
+ return fileModeHasBeenSet;
+ }
+
+ /**
+ * A 3 digit octal string, specify the user, group and
+ * other modes in the standard Unix fashion;
+ * optional, default=0755
+ * @param octalString a <code>String</code> value
+ */
+ public void setDirMode(String octalString) {
+ checkArchiveAttributesAllowed();
+ integerSetDirMode(Integer.parseInt(octalString, BASE_OCTAL));
+ }
+
+ /**
+ * specify the user, group and
+ * other modes in the standard Unix fashion;
+ * optional, default=0755
+ * <p>We use the strange name so this method doesn't appear in
+ * IntrospectionHelpers list of attribute setters.</p>
+ * @param mode a <code>int</code> value
+ * @since Ant 1.7
+ */
+ public void integerSetDirMode(int mode) {
+ dirModeHasBeenSet = true;
+ this.dirMode = UnixStat.DIR_FLAG | mode;
+ }
+
+ /**
+ * Get the dir mode of the archive fileset
+ * @param p the project to use
+ * @return the mode
+ */
+ public int getDirMode(Project p) {
+ if (isReference()) {
+ return ((ArchiveFileSet) getRef(p)).getDirMode(p);
+ }
+ dieOnCircularReference();
+ return dirMode;
+ }
+
+ /**
+ * Whether the user has specified the mode explicitly.
+ *
+ * @return true if it has been set
+ */
+ public boolean hasDirModeBeenSet() {
+ if (isReference()) {
+ return ((ArchiveFileSet) getRef(getProject())).hasDirModeBeenSet();
+ }
+ dieOnCircularReference();
+ return dirModeHasBeenSet;
+ }
+
+ /**
+ * A ArchiveFileset accepts another ArchiveFileSet or a FileSet as reference
+ * FileSets are often used by the war task for the lib attribute
+ * @param zfs the project to use
+ */
+ protected void configureFileSet(ArchiveFileSet zfs) {
+ zfs.setPrefix(prefix);
+ zfs.setFullpath(fullpath);
+ zfs.fileModeHasBeenSet = fileModeHasBeenSet;
+ zfs.fileMode = fileMode;
+ zfs.dirModeHasBeenSet = dirModeHasBeenSet;
+ zfs.dirMode = dirMode;
+ }
+
+ /**
+ * Return a ArchiveFileSet that has the same properties
+ * as this one.
+ * @return the cloned archiveFileSet
+ * @since Ant 1.6
+ */
+ public Object clone() {
+ if (isReference()) {
+ return getCheckedRef(ArchiveFileSet.class, getDataTypeName(), getProject()).clone();
+ }
+ return super.clone();
+ }
+
+ /**
+ * For file-based archivefilesets, return the same as for normal filesets;
+ * else just return the path of the zip.
+ * @return for file based archivefilesets, included files as a list
+ * of semicolon-separated filenames. else just the name of the zip.
+ */
+ public String toString() {
+ if (hasDir && getProject() != null) {
+ return super.toString();
+ }
+ return src == null ? null : src.getName();
+ }
+
+ /**
+ * Return the prefix prepended to entries in the archive file.
+ * @return the prefix.
+ * @deprecated since 1.7.
+ */
+ public String getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * Return the full pathname of the single entryZ in this fileset.
+ * @return the full pathname.
+ * @deprecated since 1.7.
+ */
+ public String getFullpath() {
+ return fullpath;
+ }
+
+ /**
+ * @return the file mode.
+ * @deprecated since 1.7.
+ */
+ public int getFileMode() {
+ return fileMode;
+ }
+
+ /**
+ * @return the dir mode.
+ * @deprecated since 1.7.
+ */
+ public int getDirMode() {
+ return dirMode;
+ }
+
+ /**
+ * A check attributes for archiveFileSet.
+ * If there is a reference, and
+ * it is a ArchiveFileSet, the archive fileset attributes
+ * cannot be used.
+ * (Note, we can only see if the reference is an archive
+ * fileset if the project has been set).
+ */
+ private void checkArchiveAttributesAllowed() {
+ if (getProject() == null
+ || (isReference()
+ && (getRefid().getReferencedObject(
+ getProject())
+ instanceof ArchiveFileSet))) {
+ checkAttributesAllowed();
+ }
+ }
+
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+
+ // takes care of nested selectors
+ super.dieOnCircularReference(stk, p);
+
+ if (!isReference()) {
+ if (src != null) {
+ pushAndInvokeCircularReferenceCheck(src, stk, p);
+ }
+ setChecked(true);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ArchiveScanner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ArchiveScanner.java
new file mode 100644
index 00000000..db5a8d46
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ArchiveScanner.java
@@ -0,0 +1,363 @@
+/*
+ * 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.tools.ant.types;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.FileResourceIterator;
+
+/**
+ * ArchiveScanner accesses the pattern matching algorithm in DirectoryScanner,
+ * which are protected methods that can only be accessed by subclassing.
+ *
+ * This implementation of FileScanner defines getIncludedFiles to return
+ * the matching archive entries.
+ *
+ * @since Ant 1.7
+ */
+public abstract class ArchiveScanner extends DirectoryScanner {
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /**
+ * The archive file which should be scanned.
+ */
+ protected File srcFile;
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * The archive resource which should be scanned.
+ */
+ private Resource src;
+
+ /**
+ * to record the last scanned zip file with its modification date
+ */
+ private Resource lastScannedResource;
+
+ /**
+ * record list of all file zip entries
+ */
+ private Map<String, Resource> fileEntries = new TreeMap<String, Resource>();
+
+ /**
+ * record list of all directory zip entries
+ */
+ private Map<String, Resource> dirEntries = new TreeMap<String, Resource>();
+
+ /**
+ * record list of matching file zip entries
+ */
+ private Map<String, Resource> matchFileEntries = new TreeMap<String, Resource>();
+
+ /**
+ * record list of matching directory zip entries
+ */
+ private Map<String, Resource> matchDirEntries = new TreeMap<String, Resource>();
+
+ /**
+ * encoding of file names.
+ *
+ * @since Ant 1.6
+ */
+ private String encoding;
+
+ /**
+ * @since Ant 1.8.0
+ */
+ private boolean errorOnMissingArchive = true;
+
+ /**
+ * Sets whether an error is thrown if an archive does not exist.
+ *
+ * @param errorOnMissingArchive true if missing archives cause errors,
+ * false if not.
+ * @since Ant 1.8.0
+ */
+ public void setErrorOnMissingArchive(boolean errorOnMissingArchive) {
+ this.errorOnMissingArchive = errorOnMissingArchive;
+ }
+
+ /**
+ * Don't scan when we have no zipfile.
+ * @since Ant 1.7
+ */
+ public void scan() {
+ if (src == null || (!src.isExists() && !errorOnMissingArchive)) {
+ return;
+ }
+ super.scan();
+ }
+
+ /**
+ * Sets the srcFile for scanning. This is the jar or zip file that
+ * is scanned for matching entries.
+ *
+ * @param srcFile the (non-null) archive file name for scanning
+ */
+ public void setSrc(File srcFile) {
+ setSrc(new FileResource(srcFile));
+ }
+
+ /**
+ * Sets the src for scanning. This is the jar or zip file that
+ * is scanned for matching entries.
+ *
+ * @param src the (non-null) archive resource
+ */
+ public void setSrc(Resource src) {
+ this.src = src;
+ FileProvider fp = src.as(FileProvider.class);
+ if (fp != null) {
+ srcFile = fp.getFile();
+ }
+ }
+
+ /**
+ * Sets encoding of file names.
+ * @param encoding the encoding format
+ * @since Ant 1.6
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Returns the names of the files which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the files which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ */
+ public String[] getIncludedFiles() {
+ if (src == null) {
+ return super.getIncludedFiles();
+ }
+ scanme();
+ return matchFileEntries.keySet().toArray(new String[matchFileEntries.size()]);
+ }
+
+ /**
+ * Override parent implementation.
+ * @return count of included files.
+ * @since Ant 1.7
+ */
+ public int getIncludedFilesCount() {
+ if (src == null) {
+ return super.getIncludedFilesCount();
+ }
+ scanme();
+ return matchFileEntries.size();
+ }
+
+ /**
+ * Returns the names of the directories which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the directories which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ */
+ public String[] getIncludedDirectories() {
+ if (src == null) {
+ return super.getIncludedDirectories();
+ }
+ scanme();
+ return matchDirEntries.keySet().toArray(new String[matchDirEntries.size()]);
+ }
+
+ /**
+ * Override parent implementation.
+ * @return count of included directories.
+ * @since Ant 1.7
+ */
+ public int getIncludedDirsCount() {
+ if (src == null) {
+ return super.getIncludedDirsCount();
+ }
+ scanme();
+ return matchDirEntries.size();
+ }
+
+ /**
+ * Get the set of Resources that represent files.
+ * @param project since Ant 1.8
+ * @return an Iterator of Resources.
+ * @since Ant 1.7
+ */
+ /* package-private for now */ Iterator<Resource> getResourceFiles(Project project) {
+ if (src == null) {
+ return new FileResourceIterator(project, getBasedir(), getIncludedFiles());
+ }
+ scanme();
+ return matchFileEntries.values().iterator();
+ }
+
+ /**
+ * Get the set of Resources that represent directories.
+ * @param project since Ant 1.8
+ * @return an Iterator of Resources.
+ * @since Ant 1.7
+ */
+ /* package-private for now */ Iterator<Resource> getResourceDirectories(Project project) {
+ if (src == null) {
+ return new FileResourceIterator(project, getBasedir(), getIncludedDirectories());
+ }
+ scanme();
+ return matchDirEntries.values().iterator();
+ }
+
+ /**
+ * Initialize DirectoryScanner data structures.
+ */
+ public void init() {
+ if (includes == null) {
+ // No includes supplied, so set it to 'matches all'
+ includes = new String[1];
+ includes[0] = "**";
+ }
+ if (excludes == null) {
+ excludes = new String[0];
+ }
+ }
+
+ /**
+ * Matches a jar entry against the includes/excludes list,
+ * normalizing the path separator.
+ *
+ * @param path the (non-null) path name to test for inclusion
+ *
+ * @return <code>true</code> if the path should be included
+ * <code>false</code> otherwise.
+ */
+ public boolean match(String path) {
+ String vpath = path;
+ if (path.length() > 0) {
+ vpath = path.replace('/', File.separatorChar).
+ replace('\\', File.separatorChar);
+ if (vpath.charAt(0) == File.separatorChar) {
+ vpath = vpath.substring(1);
+ }
+ }
+ return isIncluded(vpath) && !isExcluded(vpath);
+ }
+
+ /**
+ * Get the named Resource.
+ * @param name path name of the file sought in the archive
+ * @return the resource
+ * @since Ant 1.5.2
+ */
+ public Resource getResource(String name) {
+ if (src == null) {
+ return super.getResource(name);
+ }
+ if (name.equals("")) {
+ // special case in ZIPs, we do not want this thing included
+ return new Resource("", true, Long.MAX_VALUE, true);
+ }
+ // first check if the archive needs to be scanned again
+ scanme();
+ if (fileEntries.containsKey(name)) {
+ return fileEntries.get(name);
+ }
+ name = trimSeparator(name);
+
+ if (dirEntries.containsKey(name)) {
+ return dirEntries.get(name);
+ }
+ return new Resource(name);
+ }
+
+ /**
+ * Fills the file and directory maps with resources read from the archive.
+ *
+ * @param archive the archive to scan.
+ * @param encoding encoding used to encode file names inside the archive.
+ * @param fileEntries Map (name to resource) of non-directory
+ * resources found inside the archive.
+ * @param matchFileEntries Map (name to resource) of non-directory
+ * resources found inside the archive that matched all include
+ * patterns and didn't match any exclude patterns.
+ * @param dirEntries Map (name to resource) of directory
+ * resources found inside the archive.
+ * @param matchDirEntries Map (name to resource) of directory
+ * resources found inside the archive that matched all include
+ * patterns and didn't match any exclude patterns.
+ */
+ protected abstract void fillMapsFromArchive(Resource archive,
+ String encoding,
+ Map<String, Resource> fileEntries,
+ Map<String, Resource> matchFileEntries,
+ Map<String, Resource> dirEntries,
+ Map<String, Resource> matchDirEntries);
+
+ /**
+ * if the datetime of the archive did not change since
+ * lastScannedResource was initialized returns immediately else if
+ * the archive has not been scanned yet, then all the zip entries
+ * are put into the appropriate tables.
+ */
+ private void scanme() {
+ if (!src.isExists() && !errorOnMissingArchive) {
+ return;
+ }
+
+ //do not use a FileResource b/c it pulls File info from the filesystem:
+ Resource thisresource = new Resource(src.getName(),
+ src.isExists(),
+ src.getLastModified());
+ // spare scanning again and again
+ if (lastScannedResource != null
+ && lastScannedResource.getName().equals(thisresource.getName())
+ && lastScannedResource.getLastModified()
+ == thisresource.getLastModified()) {
+ return;
+ }
+ init();
+
+ fileEntries.clear();
+ dirEntries.clear();
+ matchFileEntries.clear();
+ matchDirEntries.clear();
+ fillMapsFromArchive(src, encoding, fileEntries, matchFileEntries,
+ dirEntries, matchDirEntries);
+
+ // record data about the last scanned resource
+ lastScannedResource = thisresource;
+ }
+
+ /**
+ * Remove trailing slash if present.
+ * @param s the file name to trim.
+ * @return the trimmed file name.
+ */
+ protected static final String trimSeparator(String s) {
+ return s.endsWith("/") ? s.substring(0, s.length() - 1) : s;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Assertions.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Assertions.java
new file mode 100644
index 00000000..a54db501
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Assertions.java
@@ -0,0 +1,359 @@
+/*
+ * 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.tools.ant.types;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * The assertion datatype. This type describes
+ * assertion settings for the &lt;java&gt; task and others.
+ * One can set the system assertions, and enable/disable those in
+ * packages and classes.
+ * Assertions can only be enabled or disabled when forking Java.
+ *
+ * Example: set system assertions and all org.apache packages except
+ * for ant, and the class org.apache.tools.ant.Main.
+ * <pre>
+ * &lt;assertions enableSystemAssertions="true" &gt;
+ * &lt;enable package="org.apache" /&gt;
+ * &lt;disable package="org.apache.ant" /&gt;
+ * &lt;enable class="org.apache.tools.ant.Main"/&gt;
+ * &lt;/assertions&gt;
+ *</pre>
+ * Disable system assertions; enable those in the anonymous package
+ * <pre>
+ * &lt;assertions enableSystemAssertions="false" &gt;
+ * &lt;enable package="..." /&gt;
+ * &lt;/assertions&gt;
+ * </pre>
+ * enable assertions in a class called Test
+ * <pre>
+ * &lt;assertions &gt;
+ * &lt;enable class="Test" /&gt;
+ * &lt;/assertions&gt;
+ * </pre>
+ * This type is a datatype, so you can declare assertions and use them later
+ *
+ * <pre>
+ * &lt;assertions id="project.assertions" &gt;
+ * &lt;enable project="org.apache.test" /&gt;
+ * &lt;/assertions&gt;
+ *
+ * &lt;assertions refid="project.assertions" /&gt;
+ *
+ * </pre>
+ * @since Ant 1.6
+ */
+public class Assertions extends DataType implements Cloneable {
+
+ /**
+ * enable/disable sys assertions; null means undefined
+ */
+ private Boolean enableSystemAssertions;
+
+ /**
+ * list of type BaseAssertion
+ */
+ private ArrayList<BaseAssertion> assertionList = new ArrayList<BaseAssertion>();
+
+
+ /**
+ * enable assertions
+ * @param assertion an enable assertion nested element
+ */
+ public void addEnable(EnabledAssertion assertion) {
+ checkChildrenAllowed();
+ assertionList.add(assertion);
+ }
+
+ /**
+ * disable assertions
+ * @param assertion a disable assertion nested element
+ */
+ public void addDisable(DisabledAssertion assertion) {
+ checkChildrenAllowed();
+ assertionList.add(assertion);
+ }
+
+ /**
+ * enable or disable system assertions.
+ * Default is not set (neither -enablesystemassersions or -disablesytemassertions
+ * are used on the command line).
+ * @param enableSystemAssertions if true enable system assertions
+ */
+ public void setEnableSystemAssertions(Boolean enableSystemAssertions) {
+ checkAttributesAllowed();
+ this.enableSystemAssertions = enableSystemAssertions;
+ }
+
+ /**
+ * Set the value of the refid attribute.
+ *
+ * <p>Subclasses may need to check whether any other attributes
+ * have been set as well or child elements have been created and
+ * thus override this method. if they do the must call
+ * <code>super.setRefid</code>.</p>
+ * @param ref the reference to use
+ */
+ public void setRefid(Reference ref) {
+ if (assertionList.size() > 0 || enableSystemAssertions != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(ref);
+ }
+
+ /**
+ * get whatever we are referencing to. This could be ourself.
+ * @return the object that contains the assertion info
+ */
+ private Assertions getFinalReference() {
+ if (getRefid() == null) {
+ return this;
+ } else {
+ Object o = getRefid().getReferencedObject(getProject());
+ if (!(o instanceof Assertions)) {
+ throw new BuildException("reference is of wrong type");
+ }
+ return (Assertions) o;
+ }
+ }
+
+ /**
+ * how many assertions are made...will resolve references before returning
+ * @return total # of commands to make
+ */
+ public int size() {
+ Assertions clause = getFinalReference();
+ return clause.getFinalSize();
+ }
+
+
+ /**
+ * what is the final size of this object
+ * @return number of assertions
+ */
+ private int getFinalSize() {
+ return assertionList.size() + (enableSystemAssertions != null ? 1 : 0);
+ }
+
+ /**
+ * add the assertions to a list in a format suitable
+ * for adding to a command line
+ * @param commandList the command line to format
+ */
+ public void applyAssertions(List<String> commandList) {
+ getProject().log("Applying assertions", Project.MSG_DEBUG);
+ Assertions clause = getFinalReference();
+ //do the system assertions
+ if (Boolean.TRUE.equals(clause.enableSystemAssertions)) {
+ getProject().log("Enabling system assertions", Project.MSG_DEBUG);
+ commandList.add("-enablesystemassertions");
+ } else if (Boolean.FALSE.equals(clause.enableSystemAssertions)) {
+ getProject().log("disabling system assertions", Project.MSG_DEBUG);
+ commandList.add("-disablesystemassertions");
+ }
+
+ //now any inner assertions
+ for (BaseAssertion assertion : clause.assertionList) {
+ String arg = assertion.toCommand();
+ getProject().log("adding assertion " + arg, Project.MSG_DEBUG);
+ commandList.add(arg);
+ }
+ }
+
+ /**
+ * apply all the assertions to the command.
+ * @param command the command line to format
+ */
+ public void applyAssertions(CommandlineJava command) {
+ Assertions clause = getFinalReference();
+ //do the system assertions
+ if (Boolean.TRUE.equals(clause.enableSystemAssertions)) {
+ addVmArgument(command, "-enablesystemassertions");
+ } else if (Boolean.FALSE.equals(clause.enableSystemAssertions)) {
+ addVmArgument(command, "-disablesystemassertions");
+ }
+
+ //now any inner assertions
+ for (BaseAssertion assertion : clause.assertionList) {
+ String arg = assertion.toCommand();
+ addVmArgument(command, arg);
+ }
+ }
+
+ /**
+ * add the assertions to a list in a format suitable
+ * for adding to a command line
+ * @param commandIterator list of commands
+ */
+ public void applyAssertions(final ListIterator<String> commandIterator) {
+ getProject().log("Applying assertions", Project.MSG_DEBUG);
+ Assertions clause = getFinalReference();
+ //do the system assertions
+ if (Boolean.TRUE.equals(clause.enableSystemAssertions)) {
+ getProject().log("Enabling system assertions", Project.MSG_DEBUG);
+ commandIterator.add("-enablesystemassertions");
+ } else if (Boolean.FALSE.equals(clause.enableSystemAssertions)) {
+ getProject().log("disabling system assertions", Project.MSG_DEBUG);
+ commandIterator.add("-disablesystemassertions");
+ }
+
+ //now any inner assertions
+ for (BaseAssertion assertion : clause.assertionList) {
+ String arg = assertion.toCommand();
+ getProject().log("adding assertion " + arg, Project.MSG_DEBUG);
+ commandIterator.add(arg);
+ }
+ }
+
+ /**
+ * helper method to add a string JVM argument to a command
+ * @param command
+ * @param arg
+ */
+ private static void addVmArgument(CommandlineJava command, String arg) {
+ Commandline.Argument argument;
+ argument = command.createVmArgument();
+ argument.setValue(arg);
+ }
+
+ /**
+ * clone the objects.
+ * This is not a full depth clone; the list of assertions is cloned,
+ * but it does not clone the underlying assertions.
+ * @return a cli
+ * @throws CloneNotSupportedException if the super class does not support cloning
+ */
+ public Object clone() throws CloneNotSupportedException {
+ Assertions that = (Assertions) super.clone();
+ that.assertionList = new ArrayList<BaseAssertion>(assertionList);
+ return that;
+ }
+
+ /**
+ * base class for our assertion elements.
+ */
+
+ public abstract static class BaseAssertion {
+ private String packageName;
+ private String className;
+
+ /**
+ * name a class
+ * @param className a class name
+ */
+ public void setClass(String className) {
+ this.className = className;
+ }
+
+ /**
+ * name a package
+ * @param packageName a package name
+ */
+ public void setPackage(String packageName) {
+ this.packageName = packageName;
+ }
+
+ /**
+ * what is the class name?
+ * @return classname or null
+ * @see #setClass
+ */
+ protected String getClassName() {
+ return className;
+ }
+
+ /**
+ * what is the package name?
+ * @return package name or null
+ * @see #setPackage
+ */
+ protected String getPackageName() {
+ return packageName;
+ }
+
+ /**
+ * get the prefix used to begin the command; -ea or -da.
+ * @return prefix
+ */
+ public abstract String getCommandPrefix();
+
+ /**
+ * create a full command string from this class
+ * @throws BuildException in case of trouble
+ * @return The command string
+ */
+ public String toCommand() {
+ //catch invalidness
+ if (getPackageName() != null && getClassName() != null) {
+ throw new BuildException("Both package and class have been set");
+ }
+ StringBuffer command = new StringBuffer(getCommandPrefix());
+ //see if it is a package or a class
+ if (getPackageName() != null) {
+ //packages get a ... prefix
+ command.append(':');
+ command.append(getPackageName());
+ if (!command.toString().endsWith("...")) {
+ //append the ... suffix if not there already
+ command.append("...");
+ }
+ } else if (getClassName() != null) {
+ //classes just get the classname
+ command.append(':');
+ command.append(getClassName());
+ }
+ return command.toString();
+ }
+ }
+
+
+ /**
+ * an enabled assertion enables things
+ */
+ public static class EnabledAssertion extends BaseAssertion {
+ /**
+ * get the prefix used to begin the command; -ea or -da.
+ * @return prefix
+ */
+ public String getCommandPrefix() {
+ return "-ea";
+ }
+
+ }
+
+ /**
+ * A disabled assertion disables things
+ */
+ public static class DisabledAssertion extends BaseAssertion {
+ /**
+ * get the prefix used to begin the command; -ea or -da.
+ * @return prefix
+ */
+ public String getCommandPrefix() {
+ return "-da";
+ }
+
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Commandline.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Commandline.java
new file mode 100644
index 00000000..c273d7aa
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Commandline.java
@@ -0,0 +1,689 @@
+/*
+ * 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.tools.ant.types;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Commandline objects help handling command lines specifying processes to
+ * execute.
+ *
+ * The class can be used to define a command line as nested elements or as a
+ * helper to define a command line by an application.
+ * <p>
+ * <code>
+ * &lt;someelement&gt;<br>
+ * &nbsp;&nbsp;&lt;acommandline executable="/executable/to/run"&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 1" /&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument line="argument_1 argument_2 argument_3" /&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 4" /&gt;<br>
+ * &nbsp;&nbsp;&lt;/acommandline&gt;<br>
+ * &lt;/someelement&gt;<br>
+ * </code>
+ * The element <code>someelement</code> must provide a method
+ * <code>createAcommandline</code> which returns an instance of this class.
+ *
+ */
+public class Commandline implements Cloneable {
+ /** win9x uses a (shudder) bat file (antRun.bat) for executing commands */
+ private static final boolean IS_WIN_9X = Os.isFamily("win9x");
+
+ /**
+ * The arguments of the command
+ */
+ private List<Argument> arguments = new ArrayList<Argument>();
+
+ /**
+ * the program to execute
+ */
+ private String executable = null;
+
+ protected static final String DISCLAIMER =
+ StringUtils.LINE_SEP
+ + "The \' characters around the executable and arguments are"
+ + StringUtils.LINE_SEP
+ + "not part of the command."
+ + StringUtils.LINE_SEP;
+
+ /**
+ * Create a command line from a string.
+ * @param toProcess the line: the first element becomes the executable, the rest
+ * the arguments.
+ */
+ public Commandline(String toProcess) {
+ super();
+ String[] tmp = translateCommandline(toProcess);
+ if (tmp != null && tmp.length > 0) {
+ setExecutable(tmp[0]);
+ for (int i = 1; i < tmp.length; i++) {
+ createArgument().setValue(tmp[i]);
+ }
+ }
+ }
+
+ /**
+ * Create an empty command line.
+ */
+ public Commandline() {
+ super();
+ }
+
+ /**
+ * Used for nested xml command line definitions.
+ */
+ public static class Argument extends ProjectComponent {
+
+ private String[] parts;
+
+ private String prefix = "";
+ private String suffix = "";
+
+ /**
+ * Set a single commandline argument.
+ *
+ * @param value a single commandline argument.
+ */
+ public void setValue(String value) {
+ parts = new String[] {value};
+ }
+
+ /**
+ * Set the line to split into several commandline arguments.
+ *
+ * @param line line to split into several commandline arguments.
+ */
+ public void setLine(String line) {
+ if (line == null) {
+ return;
+ }
+ parts = translateCommandline(line);
+ }
+
+ /**
+ * Set a single commandline argument and treats it like a
+ * PATH--ensuring the right separator for the local platform
+ * is used.
+ *
+ * @param value a single commandline argument.
+ */
+ public void setPath(Path value) {
+ parts = new String[] {value.toString()};
+ }
+
+ /**
+ * Set a single commandline argument from a reference to a
+ * path--ensuring the right separator for the local platform
+ * is used.
+ *
+ * @param value a single commandline argument.
+ */
+ public void setPathref(Reference value) {
+ Path p = new Path(getProject());
+ p.setRefid(value);
+ parts = new String[] {p.toString()};
+ }
+
+ /**
+ * Set a single commandline argument to the absolute filename
+ * of the given file.
+ *
+ * @param value a single commandline argument.
+ */
+ public void setFile(File value) {
+ parts = new String[] {value.getAbsolutePath()};
+ }
+
+ /**
+ * Set the prefix to be placed in front of every part of the
+ * argument.
+ *
+ * @param prefix fixed prefix string.
+ * @since Ant 1.8.0
+ */
+ public void setPrefix(String prefix) {
+ this.prefix = prefix != null ? prefix : "";
+ }
+
+ /**
+ * Set the suffix to be placed at the end of every part of the
+ * argument.
+ *
+ * @param suffix fixed suffix string.
+ * @since Ant 1.8.0
+ */
+ public void setSuffix(String suffix) {
+ this.suffix = suffix != null ? suffix : "";
+ }
+
+ /**
+ * Return the constituent parts of this Argument.
+ * @return an array of strings.
+ */
+ public String[] getParts() {
+ if (parts == null || parts.length == 0
+ || (prefix.length() == 0 && suffix.length() == 0)) {
+ return parts;
+ }
+ String[] fullParts = new String[parts.length];
+ for (int i = 0; i < fullParts.length; ++i) {
+ fullParts[i] = prefix + parts[i] + suffix;
+ }
+ return fullParts;
+ }
+ }
+
+ /**
+ * Class to keep track of the position of an Argument.
+ *
+ * <p>This class is there to support the srcfile and targetfile
+ * elements of &lt;apply&gt;.</p>
+ */
+ public class Marker {
+
+ private int position;
+ private int realPos = -1;
+ private String prefix = "";
+ private String suffix = "";
+
+ /**
+ * Construct a marker for the specified position.
+ * @param position the position to mark.
+ */
+ Marker(int position) {
+ this.position = position;
+ }
+
+ /**
+ * Return the number of arguments that preceded this marker.
+ *
+ * <p>The name of the executable -- if set -- is counted as the
+ * first argument.</p>
+ * @return the position of this marker.
+ */
+ public int getPosition() {
+ if (realPos == -1) {
+ realPos = (executable == null ? 0 : 1);
+ for (int i = 0; i < position; i++) {
+ Argument arg = (Argument) arguments.get(i);
+ realPos += arg.getParts().length;
+ }
+ }
+ return realPos;
+ }
+
+ /**
+ * Set the prefix to be placed in front of the inserted argument.
+ *
+ * @param prefix fixed prefix string.
+ * @since Ant 1.8.0
+ */
+ public void setPrefix(String prefix) {
+ this.prefix = prefix != null ? prefix : "";
+ }
+
+ /**
+ * Get the prefix to be placed in front of the inserted argument.
+ *
+ * @since Ant 1.8.0
+ */
+ public String getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * Set the suffix to be placed at the end of the inserted argument.
+ *
+ * @param suffix fixed suffix string.
+ * @since Ant 1.8.0
+ */
+ public void setSuffix(String suffix) {
+ this.suffix = suffix != null ? suffix : "";
+ }
+
+ /**
+ * Get the suffix to be placed at the end of the inserted argument.
+ *
+ * @since Ant 1.8.0
+ */
+ public String getSuffix() {
+ return suffix;
+ }
+
+ }
+
+ /**
+ * Create an argument object.
+ *
+ * <p>Each commandline object has at most one instance of the
+ * argument class. This method calls
+ * <code>this.createArgument(false)</code>.</p>
+ *
+ * @see #createArgument(boolean)
+ * @return the argument object.
+ */
+ public Argument createArgument() {
+ return this.createArgument(false);
+ }
+
+ /**
+ * Create an argument object and add it to our list of args.
+ *
+ * <p>Each commandline object has at most one instance of the
+ * argument class.</p>
+ *
+ * @param insertAtStart if true, the argument is inserted at the
+ * beginning of the list of args, otherwise it is appended.
+ * @return an argument to be configured
+ */
+ public Argument createArgument(boolean insertAtStart) {
+ Argument argument = new Argument();
+ if (insertAtStart) {
+ arguments.add(0, argument);
+ } else {
+ arguments.add(argument);
+ }
+ return argument;
+ }
+
+ /**
+ * Set the executable to run. All file separators in the string
+ * are converted to the platform specific value.
+ * @param executable the String executable name.
+ */
+ public void setExecutable(String executable) {
+ if (executable == null || executable.length() == 0) {
+ return;
+ }
+ this.executable = executable.replace('/', File.separatorChar)
+ .replace('\\', File.separatorChar);
+ }
+
+ /**
+ * Get the executable.
+ * @return the program to run--null if not yet set.
+ */
+ public String getExecutable() {
+ return executable;
+ }
+
+ /**
+ * Append the arguments to the existing command.
+ * @param line an array of arguments to append.
+ */
+ public void addArguments(String[] line) {
+ for (int i = 0; i < line.length; i++) {
+ createArgument().setValue(line[i]);
+ }
+ }
+
+ /**
+ * Return the executable and all defined arguments.
+ * @return the commandline as an array of strings.
+ */
+ public String[] getCommandline() {
+ final List<String> commands = new LinkedList<String>();
+ addCommandToList(commands.listIterator());
+ return commands.toArray(new String[commands.size()]);
+ }
+
+ /**
+ * Add the entire command, including (optional) executable to a list.
+ * @param list the list to add to.
+ * @since Ant 1.6
+ */
+ public void addCommandToList(ListIterator<String> list) {
+ if (executable != null) {
+ list.add(executable);
+ }
+ addArgumentsToList(list);
+ }
+
+ /**
+ * Returns all arguments defined by <code>addLine</code>,
+ * <code>addValue</code> or the argument object.
+ * @return the arguments as an array of strings.
+ */
+ public String[] getArguments() {
+ List<String> result = new ArrayList<String>(arguments.size() * 2);
+ addArgumentsToList(result.listIterator());
+ return result.toArray(new String[result.size()]);
+ }
+
+ /**
+ * Append all the arguments to the tail of a supplied list.
+ * @param list the list of arguments.
+ * @since Ant 1.6
+ */
+ public void addArgumentsToList(ListIterator<String> list) {
+ final int size = arguments.size();
+ for (int i = 0; i < size; i++) {
+ Argument arg = arguments.get(i);
+ String[] s = arg.getParts();
+ if (s != null) {
+ for (int j = 0; j < s.length; j++) {
+ list.add(s[j]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Return the command line as a string.
+ * @return the command line.
+ */
+ public String toString() {
+ return toString(getCommandline());
+ }
+
+ /**
+ * Put quotes around the given String if necessary.
+ *
+ * <p>If the argument doesn't include spaces or quotes, return it
+ * as is. If it contains double quotes, use single quotes - else
+ * surround the argument by double quotes.</p>
+ * @param argument the argument to quote if necessary.
+ * @return the quoted argument.
+ * @exception BuildException if the argument contains both, single
+ * and double quotes.
+ */
+ public static String quoteArgument(String argument) {
+ if (argument.indexOf("\"") > -1) {
+ if (argument.indexOf("\'") > -1) {
+ throw new BuildException("Can\'t handle single and double"
+ + " quotes in same argument");
+ } else {
+ return '\'' + argument + '\'';
+ }
+ } else if (argument.indexOf("\'") > -1
+ || argument.indexOf(" ") > -1
+ // WIN9x uses a bat file for executing commands
+ || (IS_WIN_9X && argument.indexOf(';') != -1)) {
+ return '\"' + argument + '\"';
+ } else {
+ return argument;
+ }
+ }
+
+ /**
+ * Quote the parts of the given array in way that makes them
+ * usable as command line arguments.
+ * @param line the list of arguments to quote.
+ * @return empty string for null or no command, else every argument split
+ * by spaces and quoted by quoting rules.
+ */
+ public static String toString(String[] line) {
+ // empty path return empty string
+ if (line == null || line.length == 0) {
+ return "";
+ }
+ // path containing one or more elements
+ final StringBuilder result = new StringBuilder();
+ for (int i = 0; i < line.length; i++) {
+ if (i > 0) {
+ result.append(' ');
+ }
+ result.append(quoteArgument(line[i]));
+ }
+ return result.toString();
+ }
+
+ /**
+ * Crack a command line.
+ * @param toProcess the command line to process.
+ * @return the command line broken into strings.
+ * An empty or null toProcess parameter results in a zero sized array.
+ */
+ public static String[] translateCommandline(String toProcess) {
+ if (toProcess == null || toProcess.length() == 0) {
+ //no command? no string
+ return new String[0];
+ }
+ // parse with a simple finite state machine
+
+ final int normal = 0;
+ final int inQuote = 1;
+ final int inDoubleQuote = 2;
+ int state = normal;
+ final StringTokenizer tok = new StringTokenizer(toProcess, "\"\' ", true);
+ final ArrayList<String> result = new ArrayList<String>();
+ final StringBuilder current = new StringBuilder();
+ boolean lastTokenHasBeenQuoted = false;
+
+ while (tok.hasMoreTokens()) {
+ String nextTok = tok.nextToken();
+ switch (state) {
+ case inQuote:
+ if ("\'".equals(nextTok)) {
+ lastTokenHasBeenQuoted = true;
+ state = normal;
+ } else {
+ current.append(nextTok);
+ }
+ break;
+ case inDoubleQuote:
+ if ("\"".equals(nextTok)) {
+ lastTokenHasBeenQuoted = true;
+ state = normal;
+ } else {
+ current.append(nextTok);
+ }
+ break;
+ default:
+ if ("\'".equals(nextTok)) {
+ state = inQuote;
+ } else if ("\"".equals(nextTok)) {
+ state = inDoubleQuote;
+ } else if (" ".equals(nextTok)) {
+ if (lastTokenHasBeenQuoted || current.length() != 0) {
+ result.add(current.toString());
+ current.setLength(0);
+ }
+ } else {
+ current.append(nextTok);
+ }
+ lastTokenHasBeenQuoted = false;
+ break;
+ }
+ }
+ if (lastTokenHasBeenQuoted || current.length() != 0) {
+ result.add(current.toString());
+ }
+ if (state == inQuote || state == inDoubleQuote) {
+ throw new BuildException("unbalanced quotes in " + toProcess);
+ }
+ return result.toArray(new String[result.size()]);
+ }
+
+ /**
+ * Size operator. This actually creates the command line, so it is not
+ * a zero cost operation.
+ * @return number of elements in the command, including the executable.
+ */
+ public int size() {
+ return getCommandline().length;
+ }
+
+ /**
+ * Generate a deep clone of the contained object.
+ * @return a clone of the contained object
+ */
+ public Object clone() {
+ try {
+ Commandline c = (Commandline) super.clone();
+ c.arguments = new ArrayList<Argument>(arguments);
+ return c;
+ } catch (CloneNotSupportedException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Clear out the whole command line.
+ */
+ public void clear() {
+ executable = null;
+ arguments.clear();
+ }
+
+ /**
+ * Clear out the arguments but leave the executable in place for
+ * another operation.
+ */
+ public void clearArgs() {
+ arguments.clear();
+ }
+
+ /**
+ * Return a marker.
+ *
+ * <p>This marker can be used to locate a position on the
+ * commandline--to insert something for example--when all
+ * parameters have been set.</p>
+ * @return a marker
+ */
+ public Marker createMarker() {
+ return new Marker(arguments.size());
+ }
+
+ /**
+ * Return a String that describes the command and arguments suitable for
+ * verbose output before a call to <code>Runtime.exec(String[])<code>.
+ * @return a string that describes the command and arguments.
+ * @since Ant 1.5
+ */
+ public String describeCommand() {
+ return describeCommand(this);
+ }
+
+ /**
+ * Return a String that describes the arguments suitable for
+ * verbose output before a call to <code>Runtime.exec(String[])<code>.
+ * @return a string that describes the arguments.
+ * @since Ant 1.5
+ */
+ public String describeArguments() {
+ return describeArguments(this);
+ }
+
+ /**
+ * Return a String that describes the command and arguments suitable for
+ * verbose output before a call to <code>Runtime.exec(String[])<code>.
+ * @param line the Commandline to describe.
+ * @return a string that describes the command and arguments.
+ * @since Ant 1.5
+ */
+ public static String describeCommand(Commandline line) {
+ return describeCommand(line.getCommandline());
+ }
+
+ /**
+ * Return a String that describes the arguments suitable for
+ * verbose output before a call to <code>Runtime.exec(String[])<code>.
+ * @param line the Commandline whose arguments to describe.
+ * @return a string that describes the arguments.
+ * @since Ant 1.5
+ */
+ public static String describeArguments(Commandline line) {
+ return describeArguments(line.getArguments());
+ }
+
+ /**
+ * Return a String that describes the command and arguments suitable for
+ * verbose output before a call to <code>Runtime.exec(String[])<code>.
+ *
+ * <p>This method assumes that the first entry in the array is the
+ * executable to run.</p>
+ * @param args the command line to describe as an array of strings
+ * @return a string that describes the command and arguments.
+ * @since Ant 1.5
+ */
+ public static String describeCommand(String[] args) {
+ if (args == null || args.length == 0) {
+ return "";
+ }
+ StringBuffer buf = new StringBuffer("Executing \'");
+ buf.append(args[0]);
+ buf.append("\'");
+ if (args.length > 1) {
+ buf.append(" with ");
+ buf.append(describeArguments(args, 1));
+ } else {
+ buf.append(DISCLAIMER);
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Return a String that describes the arguments suitable for
+ * verbose output before a call to <code>Runtime.exec(String[])<code>.
+ * @param args the command line to describe as an array of strings.
+ * @return a string that describes the arguments.
+ * @since Ant 1.5
+ */
+ public static String describeArguments(String[] args) {
+ return describeArguments(args, 0);
+ }
+
+ /**
+ * Return a String that describes the arguments suitable for
+ * verbose output before a call to <code>Runtime.exec(String[])<code>.
+ *
+ * @param args the command line to describe as an array of strings.
+ * @param offset ignore entries before this index.
+ * @return a string that describes the arguments
+ *
+ * @since Ant 1.5
+ */
+ protected static String describeArguments(String[] args, int offset) {
+ if (args == null || args.length <= offset) {
+ return "";
+ }
+ StringBuffer buf = new StringBuffer("argument");
+ if (args.length > offset) {
+ buf.append("s");
+ }
+ buf.append(":").append(StringUtils.LINE_SEP);
+ for (int i = offset; i < args.length; i++) {
+ buf.append("\'").append(args[i]).append("\'")
+ .append(StringUtils.LINE_SEP);
+ }
+ buf.append(DISCLAIMER);
+ return buf.toString();
+ }
+
+ /**
+ * Get an iterator to the arguments list.
+ * @since Ant 1.7
+ * @return an Iterator.
+ */
+ public Iterator<Argument> iterator() {
+ return arguments.iterator();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/CommandlineJava.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/CommandlineJava.java
new file mode 100644
index 00000000..9da354dd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/CommandlineJava.java
@@ -0,0 +1,699 @@
+/*
+ * 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.tools.ant.types;
+
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * A representation of a Java command line that is
+ * a composite of 2 <tt>Commandline</tt>s. One is used for the
+ * vm/options and one for the classname/arguments. It provides
+ * specific methods for a Java command line.
+ *
+ */
+public class CommandlineJava implements Cloneable {
+
+ /**
+ * commands to the JVM
+ */
+ private Commandline vmCommand = new Commandline();
+ /**
+ * actual java commands
+ */
+ private Commandline javaCommand = new Commandline();
+ /**
+ * properties to add using -D
+ */
+ private SysProperties sysProperties = new SysProperties();
+ private Path classpath = null;
+ private Path bootclasspath = null;
+ private String vmVersion;
+ private String maxMemory = null;
+ /**
+ * any assertions to make? Currently only supported in forked JVMs
+ */
+ private Assertions assertions = null;
+
+ /**
+ * Indicate whether it will execute a jar file or not, in this case
+ * the first vm option must be a -jar and the 'executable' is a jar file.
+ */
+ private boolean executeJar = false;
+
+ /**
+ * Whether system properties and bootclasspath shall be cloned.
+ * @since Ant 1.7
+ */
+ private boolean cloneVm = false;
+
+ /**
+ * Specialized Environment class for System properties.
+ */
+ public static class SysProperties extends Environment implements Cloneable {
+ // CheckStyle:VisibilityModifier OFF - bc
+ /** the system properties. */
+ Properties sys = null;
+ // CheckStyle:VisibilityModifier ON
+ private Vector<PropertySet> propertySets = new Vector<PropertySet>();
+
+ /**
+ * Get the properties as an array; this is an override of the
+ * superclass, as it evaluates all the properties.
+ * @return the array of definitions; may be null.
+ * @throws BuildException on error.
+ */
+ public String[] getVariables() throws BuildException {
+
+ List<String> definitions = new LinkedList<String>();
+ addDefinitionsToList(definitions.listIterator());
+ if (definitions.size() == 0) {
+ return null;
+ } else {
+ return definitions.toArray(new String[definitions.size()]);
+ }
+ }
+
+ /**
+ * Add all definitions (including property sets) to a list.
+ * @param listIt list iterator supporting add method.
+ */
+ public void addDefinitionsToList(ListIterator<String> listIt) {
+ String[] props = super.getVariables();
+ if (props != null) {
+ for (int i = 0; i < props.length; i++) {
+ listIt.add("-D" + props[i]);
+ }
+ }
+ Properties propertySetProperties = mergePropertySets();
+ for (Enumeration<?> e = propertySetProperties.keys();
+ e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ String value = propertySetProperties.getProperty(key);
+ listIt.add("-D" + key + "=" + value);
+ }
+ }
+
+ /**
+ * Get the size of the sysproperties instance. This merges all
+ * property sets, so is not an O(1) operation.
+ * @return the size of the sysproperties instance.
+ */
+ public int size() {
+ Properties p = mergePropertySets();
+ return variables.size() + p.size();
+ }
+
+ /**
+ * Cache the system properties and set the system properties to the
+ * new values.
+ * @throws BuildException if Security prevented this operation.
+ */
+ public void setSystem() throws BuildException {
+ try {
+ sys = System.getProperties();
+ Properties p = new Properties();
+ for (Enumeration<?> e = sys.propertyNames(); e.hasMoreElements();) {
+ String name = (String) e.nextElement();
+ String value = sys.getProperty(name);
+ if (name != null && value != null) {
+ p.put(name, value);
+ }
+ }
+ p.putAll(mergePropertySets());
+ for (Environment.Variable v : variables) {
+ v.validate();
+ p.put(v.getKey(), v.getValue());
+ }
+ System.setProperties(p);
+ } catch (SecurityException e) {
+ throw new BuildException("Cannot modify system properties", e);
+ }
+ }
+
+ /**
+ * Restore the system properties to the cached value.
+ * @throws BuildException if Security prevented this operation, or
+ * there were no system properties to restore.
+ */
+ public void restoreSystem() throws BuildException {
+ if (sys == null) {
+ throw new BuildException("Unbalanced nesting of SysProperties");
+ }
+
+ try {
+ System.setProperties(sys);
+ sys = null;
+ } catch (SecurityException e) {
+ throw new BuildException("Cannot modify system properties", e);
+ }
+ }
+
+ /**
+ * Create a deep clone.
+ * @return a cloned instance of SysProperties.
+ * @exception CloneNotSupportedException for signature.
+ */
+ @SuppressWarnings("unchecked")
+ public Object clone() throws CloneNotSupportedException {
+ try {
+ SysProperties c = (SysProperties) super.clone();
+ c.variables = (Vector<Environment.Variable>) variables.clone();
+ c.propertySets = (Vector<PropertySet>) propertySets.clone();
+ return c;
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Add a propertyset to the total set.
+ * @param ps the new property set.
+ */
+ public void addSyspropertyset(PropertySet ps) {
+ propertySets.addElement(ps);
+ }
+
+ /**
+ * Add a propertyset to the total set.
+ * @param ps the new property set.
+ * @since Ant 1.6.3
+ */
+ public void addSysproperties(SysProperties ps) {
+ variables.addAll(ps.variables);
+ propertySets.addAll(ps.propertySets);
+ }
+
+ /**
+ * Merge all property sets into a single Properties object.
+ * @return the merged object.
+ */
+ private Properties mergePropertySets() {
+ Properties p = new Properties();
+ for (PropertySet ps : propertySets) {
+ p.putAll(ps.getProperties());
+ }
+ return p;
+ }
+
+ }
+
+ /**
+ * Constructor uses the VM we are running on now.
+ */
+ public CommandlineJava() {
+ setVm(JavaEnvUtils.getJreExecutable("java"));
+ setVmversion(JavaEnvUtils.getJavaVersion());
+ }
+
+ /**
+ * Create a new argument to the java program.
+ * @return an argument to be configured.
+ */
+ public Commandline.Argument createArgument() {
+ return javaCommand.createArgument();
+ }
+
+ /**
+ * Create a new JVM argument.
+ * @return an argument to be configured.
+ */
+ public Commandline.Argument createVmArgument() {
+ return vmCommand.createArgument();
+ }
+
+ /**
+ * Add a system property.
+ * @param sysp a property to be set in the JVM.
+ */
+ public void addSysproperty(Environment.Variable sysp) {
+ sysProperties.addVariable(sysp);
+ }
+
+ /**
+ * Add a set of system properties.
+ * @param sysp a set of properties.
+ */
+ public void addSyspropertyset(PropertySet sysp) {
+ sysProperties.addSyspropertyset(sysp);
+ }
+
+ /**
+ * Add a set of system properties.
+ * @param sysp a set of properties.
+ * @since Ant 1.6.3
+ */
+ public void addSysproperties(SysProperties sysp) {
+ sysProperties.addSysproperties(sysp);
+ }
+
+ /**
+ * Set the executable used to start the new JVM.
+ * @param vm the executable to use.
+ */
+ public void setVm(String vm) {
+ vmCommand.setExecutable(vm);
+ }
+
+ /**
+ * Set the JVM version required.
+ * @param value the version required.
+ */
+ public void setVmversion(String value) {
+ vmVersion = value;
+ }
+
+ /**
+ * Set whether system properties will be copied to the cloned VM--as
+ * well as the bootclasspath unless you have explicitly specified
+ * a bootclasspath.
+ * @param cloneVm if true copy the system properties.
+ * @since Ant 1.7
+ */
+ public void setCloneVm(boolean cloneVm) {
+ this.cloneVm = cloneVm;
+ }
+
+ /**
+ * Get the current assertions.
+ * @return assertions or null.
+ */
+ public Assertions getAssertions() {
+ return assertions;
+ }
+
+ /**
+ * Add an assertion set to the command.
+ * @param assertions assertions to make.
+ */
+ public void setAssertions(Assertions assertions) {
+ this.assertions = assertions;
+ }
+
+ /**
+ * Set a jar file to execute via the -jar option.
+ * @param jarpathname the pathname of the jar to execute.
+ */
+ public void setJar(String jarpathname) {
+ javaCommand.setExecutable(jarpathname);
+ executeJar = true;
+ }
+
+ /**
+ * Get the name of the jar to be run.
+ * @return the pathname of the jar file to run via -jar option
+ * or <tt>null</tt> if there is no jar to run.
+ * @see #getClassname()
+ */
+ public String getJar() {
+ if (executeJar) {
+ return javaCommand.getExecutable();
+ }
+ return null;
+ }
+
+ /**
+ * Set the classname to execute.
+ * @param classname the fully qualified classname.
+ */
+ public void setClassname(String classname) {
+ javaCommand.setExecutable(classname);
+ executeJar = false;
+ }
+
+ /**
+ * Get the name of the class to be run.
+ * @return the name of the class to run or <tt>null</tt> if there is no class.
+ * @see #getJar()
+ */
+ public String getClassname() {
+ if (!executeJar) {
+ return javaCommand.getExecutable();
+ }
+ return null;
+ }
+
+ /**
+ * Create a classpath.
+ * @param p the project to use to create the path.
+ * @return a path to be configured.
+ */
+ public Path createClasspath(Project p) {
+ if (classpath == null) {
+ classpath = new Path(p);
+ }
+ return classpath;
+ }
+
+ /**
+ * Create a boot classpath.
+ * @param p the project to use to create the path.
+ * @return a path to be configured.
+ * @since Ant 1.6
+ */
+ public Path createBootclasspath(Project p) {
+ if (bootclasspath == null) {
+ bootclasspath = new Path(p);
+ }
+ return bootclasspath;
+ }
+
+ /**
+ * Get the vm version.
+ * @return the vm version.
+ */
+ public String getVmversion() {
+ return vmVersion;
+ }
+
+ /**
+ * Get the command line to run a Java vm.
+ * @return the list of all arguments necessary to run the vm.
+ */
+ public String[] getCommandline() {
+ //create the list
+ List<String> commands = new LinkedList<String>();
+ //fill it
+ addCommandsToList(commands.listIterator());
+ //convert to an array
+ return commands.toArray(new String[commands.size()]);
+ }
+
+ /**
+ * Add all the commands to a list identified by the iterator passed in.
+ * @param listIterator an iterator that supports the add method.
+ * @since Ant 1.6
+ */
+ private void addCommandsToList(final ListIterator<String> listIterator) {
+ //create the command to run Java, including user specified options
+ getActualVMCommand().addCommandToList(listIterator);
+ // properties are part of the vm options...
+ sysProperties.addDefinitionsToList(listIterator);
+
+ if (isCloneVm()) {
+ SysProperties clonedSysProperties = new SysProperties();
+ PropertySet ps = new PropertySet();
+ PropertySet.BuiltinPropertySetName sys =
+ new PropertySet.BuiltinPropertySetName();
+ sys.setValue("system");
+ ps.appendBuiltin(sys);
+ clonedSysProperties.addSyspropertyset(ps);
+ clonedSysProperties.addDefinitionsToList(listIterator);
+ }
+ //boot classpath
+ Path bcp = calculateBootclasspath(true);
+ if (bcp.size() > 0) {
+ listIterator.add("-Xbootclasspath:" + bcp.toString());
+ }
+ //main classpath
+ if (haveClasspath()) {
+ listIterator.add("-classpath");
+ listIterator.add(
+ classpath.concatSystemClasspath("ignore").toString());
+ }
+ //now any assertions are added
+ if (getAssertions() != null) {
+ getAssertions().applyAssertions(listIterator);
+ }
+ // JDK usage command line says that -jar must be the first option, as there is
+ // a bug in JDK < 1.4 that forces the jvm type to be specified as the first
+ // option, it is appended here as specified in the docs even though there is
+ // in fact no order.
+ if (executeJar) {
+ listIterator.add("-jar");
+ }
+ // this is the classname to run as well as its arguments.
+ // in case of 'executeJar', the executable is a jar file.
+ javaCommand.addCommandToList(listIterator);
+ }
+
+ /**
+ * Specify max memory of the JVM.
+ * -mx or -Xmx depending on VM version.
+ * @param max the string to pass to the jvm to specify the max memory.
+ */
+ public void setMaxmemory(String max) {
+ this.maxMemory = max;
+ }
+
+ /**
+ * Get a string description.
+ * @return the command line as a string.
+ */
+ public String toString() {
+ return Commandline.toString(getCommandline());
+ }
+
+ /**
+ * Return a String that describes the command and arguments suitable for
+ * verbose output before a call to <code>Runtime.exec(String[])<code>.
+ * @return the description string.
+ * @since Ant 1.5
+ */
+ public String describeCommand() {
+ return Commandline.describeCommand(getCommandline());
+ }
+
+ /**
+ * Return a String that describes the java command and arguments
+ * for in-VM executions.
+ *
+ * <p>The class name is the executable in this context.</p>
+ * @return the description string.
+ * @since Ant 1.5
+ */
+ public String describeJavaCommand() {
+ return Commandline.describeCommand(getJavaCommand());
+ }
+
+ /**
+ * Get the VM command parameters, including memory settings.
+ * @return the VM command parameters.
+ */
+ protected Commandline getActualVMCommand() {
+ Commandline actualVMCommand = (Commandline) vmCommand.clone();
+ if (maxMemory != null) {
+ if (vmVersion.startsWith("1.1")) {
+ actualVMCommand.createArgument().setValue("-mx" + maxMemory);
+ } else {
+ actualVMCommand.createArgument().setValue("-Xmx" + maxMemory);
+ }
+ }
+ return actualVMCommand;
+ }
+
+ /**
+ * Get the size of the java command line. This is a fairly intensive
+ * operation, as it has to evaluate the size of many components.
+ * @return the total number of arguments in the java command line.
+ * @see #getCommandline()
+ * @deprecated since 1.7.
+ * Please dont use this, it effectively creates the
+ * entire command.
+ */
+ public int size() {
+ int size = getActualVMCommand().size() + javaCommand.size()
+ + sysProperties.size();
+ // cloned system properties
+ if (isCloneVm()) {
+ size += System.getProperties().size();
+ }
+ // classpath is "-classpath <classpath>" -> 2 args
+ if (haveClasspath()) {
+ size += 2;
+ }
+ // bootclasspath is "-Xbootclasspath:<classpath>" -> 1 arg
+ if (calculateBootclasspath(true).size() > 0) {
+ size++;
+ }
+ // jar execution requires an additional -jar option
+ if (executeJar) {
+ size++;
+ }
+ //assertions take up space too
+ if (getAssertions() != null) {
+ size += getAssertions().size();
+ }
+ return size;
+ }
+
+ /**
+ * Get the Java command to be used.
+ * @return the java command--not a clone.
+ */
+ public Commandline getJavaCommand() {
+ return javaCommand;
+ }
+
+ /**
+ * Get the VM command, including memory.
+ * @return A deep clone of the instance's VM command, with memory settings added.
+ */
+ public Commandline getVmCommand() {
+ return getActualVMCommand();
+ }
+
+ /**
+ * Get the classpath for the command.
+ * @return the classpath or null.
+ */
+ public Path getClasspath() {
+ return classpath;
+ }
+
+ /**
+ * Get the boot classpath.
+ * @return boot classpath or null.
+ */
+ public Path getBootclasspath() {
+ return bootclasspath;
+ }
+
+ /**
+ * Cache current system properties and set them to those in this
+ * Java command.
+ * @throws BuildException if Security prevented this operation.
+ */
+ public void setSystemProperties() throws BuildException {
+ sysProperties.setSystem();
+ }
+
+ /**
+ * Restore the cached system properties.
+ * @throws BuildException if Security prevented this operation, or
+ * there was no system properties to restore
+ */
+ public void restoreSystemProperties() throws BuildException {
+ sysProperties.restoreSystem();
+ }
+
+ /**
+ * Get the system properties object.
+ * @return The system properties object.
+ */
+ public SysProperties getSystemProperties() {
+ return sysProperties;
+ }
+
+ /**
+ * Deep clone the object.
+ * @return a CommandlineJava object.
+ * @throws BuildException if anything went wrong.
+ * @throws CloneNotSupportedException never.
+ */
+ public Object clone() throws CloneNotSupportedException {
+ try {
+ CommandlineJava c = (CommandlineJava) super.clone();
+ c.vmCommand = (Commandline) vmCommand.clone();
+ c.javaCommand = (Commandline) javaCommand.clone();
+ c.sysProperties = (SysProperties) sysProperties.clone();
+ if (classpath != null) {
+ c.classpath = (Path) classpath.clone();
+ }
+ if (bootclasspath != null) {
+ c.bootclasspath = (Path) bootclasspath.clone();
+ }
+ if (assertions != null) {
+ c.assertions = (Assertions) assertions.clone();
+ }
+ return c;
+ } catch (CloneNotSupportedException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Clear out the java arguments.
+ */
+ public void clearJavaArgs() {
+ javaCommand.clearArgs();
+ }
+
+ /**
+ * Determine whether the classpath has been specified, and whether it shall
+ * really be used or be nulled by build.sysclasspath.
+ * @return true if the classpath is to be used.
+ * @since Ant 1.6
+ */
+ public boolean haveClasspath() {
+ Path fullClasspath = classpath != null
+ ? classpath.concatSystemClasspath("ignore") : null;
+ return fullClasspath != null
+ && fullClasspath.toString().trim().length() > 0;
+ }
+
+ /**
+ * Determine whether the bootclasspath has been specified, and whether it
+ * shall really be used (build.sysclasspath could be set or the VM may not
+ * support it).
+ *
+ * @param log whether to log a warning if a bootclasspath has been
+ * specified but will be ignored.
+ * @return true if the bootclasspath is to be used.
+ * @since Ant 1.6
+ */
+ protected boolean haveBootclasspath(boolean log) {
+ return calculateBootclasspath(log).size() > 0;
+ }
+
+ /**
+ * Calculate the bootclasspath based on the bootclasspath
+ * specified, the build.sysclasspath and ant.build.clonevm magic
+ * properties as well as the cloneVm attribute.
+ * @param log whether to write messages to the log.
+ * @since Ant 1.7
+ */
+ private Path calculateBootclasspath(boolean log) {
+ if (vmVersion.startsWith("1.1")) {
+ if (bootclasspath != null && log) {
+ bootclasspath.log("Ignoring bootclasspath as "
+ + "the target VM doesn't support it.");
+ }
+ } else {
+ Path b = bootclasspath;
+ if (b == null) {
+ b = new Path(null);
+ }
+ // even with no user-supplied bootclasspath
+ // build.sysclasspath could be set to something other than
+ // "ignore" and thus create one
+ return b.concatSystemBootClasspath(isCloneVm() ? "last" : "ignore");
+ }
+ return new Path(null);
+ }
+
+ /**
+ * Find out whether either of the cloneVm attribute or the magic property
+ * ant.build.clonevm has been set.
+ * @return <code>boolean</code>.
+ * @since 1.7
+ */
+ private boolean isCloneVm() {
+ return cloneVm
+ || "true".equals(System.getProperty("ant.build.clonevm"));
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Comparison.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Comparison.java
new file mode 100644
index 00000000..a057d0d7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Comparison.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.tools.ant.types;
+
+import java.util.Arrays;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * EnumeratedAttribute for generic comparisons. Accepts values
+ * "equal", "greater", "more", "less", "ne" (not equal),
+ * "ge" (greater or equal), "le" (less or equal), "eq" (equal),
+ * "gt" (greater), "lt" (less).
+ * @since Ant 1.7
+ */
+public class Comparison extends EnumeratedAttribute {
+ private static final String[] VALUES
+ = new String[] {"equal", "greater", "less",
+ "ne", "ge", "le", "eq", "gt", "lt", "more"};
+
+ /** Equal Comparison. */
+ public static final Comparison EQUAL = new Comparison("equal");
+
+ /** Not-Equal Comparison. */
+ public static final Comparison NOT_EQUAL = new Comparison("ne");
+
+ /** Greater Comparison. */
+ public static final Comparison GREATER = new Comparison("greater");
+
+ /** Less Comparison. */
+ public static final Comparison LESS = new Comparison("less");
+
+ /** Greater-or-Equal Comparison. */
+ public static final Comparison GREATER_EQUAL = new Comparison("ge");
+
+ /** Less-or-Equal Comparison. */
+ public static final Comparison LESS_EQUAL = new Comparison("le");
+
+ private static final int[] EQUAL_INDEX = {0, 4, 5, 6};
+ private static final int[] LESS_INDEX = {2, 3, 5, 8};
+ private static final int[] GREATER_INDEX = {1, 3, 4, 7, 9};
+
+ /**
+ * Default constructor.
+ */
+ public Comparison() {
+ }
+
+ /**
+ * Construct a new Comparison with the specified value.
+ * @param value the EnumeratedAttribute value.
+ */
+ public Comparison(String value) {
+ setValue(value);
+ }
+
+ /**
+ * Return the possible values.
+ * @return String[] of EnumeratedAttribute values.
+ */
+ public String[] getValues() {
+ return VALUES;
+ }
+
+ /**
+ * Evaluate a comparison result as from Comparator.compare() or Comparable.compareTo().
+ * @param comparisonResult the result to evaluate.
+ * @return true if the comparison result fell within the parameters of this Comparison.
+ */
+ public boolean evaluate(int comparisonResult) {
+ if (getIndex() == -1) {
+ throw new BuildException("Comparison value not set.");
+ }
+ int[] i = comparisonResult < 0 ? LESS_INDEX
+ : comparisonResult > 0 ? GREATER_INDEX : EQUAL_INDEX;
+ return Arrays.binarySearch(i, getIndex()) >= 0;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/DTDLocation.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/DTDLocation.java
new file mode 100644
index 00000000..9fdeee29
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/DTDLocation.java
@@ -0,0 +1,33 @@
+/*
+ * 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.tools.ant.types;
+
+/**
+ * <p>Helper class to handle the DTD nested element. Instances of
+ * this class correspond to the <code>PUBLIC</code> catalog entry type
+ * of the <a
+ * href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
+ * OASIS "Open Catalog" standard</a>.</p>
+ *
+ * <p>Possible Future Enhancement: Bring the Ant element name into
+ * conformance with the OASIS standard.</p>
+ *
+ * @see org.apache.xml.resolver.Catalog
+ */
+public class DTDLocation extends ResourceLocation {
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/DataType.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/DataType.java
new file mode 100644
index 00000000..fda4af62
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/DataType.java
@@ -0,0 +1,367 @@
+/*
+ * 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.tools.ant.types;
+
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.util.IdentityStack;
+
+/**
+ * Base class for those classes that can appear inside the build file
+ * as stand alone data types.
+ *
+ * <p>This class handles the common description attribute and provides
+ * a default implementation for reference handling and checking for
+ * circular references that is appropriate for types that can not be
+ * nested inside elements of the same type (i.e. &lt;patternset&gt;
+ * but not &lt;path&gt;).</p>
+ *
+ */
+public abstract class DataType extends ProjectComponent implements Cloneable {
+ // CheckStyle:VisibilityModifier OFF
+
+ /**
+ * Value to the refid attribute.
+ *
+ * @deprecated since 1.7.
+ * The user should not be directly referencing
+ * variable. Please use {@link #getRefid} instead.
+ */
+ protected Reference ref;
+
+ /**
+ * Are we sure we don't hold circular references?
+ *
+ * <p>Subclasses are responsible for setting this value to false
+ * if we'd need to investigate this condition (usually because a
+ * child element has been added that is a subclass of
+ * DataType).</p>
+ *
+ * @deprecated since 1.7.
+ * The user should not be directly referencing
+ * variable. Please use {@link #setChecked} or
+ * {@link #isChecked} instead.
+ */
+ protected boolean checked = true;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Has the refid attribute of this element been set?
+ * @return true if the refid attribute has been set
+ */
+ public boolean isReference() {
+ return ref != null;
+ }
+
+ /**
+ * Set the value of the refid attribute.
+ *
+ * <p>Subclasses may need to check whether any other attributes
+ * have been set as well or child elements have been created and
+ * thus override this method. if they do the must call
+ * <code>super.setRefid</code>.</p>
+ * @param ref the reference to use
+ */
+ public void setRefid(final Reference ref) {
+ this.ref = ref;
+ checked = false;
+ }
+
+ /**
+ * Gets as descriptive as possible a name used for this datatype instance.
+ * @return <code>String</code> name.
+ */
+ protected String getDataTypeName() {
+ return ComponentHelper.getElementName(getProject(), this, true);
+ }
+
+ /**
+ * Convenience method.
+ * @since Ant 1.7
+ */
+ protected void dieOnCircularReference() {
+ dieOnCircularReference(getProject());
+ }
+
+ /**
+ * Convenience method.
+ * @param p the Ant Project instance against which to resolve references.
+ * @since Ant 1.7
+ */
+ protected void dieOnCircularReference(Project p) {
+ if (checked || !isReference()) {
+ return;
+ }
+ dieOnCircularReference(new IdentityStack<Object>(this), p);
+ }
+
+ /**
+ * Check to see whether any DataType we hold references to is
+ * included in the Stack (which holds all DataType instances that
+ * directly or indirectly reference this instance, including this
+ * instance itself).
+ *
+ * <p>If one is included, throw a BuildException created by {@link
+ * #circularReference circularReference}.</p>
+ *
+ * <p>This implementation is appropriate only for a DataType that
+ * cannot hold other DataTypes as children.</p>
+ *
+ * <p>The general contract of this method is that it shouldn't do
+ * anything if {@link #checked <code>checked</code>} is true and
+ * set it to true on exit.</p>
+ * @param stack the stack of references to check.
+ * @param project the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected void dieOnCircularReference(final Stack<Object> stack,
+ final Project project)
+ throws BuildException {
+
+ if (checked || !isReference()) {
+ return;
+ }
+ Object o = ref.getReferencedObject(project);
+
+ if (o instanceof DataType) {
+ IdentityStack<Object> id = IdentityStack.getInstance(stack);
+
+ if (id.contains(o)) {
+ throw circularReference();
+ } else {
+ id.push(o);
+ ((DataType) o).dieOnCircularReference(id, project);
+ id.pop();
+ }
+ }
+ checked = true;
+ }
+
+ /**
+ * Allow DataTypes outside org.apache.tools.ant.types to indirectly call
+ * dieOnCircularReference on nested DataTypes.
+ * @param dt the DataType to check.
+ * @param stk the stack of references to check.
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ * @since Ant 1.7
+ */
+ public static void invokeCircularReferenceCheck(DataType dt, Stack<Object> stk,
+ Project p) {
+ dt.dieOnCircularReference(stk, p);
+ }
+
+ /**
+ * Allow DataTypes outside org.apache.tools.ant.types to indirectly call
+ * dieOnCircularReference on nested DataTypes.
+ *
+ * <p>Pushes dt on the stack, runs dieOnCircularReference and pops
+ * it again.</p>
+ * @param dt the DataType to check.
+ * @param stk the stack of references to check.
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ * @since Ant 1.8.0
+ */
+ public static void pushAndInvokeCircularReferenceCheck(DataType dt,
+ Stack<Object> stk,
+ Project p) {
+ stk.push(dt);
+ dt.dieOnCircularReference(stk, p);
+ stk.pop();
+ }
+
+ /**
+ * Performs the check for circular references and returns the
+ * referenced object.
+ * @return the dereferenced object.
+ * @throws BuildException if the reference is invalid (circular ref, wrong class, etc).
+ * @since Ant 1.7
+ */
+ protected Object getCheckedRef() {
+ return getCheckedRef(getProject());
+ }
+
+ /**
+ * Performs the check for circular references and returns the
+ * referenced object.
+ * @param p the Ant Project instance against which to resolve references.
+ * @return the dereferenced object.
+ * @throws BuildException if the reference is invalid (circular ref, wrong class, etc).
+ * @since Ant 1.7
+ */
+ protected Object getCheckedRef(Project p) {
+ return getCheckedRef(getClass(), getDataTypeName(), p);
+ }
+
+ /**
+ * Performs the check for circular references and returns the
+ * referenced object.
+ * @param requiredClass the class that this reference should be a subclass of.
+ * @param dataTypeName the name of the datatype that the reference should be
+ * (error message use only).
+ * @return the dereferenced object.
+ * @throws BuildException if the reference is invalid (circular ref, wrong class, etc).
+ */
+ protected <T> T getCheckedRef(final Class<T> requiredClass,
+ final String dataTypeName) {
+ return getCheckedRef(requiredClass, dataTypeName, getProject());
+ }
+
+ /**
+ * Performs the check for circular references and returns the
+ * referenced object. This version allows the fallback Project instance to be specified.
+ * @param requiredClass the class that this reference should be a subclass of.
+ * @param dataTypeName the name of the datatype that the reference should be
+ * (error message use only).
+ * @param project the fallback Project instance for dereferencing.
+ * @return the dereferenced object.
+ * @throws BuildException if the reference is invalid (circular ref, wrong class, etc),
+ * or if <code>project</code> is <code>null</code>.
+ * @since Ant 1.7
+ */
+ protected <T> T getCheckedRef(final Class<T> requiredClass,
+ final String dataTypeName, final Project project) {
+ if (project == null) {
+ throw new BuildException("No Project specified");
+ }
+ dieOnCircularReference(project);
+ Object o = ref.getReferencedObject(project);
+ if (!(requiredClass.isAssignableFrom(o.getClass()))) {
+ log("Class " + displayName(o.getClass())
+ + " is not a subclass of "
+ + displayName(requiredClass),
+ Project.MSG_VERBOSE);
+ String msg = ref.getRefId() + " doesn\'t denote a " + dataTypeName;
+ throw new BuildException(msg);
+ }
+ @SuppressWarnings("unchecked")
+ final T result = (T) o;
+ return result;
+ }
+
+ /**
+ * Creates an exception that indicates that refid has to be the
+ * only attribute if it is set.
+ * @return the exception to throw
+ */
+ protected BuildException tooManyAttributes() {
+ return new BuildException("You must not specify more than one "
+ + "attribute when using refid");
+ }
+
+ /**
+ * Creates an exception that indicates that this XML element must
+ * not have child elements if the refid attribute is set.
+ * @return the exception to throw
+ */
+ protected BuildException noChildrenAllowed() {
+ return new BuildException("You must not specify nested elements "
+ + "when using refid");
+ }
+
+ /**
+ * Creates an exception that indicates the user has generated a
+ * loop of data types referencing each other.
+ * @return the exception to throw
+ */
+ protected BuildException circularReference() {
+ return new BuildException("This data type contains a circular "
+ + "reference.");
+ }
+
+ /**
+ * The flag that is used to indicate that circular references have been checked.
+ * @return true if circular references have been checked
+ */
+ protected boolean isChecked() {
+ return checked;
+ }
+
+ /**
+ * Set the flag that is used to indicate that circular references have been checked.
+ * @param checked if true, if circular references have been checked
+ */
+ protected void setChecked(final boolean checked) {
+ this.checked = checked;
+ }
+
+ /**
+ * get the reference set on this object
+ * @return the reference or null
+ */
+ public Reference getRefid() {
+ return ref;
+ }
+
+ /**
+ * check that it is ok to set attributes, i.e that no reference is defined
+ * @since Ant 1.6
+ * @throws BuildException if not allowed
+ */
+ protected void checkAttributesAllowed() {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ }
+
+ /**
+ * check that it is ok to add children, i.e that no reference is defined
+ * @since Ant 1.6
+ * @throws BuildException if not allowed
+ */
+ protected void checkChildrenAllowed() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ }
+
+ /**
+ * Basic DataType toString().
+ * @return this DataType formatted as a String.
+ */
+ public String toString() {
+ String d = getDescription();
+ return d == null ? getDataTypeName() : getDataTypeName() + " " + d;
+ }
+
+ /**
+ * @since Ant 1.7
+ * @return a shallow copy of this DataType.
+ * @throws CloneNotSupportedException if there is a problem.
+ */
+ public Object clone() throws CloneNotSupportedException {
+ DataType dt = (DataType) super.clone();
+ dt.setDescription(getDescription());
+ if (getRefid() != null) {
+ dt.setRefid(getRefid());
+ }
+ dt.setChecked(isChecked());
+ return dt;
+ }
+
+ private String displayName(Class<?> clazz) {
+ return clazz.getName() + " (loaded via " + clazz.getClassLoader() +")";
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Description.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Description.java
new file mode 100644
index 00000000..d23f1d88
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Description.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.tools.ant.types;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.UnknownElement;
+import org.apache.tools.ant.helper.ProjectHelper2;
+import org.apache.tools.ant.helper.ProjectHelperImpl;
+
+
+/**
+ * Description is used to provide a project-wide description element
+ * (that is, a description that applies to a buildfile as a whole).
+ * If present, the &lt;description&gt; element is printed out before the
+ * target descriptions.
+ *
+ * Description has no attributes, only text. There can only be one
+ * project description per project. A second description element will
+ * overwrite the first.
+ *
+ *
+ * @ant.datatype ignore="true"
+ */
+public class Description extends DataType {
+
+ /**
+ * Adds descriptive text to the project.
+ *
+ * @param text the descriptive text
+ */
+ public void addText(String text) {
+
+ ProjectHelper ph = getProject().getReference(ProjectHelper.PROJECTHELPER_REFERENCE);
+ if (!(ph instanceof ProjectHelperImpl)) {
+ // New behavior for delayed task creation. Description
+ // will be evaluated in Project.getDescription()
+ return;
+ }
+ String currentDescription = getProject().getDescription();
+ if (currentDescription == null) {
+ getProject().setDescription(text);
+ } else {
+ getProject().setDescription(currentDescription + text);
+ }
+ }
+
+ /**
+ * Return the descriptions from all the targets of
+ * a project.
+ *
+ * @param project the project to get the descriptions for.
+ * @return a string containing the concatenated descriptions of
+ * the targets.
+ */
+ public static String getDescription(Project project) {
+ List<Target> targets = project.getReference(ProjectHelper2.REFID_TARGETS);
+ if (targets == null) {
+ return null;
+ }
+ StringBuilder description = new StringBuilder();
+ for (Target t : targets) {
+ concatDescriptions(project, t, description);
+ }
+ return description.toString();
+ }
+
+ private static void concatDescriptions(Project project, Target t,
+ StringBuilder description) {
+ if (t == null) {
+ return;
+ }
+ for (Task task : findElementInTarget(project, t, "description")) {
+ if (!(task instanceof UnknownElement)) {
+ continue;
+ }
+ UnknownElement ue = ((UnknownElement) task);
+ String descComp = ue.getWrapper().getText().toString();
+ if (descComp != null) {
+ description.append(project.replaceProperties(descComp));
+ }
+ }
+ }
+
+ private static List<Task> findElementInTarget(Project project,
+ Target t, String name) {
+ final List<Task> elems = new ArrayList<Task>();
+ for (Task task : t.getTasks()) {
+ if (name.equals(task.getTaskName())) {
+ elems.add(task);
+ }
+ }
+ return elems;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/DirSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/DirSet.java
new file mode 100644
index 00000000..35c02311
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/DirSet.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.tools.ant.types;
+
+import java.util.Iterator;
+
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.resources.FileResourceIterator;
+
+/**
+ * Subclass as hint for supporting tasks that the included directories
+ * instead of files should be used.
+ *
+ * @since Ant 1.5
+ */
+public class DirSet extends AbstractFileSet implements ResourceCollection {
+
+ /**
+ * Constructor for DirSet.
+ */
+ public DirSet() {
+ super();
+ }
+
+ /**
+ * Constructor for DirSet, with DirSet to shallowly clone.
+ * @param dirset the dirset to clone.
+ */
+ protected DirSet(DirSet dirset) {
+ super(dirset);
+ }
+
+ /**
+ * Return a DirSet that has the same basedir and same patternsets
+ * as this one.
+ * @return the cloned dirset.
+ */
+ public Object clone() {
+ if (isReference()) {
+ return ((DirSet) getRef(getProject())).clone();
+ } else {
+ return super.clone();
+ }
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return an Iterator of Resources.
+ * @since Ant 1.7
+ */
+ public Iterator<Resource> iterator() {
+ if (isReference()) {
+ return ((DirSet) getRef(getProject())).iterator();
+ }
+ return new FileResourceIterator(getProject(), getDir(getProject()),
+ getDirectoryScanner(getProject()).getIncludedDirectories());
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ * @since Ant 1.7
+ */
+ public int size() {
+ if (isReference()) {
+ return ((DirSet) getRef(getProject())).size();
+ }
+ return getDirectoryScanner(getProject()).getIncludedDirsCount();
+ }
+
+ /**
+ * Always returns true.
+ * @return true indicating that all elements will be FileResources.
+ * @since Ant 1.7
+ */
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+
+ /**
+ * Returns included directories as a list of semicolon-separated paths.
+ *
+ * @return a <code>String</code> of included directories.
+ */
+ public String toString() {
+ DirectoryScanner ds = getDirectoryScanner(getProject());
+ String[] dirs = ds.getIncludedDirectories();
+ StringBuffer sb = new StringBuffer();
+
+ for (int i = 0; i < dirs.length; i++) {
+ if (i > 0) {
+ sb.append(';');
+ }
+ sb.append(dirs[i]);
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/EnumeratedAttribute.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/EnumeratedAttribute.java
new file mode 100644
index 00000000..bc893d8d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/EnumeratedAttribute.java
@@ -0,0 +1,153 @@
+/*
+ * 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.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Helper class for attributes that can only take one of a fixed list
+ * of values.
+ *
+ * <p>See {@link org.apache.tools.ant.taskdefs.FixCRLF FixCRLF} for an
+ * example.
+ *
+ */
+public abstract class EnumeratedAttribute {
+ // CheckStyle:VisibilityModifier OFF - bc
+ /**
+ * The selected value in this enumeration.
+ */
+ protected String value;
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * the index of the selected value in the array.
+ */
+ private int index = -1;
+
+ /**
+ * This is the only method a subclass needs to implement.
+ *
+ * @return an array holding all possible values of the enumeration.
+ * The order of elements must be fixed so that <tt>indexOfValue(String)</tt>
+ * always return the same index for the same value.
+ */
+ public abstract String[] getValues();
+
+ /** bean constructor */
+ protected EnumeratedAttribute() {
+ }
+
+ /**
+ * Factory method for instantiating EAs via API in a more
+ * developer friendly way.
+ * @param clazz Class, extending EA, which to instantiate
+ * @param value The value to set on that EA
+ * @return Configured EA
+ * @throws BuildException If the class could not be found or the value
+ * is not valid for the given EA-class.
+ * @see <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=14831">
+ * http://issues.apache.org/bugzilla/show_bug.cgi?id=14831</a>
+ */
+ public static EnumeratedAttribute getInstance(
+ Class<? extends EnumeratedAttribute> clazz,
+ String value) throws BuildException {
+ if (!EnumeratedAttribute.class.isAssignableFrom(clazz)) {
+ throw new BuildException(
+ "You have to provide a subclass from EnumeratedAttribut as clazz-parameter.");
+ }
+ EnumeratedAttribute ea = null;
+ try {
+ ea = clazz.newInstance();
+ } catch (Exception e) {
+ throw new BuildException(e);
+ }
+ ea.setValue(value);
+ return ea;
+ }
+
+ /**
+ * Invoked by {@link org.apache.tools.ant.IntrospectionHelper IntrospectionHelper}.
+ * @param value the <code>String</code> value of the attribute
+ * @throws BuildException if the value is not valid for the attribute
+ */
+ public final void setValue(String value) throws BuildException {
+ int idx = indexOfValue(value);
+ if (idx == -1) {
+ throw new BuildException(value + " is not a legal value for this attribute");
+ }
+ this.index = idx;
+ this.value = value;
+ }
+
+ /**
+ * Is this value included in the enumeration?
+ * @param value the <code>String</code> value to look up
+ * @return true if the value is valid
+ */
+ public final boolean containsValue(String value) {
+ return (indexOfValue(value) != -1);
+ }
+
+ /**
+ * get the index of a value in this enumeration.
+ * @param value the string value to look for.
+ * @return the index of the value in the array of strings
+ * or -1 if it cannot be found.
+ * @see #getValues()
+ */
+ public final int indexOfValue(String value) {
+ String[] values = getValues();
+ if (values == null || value == null) {
+ return -1;
+ }
+ for (int i = 0; i < values.length; i++) {
+ if (value.equals(values[i])) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * @return the selected value.
+ */
+ public final String getValue() {
+ return value;
+ }
+
+ /**
+ * @return the index of the selected value in the array.
+ * @see #getValues()
+ */
+ public final int getIndex() {
+ return index;
+ }
+
+ /**
+ * Convert the value to its string form.
+ *
+ * @return the string form of the value.
+ */
+ public String toString() {
+ return getValue();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Environment.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Environment.java
new file mode 100644
index 00000000..5bc6d79e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Environment.java
@@ -0,0 +1,177 @@
+/*
+ * 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.tools.ant.types;
+
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Wrapper for environment variables.
+ *
+ */
+public class Environment {
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /**
+ * a vector of type Environment.Variable
+ * @see Variable
+ */
+ protected Vector<Variable> variables;
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * representation of a single env value
+ */
+ public static class Variable {
+
+ /**
+ * env key and value pair; everything gets expanded to a string
+ * during assignment
+ */
+ private String key, value;
+
+ /**
+ * Constructor for variable
+ *
+ */
+ public Variable() {
+ super();
+ }
+
+ /**
+ * set the key
+ * @param key string
+ */
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ /**
+ * set the value
+ * @param value string value
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * key accessor
+ * @return key
+ */
+ public String getKey() {
+ return this.key;
+ }
+
+ /**
+ * value accessor
+ * @return value
+ */
+ public String getValue() {
+ return this.value;
+ }
+
+ /**
+ * stringify path and assign to the value.
+ * The value will contain all path elements separated by the appropriate
+ * separator
+ * @param path path
+ */
+ public void setPath(Path path) {
+ this.value = path.toString();
+ }
+
+ /**
+ * get the absolute path of a file and assign it to the value
+ * @param file file to use as the value
+ */
+ public void setFile(java.io.File file) {
+ this.value = file.getAbsolutePath();
+ }
+
+ /**
+ * get the assignment string
+ * This is not ready for insertion into a property file without following
+ * the escaping rules of the properties class.
+ * @return a string of the form key=value.
+ * @throws BuildException if key or value are unassigned
+ */
+ public String getContent() throws BuildException {
+ validate();
+ StringBuffer sb = new StringBuffer(key.trim());
+ sb.append("=").append(value.trim());
+ return sb.toString();
+ }
+
+ /**
+ * checks whether all required attributes have been specified.
+ * @throws BuildException if key or value are unassigned
+ */
+ public void validate() {
+ if (key == null || value == null) {
+ throw new BuildException("key and value must be specified "
+ + "for environment variables.");
+ }
+ }
+ }
+
+ /**
+ * constructor
+ */
+ public Environment() {
+ variables = new Vector<Variable>();
+ }
+
+ /**
+ * add a variable.
+ * Validity checking is <i>not</i> performed at this point. Duplicates
+ * are not caught either.
+ * @param var new variable.
+ */
+ public void addVariable(Variable var) {
+ variables.addElement(var);
+ }
+
+ /**
+ * get the variable list as an array
+ * @return array of key=value assignment strings
+ * @throws BuildException if any variable is misconfigured
+ */
+ public String[] getVariables() throws BuildException {
+ if (variables.size() == 0) {
+ return null;
+ }
+ String[] result = new String[variables.size()];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = ((Variable) variables.elementAt(i)).getContent();
+ }
+ return result;
+ }
+
+ /**
+ * Get the raw vector of variables. This is not a clone.
+ * @return a potentially empty (but never null) vector of elements of type
+ * Variable
+ * @since Ant 1.7
+ */
+ public Vector<Variable> getVariablesVector() {
+ return variables;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FileList.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FileList.java
new file mode 100644
index 00000000..42e27635
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FileList.java
@@ -0,0 +1,219 @@
+/*
+ * 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.tools.ant.types;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.resources.FileResourceIterator;
+
+/**
+ * FileList represents an explicitly named list of files. FileLists
+ * are useful when you want to capture a list of files regardless of
+ * whether they currently exist. By contrast, FileSet operates as a
+ * filter, only returning the name of a matched file if it currently
+ * exists in the file system.
+ */
+public class FileList extends DataType implements ResourceCollection {
+
+ private List<String> filenames = new ArrayList<String>();
+ private File dir;
+
+ /**
+ * The default constructor.
+ *
+ */
+ public FileList() {
+ super();
+ }
+
+ /**
+ * A copy constructor.
+ *
+ * @param filelist a <code>FileList</code> value
+ */
+ protected FileList(FileList filelist) {
+ this.dir = filelist.dir;
+ this.filenames = filelist.filenames;
+ setProject(filelist.getProject());
+ }
+
+ /**
+ * Makes this instance in effect a reference to another FileList
+ * instance.
+ *
+ * <p>You must not set another attribute or nest elements inside
+ * this element if you make it a reference.</p>
+ * @param r the reference to another filelist.
+ * @exception BuildException if an error occurs.
+ */
+ public void setRefid(Reference r) throws BuildException {
+ if ((dir != null) || (filenames.size() != 0)) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Set the dir attribute.
+ *
+ * @param dir the directory this filelist is relative to.
+ * @exception BuildException if an error occurs
+ */
+ public void setDir(File dir) throws BuildException {
+ checkAttributesAllowed();
+ this.dir = dir;
+ }
+
+ /**
+ * @param p the current project
+ * @return the directory attribute
+ */
+ public File getDir(Project p) {
+ if (isReference()) {
+ return getRef(p).getDir(p);
+ }
+ return dir;
+ }
+
+ /**
+ * Set the filenames attribute.
+ *
+ * @param filenames a string contains filenames, separated by , or
+ * by whitespace.
+ */
+ public void setFiles(String filenames) {
+ checkAttributesAllowed();
+ if (filenames != null && filenames.length() > 0) {
+ StringTokenizer tok = new StringTokenizer(
+ filenames, ", \t\n\r\f", false);
+ while (tok.hasMoreTokens()) {
+ this.filenames.add(tok.nextToken());
+ }
+ }
+ }
+
+ /**
+ * Returns the list of files represented by this FileList.
+ * @param p the current project
+ * @return the list of files represented by this FileList.
+ */
+ public String[] getFiles(Project p) {
+ if (isReference()) {
+ return getRef(p).getFiles(p);
+ }
+
+ if (dir == null) {
+ throw new BuildException("No directory specified for filelist.");
+ }
+
+ if (filenames.size() == 0) {
+ throw new BuildException("No files specified for filelist.");
+ }
+
+ return filenames.toArray(new String[filenames.size()]);
+ }
+
+ /**
+ * Performs the check for circular references and returns the
+ * referenced FileList.
+ * @param p the current project
+ * @return the FileList represented by a referenced filelist.
+ */
+ protected FileList getRef(Project p) {
+ return (FileList) getCheckedRef(p);
+ }
+
+ /**
+ * Inner class corresponding to the &lt;file&gt; nested element.
+ */
+ public static class FileName {
+ private String name;
+
+ /**
+ * The name attribute of the file element.
+ *
+ * @param name the name of a file to add to the file list.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return the name of the file for this element.
+ */
+ public String getName() {
+ return name;
+ }
+ }
+
+ /**
+ * Add a nested &lt;file&gt; nested element.
+ *
+ * @param name a configured file element with a name.
+ * @since Ant 1.6.2
+ */
+ public void addConfiguredFile(FileName name) {
+ if (name.getName() == null) {
+ throw new BuildException(
+ "No name specified in nested file element");
+ }
+ filenames.add(name.getName());
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return an Iterator of Resources.
+ * @since Ant 1.7
+ */
+ public Iterator<Resource> iterator() {
+ if (isReference()) {
+ return getRef(getProject()).iterator();
+ }
+ return new FileResourceIterator(getProject(), dir,
+ filenames.toArray(new String[filenames.size()]));
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ * @since Ant 1.7
+ */
+ public int size() {
+ if (isReference()) {
+ return ((FileList) getRef(getProject())).size();
+ }
+ return filenames.size();
+ }
+
+ /**
+ * Always returns true.
+ * @return true indicating that all elements will be FileResources.
+ * @since Ant 1.7
+ */
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FileSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FileSet.java
new file mode 100644
index 00000000..c6d2127d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FileSet.java
@@ -0,0 +1,94 @@
+/*
+ * 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.tools.ant.types;
+
+import java.util.Iterator;
+
+import org.apache.tools.ant.types.resources.FileResourceIterator;
+
+/**
+ * Moved out of MatchingTask to make it a standalone object that could
+ * be referenced (by scripts for example).
+ *
+ */
+public class FileSet extends AbstractFileSet implements ResourceCollection {
+
+ /**
+ * Constructor for FileSet.
+ */
+ public FileSet() {
+ super();
+ }
+
+ /**
+ * Constructor for FileSet, with FileSet to shallowly clone.
+ * @param fileset the fileset to clone
+ */
+ protected FileSet(FileSet fileset) {
+ super(fileset);
+ }
+
+ /**
+ * Return a FileSet that has the same basedir and same patternsets
+ * as this one.
+ * @return the cloned fileset
+ */
+ public Object clone() {
+ if (isReference()) {
+ return ((FileSet) getRef(getProject())).clone();
+ } else {
+ return super.clone();
+ }
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return an Iterator of Resources.
+ * @since Ant 1.7
+ */
+ public Iterator<Resource> iterator() {
+ if (isReference()) {
+ return ((FileSet) getRef(getProject())).iterator();
+ }
+ return new FileResourceIterator(getProject(), getDir(getProject()),
+ getDirectoryScanner(getProject()).getIncludedFiles());
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ * @since Ant 1.7
+ */
+ public int size() {
+ if (isReference()) {
+ return ((FileSet) getRef(getProject())).size();
+ }
+ return getDirectoryScanner(getProject()).getIncludedFilesCount();
+ }
+
+ /**
+ * Always returns true.
+ * @return true indicating that all elements will be FileResources.
+ * @since Ant 1.7
+ */
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FilterChain.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FilterChain.java
new file mode 100644
index 00000000..b495a5a3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FilterChain.java
@@ -0,0 +1,418 @@
+/*
+ * 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.tools.ant.types;
+
+import java.util.Iterator;
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.filters.ChainableReader;
+import org.apache.tools.ant.filters.ClassConstants;
+import org.apache.tools.ant.filters.EscapeUnicode;
+import org.apache.tools.ant.filters.ExpandProperties;
+import org.apache.tools.ant.filters.HeadFilter;
+import org.apache.tools.ant.filters.LineContains;
+import org.apache.tools.ant.filters.LineContainsRegExp;
+import org.apache.tools.ant.filters.PrefixLines;
+import org.apache.tools.ant.filters.ReplaceTokens;
+import org.apache.tools.ant.filters.StripJavaComments;
+import org.apache.tools.ant.filters.StripLineBreaks;
+import org.apache.tools.ant.filters.StripLineComments;
+import org.apache.tools.ant.filters.SuffixLines;
+import org.apache.tools.ant.filters.TabsToSpaces;
+import org.apache.tools.ant.filters.TailFilter;
+import org.apache.tools.ant.filters.TokenFilter;
+
+
+/**
+ * FilterChain may contain a chained set of filter readers.
+ *
+ */
+public class FilterChain extends DataType
+ implements Cloneable {
+
+ private Vector<Object> filterReaders = new Vector<Object>();
+
+ /**
+ * Add an AntFilterReader filter.
+ *
+ * @param filterReader an <code>AntFilterReader</code> value
+ */
+ public void addFilterReader(final AntFilterReader filterReader) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(filterReader);
+ }
+
+ /**
+ * Return the filters.
+ *
+ * @return a <code>Vector</code> value containing the filters
+ */
+ public Vector<Object> getFilterReaders() {
+ if (isReference()) {
+ return ((FilterChain) getCheckedRef()).getFilterReaders();
+ }
+ dieOnCircularReference();
+ return filterReaders;
+ }
+
+ /**
+ * Add a ClassConstants filter.
+ *
+ * @param classConstants a <code>ClassConstants</code> value
+ */
+ public void addClassConstants(final ClassConstants classConstants) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(classConstants);
+ }
+
+ /**
+ * Add an ExpandProperties filter.
+ *
+ * @param expandProperties an <code>ExpandProperties</code> value
+ */
+ public void addExpandProperties(final ExpandProperties expandProperties) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(expandProperties);
+ }
+
+ /**
+ * Add a HeadFilter filter.
+ *
+ * @param headFilter a <code>HeadFilter</code> value
+ */
+ public void addHeadFilter(final HeadFilter headFilter) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(headFilter);
+ }
+
+ /**
+ * Add a LineContains filter.
+ *
+ * @param lineContains a <code>LineContains</code> value
+ */
+ public void addLineContains(final LineContains lineContains) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(lineContains);
+ }
+
+ /**
+ * Add a LineContainsRegExp filter.
+ *
+ * @param lineContainsRegExp a <code>LineContainsRegExp</code> value
+ */
+ public void addLineContainsRegExp(final LineContainsRegExp
+ lineContainsRegExp) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(lineContainsRegExp);
+ }
+
+ /**
+ * Add a PrefixLines filter.
+ *
+ * @param prefixLines a <code>PrefixLines</code> value
+ */
+ public void addPrefixLines(final PrefixLines prefixLines) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(prefixLines);
+ }
+
+ /**
+ * Add a SuffixLines filter.
+ *
+ * @param suffixLines a <code>SuffixLines</code> value
+ * @since Ant 1.8.0
+ */
+ public void addSuffixLines(final SuffixLines suffixLines) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(suffixLines);
+ }
+
+ /**
+ * Add a ReplaceTokens filter.
+ *
+ * @param replaceTokens a <code>ReplaceTokens</code> value
+ */
+ public void addReplaceTokens(final ReplaceTokens replaceTokens) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(replaceTokens);
+ }
+
+ /**
+ * Add a StripJavaCommands filter.
+ *
+ * @param stripJavaComments a <code>StripJavaComments</code> value
+ */
+ public void addStripJavaComments(final StripJavaComments
+ stripJavaComments) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(stripJavaComments);
+ }
+
+ /**
+ * Add a StripLineBreaks filter.
+ *
+ * @param stripLineBreaks a <code>StripLineBreaks</code> value
+ */
+ public void addStripLineBreaks(final StripLineBreaks
+ stripLineBreaks) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(stripLineBreaks);
+ }
+
+ /**
+ * Add a StripLineComments filter.
+ *
+ * @param stripLineComments a <code>StripLineComments</code> value
+ */
+ public void addStripLineComments(final StripLineComments
+ stripLineComments) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(stripLineComments);
+ }
+
+ /**
+ * Add a TabsToSpaces filter.
+ *
+ * @param tabsToSpaces a <code>TabsToSpaces</code> value
+ */
+ public void addTabsToSpaces(final TabsToSpaces tabsToSpaces) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(tabsToSpaces);
+ }
+
+ /**
+ * Add a TailFilter filter.
+ *
+ * @param tailFilter a <code>TailFilter</code> value
+ */
+ public void addTailFilter(final TailFilter tailFilter) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(tailFilter);
+ }
+
+ /**
+ * Add an EscapeUnicode filter.
+ *
+ * @param escapeUnicode an <code>EscapeUnicode</code> value
+ * @since Ant 1.6
+ */
+ public void addEscapeUnicode(final EscapeUnicode escapeUnicode) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(escapeUnicode);
+ }
+
+ /**
+ * Add a TokenFilter filter.
+ *
+ * @param tokenFilter a <code>TokenFilter</code> value
+ * @since Ant 1.6
+ */
+ public void addTokenFilter(final TokenFilter tokenFilter) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(tokenFilter);
+ }
+
+ /**
+ * Add a delete characters filter.
+ *
+ * @param filter a <code>TokenFilter.DeleteCharacters</code> value
+ * @since Ant 1.6
+ */
+ public void addDeleteCharacters(TokenFilter.DeleteCharacters filter) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(filter);
+ }
+
+ /**
+ * Add a containsregex filter.
+ *
+ * @param filter a <code>TokenFilter.ContainsRegex</code> value
+ * @since Ant 1.6
+ */
+ public void addContainsRegex(TokenFilter.ContainsRegex filter) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(filter);
+ }
+
+ /**
+ * Add a replaceregex filter.
+ *
+ * @param filter a <code>TokenFilter.ReplaceRegex</code> value
+ */
+ public void addReplaceRegex(TokenFilter.ReplaceRegex filter) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(filter);
+ }
+
+ /**
+ * Add a trim filter.
+ *
+ * @param filter a <code>TokenFilter.Trim</code> value
+ * @since Ant 1.6
+ */
+ public void addTrim(TokenFilter.Trim filter) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(filter);
+ }
+
+ /**
+ * Add a replacestring filter.
+ *
+ * @param filter a <code>TokenFilter.ReplaceString</code> value
+ * @since Ant 1.6
+ */
+ public void addReplaceString(
+ TokenFilter.ReplaceString filter) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(filter);
+ }
+
+ /**
+ * Add an ignoreBlank filter.
+ *
+ * @param filter a <code>TokenFilter.IgnoreBlank</code> value
+ * @since Ant 1.6
+ */
+ public void addIgnoreBlank(
+ TokenFilter.IgnoreBlank filter) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(filter);
+ }
+
+
+ /**
+ * Makes this instance in effect a reference to another FilterChain
+ * instance.
+ *
+ * <p>You must not set another attribute or nest elements inside
+ * this element if you make it a reference.</p>
+ *
+ * @param r the reference to which this instance is associated
+ * @exception BuildException if this instance already has been configured.
+ */
+ @Override
+ public void setRefid(Reference r) throws BuildException {
+ if (!filterReaders.isEmpty()) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Add a chainfilter filter.
+ *
+ * @param filter a <code>ChainableReader</code> value
+ * @since Ant 1.6
+ */
+
+ public void add(ChainableReader filter) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ filterReaders.addElement(filter);
+ }
+
+ @Override
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ for (Iterator<Object> i = filterReaders.iterator(); i.hasNext();) {
+ Object o = i.next();
+ if (o instanceof DataType) {
+ pushAndInvokeCircularReferenceCheck((DataType) o, stk, p);
+ }
+ }
+ setChecked(true);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FilterSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FilterSet.java
new file mode 100644
index 00000000..2c1f2e71
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FilterSet.java
@@ -0,0 +1,654 @@
+/*
+ * 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.tools.ant.types;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.VectorSet;
+
+/**
+ * A set of filters to be applied to something.
+ *
+ * A filter set may have begintoken and endtokens defined.
+ *
+ */
+public class FilterSet extends DataType implements Cloneable {
+
+ /**
+ * Individual filter component of filterset.
+ *
+ */
+ public static class Filter {
+ // CheckStyle:VisibilityModifier OFF - bc
+ /** Token which will be replaced in the filter operation. */
+ String token;
+
+ /** The value which will replace the token in the filtering operation. */
+ String value;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Constructor for the Filter object.
+ *
+ * @param token The token which will be replaced when filtering.
+ * @param value The value which will replace the token when filtering.
+ */
+ public Filter(String token, String value) {
+ setToken(token);
+ setValue(value);
+ }
+
+ /**
+ * No-argument constructor.
+ */
+ public Filter() {
+ }
+
+ /**
+ * Sets the Token attribute of the Filter object.
+ *
+ * @param token The new Token value.
+ */
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ /**
+ * Sets the Value attribute of the Filter object.
+ *
+ * @param value The new Value value.
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Gets the Token attribute of the Filter object.
+ *
+ * @return The Token value.
+ */
+ public String getToken() {
+ return token;
+ }
+
+ /**
+ * Gets the Value attribute of the Filter object.
+ *
+ * @return The Value value.
+ */
+ public String getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * The filtersfile nested element.
+ *
+ */
+ public class FiltersFile {
+
+ /**
+ * Constructor for the FiltersFile object.
+ */
+ public FiltersFile() {
+ }
+
+ /**
+ * Sets the file from which filters will be read.
+ *
+ * @param file the file from which filters will be read.
+ */
+ public void setFile(File file) {
+ filtersFiles.add(file);
+ }
+ }
+
+ /**
+ * EnumeratedAttribute to set behavior WRT missing filtersfiles:
+ * "fail" (default), "warn", "ignore".
+ * @since Ant 1.7
+ */
+ public static class OnMissing extends EnumeratedAttribute {
+ private static final String[] VALUES
+ = new String[] {"fail", "warn", "ignore"};
+
+ /** Fail value */
+ public static final OnMissing FAIL = new OnMissing("fail");
+ /** Warn value */
+ public static final OnMissing WARN = new OnMissing("warn");
+ /** Ignore value */
+ public static final OnMissing IGNORE = new OnMissing("ignore");
+
+ private static final int FAIL_INDEX = 0;
+ private static final int WARN_INDEX = 1;
+ private static final int IGNORE_INDEX = 2;
+
+ /**
+ * Default constructor.
+ */
+ public OnMissing() {
+ }
+
+ /**
+ * Convenience constructor.
+ * @param value the value to set.
+ */
+ public OnMissing(String value) {
+ setValue(value);
+ }
+
+ //inherit doc
+ /** {@inheritDoc}. */
+ @Override
+ public String[] getValues() {
+ return VALUES;
+ }
+ }
+
+ /** The default token start string */
+ public static final String DEFAULT_TOKEN_START = "@";
+
+ /** The default token end string */
+ public static final String DEFAULT_TOKEN_END = "@";
+
+ private String startOfToken = DEFAULT_TOKEN_START;
+ private String endOfToken = DEFAULT_TOKEN_END;
+
+ /** Contains a list of parsed tokens */
+ private Vector<String> passedTokens;
+ /** if a duplicate token is found, this is set to true */
+ private boolean duplicateToken = false;
+
+ private boolean recurse = true;
+ private Hashtable<String, String> filterHash = null;
+ private Vector<File> filtersFiles = new Vector<File>();
+ private OnMissing onMissingFiltersFile = OnMissing.FAIL;
+ private boolean readingFiles = false;
+
+ private int recurseDepth = 0;
+
+ /**
+ * List of ordered filters and filter files.
+ */
+ private Vector<Filter> filters = new Vector<Filter>();
+
+ /**
+ * Default constructor.
+ */
+ public FilterSet() {
+ }
+
+ /**
+ * Create a Filterset from another filterset.
+ *
+ * @param filterset the filterset upon which this filterset will be based.
+ */
+ protected FilterSet(FilterSet filterset) {
+ super();
+ @SuppressWarnings("unchecked")
+ Vector<Filter> clone = (Vector<Filter>) filterset.getFilters().clone();
+ this.filters = clone;
+ }
+
+ /**
+ * Get the filters in the filter set.
+ *
+ * @return a Vector of Filter instances.
+ */
+ protected synchronized Vector<Filter> getFilters() {
+ if (isReference()) {
+ return getRef().getFilters();
+ }
+ dieOnCircularReference();
+ //silly hack to avoid stack overflow...
+ if (!readingFiles) {
+ readingFiles = true;
+ final int size = filtersFiles.size();
+ for (int i = 0; i < size; i++) {
+ readFiltersFromFile(filtersFiles.get(i));
+ }
+ filtersFiles.clear();
+ readingFiles = false;
+ }
+ return filters;
+ }
+
+ /**
+ * Get the referenced filter set.
+ *
+ * @return the filterset from the reference.
+ */
+ protected FilterSet getRef() {
+ return getCheckedRef(FilterSet.class, "filterset");
+ }
+
+ /**
+ * Gets the filter hash of the FilterSet.
+ *
+ * @return The hash of the tokens and values for quick lookup.
+ */
+ public synchronized Hashtable<String, String> getFilterHash() {
+ if (isReference()) {
+ return getRef().getFilterHash();
+ }
+ dieOnCircularReference();
+ if (filterHash == null) {
+ filterHash = new Hashtable<String, String>(getFilters().size());
+ for (Enumeration<Filter> e = getFilters().elements(); e.hasMoreElements();) {
+ Filter filter = e.nextElement();
+ filterHash.put(filter.getToken(), filter.getValue());
+ }
+ }
+ return filterHash;
+ }
+
+ /**
+ * Set the file containing the filters for this filterset.
+ *
+ * @param filtersFile sets the filter file from which to read filters
+ * for this filter set.
+ * @throws BuildException if there is an error.
+ */
+ public void setFiltersfile(File filtersFile) throws BuildException {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ filtersFiles.add(filtersFile);
+ }
+
+ /**
+ * Set the string used to id the beginning of a token.
+ *
+ * @param startOfToken The new Begintoken value.
+ */
+ public void setBeginToken(String startOfToken) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (startOfToken == null || "".equals(startOfToken)) {
+ throw new BuildException("beginToken must not be empty");
+ }
+ this.startOfToken = startOfToken;
+ }
+
+ /**
+ * Get the begin token for this filterset.
+ *
+ * @return the filter set's begin token for filtering.
+ */
+ public String getBeginToken() {
+ if (isReference()) {
+ return getRef().getBeginToken();
+ }
+ return startOfToken;
+ }
+
+ /**
+ * Set the string used to id the end of a token.
+ *
+ * @param endOfToken The new Endtoken value.
+ */
+ public void setEndToken(String endOfToken) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (endOfToken == null || "".equals(endOfToken)) {
+ throw new BuildException("endToken must not be empty");
+ }
+ this.endOfToken = endOfToken;
+ }
+
+ /**
+ * Get the end token for this filterset.
+ *
+ * @return the filter set's end token for replacement delimiting.
+ */
+ public String getEndToken() {
+ if (isReference()) {
+ return getRef().getEndToken();
+ }
+ return endOfToken;
+ }
+
+ /**
+ * Set whether recursive token expansion is enabled.
+ * @param recurse <code>boolean</code> whether to recurse.
+ */
+ public void setRecurse(boolean recurse) {
+ this.recurse = recurse;
+ }
+
+ /**
+ * Get whether recursive token expansion is enabled.
+ * @return <code>boolean</code> whether enabled.
+ */
+ public boolean isRecurse() {
+ return recurse;
+ }
+
+ /**
+ * Read the filters from the given file.
+ *
+ * @param filtersFile the file from which filters are read.
+ * @exception BuildException when the file cannot be read.
+ */
+ public synchronized void readFiltersFromFile(File filtersFile) throws BuildException {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (!filtersFile.exists()) {
+ handleMissingFile("Could not read filters from file "
+ + filtersFile + " as it doesn't exist.");
+ }
+ if (filtersFile.isFile()) {
+ log("Reading filters from " + filtersFile, Project.MSG_VERBOSE);
+ FileInputStream in = null;
+ try {
+ Properties props = new Properties();
+ in = new FileInputStream(filtersFile);
+ props.load(in);
+
+ Enumeration<?> e = props.propertyNames();
+ Vector<Filter> filts = getFilters();
+ while (e.hasMoreElements()) {
+ String strPropName = (String) e.nextElement();
+ String strValue = props.getProperty(strPropName);
+ filts.addElement(new Filter(strPropName, strValue));
+ }
+ } catch (Exception ex) {
+ throw new BuildException("Could not read filters from file: "
+ + filtersFile, ex);
+ } finally {
+ FileUtils.close(in);
+ }
+ } else {
+ handleMissingFile(
+ "Must specify a file rather than a directory in "
+ + "the filtersfile attribute:" + filtersFile);
+ }
+ filterHash = null;
+ }
+
+ /**
+ * Does replacement on the given string with token matching.
+ * This uses the defined begintoken and endtoken values which default
+ * to @ for both.
+ * This resets the passedTokens and calls iReplaceTokens to
+ * do the actual replacements.
+ *
+ * @param line The line in which to process embedded tokens.
+ * @return The input string after token replacement.
+ */
+ public synchronized String replaceTokens(String line) {
+ return iReplaceTokens(line);
+ }
+
+ /**
+ * Add a new filter.
+ *
+ * @param filter the filter to be added.
+ */
+ public synchronized void addFilter(Filter filter) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ filters.addElement(filter);
+ filterHash = null;
+ }
+
+ /**
+ * Create a new FiltersFile.
+ *
+ * @return The filtersfile that was created.
+ */
+ public FiltersFile createFiltersfile() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ return new FiltersFile();
+ }
+
+ /**
+ * Add a new filter made from the given token and value.
+ *
+ * @param token The token for the new filter.
+ * @param value The value for the new filter.
+ */
+ public synchronized void addFilter(String token, String value) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ addFilter(new Filter(token, value));
+ }
+
+ /**
+ * Add a Filterset to this filter set.
+ *
+ * @param filterSet the filterset to be added to this filterset
+ */
+ public synchronized void addConfiguredFilterSet(FilterSet filterSet) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ for (Filter filter : filterSet.getFilters()) {
+ addFilter(filter);
+ }
+ }
+
+ /**
+ * Adds the properties provided by the specified PropertySet to this filterset.
+ *
+ * @param propertySet the propertyset to be added to this propertyset
+ */
+ public synchronized void addConfiguredPropertySet(PropertySet propertySet) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ Properties p = propertySet.getProperties();
+ Set<Map.Entry<Object,Object>> entries = p.entrySet();
+ for (Map.Entry<Object, Object> entry : entries) {
+ addFilter(new Filter(String.valueOf(entry.getKey()),
+ String.valueOf(entry.getValue())));
+ }
+ }
+
+ /**
+ * Test to see if this filter set has filters.
+ *
+ * @return Return true if there are filters in this set.
+ */
+ public synchronized boolean hasFilters() {
+ return getFilters().size() > 0;
+ }
+
+ /**
+ * Clone the filterset.
+ *
+ * @return a deep clone of this filterset.
+ *
+ * @throws BuildException if the clone cannot be performed.
+ */
+ @Override
+ public synchronized Object clone() throws BuildException {
+ if (isReference()) {
+ return getRef().clone();
+ }
+ try {
+ FilterSet fs = (FilterSet) super.clone();
+ @SuppressWarnings("unchecked")
+ Vector<Filter> clonedFilters = (Vector<Filter>) getFilters().clone();
+ fs.filters = clonedFilters;
+ fs.setProject(getProject());
+ return fs;
+ } catch (CloneNotSupportedException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Set the behavior WRT missing filtersfiles.
+ * @param onMissingFiltersFile the OnMissing describing the behavior.
+ */
+ public void setOnMissingFiltersFile(OnMissing onMissingFiltersFile) {
+ this.onMissingFiltersFile = onMissingFiltersFile;
+ }
+
+ /**
+ * Get the onMissingFiltersFile setting.
+ * @return the OnMissing instance.
+ */
+ public OnMissing getOnMissingFiltersFile() {
+ return onMissingFiltersFile;
+ }
+
+ /**
+ * Does replacement on the given string with token matching.
+ * This uses the defined begintoken and endtoken values which default
+ * to @ for both.
+ *
+ * @param line The line to process the tokens in.
+ * @return The string with the tokens replaced.
+ */
+ private synchronized String iReplaceTokens(String line) {
+ String beginToken = getBeginToken();
+ String endToken = getEndToken();
+ int index = line.indexOf(beginToken);
+
+ if (index > -1) {
+ Hashtable<String, String> tokens = getFilterHash();
+ try {
+ StringBuilder b = new StringBuilder();
+ int i = 0;
+ String token = null;
+ String value = null;
+
+ while (index > -1) {
+ //can't have zero-length token
+ int endIndex = line.indexOf(endToken,
+ index + beginToken.length() + 1);
+ if (endIndex == -1) {
+ break;
+ }
+ token
+ = line.substring(index + beginToken.length(), endIndex);
+ b.append(line.substring(i, index));
+ if (tokens.containsKey(token)) {
+ value = tokens.get(token);
+ if (recurse && !value.equals(token)) {
+ // we have another token, let's parse it.
+ value = replaceTokens(value, token);
+ }
+ log("Replacing: " + beginToken + token + endToken
+ + " -> " + value, Project.MSG_VERBOSE);
+ b.append(value);
+ i = index + beginToken.length() + token.length()
+ + endToken.length();
+ } else {
+ // just append first character of beginToken
+ // and search further
+ // we can't skip the complete beginToken since
+ // it may contain the start of another
+ // candidate begin token (Bugzilla 45094)
+ b.append(beginToken.charAt(0));
+ i = index + 1;
+ }
+ index = line.indexOf(beginToken, i);
+ }
+
+ b.append(line.substring(i));
+ return b.toString();
+ } catch (StringIndexOutOfBoundsException e) {
+ return line;
+ }
+ } else {
+ return line;
+ }
+ }
+
+ /**
+ * This parses tokens which point to tokens.
+ * It also maintains a list of currently used tokens, so we cannot
+ * get into an infinite loop.
+ * @param line the value / token to parse.
+ * @param parent the parent token (= the token it was parsed from).
+ */
+ private synchronized String replaceTokens(String line, String parent)
+ throws BuildException {
+ String beginToken = getBeginToken();
+ String endToken = getEndToken();
+ if (recurseDepth == 0) {
+ passedTokens = new VectorSet<String>();
+ }
+ recurseDepth++;
+ if (passedTokens.contains(parent) && !duplicateToken) {
+ duplicateToken = true;
+ System.out.println(
+ "Infinite loop in tokens. Currently known tokens : "
+ + passedTokens.toString() + "\nProblem token : " + beginToken
+ + parent + endToken + " called from " + beginToken
+ + passedTokens.lastElement().toString() + endToken);
+ recurseDepth--;
+ return parent;
+ }
+ passedTokens.addElement(parent);
+ String value = iReplaceTokens(line);
+ if (value.indexOf(beginToken) == -1 && !duplicateToken
+ && recurseDepth == 1) {
+ passedTokens = null;
+ } else if (duplicateToken) {
+ // should always be the case...
+ if (passedTokens.size() > 0) {
+ value = passedTokens.remove(passedTokens.size() - 1);
+ if (passedTokens.size() == 0) {
+ value = beginToken + value + endToken;
+ duplicateToken = false;
+ }
+ }
+ } else if (passedTokens.size() > 0) {
+ // remove last seen token when crawling out of recursion
+ passedTokens.remove(passedTokens.size() - 1);
+ }
+ recurseDepth--;
+ return value;
+ }
+
+ private void handleMissingFile(String message) {
+ switch (onMissingFiltersFile.getIndex()) {
+ case OnMissing.IGNORE_INDEX:
+ return;
+ case OnMissing.FAIL_INDEX:
+ throw new BuildException(message);
+ case OnMissing.WARN_INDEX:
+ log(message, Project.MSG_WARN);
+ return;
+ default:
+ throw new BuildException("Invalid value for onMissingFiltersFile");
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FilterSetCollection.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FilterSetCollection.java
new file mode 100644
index 00000000..8afb9631
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FilterSetCollection.java
@@ -0,0 +1,88 @@
+/*
+ * 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.tools.ant.types;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A FilterSetCollection is a collection of filtersets each of which may have
+ * a different start/end token settings.
+ *
+ */
+public class FilterSetCollection {
+
+ private List<FilterSet> filterSets = new ArrayList<FilterSet>();
+
+ /**
+ * Constructor for a FilterSetCollection.
+ */
+ public FilterSetCollection() {
+ }
+
+ /**
+ * Constructor for a FilterSetCollection.
+ * @param filterSet a filterset to start the collection with
+ */
+ public FilterSetCollection(FilterSet filterSet) {
+ addFilterSet(filterSet);
+ }
+
+
+ /**
+ * Add a filterset to the collection.
+ *
+ * @param filterSet a <code>FilterSet</code> value
+ */
+ public void addFilterSet(FilterSet filterSet) {
+ filterSets.add(filterSet);
+ }
+
+ /**
+ * Does replacement on the given string with token matching.
+ * This uses the defined begintoken and endtoken values which default to @ for both.
+ *
+ * @param line The line to process the tokens in.
+ * @return The string with the tokens replaced.
+ */
+ public String replaceTokens(String line) {
+ String replacedLine = line;
+ for (FilterSet filterSet : filterSets) {
+ replacedLine = filterSet.replaceTokens(replacedLine);
+ }
+ return replacedLine;
+ }
+
+ /**
+ * Test to see if this filter set it empty.
+ *
+ * @return Return true if there are filter in this set otherwise false.
+ */
+ public boolean hasFilters() {
+ for (FilterSet filterSet : filterSets) {
+ if (filterSet.hasFilters()) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FlexInteger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FlexInteger.java
new file mode 100644
index 00000000..d757429f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/FlexInteger.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.tools.ant.types;
+
+/**
+ * Helper class which can be used for Ant task attribute setter methods to allow
+ * the build file to specify an integer in either decimal, octal, or hexadecimal
+ * format.
+ *
+ * @see java.lang.Integer#decode(String)
+ */
+public class FlexInteger {
+ private Integer value;
+
+ /**
+ * Constructor used by Ant's introspection mechanism for attribute population
+ * @param value the value to decode
+ */
+ public FlexInteger(String value) {
+ this.value = Integer.decode(value);
+ }
+
+ /**
+ * Returns the decimal integer value
+ * @return the integer value
+ */
+ public int intValue() {
+ return value.intValue();
+ }
+
+ /**
+ * Overridden method to return the decimal value for display
+ * @return a string version of the integer
+ */
+ public String toString() {
+ return value.toString();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/LogLevel.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/LogLevel.java
new file mode 100644
index 00000000..a02b948e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/LogLevel.java
@@ -0,0 +1,87 @@
+/*
+ * 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.tools.ant.types;
+
+import org.apache.tools.ant.Project;
+
+/**
+ * The enumerated values for Ant's log level.
+ */
+public class LogLevel extends EnumeratedAttribute {
+
+ /** ERR loglevel constant. */
+ public static final LogLevel ERR = new LogLevel("error");
+
+ /** WARN loglevel constant. */
+ public static final LogLevel WARN = new LogLevel("warn");
+
+ /** INFO loglevel constant. */
+ public static final LogLevel INFO = new LogLevel("info");
+
+ /** VERBOSE loglevel constant. */
+ public static final LogLevel VERBOSE = new LogLevel("verbose");
+
+ /** DEBUG loglevel constant. */
+ public static final LogLevel DEBUG = new LogLevel("debug");
+
+ /**
+ * Public constructor.
+ */
+ public LogLevel() {
+ }
+
+ private LogLevel(String value) {
+ this();
+ setValue(value);
+ }
+
+ /**
+ * @see EnumeratedAttribute#getValues
+ * @return the strings allowed for the level attribute
+ */
+ public String[] getValues() {
+ return new String[] {
+ "error",
+ "warn",
+ "warning",
+ "info",
+ "verbose",
+ "debug"};
+ }
+
+ /**
+ * mapping of enumerated values to log levels
+ */
+ private static int[] levels = {
+ Project.MSG_ERR,
+ Project.MSG_WARN,
+ Project.MSG_WARN,
+ Project.MSG_INFO,
+ Project.MSG_VERBOSE,
+ Project.MSG_DEBUG
+ };
+
+ /**
+ * get the level of the echo of the current value
+ * @return the level
+ */
+ public int getLevel() {
+ return levels[getIndex()];
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Mapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Mapper.java
new file mode 100644
index 00000000..941e8dd8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Mapper.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.tools.ant.types;
+
+import java.util.Properties;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.CompositeMapper;
+import org.apache.tools.ant.util.ContainerMapper;
+import org.apache.tools.ant.util.FileNameMapper;
+
+/**
+ * Element to define a FileNameMapper.
+ *
+ */
+public class Mapper extends DataType implements Cloneable {
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ protected MapperType type = null;
+ protected String classname = null;
+ protected Path classpath = null;
+ protected String from = null;
+ protected String to = null;
+
+ // CheckStyle:VisibilityModifier ON
+
+ private ContainerMapper container = null;
+
+ /**
+ * Construct a new <code>Mapper</code> element.
+ * @param p the owning Ant <code>Project</code>.
+ */
+ public Mapper(Project p) {
+ setProject(p);
+ }
+
+ /**
+ * Set the type of <code>FileNameMapper</code> to use.
+ * @param type the <code>MapperType</code> enumerated attribute.
+ */
+ public void setType(MapperType type) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.type = type;
+ }
+
+ /**
+ * Cannot mix add and addconfigured in same type, so
+ * provide this to override the add method.
+ * @param fileNameMapper the <code>FileNameMapper</code> to add.
+ */
+ public void addConfigured(FileNameMapper fileNameMapper) {
+ add(fileNameMapper);
+ }
+
+ /**
+ * Add a nested <code>FileNameMapper</code>.
+ * @param fileNameMapper the <code>FileNameMapper</code> to add.
+ */
+ public void add(FileNameMapper fileNameMapper) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (container == null) {
+ if (type == null && classname == null) {
+ container = new CompositeMapper();
+ } else {
+ FileNameMapper m = getImplementation();
+ if (m instanceof ContainerMapper) {
+ container = (ContainerMapper) m;
+ } else {
+ throw new BuildException(String.valueOf(m)
+ + " mapper implementation does not support nested mappers!");
+ }
+ }
+ }
+ container.add(fileNameMapper);
+ setChecked(false);
+ }
+
+ /**
+ * Add a Mapper
+ * @param mapper the mapper to add
+ */
+ public void addConfiguredMapper(Mapper mapper) {
+ add(mapper.getImplementation());
+ }
+
+ /**
+ * Set the class name of the FileNameMapper to use.
+ * @param classname the name of the class
+ */
+ public void setClassname(String classname) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.classname = classname;
+ }
+
+ /**
+ * Set the classpath to load the FileNameMapper through (attribute).
+ * @param classpath the classpath
+ */
+ public void setClasspath(Path classpath) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (this.classpath == null) {
+ this.classpath = classpath;
+ } else {
+ this.classpath.append(classpath);
+ }
+ }
+
+ /**
+ * Set the classpath to load the FileNameMapper through (nested element).
+ * @return a path object to be configured
+ */
+ public Path createClasspath() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (this.classpath == null) {
+ this.classpath = new Path(getProject());
+ }
+ setChecked(false);
+ return this.classpath.createPath();
+ }
+
+ /**
+ * Set the classpath to load the FileNameMapper through via
+ * reference (attribute).
+ * @param ref the reference to the FileNameMapper
+ */
+ public void setClasspathRef(Reference ref) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ createClasspath().setRefid(ref);
+ }
+
+ /**
+ * Set the argument to FileNameMapper.setFrom
+ * @param from the from attribute to pass to the FileNameMapper
+ */
+ public void setFrom(String from) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.from = from;
+ }
+
+ /**
+ * Set the argument to FileNameMapper.setTo
+ * @param to the to attribute to pass to the FileNameMapper
+ */
+ public void setTo(String to) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.to = to;
+ }
+
+ /**
+ * Make this Mapper instance a reference to another Mapper.
+ *
+ * <p>You must not set any other attribute if you make it a
+ * reference.</p>
+ * @param r the reference to another mapper
+ * @throws BuildException if other attributes are set
+ */
+ public void setRefid(Reference r) throws BuildException {
+ if (type != null || from != null || to != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Returns a fully configured FileNameMapper implementation.
+ * @return a FileNameMapper object to be configured
+ * @throws BuildException on error
+ */
+ public FileNameMapper getImplementation() throws BuildException {
+ if (isReference()) {
+ dieOnCircularReference();
+ Reference r = getRefid();
+ Object o = r.getReferencedObject(getProject());
+ if (o instanceof FileNameMapper) {
+ return (FileNameMapper) o;
+ }
+ if (o instanceof Mapper) {
+ return ((Mapper) o).getImplementation();
+ }
+ String od = o == null ? "null" : o.getClass().getName();
+ throw new BuildException(od + " at reference '"
+ + r.getRefId() + "' is not a valid mapper reference.");
+ }
+
+ if (type == null && classname == null && container == null) {
+ throw new BuildException(
+ "nested mapper or "
+ + "one of the attributes type or classname is required");
+ }
+
+ if (container != null) {
+ return container;
+ }
+
+ if (type != null && classname != null) {
+ throw new BuildException(
+ "must not specify both type and classname attribute");
+ }
+
+ try {
+ FileNameMapper m = getImplementationClass().newInstance();
+ final Project p = getProject();
+ if (p != null) {
+ p.setProjectReference(m);
+ }
+ m.setFrom(from);
+ m.setTo(to);
+
+ return m;
+ } catch (BuildException be) {
+ throw be;
+ } catch (Throwable t) {
+ throw new BuildException(t);
+ }
+ }
+
+ /**
+ * Gets the Class object associated with the mapper implementation.
+ * @return <code>Class</code>.
+ * @throws ClassNotFoundException if the class cannot be found
+ */
+ protected Class<? extends FileNameMapper> getImplementationClass() throws ClassNotFoundException {
+
+ String cName = this.classname;
+ if (type != null) {
+ cName = type.getImplementation();
+ }
+
+ ClassLoader loader = (classpath == null)
+ ? getClass().getClassLoader()
+ // Memory leak in line below
+ : getProject().createClassLoader(classpath);
+
+ return Class.forName(cName, true, loader).asSubclass(FileNameMapper.class);
+ }
+
+ /**
+ * Performs the check for circular references and returns the
+ * referenced Mapper.
+ * @deprecated since Ant 1.7.1 because a mapper might ref a
+ * FileNameMapper implementation directly.
+ * @return the referenced Mapper
+ */
+ protected Mapper getRef() {
+ return getCheckedRef(Mapper.class, getDataTypeName());
+ }
+
+ /**
+ * Class as Argument to FileNameMapper.setType.
+ */
+ public static class MapperType extends EnumeratedAttribute {
+ private Properties implementations;
+
+ /** Constructor for the MapperType enumeration */
+ public MapperType() {
+ implementations = new Properties();
+ implementations.put("identity",
+ "org.apache.tools.ant.util.IdentityMapper");
+ implementations.put("flatten",
+ "org.apache.tools.ant.util.FlatFileNameMapper");
+ implementations.put("glob",
+ "org.apache.tools.ant.util.GlobPatternMapper");
+ implementations.put("merge",
+ "org.apache.tools.ant.util.MergingMapper");
+ implementations.put("regexp",
+ "org.apache.tools.ant.util.RegexpPatternMapper");
+ implementations.put("package",
+ "org.apache.tools.ant.util.PackageNameMapper");
+ implementations.put("unpackage",
+ "org.apache.tools.ant.util.UnPackageNameMapper");
+ }
+
+ /**
+ * @return the filenamemapper names
+ */
+ public String[] getValues() {
+ return new String[] {"identity", "flatten", "glob",
+ "merge", "regexp", "package", "unpackage"};
+ }
+
+ /**
+ * @return the classname for the filenamemapper name
+ */
+ public String getImplementation() {
+ return implementations.getProperty(getValue());
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Parameter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Parameter.java
new file mode 100644
index 00000000..6c16d2d9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Parameter.java
@@ -0,0 +1,82 @@
+/*
+ * 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.tools.ant.types;
+
+/**
+ * A parameter is composed of a name, type and value.
+ *
+ */
+public final class Parameter {
+ private String name = null;
+ private String type = null;
+ private String value = null;
+
+ /**
+ * Set the name attribute.
+ *
+ * @param name a <code>String</code> value
+ */
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Set the type attribute.
+ *
+ * @param type a <code>String</code> value
+ */
+ public void setType(final String type) {
+ this.type = type;
+ }
+
+ /**
+ * Set the value attribute.
+ *
+ * @param value a <code>String</code> value
+ */
+ public void setValue(final String value) {
+ this.value = value;
+ }
+
+ /**
+ * Get the name attribute.
+ *
+ * @return a <code>String</code> value
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the type attribute.
+ *
+ * @return a <code>String</code> value
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Get the value attribute.
+ *
+ * @return a <code>String</code> value
+ */
+ public String getValue() {
+ return value;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Parameterizable.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Parameterizable.java
new file mode 100644
index 00000000..7945a9a6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Parameterizable.java
@@ -0,0 +1,31 @@
+/*
+ * 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.tools.ant.types;
+
+/**
+ * Parameterizable objects take generic key value pairs.
+ *
+ */
+public interface Parameterizable {
+ /**
+ * Set the parameters
+ *
+ * @param parameters an array of name/type/value parameters.
+ */
+ void setParameters(Parameter[] parameters);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Path.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Path.java
new file mode 100644
index 00000000..db6f5e9f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Path.java
@@ -0,0 +1,775 @@
+/*
+ * 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.tools.ant.types;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.PathTokenizer;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.resources.FileResourceIterator;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * This object represents a path as used by CLASSPATH or PATH
+ * environment variable. A path might also be described as a collection
+ * of unique filesystem resources.
+ * <p>
+ * <code>
+ * &lt;sometask&gt;<br>
+ * &nbsp;&nbsp;&lt;somepath&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file.jar" /&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement
+ * path="/path/to/file2.jar:/path/to/class2;/path/to/class3" /&gt;
+ * <br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file3.jar" /&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file4.jar" /&gt;<br>
+ * &nbsp;&nbsp;&lt;/somepath&gt;<br>
+ * &lt;/sometask&gt;<br>
+ * </code>
+ * <p>
+ * The object implementation <code>sometask</code> must provide a method called
+ * <code>createSomepath</code> which returns an instance of <code>Path</code>.
+ * Nested path definitions are handled by the Path object and must be labeled
+ * <code>pathelement</code>.<p>
+ *
+ * The path element takes a parameter <code>path</code> which will be parsed
+ * and split into single elements. It will usually be used
+ * to define a path from an environment variable.
+ */
+
+public class Path extends DataType implements Cloneable, ResourceCollection {
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /** The system classpath as a Path object */
+ public static Path systemClasspath =
+ new Path(null, System.getProperty("java.class.path"));
+
+
+ /**
+ * The system bootclasspath as a Path object.
+ *
+ * @since Ant 1.6.2
+ */
+ public static Path systemBootClasspath =
+ new Path(null, System.getProperty("sun.boot.class.path"));
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /**
+ * Helper class, holds the nested <code>&lt;pathelement&gt;</code> values.
+ */
+ public class PathElement implements ResourceCollection {
+ private String[] parts;
+
+ /**
+ * Set the location.
+ *
+ * @param loc a <code>File</code> value
+ */
+ public void setLocation(File loc) {
+ parts = new String[] {translateFile(loc.getAbsolutePath())};
+ }
+
+ /**
+ * Set the path.
+ *
+ * @param path a <code>String</code> value
+ */
+ public void setPath(String path) {
+ parts = Path.translatePath(getProject(), path);
+ }
+
+ /**
+ * Return the converted pathelements.
+ *
+ * @return a <code>String[]</code> value
+ */
+ public String[] getParts() {
+ return parts;
+ }
+
+ /**
+ * Create an iterator.
+ * @return an iterator.
+ */
+ public Iterator<Resource> iterator() {
+ return new FileResourceIterator(getProject(), null, parts);
+ }
+
+ /**
+ * Check if this resource is only for filesystems.
+ * @return true.
+ */
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+
+ /**
+ * Get the number of resources.
+ * @return the number of parts.
+ */
+ public int size() {
+ return parts == null ? 0 : parts.length;
+ }
+
+ }
+
+ private Boolean preserveBC;
+
+ private Union union = null;
+ private boolean cache = false;
+
+ /**
+ * Invoked by IntrospectionHelper for <code>setXXX(Path p)</code>
+ * attribute setters.
+ * @param p the <code>Project</code> for this path.
+ * @param path the <code>String</code> path definition.
+ */
+ public Path(Project p, String path) {
+ this(p);
+ createPathElement().setPath(path);
+ }
+
+ /**
+ * Construct an empty <code>Path</code>.
+ * @param project the <code>Project</code> for this path.
+ */
+ public Path(Project project) {
+ setProject(project);
+ }
+
+ /**
+ * Adds a element definition to the path.
+ * @param location the location of the element to add (must not be
+ * <code>null</code> nor empty.
+ * @throws BuildException on error
+ */
+ public void setLocation(File location) throws BuildException {
+ checkAttributesAllowed();
+ createPathElement().setLocation(location);
+ }
+
+ /**
+ * Parses a path definition and creates single PathElements.
+ * @param path the <code>String</code> path definition.
+ * @throws BuildException on error
+ */
+ public void setPath(String path) throws BuildException {
+ checkAttributesAllowed();
+ createPathElement().setPath(path);
+ }
+
+ /**
+ * Makes this instance in effect a reference to another Path instance.
+ *
+ * <p>You must not set another attribute or nest elements inside
+ * this element if you make it a reference.</p>
+ * @param r the reference to another Path
+ * @throws BuildException on error
+ */
+ public void setRefid(Reference r) throws BuildException {
+ if (union != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Creates the nested <code>&lt;pathelement&gt;</code> element.
+ * @return the <code>PathElement</code> to be configured
+ * @throws BuildException on error
+ */
+ public PathElement createPathElement() throws BuildException {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ PathElement pe = new PathElement();
+ add(pe);
+ return pe;
+ }
+
+ /**
+ * Adds a nested <code>&lt;fileset&gt;</code> element.
+ * @param fs a <code>FileSet</code> to be added to the path
+ * @throws BuildException on error
+ */
+ public void addFileset(FileSet fs) throws BuildException {
+ if (fs.getProject() == null) {
+ fs.setProject(getProject());
+ }
+ add(fs);
+ }
+
+ /**
+ * Adds a nested <code>&lt;filelist&gt;</code> element.
+ * @param fl a <code>FileList</code> to be added to the path
+ * @throws BuildException on error
+ */
+ public void addFilelist(FileList fl) throws BuildException {
+ if (fl.getProject() == null) {
+ fl.setProject(getProject());
+ }
+ add(fl);
+ }
+
+ /**
+ * Adds a nested <code>&lt;dirset&gt;</code> element.
+ * @param dset a <code>DirSet</code> to be added to the path
+ * @throws BuildException on error
+ */
+ public void addDirset(DirSet dset) throws BuildException {
+ if (dset.getProject() == null) {
+ dset.setProject(getProject());
+ }
+ add(dset);
+ }
+
+ /**
+ * Adds a nested path
+ * @param path a <code>Path</code> to be added to the path
+ * @throws BuildException on error
+ * @since Ant 1.6
+ */
+ public void add(Path path) throws BuildException {
+ if (path == this) {
+ throw circularReference();
+ }
+ if (path.getProject() == null) {
+ path.setProject(getProject());
+ }
+ add((ResourceCollection) path);
+ }
+
+ /**
+ * Add a nested <code>ResourceCollection</code>.
+ * @param c the ResourceCollection to add.
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection c) {
+ checkChildrenAllowed();
+ if (c == null) {
+ return;
+ }
+ if (union == null) {
+ union = new Union();
+ union.setProject(getProject());
+ union.setCache(cache);
+ }
+ union.add(c);
+ setChecked(false);
+ }
+
+ /**
+ * Creates a nested <code>&lt;path&gt;</code> element.
+ * @return a <code>Path</code> to be configured
+ * @throws BuildException on error
+ */
+ public Path createPath() throws BuildException {
+ Path p = new Path(getProject());
+ add(p);
+ return p;
+ }
+
+ /**
+ * Append the contents of the other Path instance to this.
+ * @param other a <code>Path</code> to be added to the path
+ */
+ public void append(Path other) {
+ if (other == null) {
+ return;
+ }
+ add(other);
+ }
+
+ /**
+ * Adds the components on the given path which exist to this
+ * Path. Components that don't exist aren't added.
+ *
+ * @param source - source path whose components are examined for existence
+ */
+ public void addExisting(Path source) {
+ addExisting(source, false);
+ }
+
+ /**
+ * Same as addExisting, but support classpath behavior if tryUserDir
+ * is true. Classpaths are relative to user dir, not the project base.
+ * That used to break jspc test
+ *
+ * @param source the source path
+ * @param tryUserDir if true try the user directory if the file is not present
+ */
+ public void addExisting(Path source, boolean tryUserDir) {
+ String[] list = source.list();
+ File userDir = (tryUserDir) ? new File(System.getProperty("user.dir"))
+ : null;
+
+ for (int i = 0; i < list.length; i++) {
+ File f = resolveFile(getProject(), list[i]);
+
+ // probably not the best choice, but it solves the problem of
+ // relative paths in CLASSPATH
+ if (tryUserDir && !f.exists()) {
+ f = new File(userDir, list[i]);
+ }
+ if (f.exists()) {
+ setLocation(f);
+ } else if (f.getParentFile() != null && f.getParentFile().exists()
+ && containsWildcards(f.getName())) {
+ setLocation(f);
+ log("adding " + f + " which contains wildcards and may not"
+ + " do what you intend it to do depending on your OS or"
+ + " version of Java", Project.MSG_VERBOSE);
+ } else {
+ log("dropping " + f + " from path as it doesn't exist",
+ Project.MSG_VERBOSE);
+ }
+ }
+ }
+
+ /**
+ * Whether to cache the current path.
+ * @since Ant 1.8.0
+ */
+ public void setCache(boolean b) {
+ checkAttributesAllowed();
+ cache = b;
+ if (union != null) {
+ union.setCache(b);
+ }
+ }
+
+ /**
+ * Returns all path elements defined by this and nested path objects.
+ * @return list of path elements.
+ */
+ public String[] list() {
+ if (isReference()) {
+ return ((Path) getCheckedRef()).list();
+ }
+ return assertFilesystemOnly(union) == null
+ ? new String[0] : union.list();
+ }
+
+ /**
+ * Returns a textual representation of the path, which can be used as
+ * CLASSPATH or PATH environment variable definition.
+ * @return a textual representation of the path.
+ */
+ public String toString() {
+ return isReference() ? getCheckedRef().toString()
+ : union == null ? "" : union.toString();
+ }
+
+ /**
+ * Splits a PATH (with : or ; as separators) into its parts.
+ * @param project the project to use
+ * @param source a <code>String</code> value
+ * @return an array of strings, one for each path element
+ */
+ public static String[] translatePath(Project project, String source) {
+ final Vector<String> result = new Vector<String>();
+ if (source == null) {
+ return new String[0];
+ }
+ PathTokenizer tok = new PathTokenizer(source);
+ StringBuffer element = new StringBuffer();
+ while (tok.hasMoreTokens()) {
+ String pathElement = tok.nextToken();
+ try {
+ element.append(resolveFile(project, pathElement).getPath());
+ } catch (BuildException e) {
+ project.log("Dropping path element " + pathElement
+ + " as it is not valid relative to the project",
+ Project.MSG_VERBOSE);
+ }
+ for (int i = 0; i < element.length(); i++) {
+ translateFileSep(element, i);
+ }
+ result.addElement(element.toString());
+ element = new StringBuffer();
+ }
+ return result.toArray(new String[result.size()]);
+ }
+
+ /**
+ * Returns its argument with all file separator characters
+ * replaced so that they match the local OS conventions.
+ * @param source the path to convert
+ * @return the converted path
+ */
+ public static String translateFile(String source) {
+ if (source == null) {
+ return "";
+ }
+ final StringBuffer result = new StringBuffer(source);
+ for (int i = 0; i < result.length(); i++) {
+ translateFileSep(result, i);
+ }
+ return result.toString();
+ }
+
+ /**
+ * Translates occurrences at a position of / or \ to correct separator of the
+ * current platform and returns whether it had to do a
+ * replacement.
+ * @param buffer a buffer containing a string
+ * @param pos the position in the string buffer to convert
+ * @return true if the character was a / or \
+ */
+ protected static boolean translateFileSep(StringBuffer buffer, int pos) {
+ if (buffer.charAt(pos) == '/' || buffer.charAt(pos) == '\\') {
+ buffer.setCharAt(pos, File.separatorChar);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ */
+ public synchronized int size() {
+ if (isReference()) {
+ return ((Path) getCheckedRef()).size();
+ }
+ dieOnCircularReference();
+ return union == null ? 0 : assertFilesystemOnly(union).size();
+ }
+
+ /**
+ * Clone this Path.
+ * @return Path with shallowly cloned Resource children.
+ */
+ public Object clone() {
+ try {
+ Path result = (Path) super.clone();
+ result.union = union == null ? union : (Union) union.clone();
+ return result;
+ } catch (CloneNotSupportedException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Overrides the version of DataType to recurse on all DataType
+ * child elements that may have been added.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ if (union != null) {
+ pushAndInvokeCircularReferenceCheck(union, stk, p);
+ }
+ setChecked(true);
+ }
+ }
+
+ /**
+ * Resolve a filename with Project's help - if we know one that is.
+ */
+ private static File resolveFile(Project project, String relativeName) {
+ return FileUtils.getFileUtils().resolveFile(
+ (project == null) ? null : project.getBaseDir(), relativeName);
+ }
+
+ /**
+ * Concatenates the system class path in the order specified by
+ * the ${build.sysclasspath} property - using &quot;last&quot; as
+ * default value.
+ * @return the concatenated path
+ */
+ public Path concatSystemClasspath() {
+ return concatSystemClasspath("last");
+ }
+
+ /**
+ * Concatenates the system class path in the order specified by
+ * the ${build.sysclasspath} property - using the supplied value
+ * if ${build.sysclasspath} has not been set.
+ * @param defValue the order ("first", "last", "only")
+ * @return the concatenated path
+ */
+ public Path concatSystemClasspath(String defValue) {
+ return concatSpecialPath(defValue, Path.systemClasspath);
+ }
+
+ /**
+ * Concatenates the system boot class path in the order specified
+ * by the ${build.sysclasspath} property - using the supplied
+ * value if ${build.sysclasspath} has not been set.
+ * @param defValue the order ("first", "last", "only")
+ * @return the concatenated path
+ */
+ public Path concatSystemBootClasspath(String defValue) {
+ return concatSpecialPath(defValue, Path.systemBootClasspath);
+ }
+
+ /**
+ * Concatenates a class path in the order specified by the
+ * ${build.sysclasspath} property - using the supplied value if
+ * ${build.sysclasspath} has not been set.
+ */
+ private Path concatSpecialPath(String defValue, Path p) {
+ Path result = new Path(getProject());
+
+ String order = defValue;
+ String o = getProject() != null
+ ? getProject().getProperty(MagicNames.BUILD_SYSCLASSPATH)
+ : System.getProperty(MagicNames.BUILD_SYSCLASSPATH);
+ if (o != null) {
+ order = o;
+ }
+ if (order.equals("only")) {
+ // only: the developer knows what (s)he is doing
+ result.addExisting(p, true);
+
+ } else if (order.equals("first")) {
+ // first: developer could use a little help
+ result.addExisting(p, true);
+ result.addExisting(this);
+
+ } else if (order.equals("ignore")) {
+ // ignore: don't trust anyone
+ result.addExisting(this);
+
+ } else {
+ // last: don't trust the developer
+ if (!order.equals("last")) {
+ log("invalid value for " + MagicNames.BUILD_SYSCLASSPATH
+ + ": " + order,
+ Project.MSG_WARN);
+ }
+ result.addExisting(this);
+ result.addExisting(p, true);
+ }
+ return result;
+ }
+
+ /**
+ * Add the Java Runtime classes to this Path instance.
+ */
+ public void addJavaRuntime() {
+ if (JavaEnvUtils.isKaffe()) {
+ // newer versions of Kaffe (1.1.1+) won't have this,
+ // but this will be sorted by FileSet anyway.
+ File kaffeShare = new File(System.getProperty("java.home")
+ + File.separator + "share"
+ + File.separator + "kaffe");
+ if (kaffeShare.isDirectory()) {
+ FileSet kaffeJarFiles = new FileSet();
+ kaffeJarFiles.setDir(kaffeShare);
+ kaffeJarFiles.setIncludes("*.jar");
+ addFileset(kaffeJarFiles);
+ }
+ } else if ("GNU libgcj".equals(System.getProperty("java.vm.name"))) {
+ addExisting(systemBootClasspath);
+ }
+
+ if (System.getProperty("java.vendor").toLowerCase(Locale.ENGLISH).indexOf("microsoft") >= 0) {
+ // TODO is this code still necessary? is there any 1.2+ port?
+ // Pull in *.zip from packages directory
+ FileSet msZipFiles = new FileSet();
+ msZipFiles.setDir(new File(System.getProperty("java.home")
+ + File.separator + "Packages"));
+ msZipFiles.setIncludes("*.ZIP");
+ addFileset(msZipFiles);
+ } else {
+ // JDK 1.2+ seems to set java.home to the JRE directory.
+ addExisting(new Path(null,
+ System.getProperty("java.home")
+ + File.separator + "lib"
+ + File.separator + "rt.jar"));
+ // Just keep the old version as well and let addExisting
+ // sort it out.
+ addExisting(new Path(null,
+ System.getProperty("java.home")
+ + File.separator + "jre"
+ + File.separator + "lib"
+ + File.separator + "rt.jar"));
+
+ // Sun's and Apple's 1.4 have JCE and JSSE in separate jars.
+ String[] secJars = {"jce", "jsse"};
+ for (int i = 0; i < secJars.length; i++) {
+ addExisting(new Path(null,
+ System.getProperty("java.home")
+ + File.separator + "lib"
+ + File.separator + secJars[i] + ".jar"));
+ addExisting(new Path(null,
+ System.getProperty("java.home")
+ + File.separator + ".."
+ + File.separator + "Classes"
+ + File.separator + secJars[i] + ".jar"));
+ }
+
+ // IBM's 1.4 has rt.jar split into 4 smaller jars and a combined
+ // JCE/JSSE in security.jar.
+ String[] ibmJars
+ = {"core", "graphics", "security", "server", "xml"};
+ for (int i = 0; i < ibmJars.length; i++) {
+ addExisting(new Path(null,
+ System.getProperty("java.home")
+ + File.separator + "lib"
+ + File.separator + ibmJars[i] + ".jar"));
+ }
+
+ // Added for MacOS X
+ addExisting(new Path(null,
+ System.getProperty("java.home")
+ + File.separator + ".."
+ + File.separator + "Classes"
+ + File.separator + "classes.jar"));
+ addExisting(new Path(null,
+ System.getProperty("java.home")
+ + File.separator + ".."
+ + File.separator + "Classes"
+ + File.separator + "ui.jar"));
+ }
+ }
+
+ /**
+ * Emulation of extdirs feature in java >= 1.2.
+ * This method adds all files in the given
+ * directories (but not in sub-directories!) to the classpath,
+ * so that you don't have to specify them all one by one.
+ * @param extdirs - Path to append files to
+ */
+ public void addExtdirs(Path extdirs) {
+ if (extdirs == null) {
+ String extProp = System.getProperty("java.ext.dirs");
+ if (extProp != null) {
+ extdirs = new Path(getProject(), extProp);
+ } else {
+ return;
+ }
+ }
+
+ String[] dirs = extdirs.list();
+ for (int i = 0; i < dirs.length; i++) {
+ File dir = resolveFile(getProject(), dirs[i]);
+ if (dir.exists() && dir.isDirectory()) {
+ FileSet fs = new FileSet();
+ fs.setDir(dir);
+ fs.setIncludes("*");
+ addFileset(fs);
+ }
+ }
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract. The Iterator returned
+ * will throw ConcurrentModificationExceptions if ResourceCollections
+ * are added to this container while the Iterator is in use.
+ * @return a "fail-fast" Iterator.
+ */
+ public final synchronized Iterator<Resource> iterator() {
+ if (isReference()) {
+ return ((Path) getCheckedRef()).iterator();
+ }
+ dieOnCircularReference();
+ if (getPreserveBC()) {
+ return new FileResourceIterator(getProject(), null, list());
+ }
+ return union == null ? Collections.<Resource> emptySet().iterator()
+ : assertFilesystemOnly(union).iterator();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return whether this is a filesystem-only resource collection.
+ */
+ public synchronized boolean isFilesystemOnly() {
+ if (isReference()) {
+ return ((Path) getCheckedRef()).isFilesystemOnly();
+ }
+ dieOnCircularReference();
+ assertFilesystemOnly(union);
+ return true;
+ }
+
+ /**
+ * Verify the specified ResourceCollection is filesystem-only.
+ * @param rc the ResourceCollection to check.
+ * @throws BuildException if <code>rc</code> is not filesystem-only.
+ * @return the passed in ResourceCollection.
+ */
+ protected ResourceCollection assertFilesystemOnly(ResourceCollection rc) {
+ if (rc != null && !(rc.isFilesystemOnly())) {
+ throw new BuildException(getDataTypeName()
+ + " allows only filesystem resources.");
+ }
+ return rc;
+ }
+
+ /**
+ * Helps determine whether to preserve BC by calling <code>list()</code> on subclasses.
+ * The default behavior of this method is to return <code>true</code> for any subclass
+ * that implements <code>list()</code>; this can, of course, be avoided by overriding
+ * this method to return <code>false</code>. It is not expected that the result of this
+ * method should change over time, thus it is called only once.
+ * @return <code>true</code> if <code>iterator()</code> should delegate to <code>list()</code>.
+ */
+ protected boolean delegateIteratorToList() {
+ if (getClass().equals(Path.class)) {
+ return false;
+ }
+ try {
+ Method listMethod = getClass().getMethod("list", (Class[]) null);
+ return !listMethod.getDeclaringClass().equals(Path.class);
+ } catch (Exception e) {
+ //shouldn't happen, but
+ return false;
+ }
+ }
+
+ private synchronized boolean getPreserveBC() {
+ if (preserveBC == null) {
+ preserveBC = delegateIteratorToList() ? Boolean.TRUE : Boolean.FALSE;
+ }
+ return preserveBC.booleanValue();
+ }
+
+ /**
+ * Does the given file name contain wildcards?
+ * @since Ant 1.8.2
+ */
+ private static boolean containsWildcards(String path) {
+ return path != null
+ && (path.indexOf("*") > -1 || path.indexOf("?") > -1);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/PatternSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/PatternSet.java
new file mode 100644
index 00000000..9fb94050
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/PatternSet.java
@@ -0,0 +1,541 @@
+/*
+ * 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.tools.ant.types;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Named collection of include/exclude tags.
+ *
+ * <p>Moved out of MatchingTask to make it a standalone object that
+ * could be referenced (by scripts for example).
+ *
+ */
+public class PatternSet extends DataType implements Cloneable {
+ private List<NameEntry> includeList = new ArrayList<NameEntry>();
+ private List<NameEntry> excludeList = new ArrayList<NameEntry>();
+ private List<NameEntry> includesFileList = new ArrayList<NameEntry>();
+ private List<NameEntry> excludesFileList = new ArrayList<NameEntry>();
+
+ /**
+ * inner class to hold a name on list. "If" and "Unless" attributes
+ * may be used to invalidate the entry based on the existence of a
+ * property (typically set thru the use of the Available task)
+ * or value of an expression.
+ */
+ public class NameEntry {
+ private String name;
+ private Object ifCond;
+ private Object unlessCond;
+
+ /**
+ * Sets the name pattern.
+ *
+ * @param name The pattern string.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Sets the if attribute. This attribute and the "unless"
+ * attribute are used to validate the name, based on the
+ * existence of the property or the value of the evaluated
+ * property expression.
+ *
+ * @param cond A property name or expression. If the
+ * expression evaluates to false or no property of
+ * its value is present, the name is invalid.
+ * @since Ant 1.8.0
+ */
+ public void setIf(Object cond) {
+ ifCond = cond;
+ }
+
+ /**
+ * Sets the if attribute. This attribute and the "unless"
+ * attribute are used to validate the name, based on the
+ * existence of the property or the value of the evaluated
+ * property expression.
+ *
+ * @param cond A property name or expression. If the
+ * expression evaluates to false or no property of
+ * its value is present, the name is invalid.
+ */
+ public void setIf(String cond) {
+ setIf((Object) cond);
+ }
+
+ /**
+ * Sets the unless attribute. This attribute and the "if"
+ * attribute are used to validate the name, based on the
+ * existence of the property or the value of the evaluated
+ * property expression.
+ *
+ * @param cond A property name or expression. If the
+ * expression evaluates to true or a property of
+ * its value is present, the name is invalid.
+ * @since Ant 1.8.0
+ */
+ public void setUnless(Object cond) {
+ unlessCond = cond;
+ }
+
+ /**
+ * Sets the unless attribute. This attribute and the "if"
+ * attribute are used to validate the name, based on the
+ * existence of the property or the value of the evaluated
+ * property expression.
+ *
+ * @param cond A property name or expression. If the
+ * expression evaluates to true or a property of
+ * its value is present, the name is invalid.
+ */
+ public void setUnless(String cond) {
+ setUnless((Object) cond);
+ }
+
+ /**
+ * @return the name attribute.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * This validates the name - checks the if and unless
+ * properties.
+ *
+ * @param p the current project, used to check the presence or
+ * absence of a property.
+ * @return the name attribute or null if the "if" or "unless"
+ * properties are not/are set.
+ */
+ public String evalName(Project p) {
+ return valid(p) ? name : null;
+ }
+
+ private boolean valid(Project p) {
+ PropertyHelper ph = PropertyHelper.getPropertyHelper(p);
+ return ph.testIfCondition(ifCond)
+ && ph.testUnlessCondition(unlessCond);
+ }
+
+ /**
+ * @return a printable form of this object.
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ if (name == null) {
+ buf.append("noname");
+ } else {
+ buf.append(name);
+ }
+ if ((ifCond != null) || (unlessCond != null)) {
+ buf.append(":");
+ String connector = "";
+
+ if (ifCond != null) {
+ buf.append("if->");
+ buf.append(ifCond);
+ connector = ";";
+ }
+ if (unlessCond != null) {
+ buf.append(connector);
+ buf.append("unless->");
+ buf.append(unlessCond);
+ }
+ }
+ return buf.toString();
+ }
+ }
+
+ private static final class InvertedPatternSet extends PatternSet {
+ private InvertedPatternSet(PatternSet p) {
+ setProject(p.getProject());
+ addConfiguredPatternset(p);
+ }
+ public String[] getIncludePatterns(Project p) {
+ return super.getExcludePatterns(p);
+ }
+ public String[] getExcludePatterns(Project p) {
+ return super.getIncludePatterns(p);
+ }
+ }
+
+ /**
+ * Creates a new <code>PatternSet</code> instance.
+ */
+ public PatternSet() {
+ super();
+ }
+
+ /**
+ * Makes this instance in effect a reference to another PatternSet
+ * instance.
+ *
+ * <p>You must not set another attribute or nest elements inside
+ * this element if you make it a reference.</p>
+ * @param r the reference to another patternset.
+ * @throws BuildException on error.
+ */
+ public void setRefid(Reference r) throws BuildException {
+ if (!includeList.isEmpty() || !excludeList.isEmpty()) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * This is a patternset nested element.
+ *
+ * @param p a configured patternset nested element.
+ */
+ public void addConfiguredPatternset(PatternSet p) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ String[] nestedIncludes = p.getIncludePatterns(getProject());
+ String[] nestedExcludes = p.getExcludePatterns(getProject());
+
+ if (nestedIncludes != null) {
+ for (int i = 0; i < nestedIncludes.length; i++) {
+ createInclude().setName(nestedIncludes[i]);
+ }
+ }
+ if (nestedExcludes != null) {
+ for (int i = 0; i < nestedExcludes.length; i++) {
+ createExclude().setName(nestedExcludes[i]);
+ }
+ }
+ }
+
+ /**
+ * add a name entry on the include list
+ * @return a nested include element to be configured.
+ */
+ public NameEntry createInclude() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ return addPatternToList(includeList);
+ }
+
+ /**
+ * add a name entry on the include files list
+ * @return a nested includesfile element to be configured.
+ */
+ public NameEntry createIncludesFile() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ return addPatternToList(includesFileList);
+ }
+
+ /**
+ * add a name entry on the exclude list
+ * @return a nested exclude element to be configured.
+ */
+ public NameEntry createExclude() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ return addPatternToList(excludeList);
+ }
+
+ /**
+ * add a name entry on the exclude files list
+ * @return a nested excludesfile element to be configured.
+ */
+ public NameEntry createExcludesFile() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ return addPatternToList(excludesFileList);
+ }
+
+ /**
+ * Appends <code>includes</code> to the current list of include patterns.
+ * Patterns may be separated by a comma or a space.
+ *
+ * @param includes the string containing the include patterns
+ */
+ public void setIncludes(String includes) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (includes != null && includes.length() > 0) {
+ StringTokenizer tok = new StringTokenizer(includes, ", ", false);
+ while (tok.hasMoreTokens()) {
+ createInclude().setName(tok.nextToken());
+ }
+ }
+ }
+
+ /**
+ * Appends <code>excludes</code> to the current list of exclude patterns.
+ * Patterns may be separated by a comma or a space.
+ *
+ * @param excludes the string containing the exclude patterns
+ */
+ public void setExcludes(String excludes) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (excludes != null && excludes.length() > 0) {
+ StringTokenizer tok = new StringTokenizer(excludes, ", ", false);
+ while (tok.hasMoreTokens()) {
+ createExclude().setName(tok.nextToken());
+ }
+ }
+ }
+
+ /**
+ * add a name entry to the given list
+ */
+ private NameEntry addPatternToList(List<NameEntry> list) {
+ NameEntry result = new NameEntry();
+ list.add(result);
+ return result;
+ }
+
+ /**
+ * Sets the name of the file containing the includes patterns.
+ *
+ * @param includesFile The file to fetch the include patterns from.
+ * @throws BuildException on error.
+ */
+ public void setIncludesfile(File includesFile) throws BuildException {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ createIncludesFile().setName(includesFile.getAbsolutePath());
+ }
+
+ /**
+ * Sets the name of the file containing the excludes patterns.
+ *
+ * @param excludesFile The file to fetch the exclude patterns from.
+ * @throws BuildException on error.
+ */
+ public void setExcludesfile(File excludesFile) throws BuildException {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ createExcludesFile().setName(excludesFile.getAbsolutePath());
+ }
+
+ /**
+ * Reads path matching patterns from a file and adds them to the
+ * includes or excludes list (as appropriate).
+ */
+ private void readPatterns(File patternfile, List<NameEntry> patternlist, Project p)
+ throws BuildException {
+
+ BufferedReader patternReader = null;
+ try {
+ // Get a FileReader
+ patternReader = new BufferedReader(new FileReader(patternfile));
+
+ // Create one NameEntry in the appropriate pattern list for each
+ // line in the file.
+ String line = patternReader.readLine();
+ while (line != null) {
+ if (line.length() > 0) {
+ line = p.replaceProperties(line);
+ addPatternToList(patternlist).setName(line);
+ }
+ line = patternReader.readLine();
+ }
+ } catch (IOException ioe) {
+ throw new BuildException("An error occurred while reading from pattern file: "
+ + patternfile, ioe);
+ } finally {
+ FileUtils.close(patternReader);
+ }
+ }
+
+ /**
+ * Adds the patterns of the other instance to this set.
+ * @param other the other PatternSet instance.
+ * @param p the current project.
+ */
+ public void append(PatternSet other, Project p) {
+ if (isReference()) {
+ throw new BuildException("Cannot append to a reference");
+ }
+ dieOnCircularReference(p);
+ String[] incl = other.getIncludePatterns(p);
+ if (incl != null) {
+ for (int i = 0; i < incl.length; i++) {
+ createInclude().setName(incl[i]);
+ }
+ }
+ String[] excl = other.getExcludePatterns(p);
+ if (excl != null) {
+ for (int i = 0; i < excl.length; i++) {
+ createExclude().setName(excl[i]);
+ }
+ }
+ }
+
+ /**
+ * Returns the filtered include patterns.
+ * @param p the current project.
+ * @return the filtered included patterns.
+ */
+ public String[] getIncludePatterns(Project p) {
+ if (isReference()) {
+ return getRef(p).getIncludePatterns(p);
+ }
+ dieOnCircularReference(p);
+ readFiles(p);
+ return makeArray(includeList, p);
+ }
+
+ /**
+ * Returns the filtered include patterns.
+ * @param p the current project.
+ * @return the filtered excluded patterns.
+ */
+ public String[] getExcludePatterns(Project p) {
+ if (isReference()) {
+ return getRef(p).getExcludePatterns(p);
+ }
+ dieOnCircularReference(p);
+ readFiles(p);
+ return makeArray(excludeList, p);
+ }
+
+ /**
+ * Helper for FileSet classes.
+ * Check if there are patterns defined.
+ * @param p the current project.
+ * @return true if there are patterns.
+ */
+ public boolean hasPatterns(Project p) {
+ if (isReference()) {
+ return getRef(p).hasPatterns(p);
+ }
+ dieOnCircularReference(p);
+ return includesFileList.size() > 0 || excludesFileList.size() > 0
+ || includeList.size() > 0 || excludeList.size() > 0;
+ }
+
+ /**
+ * Performs the check for circular references and returns the
+ * referenced PatternSet.
+ */
+ private PatternSet getRef(Project p) {
+ return (PatternSet) getCheckedRef(p);
+ }
+
+ /**
+ * Convert a vector of NameEntry elements into an array of Strings.
+ */
+ private String[] makeArray(List<NameEntry> list, Project p) {
+ if (list.size() == 0) {
+ return null;
+ }
+ ArrayList<String> tmpNames = new ArrayList<String>();
+ for (NameEntry ne : list) {
+ String pattern = ne.evalName(p);
+ if (pattern != null && pattern.length() > 0) {
+ tmpNames.add(pattern);
+ }
+ }
+ return tmpNames.toArray(new String[tmpNames.size()]);
+ }
+
+ /**
+ * Read includesfile ot excludesfile if not already done so.
+ */
+ private void readFiles(Project p) {
+ if (includesFileList.size() > 0) {
+ for (NameEntry ne : includesFileList) {
+ String fileName = ne.evalName(p);
+ if (fileName != null) {
+ File inclFile = p.resolveFile(fileName);
+ if (!inclFile.exists()) {
+ throw new BuildException("Includesfile " + inclFile.getAbsolutePath()
+ + " not found.");
+ }
+ readPatterns(inclFile, includeList, p);
+ }
+ }
+ includesFileList.clear();
+ }
+ if (excludesFileList.size() > 0) {
+ for (NameEntry ne : excludesFileList) {
+ String fileName = ne.evalName(p);
+ if (fileName != null) {
+ File exclFile = p.resolveFile(fileName);
+ if (!exclFile.exists()) {
+ throw new BuildException("Excludesfile " + exclFile.getAbsolutePath()
+ + " not found.");
+ }
+ readPatterns(exclFile, excludeList, p);
+ }
+ }
+ excludesFileList.clear();
+ }
+ }
+
+ /**
+ * @return a printable form of this object.
+ */
+ public String toString() {
+ return "patternSet{ includes: " + includeList + " excludes: " + excludeList + " }";
+ }
+
+ /**
+ * @since Ant 1.6
+ * @return a clone of this patternset.
+ */
+ public Object clone() {
+ try {
+ PatternSet ps = (PatternSet) super.clone();
+ ps.includeList = new ArrayList<NameEntry>(includeList);
+ ps.excludeList = new ArrayList<NameEntry>(excludeList);
+ ps.includesFileList = new ArrayList<NameEntry>(includesFileList);
+ ps.excludesFileList = new ArrayList<NameEntry>(excludesFileList);
+ return ps;
+ } catch (CloneNotSupportedException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Add an inverted patternset.
+ * @param p the pattern to invert and add.
+ */
+ public void addConfiguredInvert(PatternSet p) {
+ addConfiguredPatternset(new InvertedPatternSet(p));
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Permissions.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Permissions.java
new file mode 100644
index 00000000..96da71bd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Permissions.java
@@ -0,0 +1,356 @@
+/*
+ * 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.tools.ant.types;
+
+import java.lang.reflect.Constructor;
+import java.security.UnresolvedPermission;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ExitException;
+
+/**
+ * This class implements a security manager meant for usage by tasks that run inside the
+ * Ant VM. An examples are the Java Task and JUnitTask.
+ *
+ * The basic functionality is that nothing (except for a base set of permissions) is allowed, unless
+ * the permission is granted either explicitly or implicitly.
+ * If a permission is granted this can be overruled by explicitly revoking the permission.
+ *
+ * It is not permissible to add permissions (either granted or revoked) while the Security Manager
+ * is active (after calling setSecurityManager() but before calling restoreSecurityManager()).
+ *
+ * @since Ant 1.6
+ */
+public class Permissions {
+
+ private final List<Permission> grantedPermissions = new LinkedList<Permission>();
+ private final List<Permission> revokedPermissions = new LinkedList<Permission>();
+ private java.security.Permissions granted = null;
+ private SecurityManager origSm = null;
+ private boolean active = false;
+ private final boolean delegateToOldSM;
+
+ // Mandatory constructor for permission object.
+ private static final Class<?>[] PARAMS = {String.class, String.class};
+
+ /**
+ * Create a set of Permissions. Equivalent to calling
+ * <code>new Permissions(false)</code>.
+ */
+ public Permissions() {
+ this(false);
+ }
+
+ /**
+ * Create a set of permissions.
+ * @param delegateToOldSM if <code>true</code> the old security manager
+ * will be used if the permission has not been explicitly granted or revoked
+ * in this instance.
+ */
+ public Permissions(final boolean delegateToOldSM) {
+ this.delegateToOldSM = delegateToOldSM;
+ }
+
+ /**
+ * Adds a permission to be granted.
+ * @param perm The Permissions.Permission to be granted.
+ */
+ public void addConfiguredGrant(final Permissions.Permission perm) {
+ grantedPermissions.add(perm);
+ }
+
+ /**
+ * Adds a permission to be revoked.
+ * @param perm The Permissions.Permission to be revoked
+ */
+ public void addConfiguredRevoke(final Permissions.Permission perm) {
+ revokedPermissions.add(perm);
+ }
+
+ /**
+ * To be used by tasks wishing to use this security model before executing the part to be
+ * subject to these Permissions. Note that setting the SecurityManager too early may
+ * prevent your part from starting, as for instance changing classloaders may be prohibited.
+ * The classloader for the new situation is supposed to be present.
+ * @throws BuildException on error
+ */
+ public synchronized void setSecurityManager() throws BuildException {
+ origSm = System.getSecurityManager();
+ init();
+ System.setSecurityManager(new MySM());
+ active = true;
+ }
+
+ /**
+ * Initializes the list of granted permissions, checks the list of revoked permissions.
+ */
+ private void init() throws BuildException {
+ granted = new java.security.Permissions();
+ for (final Permissions.Permission p : revokedPermissions) {
+ if (p.getClassName() == null) {
+ throw new BuildException("Revoked permission " + p + " does not contain a class.");
+ }
+ }
+ for (final Permissions.Permission p : grantedPermissions) {
+ if (p.getClassName() == null) {
+ throw new BuildException("Granted permission " + p
+ + " does not contain a class.");
+ } else {
+ final java.security.Permission perm = createPermission(p);
+ granted.add(perm);
+ }
+ }
+ // Add base set of permissions
+ granted.add(new java.net.SocketPermission("localhost:1024-", "listen"));
+ granted.add(new java.util.PropertyPermission("java.version", "read"));
+ granted.add(new java.util.PropertyPermission("java.vendor", "read"));
+ granted.add(new java.util.PropertyPermission("java.vendor.url", "read"));
+ granted.add(new java.util.PropertyPermission("java.class.version", "read"));
+ granted.add(new java.util.PropertyPermission("os.name", "read"));
+ granted.add(new java.util.PropertyPermission("os.version", "read"));
+ granted.add(new java.util.PropertyPermission("os.arch", "read"));
+ granted.add(new java.util.PropertyPermission("file.encoding", "read"));
+ granted.add(new java.util.PropertyPermission("file.separator", "read"));
+ granted.add(new java.util.PropertyPermission("path.separator", "read"));
+ granted.add(new java.util.PropertyPermission("line.separator", "read"));
+ granted.add(new java.util.PropertyPermission("java.specification.version", "read"));
+ granted.add(new java.util.PropertyPermission("java.specification.vendor", "read"));
+ granted.add(new java.util.PropertyPermission("java.specification.name", "read"));
+ granted.add(new java.util.PropertyPermission("java.vm.specification.version", "read"));
+ granted.add(new java.util.PropertyPermission("java.vm.specification.vendor", "read"));
+ granted.add(new java.util.PropertyPermission("java.vm.specification.name", "read"));
+ granted.add(new java.util.PropertyPermission("java.vm.version", "read"));
+ granted.add(new java.util.PropertyPermission("java.vm.vendor", "read"));
+ granted.add(new java.util.PropertyPermission("java.vm.name", "read"));
+ }
+
+ private java.security.Permission createPermission(
+ final Permissions.Permission permission) {
+ try {
+ // First add explicitly already resolved permissions will not be
+ // resolved when added as unresolved permission.
+ final Class<? extends java.security.Permission> clazz = Class.forName(
+ permission.getClassName()).asSubclass(java.security.Permission.class);
+ final String name = permission.getName();
+ final String actions = permission.getActions();
+ final Constructor<? extends java.security.Permission> ctr = clazz.getConstructor(PARAMS);
+ return ctr.newInstance(new Object[] {name, actions});
+ } catch (final Exception e) {
+ // Let the UnresolvedPermission handle it.
+ return new UnresolvedPermission(permission.getClassName(),
+ permission.getName(), permission.getActions(), null);
+ }
+ }
+
+ /**
+ * To be used by tasks that just finished executing the parts subject to these permissions.
+ */
+ public synchronized void restoreSecurityManager() {
+ active = false;
+ System.setSecurityManager(origSm);
+ }
+
+ /**
+ * This inner class implements the actual SecurityManager that can be used by tasks
+ * supporting Permissions.
+ */
+ private class MySM extends SecurityManager {
+
+ /**
+ * Exit is treated in a special way in order to be able to return the exit code
+ * towards tasks.
+ * An ExitException is thrown instead of a simple SecurityException to indicate the exit
+ * code.
+ * Overridden from java.lang.SecurityManager
+ * @param status The exit status requested.
+ */
+ @Override
+ public void checkExit(final int status) {
+ final java.security.Permission perm = new java.lang.RuntimePermission("exitVM", null);
+ try {
+ checkPermission(perm);
+ } catch (final SecurityException e) {
+ throw new ExitException(e.getMessage(), status);
+ }
+ }
+
+ /**
+ * The central point in checking permissions.
+ * Overridden from java.lang.SecurityManager
+ *
+ * @param perm The permission requested.
+ */
+ @Override
+ public void checkPermission(final java.security.Permission perm) {
+ if (active) {
+ if (delegateToOldSM && !perm.getName().equals("exitVM")) {
+ boolean permOK = false;
+ if (granted.implies(perm)) {
+ permOK = true;
+ }
+ checkRevoked(perm);
+ /*
+ if the permission was not explicitly granted or revoked
+ the original security manager will do its work
+ */
+ if (!permOK && origSm != null) {
+ origSm.checkPermission(perm);
+ }
+ } else {
+ if (!granted.implies(perm)) {
+ throw new SecurityException("Permission " + perm + " was not granted.");
+ }
+ checkRevoked(perm);
+ }
+ }
+ }
+
+ /**
+ * throws an exception if this permission is revoked
+ * @param perm the permission being checked
+ */
+ private void checkRevoked(final java.security.Permission perm) {
+ for (final Permissions.Permission revoked : revokedPermissions) {
+ if (revoked.matches(perm)) {
+ throw new SecurityException("Permission " + perm + " was revoked.");
+ }
+ }
+ }
+ }
+
+ /** Represents a permission. */
+ public static class Permission {
+ private String className;
+ private String name;
+ private String actionString;
+ private Set<String> actions;
+
+ /**
+ * Set the class, mandatory.
+ * @param aClass The class name of the permission.
+ */
+ public void setClass(final String aClass) {
+ className = aClass.trim();
+ }
+
+ /**
+ * Get the class of the permission.
+ * @return The class name of the permission.
+ */
+ public String getClassName() {
+ return className;
+ }
+
+ /**
+ * Set the name of the permission.
+ * @param aName The name of the permission.
+ */
+ public void setName(final String aName) {
+ name = aName.trim();
+ }
+
+ /**
+ * Get the name of the permission.
+ * @return The name of the permission.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the actions.
+ * @param actions The actions of the permission.
+ */
+ public void setActions(final String actions) {
+ actionString = actions;
+ if (actions.length() > 0) {
+ this.actions = parseActions(actions);
+ }
+ }
+
+ /**
+ * Get the actions.
+ * @return The actions of the permission.
+ */
+ public String getActions() {
+ return actionString;
+ }
+
+ /**
+ * Learn whether the permission matches in case of a revoked permission.
+ * @param perm The permission to check against.
+ */
+ boolean matches(final java.security.Permission perm) {
+ if (!className.equals(perm.getClass().getName())) {
+ return false;
+ }
+ if (name != null) {
+ if (name.endsWith("*")) {
+ if (!perm.getName().startsWith(name.substring(0, name.length() - 1))) {
+ return false;
+ }
+ } else {
+ if (!name.equals(perm.getName())) {
+ return false;
+ }
+ }
+ }
+ if (actions != null) {
+ final Set<String> as = parseActions(perm.getActions());
+ final int size = as.size();
+ as.removeAll(actions);
+ if (as.size() == size) {
+ // None of the actions revoked, so all allowed.
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Parses the actions into a set of separate strings.
+ * @param actions The actions to be parsed.
+ */
+ private Set<String> parseActions(final String actions) {
+ final Set<String> result = new HashSet<String>();
+ final StringTokenizer tk = new StringTokenizer(actions, ",");
+ while (tk.hasMoreTokens()) {
+ final String item = tk.nextToken().trim();
+ if (!item.equals("")) {
+ result.add(item);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Get a string description of the permissions.
+ * @return string description of the permissions.
+ */
+ @Override
+ public String toString() {
+ return ("Permission: " + className + " (\"" + name + "\", \"" + actions + "\")");
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/PropertySet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/PropertySet.java
new file mode 100644
index 00000000..f5992044
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/PropertySet.java
@@ -0,0 +1,577 @@
+/*
+ * 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.tools.ant.types;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Stack;
+import java.util.TreeMap;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.resources.MappedResource;
+import org.apache.tools.ant.types.resources.PropertyResource;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.regexp.RegexpMatcher;
+import org.apache.tools.ant.util.regexp.RegexpMatcherFactory;
+
+/**
+ * A set of properties.
+ *
+ * @since Ant 1.6
+ */
+public class PropertySet extends DataType implements ResourceCollection {
+
+ private boolean dynamic = true;
+ private boolean negate = false;
+ private Set<String> cachedNames;
+ private List<PropertyRef> ptyRefs = new ArrayList<PropertyRef>();
+ private List<PropertySet> setRefs = new ArrayList<PropertySet>();
+ private Mapper mapper;
+
+ /**
+ * This is a nested class containing a reference to some properties
+ * and optionally a source of properties.
+ */
+ public static class PropertyRef {
+
+ private int count;
+ private String name;
+ private String regex;
+ private String prefix;
+ private String builtin;
+
+ /**
+ * Set the name.
+ * @param name a <code>String</code> value.
+ */
+ public void setName(String name) {
+ assertValid("name", name);
+ this.name = name;
+ }
+
+ /**
+ * Set the regular expression to use to filter the properties.
+ * @param regex a regular expression.
+ */
+ public void setRegex(String regex) {
+ assertValid("regex", regex);
+ this.regex = regex;
+ }
+
+ /**
+ * Set the prefix to use.
+ * @param prefix a <code>String</code> value.
+ */
+ public void setPrefix(String prefix) {
+ assertValid("prefix", prefix);
+ this.prefix = prefix;
+ }
+
+ /**
+ * Builtin property names - all, system or commandline.
+ * @param b an enumerated <code>BuildinPropertySetName</code> value.
+ */
+ public void setBuiltin(BuiltinPropertySetName b) {
+ String pBuiltIn = b.getValue();
+ assertValid("builtin", pBuiltIn);
+ this.builtin = pBuiltIn;
+ }
+
+ private void assertValid(String attr, String value) {
+ if (value == null || value.length() < 1) {
+ throw new BuildException("Invalid attribute: " + attr);
+ }
+
+ if (++count != 1) {
+ throw new BuildException("Attributes name, regex, and "
+ + "prefix are mutually exclusive");
+ }
+ }
+
+ /**
+ * A debug toString().
+ * @return a string version of this object.
+ */
+ public String toString() {
+ return "name=" + name + ", regex=" + regex + ", prefix=" + prefix
+ + ", builtin=" + builtin;
+ }
+
+ } //end nested class
+
+ /**
+ * Allow properties of a particular name in the set.
+ * @param name the property name to allow.
+ */
+ public void appendName(String name) {
+ PropertyRef r = new PropertyRef();
+ r.setName(name);
+ addPropertyref(r);
+ }
+
+ /**
+ * Allow properties whose names match a regex in the set.
+ * @param regex the regular expression to use.
+ */
+ public void appendRegex(String regex) {
+ PropertyRef r = new PropertyRef();
+ r.setRegex(regex);
+ addPropertyref(r);
+ }
+
+ /**
+ * Allow properties whose names start with a prefix in the set.
+ * @param prefix the prefix to use.
+ */
+ public void appendPrefix(String prefix) {
+ PropertyRef r = new PropertyRef();
+ r.setPrefix(prefix);
+ addPropertyref(r);
+ }
+
+ /**
+ * Allow builtin (all, system or commandline) properties in the set.
+ * @param b the type of builtin properties.
+ */
+ public void appendBuiltin(BuiltinPropertySetName b) {
+ PropertyRef r = new PropertyRef();
+ r.setBuiltin(b);
+ addPropertyref(r);
+ }
+
+ /**
+ * Set a mapper to change property names.
+ * @param type mapper type.
+ * @param from source pattern.
+ * @param to output pattern.
+ */
+ public void setMapper(String type, String from, String to) {
+ Mapper m = createMapper();
+ Mapper.MapperType mapperType = new Mapper.MapperType();
+ mapperType.setValue(type);
+ m.setType(mapperType);
+ m.setFrom(from);
+ m.setTo(to);
+ }
+
+ /**
+ * Add a property reference (nested element) to the references to be used.
+ * @param ref a property reference.
+ */
+ public void addPropertyref(PropertyRef ref) {
+ assertNotReference();
+ setChecked(false);
+ ptyRefs.add(ref);
+ }
+
+ /**
+ * Add another property set to this set.
+ * @param ref another property set.
+ */
+ public void addPropertyset(PropertySet ref) {
+ assertNotReference();
+ setChecked(false);
+ setRefs.add(ref);
+ }
+
+ /**
+ * Create a mapper to map the property names.
+ * @return a mapper to be configured.
+ */
+ public Mapper createMapper() {
+ assertNotReference();
+ if (mapper != null) {
+ throw new BuildException("Too many <mapper>s!");
+ }
+ mapper = new Mapper(getProject());
+ setChecked(false);
+ return mapper;
+ }
+
+ /**
+ * Add a nested FileNameMapper.
+ * @param fileNameMapper the mapper to add.
+ * @since Ant 1.6.3
+ */
+ public void add(FileNameMapper fileNameMapper) {
+ createMapper().add(fileNameMapper);
+ }
+
+ /**
+ * Set whether to reevaluate the set every time the set is used.
+ * Default is true.
+ *
+ * @param dynamic if true, reevaluate the property set each time
+ * the set is used. if false cache the property set
+ * the first time and use the cached set on subsequent
+ * occasions.
+ */
+ public void setDynamic(boolean dynamic) {
+ assertNotReference();
+ this.dynamic = dynamic;
+ }
+
+ /**
+ * Set whether to negate results.
+ * If "true", all properties not selected by nested elements will be returned.
+ * Default is "false".
+ * @param negate if true, negate the selection criteria.
+ */
+ public void setNegate(boolean negate) {
+ assertNotReference();
+ this.negate = negate;
+ }
+
+ /**
+ * Get the dynamic attribute.
+ * @return true if the property set is to be evaluated each time it is used.
+ */
+ public boolean getDynamic() {
+ if (isReference()) {
+ return getRef().dynamic;
+ }
+ dieOnCircularReference();
+ return dynamic;
+ }
+
+ /**
+ * Get the mapper attribute.
+ * @return the mapper attribute.
+ */
+ public Mapper getMapper() {
+ if (isReference()) {
+ return getRef().mapper;
+ }
+ dieOnCircularReference();
+ return mapper;
+ }
+
+ /**
+ * Convert the system properties to a hashtable.
+ * Use propertynames to get the list of properties (including
+ * default ones).
+ */
+ private Hashtable<String, Object> getAllSystemProperties() {
+ Hashtable<String, Object> ret = new Hashtable<String, Object>();
+ for (Enumeration<?> e = System.getProperties().propertyNames();
+ e.hasMoreElements();) {
+ String name = (String) e.nextElement();
+ ret.put(name, System.getProperties().getProperty(name));
+ }
+ return ret;
+ }
+
+ /**
+ * This is the operation to get the existing or recalculated properties.
+ * @return the properties for this propertyset.
+ */
+ public Properties getProperties() {
+ final Properties result = new Properties();
+ result.putAll(getPropertyMap());
+ return result;
+ }
+
+ /**
+ *
+ * @return Map
+ * @since 1.9.0
+ */
+ private Map<String, Object> getPropertyMap() {
+ if (isReference()) {
+ return getRef().getPropertyMap();
+ }
+ dieOnCircularReference();
+ final Mapper myMapper = getMapper();
+ final FileNameMapper m = myMapper == null ? null : myMapper.getImplementation();
+
+ final Map<String, Object> effectiveProperties = getEffectiveProperties();
+ final Set<String> propertyNames = getPropertyNames(effectiveProperties);
+ final Map<String, Object> result = new HashMap<String, Object>();
+
+ //iterate through the names, get the matching values
+ for (String name : propertyNames) {
+ Object value = effectiveProperties.get(name);
+ // TODO should we include null properties?
+ // TODO should we query the PropertyHelper for property value to grab potentially shadowed values?
+ if (value != null) {
+ // may be null if a system property has been added
+ // after the project instance has been initialized
+ if (m != null) {
+ //map the names
+ String[] newname = m.mapFileName(name);
+ if (newname != null) {
+ name = newname[0];
+ }
+ }
+ result.put(name, value);
+ }
+ }
+ return result;
+
+ }
+
+ private Map<String, Object> getEffectiveProperties() {
+ final Project prj = getProject();
+ final Map<String, Object> result = prj == null ? getAllSystemProperties() : prj.getProperties();
+ //quick & dirty, to make nested mapped p-sets work:
+ for (PropertySet set : setRefs) {
+ result.putAll(set.getPropertyMap());
+ }
+ return result;
+ }
+
+ private Set<String> getPropertyNames(Map<String, Object> props) {
+ Set<String> names;
+ if (getDynamic() || cachedNames == null) {
+ names = new HashSet<String>();
+ addPropertyNames(names, props);
+ // Add this PropertySet's nested PropertySets' property names.
+ for (PropertySet set : setRefs) {
+ names.addAll(set.getPropertyMap().keySet());
+ }
+ if (negate) {
+ //make a copy...
+ HashSet<String> complement = new HashSet<String>(props.keySet());
+ complement.removeAll(names);
+ names = complement;
+ }
+ if (!getDynamic()) {
+ cachedNames = names;
+ }
+ } else {
+ names = cachedNames;
+ }
+ return names;
+ }
+
+ /**
+ * @param names the output Set to fill with the property names
+ * matching this PropertySet selection criteria.
+ * @param props the current Project properties, passed in to
+ * avoid needless duplication of the Hashtable during recursion.
+ */
+ private void addPropertyNames(Set<String> names, Map<String, Object> props) {
+ if (isReference()) {
+ getRef().addPropertyNames(names, props);
+ }
+ dieOnCircularReference();
+ // Add this PropertySet's property names.
+ for (PropertyRef r : ptyRefs) {
+ if (r.name != null) {
+ if (props.get(r.name) != null) {
+ names.add(r.name);
+ }
+ } else if (r.prefix != null) {
+ for (String name : props.keySet()) {
+ if (name.startsWith(r.prefix)) {
+ names.add(name);
+ }
+ }
+ } else if (r.regex != null) {
+ RegexpMatcherFactory matchMaker = new RegexpMatcherFactory();
+ RegexpMatcher matcher = matchMaker.newRegexpMatcher();
+ matcher.setPattern(r.regex);
+ for (String name : props.keySet()) {
+ if (matcher.matches(name)) {
+ names.add(name);
+ }
+ }
+ } else if (r.builtin != null) {
+
+ if (r.builtin.equals(BuiltinPropertySetName.ALL)) {
+ names.addAll(props.keySet());
+ } else if (r.builtin.equals(BuiltinPropertySetName.SYSTEM)) {
+ names.addAll(getAllSystemProperties().keySet());
+ } else if (r.builtin.equals(BuiltinPropertySetName
+ .COMMANDLINE)) {
+ names.addAll(getProject().getUserProperties().keySet());
+ } else {
+ throw new BuildException("Impossible: Invalid builtin "
+ + "attribute!");
+ }
+ } else {
+ throw new BuildException("Impossible: Invalid PropertyRef!");
+ }
+ }
+ }
+
+ /**
+ * Performs the check for circular references and returns the
+ * referenced PropertySet.
+ * @return the referenced PropertySet.
+ */
+ protected PropertySet getRef() {
+ return (PropertySet) getCheckedRef(PropertySet.class, "propertyset");
+ }
+
+ /**
+ * Sets the value of the refid attribute.
+ *
+ * @param r the reference this datatype should point to.
+ * @throws BuildException if another attribute was set, since
+ * refid and all other attributes are mutually exclusive.
+ */
+ public final void setRefid(Reference r) {
+ if (!noAttributeSet) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Ensures this data type is not a reference.
+ *
+ * <p>Calling this method as the first line of every bean method of
+ * this data type (setXyz, addXyz, createXyz) ensure proper handling
+ * of the refid attribute.</p>
+ *
+ * @throws BuildException if the refid attribute was already set, since
+ * refid and all other attributes are mutually exclusive.
+ */
+ protected final void assertNotReference() {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ noAttributeSet = false;
+ }
+
+ /**
+ * Flag which tracks whether any attribute has been set; used by
+ * {@link #assertNotReference()} and {@link #setRefid(Reference)}.
+ */
+ private boolean noAttributeSet = true;
+
+ /**
+ * Used for propertyref's builtin attribute.
+ */
+ public static class BuiltinPropertySetName extends EnumeratedAttribute {
+ static final String ALL = "all";
+ static final String SYSTEM = "system";
+ static final String COMMANDLINE = "commandline";
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return new String[] {ALL, SYSTEM, COMMANDLINE};
+ }
+ }
+
+ /**
+ * A debug toString.
+ * This gets a comma separated list of key=value pairs for
+ * the properties in the set.
+ * The output order is sorted according to the keys' <i>natural order</i>.
+ * @return a string rep of this object.
+ */
+ public String toString() {
+ if (isReference()) {
+ return getRef().toString();
+ }
+ dieOnCircularReference();
+ StringBuilder b = new StringBuilder();
+ TreeMap<String, Object> sorted = new TreeMap<String, Object>(getPropertyMap());
+ for (Entry<String, Object> e : sorted.entrySet()) {
+ if (b.length() != 0) {
+ b.append(", ");
+ }
+ b.append(e.getKey());
+ b.append("=");
+ b.append(e.getValue());
+ }
+ return b.toString();
+ }
+
+ /**
+ * Fulfill the ResourceCollection interface.
+ * @return an Iterator of Resources.
+ * @since Ant 1.7
+ */
+ public Iterator<Resource> iterator() {
+ if (isReference()) {
+ return getRef().iterator();
+ }
+ dieOnCircularReference();
+ final Set<String> names = getPropertyNames(getEffectiveProperties());
+
+ Mapper myMapper = getMapper();
+ final FileNameMapper m = myMapper == null ? null : myMapper.getImplementation();
+ final Iterator<String> iter = names.iterator();
+
+ return new Iterator<Resource>() {
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+ public Resource next() {
+ PropertyResource p = new PropertyResource(getProject(), iter.next());
+ return m == null ? (Resource) p : new MappedResource(p, m);
+ }
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return the size of this ResourceCollection.
+ */
+ public int size() {
+ return isReference() ? getRef().size() : getProperties().size();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return whether this is a filesystem-only resource collection.
+ */
+ public boolean isFilesystemOnly() {
+ if (isReference()) {
+ return getRef().isFilesystemOnly();
+ }
+ dieOnCircularReference();
+ return false;
+ }
+
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ if (mapper != null) {
+ pushAndInvokeCircularReferenceCheck(mapper, stk, p);
+ }
+ for (PropertySet propertySet : setRefs) {
+ pushAndInvokeCircularReferenceCheck(propertySet, stk,
+ p);
+ }
+ setChecked(true);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Quantifier.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Quantifier.java
new file mode 100644
index 00000000..ac1b84ce
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Quantifier.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.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * EnumeratedAttribute for quantifier comparisons. Evaluates a
+ * <code>boolean[]</code> or raw <code>true</code> and <code>false</code>
+ * counts. Accepts the following values:<ul>
+ * <li>"all"</li> - none <code>false</code>
+ * <li>"each"</li> - none <code>false</code>
+ * <li>"every"</li> - none <code>false</code>
+ * <li>"any"</li> - at least one <code>true</code>
+ * <li>"some"</li> - at least one <code>true</code>
+ * <li>"one"</li> - exactly one <code>true</code>
+ * <li>"majority"</li> - more <code>true</code> than <code>false</code>
+ * <li>"most"</li> - more <code>true</code> than <code>false</code>
+ * <li>"none"</li> - none <code>true</code>
+ * </ul>
+ * @since Ant 1.7
+ */
+public class Quantifier extends EnumeratedAttribute {
+ private static final String[] VALUES
+ = new String[] {"all", "each", "every", "any", "some", "one",
+ "majority", "most", "none"};
+
+ /** ALL instance */
+ public static final Quantifier ALL = new Quantifier("all");
+ /** ANY instance */
+ public static final Quantifier ANY = new Quantifier("any");
+ /** ONE instance */
+ public static final Quantifier ONE = new Quantifier("one");
+ /** MAJORITY instance */
+ public static final Quantifier MAJORITY = new Quantifier("majority");
+ /** NONE instance */
+ public static final Quantifier NONE = new Quantifier("none");
+
+ private abstract static class Predicate {
+ abstract boolean eval(int t, int f);
+ }
+
+ private static final Predicate ALL_PRED = new Predicate() {
+ boolean eval(int t, int f) { return f == 0; }
+ };
+
+ private static final Predicate ANY_PRED = new Predicate() {
+ boolean eval(int t, int f) { return t > 0; }
+ };
+
+ private static final Predicate ONE_PRED = new Predicate() {
+ boolean eval(int t, int f) { return t == 1; }
+ };
+
+ private static final Predicate MAJORITY_PRED = new Predicate() {
+ boolean eval(int t, int f) { return t > f; }
+ };
+
+ private static final Predicate NONE_PRED = new Predicate() {
+ boolean eval(int t, int f) { return t == 0; }
+ };
+
+ private static final Predicate[] PREDS = new Predicate[VALUES.length];
+
+ static {
+ // CheckStyle:MagicNumber OFF
+ PREDS[0] = ALL_PRED;
+ PREDS[1] = ALL_PRED;
+ PREDS[2] = ALL_PRED;
+ PREDS[3] = ANY_PRED;
+ PREDS[4] = ANY_PRED;
+ PREDS[5] = ONE_PRED;
+ PREDS[6] = MAJORITY_PRED;
+ PREDS[7] = MAJORITY_PRED;
+ PREDS[8] = NONE_PRED;
+ // CheckStyle:MagicNumber ON
+ }
+
+ /**
+ * Default constructor.
+ */
+ public Quantifier() {
+ }
+
+ /**
+ * Construct a new Quantifier with the specified value.
+ * @param value the EnumeratedAttribute value.
+ */
+ public Quantifier(String value) {
+ setValue(value);
+ }
+
+ /**
+ * Return the possible values.
+ * @return String[] of EnumeratedAttribute values.
+ */
+ public String[] getValues() {
+ return VALUES;
+ }
+
+ /**
+ * Evaluate a <code>boolean<code> array.
+ * @param b the <code>boolean[]</code> to evaluate.
+ * @return true if the argument fell within the parameters of this Quantifier.
+ */
+ public boolean evaluate(boolean[] b) {
+ int t = 0;
+ for (int i = 0; i < b.length; i++) {
+ if (b[i]) {
+ t++;
+ }
+ }
+ return evaluate(t, b.length - t);
+ }
+
+ /**
+ * Evaluate integer <code>true</code> vs. <code>false</code> counts.
+ * @param t the number of <code>true</code> values.
+ * @param f the number of <code>false</code> values.
+ * @return true if the arguments fell within the parameters of this Quantifier.
+ */
+ public boolean evaluate(int t, int f) {
+ int index = getIndex();
+ if (index == -1) {
+ throw new BuildException("Quantifier value not set.");
+ }
+ return PREDS[index].eval(t, f);
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/RedirectorElement.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/RedirectorElement.java
new file mode 100644
index 00000000..d27b199c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/RedirectorElement.java
@@ -0,0 +1,630 @@
+/*
+ * 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.tools.ant.types;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Redirector;
+
+/**
+ * Element representation of a <code>Redirector</code>.
+ * @since Ant 1.6.2
+ */
+public class RedirectorElement extends DataType {
+
+ /**
+ * Whether the input mapper was set via <code>setOutput</code>.
+ */
+ private boolean usingInput = false;
+
+ /**
+ * Whether the output mapper was set via <code>setOutput</code>.
+ */
+ private boolean usingOutput = false;
+
+ /**
+ * Whether the error mapper was set via <code>setError</code>.
+ */
+ private boolean usingError = false;
+
+ /**
+ * Indicates if standard error should be logged to Ant's log system
+ * rather than the output. This has no effect if standard error is
+ * redirected to a file or property.
+ */
+ private Boolean logError;
+
+ /** The name of the property into which output is to be stored */
+ private String outputProperty;
+
+ /** The name of the property into which error output is to be stored */
+ private String errorProperty;
+
+ /** String from which input is taken */
+ private String inputString;
+
+ /** Flag which indicates if error and output files are to be appended. */
+ private Boolean append;
+
+ /** Flag which indicates that output should be always sent to the log */
+ private Boolean alwaysLog;
+
+ /** Flag which indicates whether files should be created even if empty. */
+ private Boolean createEmptyFiles;
+
+ /** Input file mapper. */
+ private Mapper inputMapper;
+
+ /** Output file mapper. */
+ private Mapper outputMapper;
+
+ /** Error file mapper. */
+ private Mapper errorMapper;
+
+ /** input filter chains. */
+ private Vector<FilterChain> inputFilterChains = new Vector<FilterChain>();
+
+ /** output filter chains. */
+ private Vector<FilterChain> outputFilterChains = new Vector<FilterChain>();
+
+ /** error filter chains. */
+ private Vector<FilterChain> errorFilterChains = new Vector<FilterChain>();
+
+ /** The output encoding */
+ private String outputEncoding;
+
+ /** The error encoding */
+ private String errorEncoding;
+
+ /** The input encoding */
+ private String inputEncoding;
+
+ /** whether to log the inputstring */
+ private Boolean logInputString;
+
+ /** Is the output binary or can we safely split it into lines? */
+ private boolean outputIsBinary = false;
+
+ /**
+ * Add the input file mapper.
+ * @param inputMapper <code>Mapper</code>.
+ */
+ public void addConfiguredInputMapper(Mapper inputMapper) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (this.inputMapper != null) {
+ if (usingInput) {
+ throw new BuildException("attribute \"input\""
+ + " cannot coexist with a nested <inputmapper>");
+ } else {
+ throw new BuildException("Cannot have > 1 <inputmapper>");
+ }
+ }
+ setChecked(false);
+ this.inputMapper = inputMapper;
+ }
+
+ /**
+ * Add the output file mapper.
+ * @param outputMapper <code>Mapper</code>.
+ */
+ public void addConfiguredOutputMapper(Mapper outputMapper) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (this.outputMapper != null) {
+ if (usingOutput) {
+ throw new BuildException("attribute \"output\""
+ + " cannot coexist with a nested <outputmapper>");
+ } else {
+ throw new BuildException("Cannot have > 1 <outputmapper>");
+ }
+ }
+ setChecked(false);
+ this.outputMapper = outputMapper;
+ }
+
+ /**
+ * Add the error file mapper.
+ * @param errorMapper <code>Mapper</code>.
+ */
+ public void addConfiguredErrorMapper(Mapper errorMapper) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (this.errorMapper != null) {
+ if (usingError) {
+ throw new BuildException("attribute \"error\""
+ + " cannot coexist with a nested <errormapper>");
+ } else {
+ throw new BuildException("Cannot have > 1 <errormapper>");
+ }
+ }
+ setChecked(false);
+ this.errorMapper = errorMapper;
+ }
+
+ /**
+ * Make this instance in effect a reference to another instance.
+ *
+ * <p>You must not set another attribute or nest elements inside
+ * this element if you make it a reference.</p>
+ * @param r the reference to use.
+ * @throws BuildException on error.
+ */
+ public void setRefid(Reference r) throws BuildException {
+ if (usingInput
+ || usingOutput
+ || usingError
+ || inputString != null
+ || logError != null
+ || append != null
+ || createEmptyFiles != null
+ || inputEncoding != null
+ || outputEncoding != null
+ || errorEncoding != null
+ || outputProperty != null
+ || errorProperty != null
+ || logInputString != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Set the input to use for the task.
+ * @param input the file from which input is read.
+ */
+ public void setInput(File input) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (inputString != null) {
+ throw new BuildException("The \"input\" and \"inputstring\" "
+ + "attributes cannot both be specified");
+ }
+ usingInput = true;
+ inputMapper = createMergeMapper(input);
+ }
+
+ /**
+ * Set the string to use as input
+ * @param inputString the string which is used as the input source
+ */
+ public void setInputString(String inputString) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (usingInput) {
+ throw new BuildException("The \"input\" and \"inputstring\" "
+ + "attributes cannot both be specified");
+ }
+ this.inputString = inputString;
+ }
+
+ /**
+ * Set whether to include the value of the input string in log messages.
+ * Defaults to true.
+ * @param logInputString true or false.
+ * @since Ant 1.7
+ */
+ public void setLogInputString(boolean logInputString) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.logInputString = logInputString ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ /**
+ * File the output of the process is redirected to. If error is not
+ * redirected, it too will appear in the output.
+ *
+ * @param out the file to which output stream is written.
+ */
+ public void setOutput(File out) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (out == null) {
+ throw new IllegalArgumentException("output file specified as null");
+ }
+ usingOutput = true;
+ outputMapper = createMergeMapper(out);
+ }
+
+ /**
+ * Set the output encoding.
+ * @param outputEncoding <code>String</code>.
+ */
+ public void setOutputEncoding(String outputEncoding) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.outputEncoding = outputEncoding;
+ }
+
+ /**
+ * Set the error encoding.
+ *
+ * @param errorEncoding <code>String</code>.
+ */
+ public void setErrorEncoding(String errorEncoding) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.errorEncoding = errorEncoding;
+ }
+
+ /**
+ * Set the input encoding.
+ * @param inputEncoding <code>String</code>.
+ */
+ public void setInputEncoding(String inputEncoding) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.inputEncoding = inputEncoding;
+ }
+
+ /**
+ * Controls whether error output of exec is logged. This is only useful
+ * when output is being redirected and error output is desired in the
+ * Ant log.
+ * @param logError if true the standard error is sent to the Ant log system
+ * and not sent to output.
+ */
+ public void setLogError(boolean logError) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.logError = ((logError) ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Set the file to which standard error is to be redirected.
+ * @param error the file to which error is to be written.
+ */
+ public void setError(File error) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (error == null) {
+ throw new IllegalArgumentException("error file specified as null");
+ }
+ usingError = true;
+ errorMapper = createMergeMapper(error);
+ }
+
+ /**
+ * Property name whose value should be set to the output of
+ * the process.
+ * @param outputProperty the name of the property to be set with the
+ * task's output.
+ */
+ public void setOutputProperty(String outputProperty) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.outputProperty = outputProperty;
+ }
+
+ /**
+ * Whether output should be appended to or overwrite an existing file.
+ * Defaults to false.
+ * @param append if true output and error streams are appended to their
+ * respective files, if specified.
+ */
+ public void setAppend(boolean append) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.append = ((append) ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * If true, (error and non-error) output will be "teed", redirected
+ * as specified while being sent to Ant's logging mechanism as if no
+ * redirection had taken place. Defaults to false.
+ * @param alwaysLog <code>boolean</code>
+ * @since Ant 1.6.3
+ */
+ public void setAlwaysLog(boolean alwaysLog) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.alwaysLog = ((alwaysLog) ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Whether output and error files should be created even when empty.
+ * Defaults to true.
+ * @param createEmptyFiles <code>boolean</code>.
+ */
+ public void setCreateEmptyFiles(boolean createEmptyFiles) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.createEmptyFiles = ((createEmptyFiles)
+ ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Property name whose value should be set to the error of
+ * the process.
+ * @param errorProperty the name of the property to be set
+ * with the error output.
+ */
+ public void setErrorProperty(String errorProperty) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.errorProperty = errorProperty;
+ }
+
+ /**
+ * Create a nested input <code>FilterChain</code>.
+ * @return <code>FilterChain</code>.
+ */
+ public FilterChain createInputFilterChain() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ FilterChain result = new FilterChain();
+ result.setProject(getProject());
+ inputFilterChains.add(result);
+ setChecked(false);
+ return result;
+ }
+
+ /**
+ * Create a nested output <code>FilterChain</code>.
+ * @return <code>FilterChain</code>.
+ */
+ public FilterChain createOutputFilterChain() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ FilterChain result = new FilterChain();
+ result.setProject(getProject());
+ outputFilterChains.add(result);
+ setChecked(false);
+ return result;
+ }
+
+ /**
+ * Create a nested error <code>FilterChain</code>.
+ * @return <code>FilterChain</code>.
+ */
+ public FilterChain createErrorFilterChain() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ FilterChain result = new FilterChain();
+ result.setProject(getProject());
+ errorFilterChains.add(result);
+ setChecked(false);
+ return result;
+ }
+
+ /**
+ * Whether to consider the output created by the process binary.
+ *
+ * <p>Binary output will not be split into lines which may make
+ * error and normal output look mixed up when they get written to
+ * the same stream.</p>
+ * @since 1.9.4
+ */
+ public void setBinaryOutput(boolean b) {
+ outputIsBinary = b;
+ }
+
+ /**
+ * Configure the specified <code>Redirector</code>.
+ * @param redirector <code>Redirector</code>.
+ */
+ public void configure(Redirector redirector) {
+ configure(redirector, null);
+ }
+
+ /**
+ * Configure the specified <code>Redirector</code>
+ * for the specified sourcefile.
+ * @param redirector <code>Redirector</code>.
+ * @param sourcefile <code>String</code>.
+ */
+ public void configure(Redirector redirector, String sourcefile) {
+ if (isReference()) {
+ getRef().configure(redirector, sourcefile);
+ return;
+ }
+ dieOnCircularReference();
+ if (alwaysLog != null) {
+ redirector.setAlwaysLog(alwaysLog.booleanValue());
+ }
+ if (logError != null) {
+ redirector.setLogError(logError.booleanValue());
+ }
+ if (append != null) {
+ redirector.setAppend(append.booleanValue());
+ }
+ if (createEmptyFiles != null) {
+ redirector.setCreateEmptyFiles(createEmptyFiles.booleanValue());
+ }
+ if (outputProperty != null) {
+ redirector.setOutputProperty(outputProperty);
+ }
+ if (errorProperty != null) {
+ redirector.setErrorProperty(errorProperty);
+ }
+ if (inputString != null) {
+ redirector.setInputString(inputString);
+ }
+ if (logInputString != null) {
+ redirector.setLogInputString(logInputString.booleanValue());
+ }
+ if (inputMapper != null) {
+ String[] inputTargets = null;
+ try {
+ inputTargets =
+ inputMapper.getImplementation().mapFileName(sourcefile);
+ } catch (NullPointerException enPeaEx) {
+ if (sourcefile != null) {
+ throw enPeaEx;
+ }
+ }
+ if (inputTargets != null && inputTargets.length > 0) {
+ redirector.setInput(toFileArray(inputTargets));
+ }
+ }
+ if (outputMapper != null) {
+ String[] outputTargets = null;
+ try {
+ outputTargets =
+ outputMapper.getImplementation().mapFileName(sourcefile);
+ } catch (NullPointerException enPeaEx) {
+ if (sourcefile != null) {
+ throw enPeaEx;
+ }
+ }
+ if (outputTargets != null && outputTargets.length > 0) {
+ redirector.setOutput(toFileArray(outputTargets));
+ }
+ }
+ if (errorMapper != null) {
+ String[] errorTargets = null;
+ try {
+ errorTargets =
+ errorMapper.getImplementation().mapFileName(sourcefile);
+ } catch (NullPointerException enPeaEx) {
+ if (sourcefile != null) {
+ throw enPeaEx;
+ }
+ }
+ if (errorTargets != null && errorTargets.length > 0) {
+ redirector.setError(toFileArray(errorTargets));
+ }
+ }
+ if (inputFilterChains.size() > 0) {
+ redirector.setInputFilterChains(inputFilterChains);
+ }
+ if (outputFilterChains.size() > 0) {
+ redirector.setOutputFilterChains(outputFilterChains);
+ }
+ if (errorFilterChains.size() > 0) {
+ redirector.setErrorFilterChains(errorFilterChains);
+ }
+ if (inputEncoding != null) {
+ redirector.setInputEncoding(inputEncoding);
+ }
+ if (outputEncoding != null) {
+ redirector.setOutputEncoding(outputEncoding);
+ }
+ if (errorEncoding != null) {
+ redirector.setErrorEncoding(errorEncoding);
+ }
+ redirector.setBinaryOutput(outputIsBinary);
+ }
+
+ /**
+ * Create a merge mapper pointing to the specified destination file.
+ * @param destfile <code>File</code>
+ * @return <code>Mapper</code>.
+ */
+ protected Mapper createMergeMapper(File destfile) {
+ Mapper result = new Mapper(getProject());
+ result.setClassname(
+ org.apache.tools.ant.util.MergingMapper.class.getName());
+ result.setTo(destfile.getAbsolutePath());
+ return result;
+ }
+
+ /**
+ * Return a <code>File[]</code> from the specified set of filenames.
+ * @param name <code>String[]</code>
+ * @return <code>File[]</code>.
+ */
+ protected File[] toFileArray(String[] name) {
+ if (name == null) {
+ return null;
+ }
+ //remove any null elements
+ ArrayList<File> list = new ArrayList<File>(name.length);
+ for (int i = 0; i < name.length; i++) {
+ if (name[i] != null) {
+ list.add(getProject().resolveFile(name[i]));
+ }
+ }
+ return (File[]) (list.toArray(new File[list.size()]));
+ }
+
+ /**
+ * Overrides the version of DataType to recurse on all DataType
+ * child elements that may have been added.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ Mapper[] m = new Mapper[] {inputMapper, outputMapper, errorMapper};
+ for (int i = 0; i < m.length; i++) {
+ if (m[i] != null) {
+ stk.push(m[i]);
+ m[i].dieOnCircularReference(stk, p);
+ stk.pop();
+ }
+ }
+ @SuppressWarnings("unchecked")
+ final List<? extends List<FilterChain>> filterChainLists = Arrays
+ .<List<FilterChain>> asList(inputFilterChains, outputFilterChains,
+ errorFilterChains);
+ for (List<FilterChain> filterChains : filterChainLists) {
+ if (filterChains != null) {
+ for (FilterChain fc : filterChains) {
+ pushAndInvokeCircularReferenceCheck(fc, stk, p);
+ }
+ }
+ }
+ setChecked(true);
+ }
+ }
+
+ /**
+ * Perform the check for circular references, returning the
+ * referenced RedirectorElement.
+ * @return the referenced RedirectorElement.
+ */
+ private RedirectorElement getRef() {
+ return (RedirectorElement) getCheckedRef();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Reference.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Reference.java
new file mode 100644
index 00000000..e5e9b25c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Reference.java
@@ -0,0 +1,134 @@
+/*
+ * 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.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Class to hold a reference to another object in the project.
+ *
+ */
+public class Reference {
+
+ private String refid;
+ private Project project;
+
+ /**
+ * Create a reference.
+ * @deprecated since 1.7.
+ * Please use {@link Reference#Reference(Project,String)}
+ * instead.
+ */
+ public Reference() {
+ }
+
+ /**
+ * Create a reference to a named ID.
+ * @param id the name of this reference
+ * @deprecated since 1.7.
+ * Please use {@link Reference#Reference(Project,String)}
+ * instead.
+ */
+ public Reference(String id) {
+ setRefId(id);
+ }
+
+ /**
+ * Create a reference to a named ID in a particular project.
+ * @param p the project this reference is associated with
+ * @param id the name of this reference
+ * @since Ant 1.6.3
+ */
+ public Reference(Project p, String id) {
+ setRefId(id);
+ setProject(p);
+ }
+
+ /**
+ * Set the reference id. Should not normally be necessary;
+ * use {@link Reference#Reference(Project, String)}.
+ * @param id the reference id to use
+ */
+ public void setRefId(String id) {
+ refid = id;
+ }
+
+ /**
+ * Get the reference id of this reference.
+ * @return the reference id
+ */
+ public String getRefId() {
+ return refid;
+ }
+
+ /**
+ * Set the associated project. Should not normally be necessary;
+ * use {@link Reference#Reference(Project,String)}.
+ * @param p the project to use
+ * @since Ant 1.6.3
+ */
+ public void setProject(Project p) {
+ this.project = p;
+ }
+
+ /**
+ * Get the associated project, if any; may be null.
+ * @return the associated project
+ * @since Ant 1.6.3
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * Resolve the reference, using the associated project if
+ * it set, otherwise use the passed in project.
+ * @param fallback the fallback project to use if the project attribute of
+ * reference is not set.
+ * @return the dereferenced object.
+ * @throws BuildException if the reference cannot be dereferenced.
+ */
+ public Object getReferencedObject(Project fallback) throws BuildException {
+ if (refid == null) {
+ throw new BuildException("No reference specified");
+ }
+
+ Object o = project == null ? fallback.getReference(refid) : project.getReference(refid);
+ if (o == null) {
+ throw new BuildException("Reference " + refid + " not found.");
+ }
+ return o;
+ }
+
+ /**
+ * Resolve the reference, looking in the associated project.
+ * @see Project#getReference
+ * @return the dereferenced object.
+ * @throws BuildException if the project is null or the reference cannot be dereferenced
+ * @since Ant 1.6.3
+ */
+ public Object getReferencedObject() throws BuildException {
+ if (project == null) {
+ throw new BuildException("No project set on reference to " + refid);
+ }
+ return getReferencedObject(project);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/RegularExpression.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/RegularExpression.java
new file mode 100644
index 00000000..18ee3f1f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/RegularExpression.java
@@ -0,0 +1,141 @@
+/*
+ * 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.tools.ant.types;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.regexp.Regexp;
+import org.apache.tools.ant.util.regexp.RegexpFactory;
+
+/**
+ * A regular expression datatype. Keeps an instance of the
+ * compiled expression for speed purposes. This compiled
+ * expression is lazily evaluated (it is compiled the first
+ * time it is needed). The syntax is the dependent on which
+ * regular expression type you are using. The system property
+ * "ant.regexp.regexpimpl" will be the classname of the implementation
+ * that will be used.
+ *
+ * <pre>
+ * Available implementations:
+ *
+ * org.apache.tools.ant.util.regexp.Jdk14RegexpRegexp (default)
+ * Based on the JDK's built-in regular expression package
+ *
+ * org.apache.tools.ant.util.regexp.JakartaOroRegexp
+ * Based on the jakarta-oro package
+ *
+ * org.apache.tools.ant.util.regexp.JakartaRegexpRegexp
+ * Based on the jakarta-regexp package
+ * </pre>
+ *
+ * <pre>
+ * &lt;regexp [ [id="id"] pattern="expression" | refid="id" ]
+ * /&gt;
+ * </pre>
+ *
+ * @see org.apache.oro.text.regex.Perl5Compiler
+ * @see org.apache.regexp.RE
+ * @see java.util.regex.Pattern
+ *
+ * @see org.apache.tools.ant.util.regexp.Regexp
+ *
+ * @ant.datatype name="regexp"
+ */
+public class RegularExpression extends DataType {
+ /** Name of this data type */
+ public static final String DATA_TYPE_NAME = "regexp";
+ private boolean alreadyInit = false;
+
+ // The regular expression factory
+ private static final RegexpFactory FACTORY = new RegexpFactory();
+
+ private Regexp regexp = null;
+ // temporary variable
+ private String myPattern;
+ private boolean setPatternPending = false;
+
+ /**
+ * default constructor
+ */
+ public RegularExpression() {
+ }
+
+ private void init(Project p) {
+ if (!alreadyInit) {
+ this.regexp = FACTORY.newRegexp(p);
+ alreadyInit = true;
+ }
+ }
+ private void setPattern() {
+ if (setPatternPending) {
+ regexp.setPattern(myPattern);
+ setPatternPending = false;
+ }
+ }
+ /**
+ * sets the regular expression pattern
+ * @param pattern regular expression pattern
+ */
+ public void setPattern(String pattern) {
+ if (regexp == null) {
+ myPattern = pattern;
+ setPatternPending = true;
+ } else {
+ regexp.setPattern(pattern);
+ }
+ }
+
+ /***
+ * Gets the pattern string for this RegularExpression in the
+ * given project.
+ * @param p project
+ * @return pattern
+ */
+ public String getPattern(Project p) {
+ init(p);
+ if (isReference()) {
+ return getRef(p).getPattern(p);
+ }
+ setPattern();
+ return regexp.getPattern();
+ }
+
+ /**
+ * provides a reference to the Regexp contained in this
+ * @param p project
+ * @return Regexp instance associated with this RegularExpression instance
+ */
+ public Regexp getRegexp(Project p) {
+ init(p);
+ if (isReference()) {
+ return getRef(p).getRegexp(p);
+ }
+ setPattern();
+ return this.regexp;
+ }
+
+ /***
+ * Get the RegularExpression this reference refers to in
+ * the given project. Check for circular references too
+ * @param p project
+ * @return resolved RegularExpression instance
+ */
+ public RegularExpression getRef(Project p) {
+ return (RegularExpression) getCheckedRef(p);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Resource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Resource.java
new file mode 100644
index 00000000..426a5b9e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Resource.java
@@ -0,0 +1,439 @@
+/*
+ * 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.tools.ant.types;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.apache.tools.ant.types.resources.FileProvider;
+
+/**
+ * Describes a "File-like" resource (File, ZipEntry, etc.).
+ *
+ * This class is meant to be used by classes needing to record path
+ * and date/time information about a file, a zip entry or some similar
+ * resource (URL, archive in a version control repository, ...).
+ *
+ * @since Ant 1.5.2
+ * @see org.apache.tools.ant.types.resources.Touchable
+ */
+public class Resource extends DataType implements Comparable<Resource>, ResourceCollection {
+
+ /** Constant unknown size */
+ public static final long UNKNOWN_SIZE = -1;
+
+ /** Constant unknown datetime for getLastModified */
+ public static final long UNKNOWN_DATETIME = 0L;
+
+ /** Magic number */
+ protected static final int MAGIC = getMagicNumber("Resource".getBytes());
+
+ private static final int NULL_NAME = getMagicNumber("null name".getBytes());
+
+ /**
+ * Create a "magic number" for use in hashCode calculations.
+ * @param seed byte[] to seed with.
+ * @return a magic number as int.
+ */
+ protected static int getMagicNumber(byte[] seed) {
+ return new BigInteger(seed).intValue();
+ }
+
+ private String name = null;
+ private Boolean exists = null;
+ private Long lastmodified = null;
+ private Boolean directory = null;
+ private Long size = null;
+
+ /**
+ * Default constructor.
+ */
+ public Resource() {
+ }
+
+ /**
+ * Only sets the name.
+ *
+ * <p>This is a dummy, used for not existing resources.</p>
+ *
+ * @param name relative path of the resource. Expects
+ * &quot;/&quot; to be used as the directory separator.
+ */
+ public Resource(String name) {
+ this(name, false, 0, false);
+ }
+
+ /**
+ * Sets the name, lastmodified flag, and exists flag.
+ *
+ * @param name relative path of the resource. Expects
+ * &quot;/&quot; to be used as the directory separator.
+ * @param exists if true, this resource exists.
+ * @param lastmodified the last modification time of this resource.
+ */
+ public Resource(String name, boolean exists, long lastmodified) {
+ this(name, exists, lastmodified, false);
+ }
+
+ /**
+ * Sets the name, lastmodified flag, exists flag, and directory flag.
+ *
+ * @param name relative path of the resource. Expects
+ * &quot;/&quot; to be used as the directory separator.
+ * @param exists if true the resource exists
+ * @param lastmodified the last modification time of the resource
+ * @param directory if true, this resource is a directory
+ */
+ public Resource(String name, boolean exists, long lastmodified, boolean directory) {
+ this(name, exists, lastmodified, directory, UNKNOWN_SIZE);
+ }
+
+ /**
+ * Sets the name, lastmodified flag, exists flag, directory flag, and size.
+ *
+ * @param name relative path of the resource. Expects
+ * &quot;/&quot; to be used as the directory separator.
+ * @param exists if true the resource exists
+ * @param lastmodified the last modification time of the resource
+ * @param directory if true, this resource is a directory
+ * @param size the size of this resource.
+ */
+ public Resource(String name, boolean exists, long lastmodified, boolean directory, long size) {
+ this.name = name;
+ setName(name);
+ setExists(exists);
+ setLastModified(lastmodified);
+ setDirectory(directory);
+ setSize(size);
+ }
+
+ /**
+ * Name attribute will contain the path of a file relative to the
+ * root directory of its fileset or the recorded path of a zip
+ * entry.
+ *
+ * <p>example for a file with fullpath /var/opt/adm/resource.txt
+ * in a file set with root dir /var/opt it will be
+ * adm/resource.txt.</p>
+ *
+ * <p>&quot;/&quot; will be used as the directory separator.</p>
+ * @return the name of this resource.
+ */
+ public String getName() {
+ return isReference() ? ((Resource) getCheckedRef()).getName() : name;
+ }
+
+ /**
+ * Set the name of this Resource.
+ * @param name relative path of the resource. Expects
+ * &quot;/&quot; to be used as the directory separator.
+ */
+ public void setName(String name) {
+ checkAttributesAllowed();
+ this.name = name;
+ }
+
+ /**
+ * The exists attribute tells whether a resource exists.
+ * @return true if this resource exists.
+ */
+ public boolean isExists() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).isExists();
+ }
+ //default true:
+ return exists == null || exists.booleanValue();
+ }
+
+ /**
+ * Set the exists attribute.
+ * @param exists if true, this resource exists.
+ */
+ public void setExists(boolean exists) {
+ checkAttributesAllowed();
+ this.exists = exists ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ /**
+ * Tells the modification time in milliseconds since 01.01.1970 (the "epoch").
+ *
+ * @return the modification time, if that is meaningful
+ * (e.g. for a file resource which exists);
+ * 0 if the resource does not exist, to mirror the behavior
+ * of {@link java.io.File#lastModified};
+ * or 0 if the notion of modification time is meaningless for this class
+ * of resource (e.g. an inline string)
+ */
+ public long getLastModified() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getLastModified();
+ }
+ if (!isExists() || lastmodified == null) {
+ return UNKNOWN_DATETIME;
+ }
+ long result = lastmodified.longValue();
+ return result < UNKNOWN_DATETIME ? UNKNOWN_DATETIME : result;
+ }
+
+ /**
+ * Set the last modification attribute.
+ * @param lastmodified the modification time in milliseconds since 01.01.1970.
+ */
+ public void setLastModified(long lastmodified) {
+ checkAttributesAllowed();
+ this.lastmodified = new Long(lastmodified);
+ }
+
+ /**
+ * Tells if the resource is a directory.
+ * @return boolean flag indicating if the resource is a directory.
+ */
+ public boolean isDirectory() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).isDirectory();
+ }
+ //default false:
+ return directory != null && directory.booleanValue();
+ }
+
+ /**
+ * Set the directory attribute.
+ * @param directory if true, this resource is a directory.
+ */
+ public void setDirectory(boolean directory) {
+ checkAttributesAllowed();
+ this.directory = directory ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ /**
+ * Set the size of this Resource.
+ * @param size the size, as a long.
+ * @since Ant 1.6.3
+ */
+ public void setSize(long size) {
+ checkAttributesAllowed();
+ this.size = new Long(size > UNKNOWN_SIZE ? size : UNKNOWN_SIZE);
+ }
+
+ /**
+ * Get the size of this Resource.
+ * @return the size, as a long, 0 if the Resource does not exist (for
+ * compatibility with java.io.File), or UNKNOWN_SIZE if not known.
+ * @since Ant 1.6.3
+ */
+ public long getSize() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getSize();
+ }
+ return isExists()
+ ? (size != null ? size.longValue() : UNKNOWN_SIZE)
+ : 0L;
+ }
+
+ /**
+ * Clone this Resource.
+ * @return copy of this.
+ */
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new UnsupportedOperationException(
+ "CloneNotSupportedException for a Resource caught. "
+ + "Derived classes must support cloning.");
+ }
+ }
+
+ /**
+ * Delegates to a comparison of names.
+ * @param other the object to compare to.
+ * @return a negative integer, zero, or a positive integer as this Resource
+ * is less than, equal to, or greater than the specified Resource.
+ * @since Ant 1.6
+ */
+ public int compareTo(Resource other) {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).compareTo(other);
+ }
+ return toString().compareTo(other.toString());
+ }
+
+ /**
+ * Implement basic Resource equality.
+ * @param other the object to check against.
+ * @return true if the specified Object is equal to this Resource.
+ * @since Ant 1.7
+ */
+ public boolean equals(Object other) {
+ if (isReference()) {
+ return getCheckedRef().equals(other);
+ }
+ return other != null && other.getClass().equals(getClass())
+ && compareTo((Resource) other) == 0;
+ }
+
+ /**
+ * Get the hash code for this Resource.
+ * @return hash code as int.
+ * @since Ant 1.7
+ */
+ public int hashCode() {
+ if (isReference()) {
+ return getCheckedRef().hashCode();
+ }
+ String name = getName();
+ return MAGIC * (name == null ? NULL_NAME : name.hashCode());
+ }
+
+ /**
+ * Get an InputStream for the Resource.
+ * @return an InputStream containing this Resource's content.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if InputStreams are not
+ * supported for this Resource type.
+ * @since Ant 1.7
+ */
+ public InputStream getInputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getInputStream();
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ * @since Ant 1.7
+ */
+ public OutputStream getOutputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getOutputStream();
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return an Iterator of Resources.
+ * @since Ant 1.7
+ */
+ public Iterator<Resource> iterator() {
+ return isReference() ? ((Resource) getCheckedRef()).iterator()
+ : new Iterator<Resource>() {
+ private boolean done = false;
+ public boolean hasNext() {
+ return !done;
+ }
+ public Resource next() {
+ if (done) {
+ throw new NoSuchElementException();
+ }
+ done = true;
+ return Resource.this;
+ }
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return the size of this ResourceCollection.
+ * @since Ant 1.7
+ */
+ public int size() {
+ return isReference() ? ((Resource) getCheckedRef()).size() : 1;
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return whether this Resource is a FileProvider.
+ * @since Ant 1.7
+ */
+ public boolean isFilesystemOnly() {
+ return (isReference() && ((Resource) getCheckedRef()).isFilesystemOnly())
+ || this.as(FileProvider.class) != null;
+ }
+
+ /**
+ * Get the string representation of this Resource.
+ * @return this Resource formatted as a String.
+ * @since Ant 1.7
+ */
+ public String toString() {
+ if (isReference()) {
+ return getCheckedRef().toString();
+ }
+ String n = getName();
+ return n == null ? "(anonymous)" : n;
+ }
+
+ /**
+ * Get a long String representation of this Resource.
+ * This typically should be the value of <code>toString()</code>
+ * prefixed by a type description.
+ * @return this Resource formatted as a long String.
+ * @since Ant 1.7
+ */
+ public final String toLongString() {
+ return isReference() ? ((Resource) getCheckedRef()).toLongString()
+ : getDataTypeName() + " \"" + toString() + '"';
+ }
+
+ /**
+ * Overrides the base version.
+ * @param r the Reference to set.
+ */
+ public void setRefid(Reference r) {
+ if (name != null
+ || exists != null
+ || lastmodified != null
+ || directory != null
+ || size != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Returns a view of this resource that implements the interface
+ * given as the argument or null if there is no such view.
+ *
+ * <p>This allows extension interfaces to be added to resources
+ * without growing the number of permutations of interfaces
+ * decorators/adapters need to implement.</p>
+ *
+ * <p>This implementation of the method will return the current
+ * instance itself if it can be assigned to the given class.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public <T> T as(Class<T> clazz) {
+ return clazz.isAssignableFrom(getClass()) ? clazz.cast(this) : null;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ResourceCollection.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ResourceCollection.java
new file mode 100644
index 00000000..a82c8b59
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ResourceCollection.java
@@ -0,0 +1,50 @@
+/*
+ * 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.tools.ant.types;
+
+import java.util.Iterator;
+
+/**
+ * Interface describing a collection of Resources.
+ * @since Ant 1.7
+ */
+public interface ResourceCollection extends Iterable<Resource> {
+
+ /**
+ * Gets the contents of this collection.
+ * @return all resources in the collection
+ */
+ Iterator<Resource> iterator();
+
+ /**
+ * Learn the number of contained Resources.
+ * @return number of elements as int.
+ */
+ int size();
+
+ /**
+ * Indicate whether this ResourceCollection is composed entirely of
+ * Resources accessible via local filesystem conventions. If true,
+ * all resources returned from this collection should
+ * respond with a {@link org.apache.tools.ant.types.resources.FileProvider}
+ * when asked via {@link Resource#as}.
+ * @return whether this is a filesystem-only resource collection.
+ */
+ boolean isFilesystemOnly();
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ResourceFactory.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ResourceFactory.java
new file mode 100644
index 00000000..515318ef
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ResourceFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.tools.ant.types;
+
+/**
+ * this interface should be implemented by classes (Scanners) needing
+ * to deliver information about resources.
+ *
+ * @since Ant 1.5.2
+ */
+public interface ResourceFactory {
+
+ /**
+ * Query a resource (file, zipentry, ...) by name
+ *
+ * @param name relative path of the resource about which
+ * information is sought. Expects &quot;/&quot; to be used as the
+ * directory separator.
+ * @return instance of Resource; the exists attribute of Resource
+ * will tell whether the sought resource exists
+ */
+ Resource getResource(String name);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ResourceLocation.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ResourceLocation.java
new file mode 100644
index 00000000..c5a44ea2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ResourceLocation.java
@@ -0,0 +1,106 @@
+/*
+ * 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.tools.ant.types;
+
+import java.net.URL;
+
+/**
+ * <p>Helper class to handle the <code>&lt;dtd&gt;</code> and
+ * <code>&lt;entity&gt;</code> nested elements. These correspond to
+ * the <code>PUBLIC</code> and <code>URI</code> catalog entry types,
+ * respectively, as defined in the <a
+ * href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
+ * OASIS "Open Catalog" standard</a>.</p>
+ *
+ * <p>Possible Future Enhancements:
+ * <ul>
+ * <li>Bring the Ant element names into conformance with the OASIS standard</li>
+ * <li>Add support for additional OASIS catalog entry types</li>
+ * </ul>
+ * </p>
+ *
+ * @see org.apache.xml.resolver.Catalog
+ * @since Ant 1.6
+ */
+public class ResourceLocation {
+
+ //-- Fields ----------------------------------------------------------------
+ /** publicId of the dtd/entity. */
+ private String publicId = null;
+
+ /** location of the dtd/entity - a file/resource/URL. */
+ private String location = null;
+
+ /**
+ * base URL of the dtd/entity, or null. If null, the Ant project
+ * basedir is assumed. If the location specifies a relative
+ * URL/pathname, it is resolved using the base. The default base
+ * for an external catalog file is the directory in which it is
+ * located.
+ */
+ private URL base = null;
+
+ //-- Methods ---------------------------------------------------------------
+
+ /**
+ * @param publicId uniquely identifies the resource.
+ */
+ public void setPublicId(String publicId) {
+ this.publicId = publicId;
+ }
+
+ /**
+ * @param location the location of the resource associated with the
+ * publicId.
+ */
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * @param base the base URL of the resource associated with the
+ * publicId. If the location specifies a relative URL/pathname,
+ * it is resolved using the base. The default base for an
+ * external catalog file is the directory in which it is located.
+ */
+ public void setBase(URL base) {
+ this.base = base;
+ }
+
+ /**
+ * @return the publicId of the resource.
+ */
+ public String getPublicId() {
+ return publicId;
+ }
+
+ /**
+ * @return the location of the resource identified by the publicId.
+ */
+ public String getLocation() {
+ return location;
+ }
+
+ /**
+ * @return the base of the resource identified by the publicId.
+ */
+ public URL getBase() {
+ return base;
+ }
+
+} //-- ResourceLocation
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Substitution.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Substitution.java
new file mode 100644
index 00000000..343f4c49
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/Substitution.java
@@ -0,0 +1,77 @@
+/*
+ * 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.tools.ant.types;
+
+
+import org.apache.tools.ant.Project;
+
+/***
+ * A regular expression substitution datatype. It is an expression
+ * that is meant to replace a regular expression.
+ *
+ * <pre>
+ * &lt;substitution [ [id="id"] expression="expression" | refid="id" ]
+ * /&gt;
+ * </pre>
+ *
+ * @see org.apache.oro.text.regex.Perl5Substitution
+ */
+public class Substitution extends DataType {
+ /** The name of this data type */
+ public static final String DATA_TYPE_NAME = "substitution";
+
+ private String expression;
+
+ /** Constructor for Substitution. */
+ public Substitution() {
+ this.expression = null;
+ }
+
+ /**
+ * Set the pattern string for this regular expression substitution.
+ * @param expression the regular expression to use
+ */
+ public void setExpression(String expression) {
+ this.expression = expression;
+ }
+
+ /***
+ * Gets the pattern string for this RegularExpression in the
+ * given project.
+ * @param p the project to look for the regular expression if this object is
+ * a reference
+ * @return the pattern string
+ */
+ public String getExpression(Project p) {
+ if (isReference()) {
+ return getRef(p).getExpression(p);
+ }
+
+ return expression;
+ }
+
+ /***
+ * Get the RegularExpression this reference refers to in
+ * the given project. Check for circular references too.
+ * @param p the project to look for the regular expression reference
+ * @return the resolved reference
+ */
+ public Substitution getRef(Project p) {
+ return (Substitution) getCheckedRef(p);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/TarFileSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/TarFileSet.java
new file mode 100644
index 00000000..6446e9bf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/TarFileSet.java
@@ -0,0 +1,269 @@
+/*
+ * 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.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * A TarFileSet is a FileSet with extra attributes useful in the context of
+ * Tar/Jar tasks.
+ *
+ * A TarFileSet extends FileSets with the ability to extract a subset of the
+ * entries of a Tar file for inclusion in another Tar file. It also includes
+ * a prefix attribute which is prepended to each entry in the output Tar file.
+ *
+ */
+public class TarFileSet extends ArchiveFileSet {
+
+ private boolean userNameSet;
+ private boolean groupNameSet;
+ private boolean userIdSet;
+ private boolean groupIdSet;
+
+ private String userName = "";
+ private String groupName = "";
+ private int uid;
+ private int gid;
+
+ /** Constructor for TarFileSet */
+ public TarFileSet() {
+ super();
+ }
+
+ /**
+ * Constructor using a fileset argument.
+ * @param fileset the fileset to use
+ */
+ protected TarFileSet(FileSet fileset) {
+ super(fileset);
+ }
+
+ /**
+ * Constructor using a tarfileset argument.
+ * @param fileset the tarfileset to use
+ */
+ protected TarFileSet(TarFileSet fileset) {
+ super(fileset);
+ }
+
+ /**
+ * The username for the tar entry
+ * This is not the same as the UID.
+ * @param userName the user name for the tar entry.
+ */
+ public void setUserName(String userName) {
+ checkTarFileSetAttributesAllowed();
+ userNameSet = true;
+ this.userName = userName;
+ }
+
+ /**
+ * @return the user name for the tar entry
+ */
+ public String getUserName() {
+ if (isReference()) {
+ return ((TarFileSet) getCheckedRef()).getUserName();
+ }
+ return userName;
+ }
+
+ /**
+ * @return whether the user name has been explicitly set.
+ */
+ public boolean hasUserNameBeenSet() {
+ return userNameSet;
+ }
+
+ /**
+ * The uid for the tar entry
+ * This is not the same as the User name.
+ * @param uid the id of the user for the tar entry.
+ */
+ public void setUid(int uid) {
+ checkTarFileSetAttributesAllowed();
+ userIdSet = true;
+ this.uid = uid;
+ }
+
+ /**
+ * @return the uid for the tar entry
+ */
+ public int getUid() {
+ if (isReference()) {
+ return ((TarFileSet) getCheckedRef()).getUid();
+ }
+ return uid;
+ }
+
+ /**
+ * @return whether the user id has been explicitly set.
+ */
+ public boolean hasUserIdBeenSet() {
+ return userIdSet;
+ }
+
+ /**
+ * The groupname for the tar entry; optional, default=""
+ * This is not the same as the GID.
+ * @param groupName the group name string.
+ */
+ public void setGroup(String groupName) {
+ checkTarFileSetAttributesAllowed();
+ groupNameSet = true;
+ this.groupName = groupName;
+ }
+
+ /**
+ * @return the group name string.
+ */
+ public String getGroup() {
+ if (isReference()) {
+ return ((TarFileSet) getCheckedRef()).getGroup();
+ }
+ return groupName;
+ }
+
+ /**
+ * @return whether the group name has been explicitly set.
+ */
+ public boolean hasGroupBeenSet() {
+ return groupNameSet;
+ }
+
+ /**
+ * The GID for the tar entry; optional, default="0"
+ * This is not the same as the group name.
+ * @param gid the group id.
+ */
+ public void setGid(int gid) {
+ checkTarFileSetAttributesAllowed();
+ groupIdSet = true;
+ this.gid = gid;
+ }
+
+ /**
+ * @return the group identifier.
+ */
+ public int getGid() {
+ if (isReference()) {
+ return ((TarFileSet) getCheckedRef()).getGid();
+ }
+ return gid;
+ }
+
+ /**
+ * @return whether the group id has been explicitly set.
+ */
+ public boolean hasGroupIdBeenSet() {
+ return groupIdSet;
+ }
+
+ /**
+ * Create a new scanner.
+ * @return the created scanner.
+ */
+ protected ArchiveScanner newArchiveScanner() {
+ TarScanner zs = new TarScanner();
+ zs.setEncoding(getEncoding());
+ return zs;
+ }
+
+ /**
+ * Makes this instance in effect a reference to another instance.
+ *
+ * <p>You must not set another attribute or nest elements inside
+ * this element if you make it a reference.</p>
+ * @param r the <code>Reference</code> to use.
+ * @throws BuildException on error
+ */
+ public void setRefid(Reference r) throws BuildException {
+ if (userNameSet || userIdSet || groupNameSet || groupIdSet) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * A TarFileset accepts another TarFileSet or a FileSet as reference
+ * FileSets are often used by the war task for the lib attribute
+ * @param p the project to use
+ * @return the abstract fileset instance
+ */
+ protected AbstractFileSet getRef(Project p) {
+ dieOnCircularReference(p);
+ Object o = getRefid().getReferencedObject(p);
+ if (o instanceof TarFileSet) {
+ return (AbstractFileSet) o;
+ } else if (o instanceof FileSet) {
+ TarFileSet zfs = new TarFileSet((FileSet) o);
+ configureFileSet(zfs);
+ return zfs;
+ } else {
+ String msg = getRefid().getRefId() + " doesn\'t denote a tarfileset or a fileset";
+ throw new BuildException(msg);
+ }
+ }
+
+ /**
+ * Configure a fileset based on this fileset.
+ * If the fileset is a TarFileSet copy in the tarfileset
+ * specific attributes.
+ * @param zfs the archive fileset to configure.
+ */
+ protected void configureFileSet(ArchiveFileSet zfs) {
+ super.configureFileSet(zfs);
+ if (zfs instanceof TarFileSet) {
+ TarFileSet tfs = (TarFileSet) zfs;
+ tfs.setUserName(userName);
+ tfs.setGroup(groupName);
+ tfs.setUid(uid);
+ tfs.setGid(gid);
+ }
+ }
+
+ /**
+ * Return a TarFileSet that has the same properties
+ * as this one.
+ * @return the cloned tarFileSet
+ */
+ public Object clone() {
+ if (isReference()) {
+ return ((TarFileSet) getRef(getProject())).clone();
+ } else {
+ return super.clone();
+ }
+ }
+
+ /**
+ * A check attributes for TarFileSet.
+ * If there is a reference, and
+ * it is a TarFileSet, the tar fileset attributes
+ * cannot be used.
+ */
+ private void checkTarFileSetAttributesAllowed() {
+ if (getProject() == null
+ || (isReference()
+ && (getRefid().getReferencedObject(
+ getProject())
+ instanceof TarFileSet))) {
+ checkAttributesAllowed();
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/TarScanner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/TarScanner.java
new file mode 100644
index 00000000..a3c7f6d5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/TarScanner.java
@@ -0,0 +1,87 @@
+/*
+ * 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.tools.ant.types;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.resources.TarResource;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.tar.TarEntry;
+import org.apache.tools.tar.TarInputStream;
+
+/**
+ * Scans tar archives for resources.
+ */
+public class TarScanner extends ArchiveScanner {
+
+ /**
+ * Fills the file and directory maps with resources read from the
+ * archive.
+ *
+ * @param src the archive to scan.
+ * @param encoding encoding used to encode file names inside the archive.
+ * @param fileEntries Map (name to resource) of non-directory
+ * resources found inside the archive.
+ * @param matchFileEntries Map (name to resource) of non-directory
+ * resources found inside the archive that matched all include
+ * patterns and didn't match any exclude patterns.
+ * @param dirEntries Map (name to resource) of directory
+ * resources found inside the archive.
+ * @param matchDirEntries Map (name to resource) of directory
+ * resources found inside the archive that matched all include
+ * patterns and didn't match any exclude patterns.
+ */
+ protected void fillMapsFromArchive(Resource src, String encoding,
+ Map<String, Resource> fileEntries, Map<String, Resource> matchFileEntries,
+ Map<String, Resource> dirEntries, Map<String, Resource> matchDirEntries) {
+
+ TarEntry entry = null;
+ TarInputStream ti = null;
+
+ try {
+ try {
+ ti = new TarInputStream(src.getInputStream(), encoding);
+ } catch (IOException ex) {
+ throw new BuildException("problem opening " + srcFile, ex);
+ }
+ while ((entry = ti.getNextEntry()) != null) {
+ Resource r = new TarResource(src, entry);
+ String name = entry.getName();
+ if (entry.isDirectory()) {
+ name = trimSeparator(name);
+ dirEntries.put(name, r);
+ if (match(name)) {
+ matchDirEntries.put(name, r);
+ }
+ } else {
+ fileEntries.put(name, r);
+ if (match(name)) {
+ matchFileEntries.put(name, r);
+ }
+ }
+ }
+ } catch (IOException ex) {
+ throw new BuildException("problem reading " + srcFile, ex);
+ } finally {
+ FileUtils.close(ti);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/TimeComparison.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/TimeComparison.java
new file mode 100644
index 00000000..15c136e6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/TimeComparison.java
@@ -0,0 +1,122 @@
+/*
+ * 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.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * EnumeratedAttribute for time comparisons. Accepts values
+ * "before", "after", "equal".
+ * @since Ant 1.7
+ */
+public class TimeComparison extends EnumeratedAttribute {
+ private static final String[] VALUES
+ = new String[] {"before", "after", "equal"};
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** Before Comparison. */
+ public static final TimeComparison BEFORE = new TimeComparison("before");
+
+ /** After Comparison. */
+ public static final TimeComparison AFTER = new TimeComparison("after");
+
+ /** Equal Comparison. */
+ public static final TimeComparison EQUAL = new TimeComparison("equal");
+
+ /**
+ * Default constructor.
+ */
+ public TimeComparison() {
+ }
+
+ /**
+ * Construct a new TimeComparison with the specified value.
+ * @param value the EnumeratedAttribute value.
+ */
+ public TimeComparison(String value) {
+ setValue(value);
+ }
+
+ /**
+ * Return the possible values.
+ * @return String[] of EnumeratedAttribute values.
+ */
+ public String[] getValues() {
+ return VALUES;
+ }
+
+ /**
+ * Evaluate two times against this TimeComparison.
+ * @param t1 the first time to compare.
+ * @param t2 the second time to compare.
+ * @return true if the comparison result fell within the parameters of this TimeComparison.
+ */
+ public boolean evaluate(long t1, long t2) {
+ return evaluate(t1, t2, FILE_UTILS.getFileTimestampGranularity());
+ }
+
+ /**
+ * Evaluate two times against this TimeComparison.
+ * @param t1 the first time to compare.
+ * @param t2 the second time to compare.
+ * @param g the timestamp granularity.
+ * @return true if the comparison result fell within the parameters of this TimeComparison.
+ */
+ public boolean evaluate(long t1, long t2, long g) {
+ int cmp = getIndex();
+ if (cmp == -1) {
+ throw new BuildException("TimeComparison value not set.");
+ }
+ if (cmp == 0) {
+ return t1 - g < t2;
+ }
+ if (cmp == 1) {
+ return t1 + g > t2;
+ }
+ return Math.abs(t1 - t2) <= g;
+ }
+
+ /**
+ * Compare two times.
+ * @param t1 the first time to compare.
+ * @param t2 the second time to compare.
+ * @return a negative integer, a positive integer, or zero as t1 is
+ * before, after, or equal to t2 accounting for the default granularity.
+ */
+ public static int compare(long t1, long t2) {
+ return compare(t1, t2, FILE_UTILS.getFileTimestampGranularity());
+ }
+
+ /**
+ * Compare two times.
+ * @param t1 the first time to compare.
+ * @param t2 the second time to compare.
+ * @param g the timestamp granularity.
+ * @return a negative integer, a positive integer, or zero as t1 is
+ * before, after, or equal to t2 accounting for the specified granularity.
+ */
+ public static int compare(long t1, long t2, long g) {
+ long diff = t1 - t2;
+ long abs = Math.abs(diff);
+ return abs > Math.abs(g) ? (int) (diff / abs) : 0;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/XMLCatalog.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/XMLCatalog.java
new file mode 100644
index 00000000..bd9be431
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/XMLCatalog.java
@@ -0,0 +1,1128 @@
+/*
+ * 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.tools.ant.types;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Stack;
+import java.util.Vector;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXSource;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+
+
+/**
+ * <p>This data type provides a catalog of resource locations (such as
+ * DTDs and XML entities), based on the <a
+ * href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
+ * OASIS "Open Catalog" standard</a>. The catalog entries are used
+ * both for Entity resolution and URI resolution, in accordance with
+ * the {@link org.xml.sax.EntityResolver EntityResolver} and {@link
+ * javax.xml.transform.URIResolver URIResolver} interfaces as defined
+ * in the <a href="http://java.sun.com/xml/jaxp">Java API for XML
+ * Processing Specification</a>.</p>
+ *
+ * <p>Resource locations can be specified either in-line or in
+ * external catalog file(s), or both. In order to use an external
+ * catalog file, the xml-commons resolver library ("resolver.jar")
+ * must be in your classpath. External catalog files may be either <a
+ * href="http://oasis-open.org/committees/entity/background/9401.html">
+ * plain text format</a> or <a
+ * href="http://www.oasis-open.org/committees/entity/spec-2001-08-06.html">
+ * XML format</a>. If the xml-commons resolver library is not found
+ * in the classpath, external catalog files, specified in
+ * <code>&lt;catalogpath&gt;</code> paths, will be ignored and a warning will
+ * be logged. In this case, however, processing of inline entries will proceed
+ * normally.</p>
+ *
+ * <p>Currently, only <code>&lt;dtd&gt;</code> and
+ * <code>&lt;entity&gt;</code> elements may be specified inline; these
+ * correspond to OASIS catalog entry types <code>PUBLIC</code> and
+ * <code>URI</code> respectively.</p>
+ *
+ * <p>The following is a usage example:</p>
+ *
+ * <code>
+ * &lt;xmlcatalog&gt;<br>
+ * &nbsp;&nbsp;&lt;dtd publicId="" location="/path/to/file.jar" /&gt;<br>
+ * &nbsp;&nbsp;&lt;dtd publicId="" location="/path/to/file2.jar" /&gt;<br>
+ * &nbsp;&nbsp;&lt;entity publicId="" location="/path/to/file3.jar" /&gt;<br>
+ * &nbsp;&nbsp;&lt;entity publicId="" location="/path/to/file4.jar" /&gt;<br>
+ * &nbsp;&nbsp;&lt;catalogpath&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/etc/sgml/catalog"/&gt;<br>
+ * &nbsp;&nbsp;&lt;/catalogpath&gt;<br>
+ * &nbsp;&nbsp;&lt;catalogfiles dir="/opt/catalogs/" includes="**\catalog.xml" /&gt;<br>
+ * &lt;/xmlcatalog&gt;<br>
+ * </code>
+ * <p>
+ * Tasks wishing to use <code>&lt;xmlcatalog&gt;</code> must provide a method called
+ * <code>createXMLCatalog</code> which returns an instance of
+ * <code>XMLCatalog</code>. Nested DTD and entity definitions are handled by
+ * the XMLCatalog object and must be labeled <code>dtd</code> and
+ * <code>entity</code> respectively.</p>
+ *
+ * <p>The following is a description of the resolution algorithm:
+ * entities/URIs/dtds are looked up in each of the following contexts,
+ * stopping when a valid and readable resource is found:
+ * <ol>
+ * <li>In the local filesystem</li>
+ * <li>In the classpath</li>
+ * <li>Using the Apache xml-commons resolver (if it is available)</li>
+ * <li>In URL-space</li>
+ * </ol>
+ * </p>
+ *
+ * <p>See {@link
+ * org.apache.tools.ant.taskdefs.optional.XMLValidateTask
+ * XMLValidateTask} for an example of a task that has integrated
+ * support for XMLCatalogs.</p>
+ *
+ * <p>Possible future extension could provide for additional OASIS
+ * entry types to be specified inline.</p>
+ *
+ */
+public class XMLCatalog extends DataType
+ implements Cloneable, EntityResolver, URIResolver {
+
+ /** helper for some File.toURL connversions */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ //-- Fields ----------------------------------------------------------------
+
+ /** Holds dtd/entity objects until needed. */
+ private Vector<ResourceLocation> elements = new Vector<ResourceLocation>();
+
+ /**
+ * Classpath in which to attempt to resolve resources.
+ */
+ private Path classpath;
+
+ /**
+ * Path listing external catalog files to search when resolving entities
+ */
+ private Path catalogPath;
+
+ /**
+ * The name of the bridge to the Apache xml-commons resolver
+ * class, used to determine whether resolver.jar is present in the
+ * classpath.
+ */
+ public static final String APACHE_RESOLVER
+ = "org.apache.tools.ant.types.resolver.ApacheCatalogResolver";
+
+ /**
+ * Resolver base class
+ */
+ public static final String CATALOG_RESOLVER
+ = "org.apache.xml.resolver.tools.CatalogResolver";
+
+ //-- Methods ---------------------------------------------------------------
+
+ /**
+ * Default constructor
+ */
+ public XMLCatalog() {
+ setChecked(false);
+ }
+
+ /**
+ * Returns the elements of the catalog - ResourceLocation objects.
+ *
+ * @return the elements of the catalog - ResourceLocation objects
+ */
+ private Vector<ResourceLocation> getElements() {
+ return getRef().elements;
+ }
+
+ /**
+ * Returns the classpath in which to attempt to resolve resources.
+ *
+ * @return the classpath
+ */
+ private Path getClasspath() {
+ return getRef().classpath;
+ }
+
+ /**
+ * Allows nested classpath elements. Not allowed if this catalog
+ * is itself a reference to another catalog -- that is, a catalog
+ * cannot both refer to another <em>and</em> contain elements or
+ * other attributes.
+ *
+ * @return a Path instance to be configured.
+ */
+ public Path createClasspath() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (this.classpath == null) {
+ this.classpath = new Path(getProject());
+ }
+ setChecked(false);
+ return this.classpath.createPath();
+ }
+
+ /**
+ * Allows simple classpath string. Not allowed if this catalog is
+ * itself a reference to another catalog -- that is, a catalog
+ * cannot both refer to another <em>and</em> contain elements or
+ * other attributes.
+ *
+ * @param classpath the classpath to use to look up entities.
+ */
+ public void setClasspath(Path classpath) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (this.classpath == null) {
+ this.classpath = classpath;
+ } else {
+ this.classpath.append(classpath);
+ }
+ setChecked(false);
+ }
+
+ /**
+ * Allows classpath reference. Not allowed if this catalog is
+ * itself a reference to another catalog -- that is, a catalog
+ * cannot both refer to another <em>and</em> contain elements or
+ * other attributes.
+ *
+ * @param r an Ant reference containing a classpath.
+ */
+ public void setClasspathRef(Reference r) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ createClasspath().setRefid(r);
+ setChecked(false);
+ }
+
+ /** Creates a nested <code>&lt;catalogpath&gt;</code> element.
+ * Not allowed if this catalog is itself a reference to another
+ * catalog -- that is, a catalog cannot both refer to another
+ * <em>and</em> contain elements or other attributes.
+ *
+ * @return a path to be configured as the catalog path.
+ * @exception BuildException
+ * if this is a reference and no nested elements are allowed.
+ */
+ public Path createCatalogPath() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (this.catalogPath == null) {
+ this.catalogPath = new Path(getProject());
+ }
+ setChecked(false);
+ return this.catalogPath.createPath();
+ }
+
+ /**
+ * Allows catalogpath reference. Not allowed if this catalog is
+ * itself a reference to another catalog -- that is, a catalog
+ * cannot both refer to another <em>and</em> contain elements or
+ * other attributes.
+ *
+ * @param r an Ant reference containing a classpath to be used as
+ * the catalog path.
+ */
+ public void setCatalogPathRef(Reference r) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ createCatalogPath().setRefid(r);
+ setChecked(false);
+ }
+
+
+ /**
+ * Returns the catalog path in which to attempt to resolve DTDs.
+ *
+ * @return the catalog path
+ */
+ public Path getCatalogPath() {
+ return getRef().catalogPath;
+ }
+
+
+ /**
+ * Creates the nested <code>&lt;dtd&gt;</code> element. Not
+ * allowed if this catalog is itself a reference to another
+ * catalog -- that is, a catalog cannot both refer to another
+ * <em>and</em> contain elements or other attributes.
+ *
+ * @param dtd the information about the PUBLIC resource mapping to
+ * be added to the catalog
+ * @exception BuildException if this is a reference and no nested
+ * elements are allowed.
+ */
+ public void addDTD(ResourceLocation dtd) throws BuildException {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+
+ getElements().addElement(dtd);
+ setChecked(false);
+ }
+
+ /**
+ * Creates the nested <code>&lt;entity&gt;</code> element. Not
+ * allowed if this catalog is itself a reference to another
+ * catalog -- that is, a catalog cannot both refer to another
+ * <em>and</em> contain elements or other attributes.
+ *
+ * @param entity the information about the URI resource mapping to be
+ * added to the catalog.
+ * @exception BuildException if this is a reference and no nested
+ * elements are allowed.
+ */
+ public void addEntity(ResourceLocation entity) throws BuildException {
+ addDTD(entity);
+ }
+
+ /**
+ * Loads a nested <code>&lt;xmlcatalog&gt;</code> into our
+ * definition. Not allowed if this catalog is itself a reference
+ * to another catalog -- that is, a catalog cannot both refer to
+ * another <em>and</em> contain elements or other attributes.
+ *
+ * @param catalog Nested XMLCatalog
+ */
+ public void addConfiguredXMLCatalog(XMLCatalog catalog) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+
+ // Add all nested elements to our catalog
+ getElements().addAll(catalog.getElements());
+
+ // Append the classpath of the nested catalog
+ Path nestedClasspath = catalog.getClasspath();
+ createClasspath().append(nestedClasspath);
+
+ // Append the catalog path of the nested catalog
+ Path nestedCatalogPath = catalog.getCatalogPath();
+ createCatalogPath().append(nestedCatalogPath);
+ setChecked(false);
+ }
+
+ /**
+ * Makes this instance in effect a reference to another XMLCatalog
+ * instance.
+ *
+ * <p>You must not set another attribute or nest elements inside
+ * this element if you make it a reference. That is, a catalog
+ * cannot both refer to another <em>and</em> contain elements or
+ * attributes.</p>
+ *
+ * @param r the reference to which this catalog instance is associated
+ * @exception BuildException if this instance already has been configured.
+ */
+ public void setRefid(Reference r) throws BuildException {
+ if (!elements.isEmpty()) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Implements the EntityResolver.resolveEntity() interface method.
+ * @param publicId the public id to resolve.
+ * @param systemId the system id to resolve.
+ * @throws SAXException if there is a parsing problem.
+ * @throws IOException if there is an IO problem.
+ * @return the resolved entity.
+ * @see org.xml.sax.EntityResolver#resolveEntity
+ */
+ public InputSource resolveEntity(String publicId, String systemId)
+ throws SAXException, IOException {
+
+ if (isReference()) {
+ return getRef().resolveEntity(publicId, systemId);
+ }
+
+ dieOnCircularReference();
+
+ log("resolveEntity: '" + publicId + "': '" + systemId + "'",
+ Project.MSG_DEBUG);
+
+ InputSource inputSource =
+ getCatalogResolver().resolveEntity(publicId, systemId);
+
+ if (inputSource == null) {
+ log("No matching catalog entry found, parser will use: '"
+ + systemId + "'", Project.MSG_DEBUG);
+ }
+
+ return inputSource;
+ }
+
+ /**
+ * Implements the URIResolver.resolve() interface method.
+ * @param href an href attribute.
+ * @param base the base URI.
+ * @return a Source object, or null if href cannot be resolved.
+ * @throws TransformerException if an error occurs.
+ * @see javax.xml.transform.URIResolver#resolve
+ */
+ public Source resolve(String href, String base)
+ throws TransformerException {
+
+ if (isReference()) {
+ return getRef().resolve(href, base);
+ }
+
+ dieOnCircularReference();
+
+ SAXSource source = null;
+
+ String uri = removeFragment(href);
+
+ log("resolve: '" + uri + "' with base: '" + base + "'", Project.MSG_DEBUG);
+
+ source = (SAXSource) getCatalogResolver().resolve(uri, base);
+
+ if (source == null) {
+ log("No matching catalog entry found, parser will use: '"
+ + href + "'", Project.MSG_DEBUG);
+ //
+ // Cannot return a null source, because we have to call
+ // setEntityResolver (see setEntityResolver javadoc comment)
+ //
+ source = new SAXSource();
+ URL baseURL = null;
+ try {
+ if (base == null) {
+ baseURL = FILE_UTILS.getFileURL(getProject().getBaseDir());
+ } else {
+ baseURL = new URL(base);
+ }
+ URL url = (uri.length() == 0 ? baseURL : new URL(baseURL, uri));
+ source.setInputSource(new InputSource(url.toString()));
+ } catch (MalformedURLException ex) {
+ // At this point we are probably in failure mode, but
+ // try to use the bare URI as a last gasp
+ source.setInputSource(new InputSource(uri));
+ }
+ }
+
+ setEntityResolver(source);
+ return source;
+ }
+
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ if (classpath != null) {
+ pushAndInvokeCircularReferenceCheck(classpath, stk, p);
+ }
+ if (catalogPath != null) {
+ pushAndInvokeCircularReferenceCheck(catalogPath, stk, p);
+ }
+ setChecked(true);
+ }
+ }
+
+ /**
+ * @since Ant 1.6
+ */
+ private XMLCatalog getRef() {
+ if (!isReference()) {
+ return this;
+ }
+ return getCheckedRef(XMLCatalog.class, "xmlcatalog");
+ }
+
+ /**
+ * The instance of the CatalogResolver strategy to use.
+ */
+ private CatalogResolver catalogResolver = null;
+
+ /**
+ * Factory method for creating the appropriate CatalogResolver
+ * strategy implementation.
+ * <p> Until we query the classpath, we don't know whether the Apache
+ * resolver (Norm Walsh's library from xml-commons) is available or not.
+ * This method determines whether the library is available and creates the
+ * appropriate implementation of CatalogResolver based on the answer.</p>
+ * <p>This is an application of the Gang of Four Strategy Pattern
+ * combined with Template Method.</p>
+ */
+ private CatalogResolver getCatalogResolver() {
+
+ if (catalogResolver == null) {
+
+ AntClassLoader loader = null;
+ // Memory-Leak in line below
+ loader = getProject().createClassLoader(Path.systemClasspath);
+
+ try {
+ Class<?> clazz = Class.forName(APACHE_RESOLVER, true, loader);
+
+ // The Apache resolver is present - Need to check if it can
+ // be seen by the catalog resolver class. Start by getting
+ // the actual loader
+ ClassLoader apacheResolverLoader = clazz.getClassLoader();
+
+ // load the base class through this loader.
+ Class<?> baseResolverClass
+ = Class.forName(CATALOG_RESOLVER, true, apacheResolverLoader);
+
+ // and find its actual loader
+ ClassLoader baseResolverLoader
+ = baseResolverClass.getClassLoader();
+
+ // We have the loader which is being used to load the
+ // CatalogResolver. Can it see the ApacheResolver? The
+ // base resolver will only be able to create the ApacheResolver
+ // if it can see it - doesn't use the context loader.
+ clazz = Class.forName(APACHE_RESOLVER, true, baseResolverLoader);
+
+ Object obj = clazz.newInstance();
+ //
+ // Success! The xml-commons resolver library is
+ // available, so use it.
+ //
+ catalogResolver = new ExternalResolver(clazz, obj);
+ } catch (Throwable ex) {
+ //
+ // The xml-commons resolver library is not
+ // available, so we can't use it.
+ //
+ catalogResolver = new InternalResolver();
+ if (getCatalogPath() != null
+ && getCatalogPath().list().length != 0) {
+ log("Warning: XML resolver not found; external catalogs"
+ + " will be ignored", Project.MSG_WARN);
+ }
+ log("Failed to load Apache resolver: " + ex, Project.MSG_DEBUG);
+ }
+ }
+ return catalogResolver;
+ }
+
+ /**
+ * <p>This is called from the URIResolver to set an EntityResolver
+ * on the SAX parser to be used for new XML documents that are
+ * encountered as a result of the document() function, xsl:import,
+ * or xsl:include. This is done because the XSLT processor calls
+ * out to the SAXParserFactory itself to create a new SAXParser to
+ * parse the new document. The new parser does not automatically
+ * inherit the EntityResolver of the original (although arguably
+ * it should). See below:</p>
+ *
+ * <tt>"If an application wants to set the ErrorHandler or
+ * EntityResolver for an XMLReader used during a transformation,
+ * it should use a URIResolver to return the SAXSource which
+ * provides (with getXMLReader) a reference to the XMLReader"</tt>
+ *
+ * <p>...quoted from page 118 of the Java API for XML
+ * Processing 1.1 specification</p>
+ *
+ */
+ private void setEntityResolver(SAXSource source) throws TransformerException {
+
+ XMLReader reader = source.getXMLReader();
+ if (reader == null) {
+ SAXParserFactory spFactory = SAXParserFactory.newInstance();
+ spFactory.setNamespaceAware(true);
+ try {
+ reader = spFactory.newSAXParser().getXMLReader();
+ } catch (ParserConfigurationException ex) {
+ throw new TransformerException(ex);
+ } catch (SAXException ex) {
+ throw new TransformerException(ex);
+ }
+ }
+ reader.setEntityResolver(this);
+ source.setXMLReader(reader);
+ }
+
+ /**
+ * Find a ResourceLocation instance for the given publicId.
+ *
+ * @param publicId the publicId of the Resource for which local information
+ * is required.
+ * @return a ResourceLocation instance with information on the local location
+ * of the Resource or null if no such information is available.
+ */
+ private ResourceLocation findMatchingEntry(String publicId) {
+ for (ResourceLocation element : getElements()) {
+ if (element.getPublicId().equals(publicId)) {
+ return element;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Utility method to remove trailing fragment from a URI.
+ * For example,
+ * <code>http://java.sun.com/index.html#chapter1</code>
+ * would return <code>http://java.sun.com/index.html</code>.
+ *
+ * @param uri The URI to process. It may or may not contain a
+ * fragment.
+ * @return The URI sans fragment.
+ */
+ private String removeFragment(String uri) {
+ String result = uri;
+ int hashPos = uri.indexOf("#");
+ if (hashPos >= 0) {
+ result = uri.substring(0, hashPos);
+ }
+ return result;
+ }
+
+ /**
+ * Utility method to lookup a ResourceLocation in the filesystem.
+ *
+ * @return An InputSource for reading the file, or <code>null</code>
+ * if the file does not exist or is not readable.
+ */
+ private InputSource filesystemLookup(ResourceLocation matchingEntry) {
+
+ String uri = matchingEntry.getLocation();
+ // the following line seems to be necessary on Windows under JDK 1.2
+ uri = uri.replace(File.separatorChar, '/');
+ URL baseURL = null;
+
+ //
+ // The ResourceLocation may specify a relative path for its
+ // location attribute. This is resolved using the appropriate
+ // base.
+ //
+ if (matchingEntry.getBase() != null) {
+ baseURL = matchingEntry.getBase();
+ } else {
+ try {
+ baseURL = FILE_UTILS.getFileURL(getProject().getBaseDir());
+ } catch (MalformedURLException ex) {
+ throw new BuildException("Project basedir cannot be converted to a URL");
+ }
+ }
+
+ InputSource source = null;
+ URL url = null;
+ try {
+ url = new URL(baseURL, uri);
+ } catch (MalformedURLException ex) {
+ // this processing is useful under Windows when the location of the DTD
+ // has been given as an absolute path
+ // see Bugzilla Report 23913
+ File testFile = new File(uri);
+ if (testFile.exists() && testFile.canRead()) {
+ log("uri : '"
+ + uri + "' matches a readable file", Project.MSG_DEBUG);
+ try {
+ url = FILE_UTILS.getFileURL(testFile);
+ } catch (MalformedURLException ex1) {
+ throw new BuildException(
+ "could not find an URL for :" + testFile.getAbsolutePath());
+ }
+ } else {
+ log("uri : '"
+ + uri + "' does not match a readable file", Project.MSG_DEBUG);
+
+ }
+ }
+
+ if (url != null && url.getProtocol().equals("file")) {
+ String fileName = FILE_UTILS.fromURI(url.toString());
+ if (fileName != null) {
+ log("fileName " + fileName, Project.MSG_DEBUG);
+ File resFile = new File(fileName);
+ if (resFile.exists() && resFile.canRead()) {
+ try {
+ source = new InputSource(new FileInputStream(resFile));
+ String sysid = JAXPUtils.getSystemId(resFile);
+ source.setSystemId(sysid);
+ log("catalog entry matched a readable file: '"
+ + sysid + "'", Project.MSG_DEBUG);
+ } catch (IOException ex) {
+ // ignore
+ }
+ }
+ }
+ }
+ return source;
+ }
+
+ /**
+ * Utility method to lookup a ResourceLocation in the classpath.
+ *
+ * @return An InputSource for reading the resource, or <code>null</code>
+ * if the resource does not exist in the classpath or is not readable.
+ */
+ private InputSource classpathLookup(ResourceLocation matchingEntry) {
+
+ InputSource source = null;
+
+ AntClassLoader loader = null;
+ Path cp = classpath;
+ if (cp != null) {
+ cp = classpath.concatSystemClasspath("ignore");
+ } else {
+ cp = (new Path(getProject())).concatSystemClasspath("last");
+ }
+ loader = getProject().createClassLoader(cp);
+
+ //
+ // for classpath lookup we ignore the base directory
+ //
+ InputStream is
+ = loader.getResourceAsStream(matchingEntry.getLocation());
+
+ if (is != null) {
+ source = new InputSource(is);
+ URL entryURL = loader.getResource(matchingEntry.getLocation());
+ String sysid = entryURL.toExternalForm();
+ source.setSystemId(sysid);
+ log("catalog entry matched a resource in the classpath: '"
+ + sysid + "'", Project.MSG_DEBUG);
+ }
+
+ return source;
+ }
+
+ /**
+ * Utility method to lookup a ResourceLocation in URL-space.
+ *
+ * @return An InputSource for reading the resource, or <code>null</code>
+ * if the resource does not identify a valid URL or is not readable.
+ */
+ private InputSource urlLookup(ResourceLocation matchingEntry) {
+
+ String uri = matchingEntry.getLocation();
+ URL baseURL = null;
+
+ //
+ // The ResourceLocation may specify a relative url for its
+ // location attribute. This is resolved using the appropriate
+ // base.
+ //
+ if (matchingEntry.getBase() != null) {
+ baseURL = matchingEntry.getBase();
+ } else {
+ try {
+ baseURL = FILE_UTILS.getFileURL(getProject().getBaseDir());
+ } catch (MalformedURLException ex) {
+ throw new BuildException("Project basedir cannot be converted to a URL");
+ }
+ }
+
+ InputSource source = null;
+ URL url = null;
+
+ try {
+ url = new URL(baseURL, uri);
+ } catch (MalformedURLException ex) {
+ // ignore
+ }
+
+ if (url != null) {
+ try {
+ InputStream is = null;
+ URLConnection conn = url.openConnection();
+ if (conn != null) {
+ conn.setUseCaches(false);
+ is = conn.getInputStream();
+ }
+ if (is != null) {
+ source = new InputSource(is);
+ String sysid = url.toExternalForm();
+ source.setSystemId(sysid);
+ log("catalog entry matched as a URL: '"
+ + sysid + "'", Project.MSG_DEBUG);
+ }
+ } catch (IOException ex) {
+ // ignore
+ }
+ }
+
+ return source;
+
+ }
+
+ /**
+ * Interface implemented by both the InternalResolver strategy and
+ * the ExternalResolver strategy.
+ */
+ private interface CatalogResolver extends URIResolver, EntityResolver {
+
+ InputSource resolveEntity(String publicId, String systemId);
+
+ Source resolve(String href, String base) throws TransformerException;
+ }
+
+ /**
+ * The InternalResolver strategy is used if the Apache resolver
+ * library (Norm Walsh's library from xml-commons) is not
+ * available. In this case, external catalog files will be
+ * ignored.
+ *
+ */
+ private class InternalResolver implements CatalogResolver {
+
+ public InternalResolver() {
+ log("Apache resolver library not found, internal resolver will be used",
+ Project.MSG_VERBOSE);
+ }
+
+ public InputSource resolveEntity(String publicId,
+ String systemId) {
+ InputSource result = null;
+ ResourceLocation matchingEntry = findMatchingEntry(publicId);
+
+ if (matchingEntry != null) {
+
+ log("Matching catalog entry found for publicId: '"
+ + matchingEntry.getPublicId() + "' location: '"
+ + matchingEntry.getLocation() + "'",
+ Project.MSG_DEBUG);
+
+ result = filesystemLookup(matchingEntry);
+
+ if (result == null) {
+ result = classpathLookup(matchingEntry);
+ }
+
+ if (result == null) {
+ result = urlLookup(matchingEntry);
+ }
+ }
+ return result;
+ }
+
+ public Source resolve(String href, String base)
+ throws TransformerException {
+
+ SAXSource result = null;
+ InputSource source = null;
+
+ ResourceLocation matchingEntry = findMatchingEntry(href);
+
+ if (matchingEntry != null) {
+
+ log("Matching catalog entry found for uri: '"
+ + matchingEntry.getPublicId() + "' location: '"
+ + matchingEntry.getLocation() + "'",
+ Project.MSG_DEBUG);
+
+ //
+ // Use the passed in base in preference to the base
+ // from matchingEntry, which is either null or the
+ // directory in which the external catalog file from
+ // which it was obtained is located. We make a copy
+ // so matchingEntry's original base is untouched.
+ //
+ // This is the standard behavior as per my reading of
+ // the JAXP and XML Catalog specs. CKS 11/7/2002
+ //
+ ResourceLocation entryCopy = matchingEntry;
+ if (base != null) {
+ try {
+ URL baseURL = new URL(base);
+ entryCopy = new ResourceLocation();
+ entryCopy.setBase(baseURL);
+ } catch (MalformedURLException ex) {
+ // ignore
+ }
+ }
+ entryCopy.setPublicId(matchingEntry.getPublicId());
+ entryCopy.setLocation(matchingEntry.getLocation());
+
+ source = filesystemLookup(entryCopy);
+
+ if (source == null) {
+ source = classpathLookup(entryCopy);
+ }
+
+ if (source == null) {
+ source = urlLookup(entryCopy);
+ }
+
+ if (source != null) {
+ result = new SAXSource(source);
+ }
+ }
+ return result;
+ }
+ }
+
+ /**
+ * The ExternalResolver strategy is used if the Apache resolver
+ * library (Norm Walsh's library from xml-commons) is available in
+ * the classpath. The ExternalResolver is a essentially a superset
+ * of the InternalResolver.
+ *
+ */
+ private class ExternalResolver implements CatalogResolver {
+
+ private Method setXMLCatalog = null;
+ private Method parseCatalog = null;
+ private Method resolveEntity = null;
+ private Method resolve = null;
+
+ /** The instance of the ApacheCatalogResolver bridge class */
+ private Object resolverImpl = null;
+
+ private boolean externalCatalogsProcessed = false;
+
+ public ExternalResolver(Class<?> resolverImplClass,
+ Object resolverImpl) {
+
+ this.resolverImpl = resolverImpl;
+
+ //
+ // Get Method instances for each of the methods we need to
+ // call on the resolverImpl using reflection. We can't
+ // call them directly, because they require on the
+ // xml-commons resolver library which may not be available
+ // in the classpath.
+ //
+ try {
+ setXMLCatalog =
+ resolverImplClass.getMethod("setXMLCatalog",
+ new Class[] {XMLCatalog.class});
+
+ parseCatalog =
+ resolverImplClass.getMethod("parseCatalog",
+ new Class[] {String.class});
+
+ resolveEntity =
+ resolverImplClass.getMethod("resolveEntity",
+ new Class[] {String.class, String.class});
+
+ resolve =
+ resolverImplClass.getMethod("resolve",
+ new Class[] {String.class, String.class});
+ } catch (NoSuchMethodException ex) {
+ throw new BuildException(ex);
+ }
+
+ log("Apache resolver library found, xml-commons resolver will be used",
+ Project.MSG_VERBOSE);
+ }
+
+ public InputSource resolveEntity(String publicId,
+ String systemId) {
+ InputSource result = null;
+
+ processExternalCatalogs();
+
+ ResourceLocation matchingEntry = findMatchingEntry(publicId);
+
+ if (matchingEntry != null) {
+
+ log("Matching catalog entry found for publicId: '"
+ + matchingEntry.getPublicId() + "' location: '"
+ + matchingEntry.getLocation() + "'",
+ Project.MSG_DEBUG);
+
+ result = filesystemLookup(matchingEntry);
+
+ if (result == null) {
+ result = classpathLookup(matchingEntry);
+ }
+
+ if (result == null) {
+ try {
+ result =
+ (InputSource) resolveEntity.invoke(resolverImpl,
+ new Object[] {publicId, systemId});
+ } catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+ }
+ } else {
+ //
+ // We didn't match a ResourceLocation, but since we
+ // only support PUBLIC and URI entry types internally,
+ // it is still possible that there is another entry in
+ // an external catalog that will match. We call
+ // Apache resolver's resolveEntity method to cover
+ // this possibility.
+ //
+ try {
+ result =
+ (InputSource) resolveEntity.invoke(resolverImpl,
+ new Object[] {publicId, systemId});
+ } catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+ }
+
+ return result;
+ }
+
+ public Source resolve(String href, String base)
+ throws TransformerException {
+
+ SAXSource result = null;
+ InputSource source = null;
+
+ processExternalCatalogs();
+
+ ResourceLocation matchingEntry = findMatchingEntry(href);
+
+ if (matchingEntry != null) {
+
+ log("Matching catalog entry found for uri: '"
+ + matchingEntry.getPublicId() + "' location: '"
+ + matchingEntry.getLocation() + "'",
+ Project.MSG_DEBUG);
+
+ //
+ // Use the passed in base in preference to the base
+ // from matchingEntry, which is either null or the
+ // directory in which the external catalog file from
+ // which it was obtained is located. We make a copy
+ // so matchingEntry's original base is untouched. Of
+ // course, if there is no base, no need to make a
+ // copy...
+ //
+ // This is the standard behavior as per my reading of
+ // the JAXP and XML Catalog specs. CKS 11/7/2002
+ //
+ ResourceLocation entryCopy = matchingEntry;
+ if (base != null) {
+ try {
+ URL baseURL = new URL(base);
+ entryCopy = new ResourceLocation();
+ entryCopy.setBase(baseURL);
+ } catch (MalformedURLException ex) {
+ // ignore
+ }
+ }
+ entryCopy.setPublicId(matchingEntry.getPublicId());
+ entryCopy.setLocation(matchingEntry.getLocation());
+
+ source = filesystemLookup(entryCopy);
+
+ if (source == null) {
+ source = classpathLookup(entryCopy);
+ }
+
+ if (source != null) {
+ result = new SAXSource(source);
+ } else {
+ try {
+ result =
+ (SAXSource) resolve.invoke(resolverImpl,
+ new Object[] {href, base});
+ } catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+ }
+ } else {
+ //
+ // We didn't match a ResourceLocation, but since we
+ // only support PUBLIC and URI entry types internally,
+ // it is still possible that there is another entry in
+ // an external catalog that will match. We call
+ // Apache resolver's resolveEntity method to cover
+ // this possibility.
+ //
+ if (base == null) {
+ try {
+ base = FILE_UTILS.getFileURL(getProject().getBaseDir()).toString();
+ } catch (MalformedURLException x) {
+ throw new TransformerException(x);
+ }
+ }
+ try {
+ result =
+ (SAXSource) resolve.invoke(resolverImpl,
+ new Object[] {href, base});
+ } catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Process each external catalog file specified in a
+ * <code>&lt;catalogpath&gt;</code>. It will be
+ * parsed by the resolver library, and the individual elements
+ * will be added back to us (that is, the controlling
+ * XMLCatalog instance) via a callback mechanism.
+ */
+ private void processExternalCatalogs() {
+
+ if (!externalCatalogsProcessed) {
+
+ try {
+ setXMLCatalog.invoke(resolverImpl,
+ new Object[] {XMLCatalog.this});
+ } catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+
+ // Parse each catalog listed in nested <catalogpath> elements
+ Path catPath = getCatalogPath();
+ if (catPath != null) {
+ log("Using catalogpath '" + getCatalogPath() + "'",
+ Project.MSG_DEBUG);
+ String[] catPathList = getCatalogPath().list();
+
+ for (int i = 0; i < catPathList.length; i++) {
+ File catFile = new File(catPathList[i]);
+ log("Parsing " + catFile, Project.MSG_DEBUG);
+ try {
+ parseCatalog.invoke(resolverImpl,
+ new Object[] {catFile.getPath()});
+ } catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+ }
+ }
+ }
+ externalCatalogsProcessed = true;
+ }
+ }
+} //-- XMLCatalog
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ZipFileSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ZipFileSet.java
new file mode 100644
index 00000000..24f0ccd5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ZipFileSet.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.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * A ZipFileSet is a FileSet with extra attributes useful in the context of
+ * Zip/Jar tasks.
+ *
+ * A ZipFileSet extends FileSets with the ability to extract a subset of the
+ * entries of a Zip file for inclusion in another Zip file. It also includes
+ * a prefix attribute which is prepended to each entry in the output Zip file.
+ *
+ * Since ant 1.6 ZipFileSet can be defined with an id and referenced in packaging tasks
+ *
+ */
+public class ZipFileSet extends ArchiveFileSet {
+
+ /** Constructor for ZipFileSet */
+ public ZipFileSet() {
+ super();
+ }
+
+ /**
+ * Constructor using a fileset argument.
+ * @param fileset the fileset to use
+ */
+ protected ZipFileSet(FileSet fileset) {
+ super(fileset);
+ }
+
+ /**
+ * Constructor using a zipfileset argument.
+ * @param fileset the zipfileset to use
+ */
+ protected ZipFileSet(ZipFileSet fileset) {
+ super(fileset);
+ }
+
+ /**
+ * Return a new archive scanner based on this one.
+ * @return a new ZipScanner with the same encoding as this one.
+ */
+ protected ArchiveScanner newArchiveScanner() {
+ ZipScanner zs = new ZipScanner();
+ zs.setEncoding(getEncoding());
+ return zs;
+ }
+
+ /**
+ * A ZipFileset accepts another ZipFileSet or a FileSet as reference
+ * FileSets are often used by the war task for the lib attribute
+ * @param p the project to use
+ * @return the abstract fileset instance
+ */
+ protected AbstractFileSet getRef(Project p) {
+ dieOnCircularReference(p);
+ Object o = getRefid().getReferencedObject(p);
+ if (o instanceof ZipFileSet) {
+ return (AbstractFileSet) o;
+ } else if (o instanceof FileSet) {
+ ZipFileSet zfs = new ZipFileSet((FileSet) o);
+ configureFileSet(zfs);
+ return zfs;
+ } else {
+ String msg = getRefid().getRefId() + " doesn\'t denote a zipfileset or a fileset";
+ throw new BuildException(msg);
+ }
+ }
+
+ /**
+ * Return a ZipFileSet that has the same properties
+ * as this one.
+ * @return the cloned zipFileSet
+ */
+ public Object clone() {
+ if (isReference()) {
+ return ((ZipFileSet) getRef(getProject())).clone();
+ } else {
+ return super.clone();
+ }
+ }
+
+ /**
+ * A check attributes for zipFileSet.
+ * If there is a reference, and
+ * it is a ZipFileSet, the zip fileset attributes
+ * cannot be used.
+ */
+ private void checkZipFileSetAttributesAllowed() {
+ if (getProject() == null
+ || (isReference()
+ && (getRefid().getReferencedObject(
+ getProject())
+ instanceof ZipFileSet))) {
+ checkAttributesAllowed();
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ZipScanner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ZipScanner.java
new file mode 100644
index 00000000..e64b7172
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/ZipScanner.java
@@ -0,0 +1,99 @@
+/*
+ * 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.tools.ant.types;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.zip.ZipException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.ZipResource;
+import org.apache.tools.zip.ZipEntry;
+import org.apache.tools.zip.ZipFile;
+
+/**
+ * Scans zip archives for resources.
+ */
+public class ZipScanner extends ArchiveScanner {
+
+ /**
+ * Fills the file and directory maps with resources read from the
+ * archive.
+ *
+ * @param src the archive to scan.
+ * @param encoding encoding used to encode file names inside the archive.
+ * @param fileEntries Map (name to resource) of non-directory
+ * resources found inside the archive.
+ * @param matchFileEntries Map (name to resource) of non-directory
+ * resources found inside the archive that matched all include
+ * patterns and didn't match any exclude patterns.
+ * @param dirEntries Map (name to resource) of directory
+ * resources found inside the archive.
+ * @param matchDirEntries Map (name to resource) of directory
+ * resources found inside the archive that matched all include
+ * patterns and didn't match any exclude patterns.
+ */
+ protected void fillMapsFromArchive(Resource src, String encoding,
+ Map<String, Resource> fileEntries, Map<String, Resource> matchFileEntries,
+ Map<String, Resource> dirEntries, Map<String, Resource> matchDirEntries) {
+ ZipEntry entry = null;
+ ZipFile zf = null;
+
+ File srcFile = null;
+ FileProvider fp = src.as(FileProvider.class);
+ if (fp != null) {
+ srcFile = fp.getFile();
+ } else {
+ throw new BuildException("Only file provider resources are supported");
+ }
+
+ try {
+ try {
+ zf = new ZipFile(srcFile, encoding);
+ } catch (ZipException ex) {
+ throw new BuildException("Problem reading " + srcFile, ex);
+ } catch (IOException ex) {
+ throw new BuildException("Problem opening " + srcFile, ex);
+ }
+ Enumeration<ZipEntry> e = zf.getEntries();
+ while (e.hasMoreElements()) {
+ entry = e.nextElement();
+ Resource r = new ZipResource(srcFile, encoding, entry);
+ String name = entry.getName();
+ if (entry.isDirectory()) {
+ name = trimSeparator(name);
+ dirEntries.put(name, r);
+ if (match(name)) {
+ matchDirEntries.put(name, r);
+ }
+ } else {
+ fileEntries.put(name, r);
+ if (match(name)) {
+ matchFileEntries.put(name, r);
+ }
+ }
+ }
+ } finally {
+ ZipFile.closeQuietly(zf);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/conditions/antlib.xml b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/conditions/antlib.xml
new file mode 100644
index 00000000..ff407b28
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/conditions/antlib.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0"?>
+<antlib>
+ <!--
+/*
+ * 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.
+ *
+ */
+
+ -->
+ <!-- Ant 1.6+ antlib declaration for conditions:
+ Use with the declaration xmlns:cond="antlib:org.apache.tools.ant.types.conditions"
+ to
+ trigger Ant's autoload of this file into namespace 'cond' (or whatever name
+ suits).
+
+ Please keep this list in alphabetical order; it is easier to verify that way.
+
+ Additionally, ConditionBase uses this antlib to discover built-in conditions.
+ Prior to Ant 1.7, a new built-in condition required an addXXX method to be
+ added to ConditionBase. Conditions added in or after version 1.7 need only
+ to be added to this antlib.
+ -->
+
+ <typedef name="and" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.And"/>
+ <typedef name="antversion" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.AntVersion"/>
+ <typedef name="contains" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Contains"/>
+ <typedef name="equals" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Equals"/>
+ <typedef name="filesmatch" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.FilesMatch"/>
+ <typedef name="hasfreespace" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.HasFreeSpace"/>
+ <typedef name="http" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Http"/>
+ <typedef name="isfailure" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsFailure"/>
+ <typedef name="isfalse" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsFalse"/>
+ <typedef name="isfileselected" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsFileSelected"/>
+ <typedef name="islastmodified" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsLastModified"/>
+ <typedef name="isreachable" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsReachable"/>
+ <typedef name="isreference" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsReference"/>
+ <typedef name="isset" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsSet"/>
+ <typedef name="issigned" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsSigned"/>
+ <typedef name="istrue" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.IsTrue"/>
+ <typedef name="not" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Not"/>
+ <typedef name="matches" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Matches"/>
+ <typedef name="or" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Or"/>
+ <typedef name="os" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Os"/>
+ <typedef name="parsersupports" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.ParserSupports"/>
+ <typedef name="resourceexists" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.ResourceExists"/>
+ <typedef name="resourcesmatch" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.ResourcesMatch"/>
+ <typedef name="resourcecontains" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.ResourceContains"/>
+ <typedef name="scriptcondition" onerror="ignore"
+ classname="org.apache.tools.ant.types.optional.ScriptCondition"/>
+ <typedef name="socket" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Socket"/>
+ <typedef name="typefound" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.TypeFound"/>
+ <typedef name="xor" onerror="ignore"
+ classname="org.apache.tools.ant.taskdefs.condition.Xor"/>
+</antlib>
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/defaults.properties b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/defaults.properties
new file mode 100644
index 00000000..29771dbb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/defaults.properties
@@ -0,0 +1,101 @@
+# 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.
+#
+description=org.apache.tools.ant.types.Description
+filterchain=org.apache.tools.ant.types.FilterChain
+filterreader=org.apache.tools.ant.types.AntFilterReader
+filterset=org.apache.tools.ant.types.FilterSet
+mapper=org.apache.tools.ant.types.Mapper
+redirector=org.apache.tools.ant.types.RedirectorElement
+patternset=org.apache.tools.ant.types.PatternSet
+regexp=org.apache.tools.ant.types.RegularExpression
+substitution=org.apache.tools.ant.types.Substitution
+xmlcatalog=org.apache.tools.ant.types.XMLCatalog
+extensionSet=org.apache.tools.ant.taskdefs.optional.extension.ExtensionSet
+extension=org.apache.tools.ant.taskdefs.optional.extension.ExtensionAdapter
+selector=org.apache.tools.ant.types.selectors.SelectSelector
+signedselector=org.apache.tools.ant.types.selectors.SignedSelector
+scriptfilter=org.apache.tools.ant.types.optional.ScriptFilter
+assertions=org.apache.tools.ant.types.Assertions
+concatfilter=org.apache.tools.ant.filters.ConcatFilter
+mavenrepository=org.apache.tools.ant.taskdefs.repository.MavenRepository
+scriptselector=org.apache.tools.ant.types.optional.ScriptSelector
+scriptmapper=org.apache.tools.ant.types.optional.ScriptMapper
+
+# different filename mappers
+identitymapper=org.apache.tools.ant.util.IdentityMapper
+flattenmapper=org.apache.tools.ant.util.FlatFileNameMapper
+globmapper=org.apache.tools.ant.util.GlobPatternMapper
+mergemapper=org.apache.tools.ant.util.MergingMapper
+regexpmapper=org.apache.tools.ant.util.RegexpPatternMapper
+packagemapper=org.apache.tools.ant.util.PackageNameMapper
+unpackagemapper=org.apache.tools.ant.util.UnPackageNameMapper
+compositemapper=org.apache.tools.ant.util.CompositeMapper
+chainedmapper=org.apache.tools.ant.util.ChainedMapper
+filtermapper=org.apache.tools.ant.types.mappers.FilterMapper
+firstmatchmapper=org.apache.tools.ant.util.FirstMatchMapper
+cutdirsmapper=org.apache.tools.ant.types.mappers.CutDirsMapper
+
+#this condition is in here because it is the sole
+#condition defined in Ant1.6
+#please add new conditions to oata.types.conditions/antlib.xml instead of
+#here, to avoid namespace clash with things like selectors.
+isfileselected=org.apache.tools.ant.taskdefs.condition.IsFileSelected
+scriptcondition=org.apache.tools.ant.types.optional.ScriptCondition
+
+#ResourceCollections:
+dirset=org.apache.tools.ant.types.DirSet
+filelist=org.apache.tools.ant.types.FileList
+fileset=org.apache.tools.ant.types.FileSet
+path=org.apache.tools.ant.types.Path
+propertyset=org.apache.tools.ant.types.PropertySet
+zipfileset=org.apache.tools.ant.types.ZipFileSet
+classfileset=org.apache.tools.ant.types.optional.depend.ClassfileSet
+libfileset=org.apache.tools.ant.taskdefs.optional.extension.LibFileSet
+files=org.apache.tools.ant.types.resources.Files
+restrict=org.apache.tools.ant.types.resources.Restrict
+union=org.apache.tools.ant.types.resources.Union
+difference=org.apache.tools.ant.types.resources.Difference
+intersect=org.apache.tools.ant.types.resources.Intersect
+sort=org.apache.tools.ant.types.resources.Sort
+resources=org.apache.tools.ant.types.resources.Resources
+allbutfirst=org.apache.tools.ant.types.resources.AllButFirst
+allbutlast=org.apache.tools.ant.types.resources.AllButLast
+first=org.apache.tools.ant.types.resources.First
+last=org.apache.tools.ant.types.resources.Last
+tarfileset=org.apache.tools.ant.types.TarFileSet
+tokens=org.apache.tools.ant.types.resources.Tokens
+mappedresources=org.apache.tools.ant.types.resources.MappedResourceCollection
+archives=org.apache.tools.ant.types.resources.Archives
+resourcelist=org.apache.tools.ant.types.resources.ResourceList
+
+#Resources (single-element ResourceCollections):
+resource=org.apache.tools.ant.types.Resource
+file=org.apache.tools.ant.types.resources.FileResource
+url=org.apache.tools.ant.types.resources.URLResource
+string=org.apache.tools.ant.types.resources.StringResource
+zipentry=org.apache.tools.ant.types.resources.ZipResource
+propertyresource=org.apache.tools.ant.types.resources.PropertyResource
+tarentry=org.apache.tools.ant.types.resources.TarResource
+gzipresource=org.apache.tools.ant.types.resources.GZipResource
+bzip2resource=org.apache.tools.ant.types.resources.BZip2Resource
+javaresource=org.apache.tools.ant.types.resources.JavaResource
+multirootfileset=org.apache.tools.ant.types.resources.MultiRootFileSet
+javaconstant=org.apache.tools.ant.types.resources.JavaConstantResource
+
+#tokenizer implementations
+linetokenizer=org.apache.tools.ant.util.LineTokenizer
+stringtokenizer=org.apache.tools.ant.util.StringTokenizer
+filetokenizer=org.apache.tools.ant.util.FileTokenizer
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/mappers/CutDirsMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/mappers/CutDirsMapper.java
new file mode 100644
index 00000000..b9e7cfb4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/mappers/CutDirsMapper.java
@@ -0,0 +1,76 @@
+/*
+ * 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.tools.ant.types.mappers;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileNameMapper;
+
+/**
+ * A mapper that strips of the a configurable number of leading
+ * directories from a file name.
+ *
+ * <p>This mapper was inspired by a user-list thread that mentioned
+ * wget's --cut-dirs option.</p>
+ *
+ * @see <a href="http://mail-archives.apache.org/mod_mbox/ant-user/201009.mbox/%3C51772743BEA5D44A9EA5BF52AADDD6FB010E96F6@hammai008.delphi.local%3E">
+ * simplify copy with regexpmapper</a>
+ */
+public class CutDirsMapper implements FileNameMapper {
+ private int dirs = 0;
+
+ /**
+ * The number of leading directories to cut.
+ */
+ public void setDirs(final int dirs) {
+ this.dirs = dirs;
+ }
+
+ /**
+ * Empty implementation.
+ * @param ignore ignored.
+ */
+ public void setFrom(final String ignore) {
+ }
+
+ /**
+ * Empty implementation.
+ * @param ignore ignored.
+ */
+ public void setTo(final String ignore) {
+ }
+
+ /** {@inheritDoc}. */
+ public String[] mapFileName(final String sourceFileName) {
+ if (dirs <= 0) {
+ throw new BuildException("dirs must be set to a positive number");
+ }
+ final char fileSep = File.separatorChar;
+ final String fileSepCorrected =
+ sourceFileName.replace('/', fileSep).replace('\\', fileSep);
+ int nthMatch = fileSepCorrected.indexOf(fileSep);
+ for (int n = 1; nthMatch > -1 && n < dirs; n++) {
+ nthMatch = fileSepCorrected.indexOf(fileSep, nthMatch + 1);
+ }
+ if (nthMatch == -1) {
+ return null;
+ }
+ return new String[] {sourceFileName.substring(nthMatch + 1)};
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/mappers/FilterMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/mappers/FilterMapper.java
new file mode 100644
index 00000000..501da500
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/mappers/FilterMapper.java
@@ -0,0 +1,87 @@
+/*
+ * 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.tools.ant.types.mappers;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.UnsupportedAttributeException;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * This is a FileNameMapper based on a FilterChain.
+ */
+public class FilterMapper extends FilterChain implements FileNameMapper {
+
+ private static final int BUFFER_SIZE = 8192;
+
+ /**
+ * From attribute not supported.
+ * @param from a string
+ * @throws BuildException always
+ */
+ public void setFrom(String from) {
+ throw new UnsupportedAttributeException(
+ "filtermapper doesn't support the \"from\" attribute.", "from");
+ }
+
+ /**
+ * From attribute not supported.
+ * @param to a string
+ * @throws BuildException always
+ */
+ public void setTo(String to) {
+ throw new UnsupportedAttributeException(
+ "filtermapper doesn't support the \"to\" attribute.", "to");
+ }
+
+ /**
+ * Return the result of the filters on the sourcefilename.
+ * @param sourceFileName the filename to map
+ * @return a one-element array of converted filenames, or null if
+ * the filterchain returns an empty string.
+ */
+ public String[] mapFileName(String sourceFileName) {
+ try {
+ Reader stringReader = new StringReader(sourceFileName);
+ ChainReaderHelper helper = new ChainReaderHelper();
+ helper.setBufferSize(BUFFER_SIZE);
+ helper.setPrimaryReader(stringReader);
+ helper.setProject(getProject());
+ Vector<FilterChain> filterChains = new Vector<FilterChain>();
+ filterChains.add(this);
+ helper.setFilterChains(filterChains);
+ String result = FileUtils.safeReadFully(helper.getAssembledReader());
+ if (result.length() == 0) {
+ return null;
+ } else {
+ return new String[] {result};
+ }
+ } catch (BuildException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new BuildException(ex);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/AbstractScriptComponent.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/AbstractScriptComponent.java
new file mode 100644
index 00000000..df5a3cec
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/AbstractScriptComponent.java
@@ -0,0 +1,158 @@
+/*
+ * 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.tools.ant.types.optional;
+
+import java.io.File;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.ScriptRunnerBase;
+import org.apache.tools.ant.util.ScriptRunnerHelper;
+
+/**
+ * This is a {@link ProjectComponent} that has script support built in
+ * Use it as a foundation for scriptable things.
+ */
+public abstract class AbstractScriptComponent extends ProjectComponent {
+ /**
+ * script runner helper
+ */
+ private ScriptRunnerHelper helper = new ScriptRunnerHelper();
+
+ /**
+ * script runner.
+ */
+ private ScriptRunnerBase runner = null;
+
+ /**
+ * Set the project.
+ * @param project the owner of this component.
+ */
+ public void setProject(Project project) {
+ super.setProject(project);
+ helper.setProjectComponent(this);
+ }
+
+ /**
+ * Get our script runner
+ * @return the runner
+ */
+ public ScriptRunnerBase getRunner() {
+ initScriptRunner();
+ return runner;
+ }
+
+ /**
+ * Load the script from an external file ; optional.
+ *
+ * @param file the file containing the script source.
+ */
+ public void setSrc(File file) {
+ helper.setSrc(file);
+ }
+
+ /**
+ * The script text.
+ *
+ * @param text a component of the script text to be added.
+ */
+ public void addText(String text) {
+ helper.addText(text);
+ }
+
+ /**
+ * Defines the manager.
+ *
+ * @param manager the scripting manager.
+ */
+ public void setManager(String manager) {
+ helper.setManager(manager);
+ }
+
+ /**
+ * Defines the language (required).
+ *
+ * @param language the scripting language name for the script.
+ */
+ public void setLanguage(String language) {
+ helper.setLanguage(language);
+ }
+
+ /**
+ * Initialize the script runner. Calls this before running the system
+ */
+ protected void initScriptRunner() {
+ if (runner != null) {
+ return;
+ }
+ helper.setProjectComponent(this);
+ runner = helper.getScriptRunner();
+ }
+ /**
+ * Set the classpath to be used when searching for classes and resources.
+ *
+ * @param classpath an Ant Path object containing the search path.
+ */
+ public void setClasspath(Path classpath) {
+ helper.setClasspath(classpath);
+ }
+
+ /**
+ * Classpath to be used when searching for classes and resources.
+ *
+ * @return an empty Path instance to be configured by Ant.
+ */
+ public Path createClasspath() {
+ return helper.createClasspath();
+ }
+
+ /**
+ * Set the classpath by reference.
+ *
+ * @param r a Reference to a Path instance to be used as the classpath
+ * value.
+ */
+ public void setClasspathRef(Reference r) {
+ helper.setClasspathRef(r);
+ }
+
+ /**
+ * Run a script
+ * @param execName name of the script
+ */
+ protected void executeScript(String execName) {
+ getRunner().executeScript(execName);
+ }
+
+ /**
+ * Set the setbeans attribute.
+ * If this is true, &lt;script&gt; will create variables in the
+ * script instance for all
+ * properties, targets and references of the current project.
+ * It this is false, only the project and self variables will
+ * be set.
+ * The default is true.
+ * @param setBeans the value to set.
+ * @since Ant 1.8.0
+ */
+ public void setSetBeans(boolean setBeans) {
+ helper.setSetBeans(setBeans);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptCondition.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptCondition.java
new file mode 100644
index 00000000..fac02bfa
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptCondition.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.tools.ant.types.optional;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+
+/**
+ * A condition that lets you include script.
+ * The condition component sets a bean "self", whose attribute "value"
+ * must be set to true for the condition to succeed, false to fail.
+ * The default is 'false'
+ */
+public class ScriptCondition extends AbstractScriptComponent implements Condition {
+
+ /**
+ * result field
+ */
+ private boolean value = false;
+
+ /**
+ * Is this condition true?
+ *
+ * @return true if the condition is true
+ *
+ * @throws org.apache.tools.ant.BuildException
+ * if an error occurs
+ */
+ public boolean eval() throws BuildException {
+ initScriptRunner();
+ executeScript("ant_condition");
+ return getValue();
+ }
+
+ /**
+ * get the current value of the condition
+ * @return true if the condition
+ */
+ public boolean getValue() {
+ return value;
+ }
+
+ /**
+ * set the value of the condition.
+ * This is used by the script to pass the return value.
+ * It can be used by an attribute, in which case it sets the default
+ * value
+ * @param value the value to set the condition to
+ */
+ public void setValue(boolean value) {
+ this.value = value;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptFilter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptFilter.java
new file mode 100644
index 00000000..3f6ec88f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptFilter.java
@@ -0,0 +1,183 @@
+/*
+ * 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.tools.ant.types.optional;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.filters.TokenFilter;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.ScriptRunnerBase;
+import org.apache.tools.ant.util.ScriptRunnerHelper;
+
+/**
+ * Most of this is CAP (Cut And Paste) from the Script task
+ * ScriptFilter class, implements TokenFilter.Filter
+ * for scripts to use.
+ * This provides the same beans as the Script Task
+ * to a script.
+ * The script is meant to use get self.token and
+ * set self.token in the reply.
+ *
+ * @since Ant 1.6
+ */
+public class ScriptFilter extends TokenFilter.ChainableReaderFilter {
+ /** script runner helper */
+ private ScriptRunnerHelper helper = new ScriptRunnerHelper();
+
+ /** script runner. */
+ private ScriptRunnerBase runner = null;
+
+ /** the token used by the script */
+ private String token;
+
+ /**
+ * Set the project.
+ * @param project the owner of this component.
+ */
+ public void setProject(Project project) {
+ super.setProject(project);
+ helper.setProjectComponent(this);
+ }
+
+ /**
+ * Defines the language (required).
+ *
+ * @param language the scripting language name for the script.
+ */
+ public void setLanguage(String language) {
+ helper.setLanguage(language);
+ }
+
+ /**
+ * Initialize.
+ *
+ * @exception BuildException if someting goes wrong
+ */
+ private void init() throws BuildException {
+ if (runner != null) {
+ return;
+ }
+ runner = helper.getScriptRunner();
+ }
+
+ /**
+ * The current token
+ *
+ * @param token the string filtered by the script
+ */
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ /**
+ * The current token
+ *
+ * @return the string filtered by the script
+ */
+ public String getToken() {
+ return token;
+ }
+
+ /**
+ * Called filter the token.
+ * This sets the token in this object, calls
+ * the script and returns the token.
+ *
+ * @param token the token to be filtered
+ * @return the filtered token
+ */
+ public String filter(String token) {
+ init();
+ setToken(token);
+ runner.executeScript("ant_filter");
+ return getToken();
+ }
+
+ /**
+ * Load the script from an external file ; optional.
+ *
+ * @param file the file containing the script source.
+ */
+ public void setSrc(File file) {
+ helper.setSrc(file);
+ }
+
+ /**
+ * The script text.
+ *
+ * @param text a component of the script text to be added.
+ */
+ public void addText(String text) {
+ helper.addText(text);
+ }
+
+ /**
+ * Defines the manager.
+ *
+ * @param manager the scripting manager.
+ */
+ public void setManager(String manager) {
+ helper.setManager(manager);
+ }
+ /**
+ * Set the classpath to be used when searching for classes and resources.
+ *
+ * @param classpath an Ant Path object containing the search path.
+ */
+ public void setClasspath(Path classpath) {
+ helper.setClasspath(classpath);
+ }
+
+ /**
+ * Classpath to be used when searching for classes and resources.
+ *
+ * @return an empty Path instance to be configured by Ant.
+ */
+ public Path createClasspath() {
+ return helper.createClasspath();
+ }
+
+ /**
+ * Set the classpath by reference.
+ *
+ * @param r a Reference to a Path instance to be used as the classpath
+ * value.
+ */
+ public void setClasspathRef(Reference r) {
+ helper.setClasspathRef(r);
+ }
+
+ /**
+ * Set the setbeans attribute.
+ * If this is true, &lt;script&gt; will create variables in the
+ * script instance for all
+ * properties, targets and references of the current project.
+ * It this is false, only the project and self variables will
+ * be set.
+ * The default is true.
+ * @param setBeans the value to set.
+ * @since Ant 1.8.0
+ */
+ public void setSetBeans(boolean setBeans) {
+ helper.setSetBeans(setBeans);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptMapper.java
new file mode 100644
index 00000000..38dab0bb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptMapper.java
@@ -0,0 +1,91 @@
+/*
+ * 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.tools.ant.types.optional;
+
+import java.util.ArrayList;
+
+import org.apache.tools.ant.util.FileNameMapper;
+
+/**
+ * Script support at map time.
+ * @since Ant1.7
+ */
+public class ScriptMapper extends AbstractScriptComponent implements FileNameMapper {
+
+ private ArrayList<String> files;
+
+
+ /**
+ * Sets the from part of the transformation rule.
+ *
+ * @param from a string.
+ */
+ public void setFrom(String from) {
+
+ }
+
+ /**
+ * Sets the to part of the transformation rule.
+ *
+ * @param to a string.
+ */
+ public void setTo(String to) {
+
+ }
+
+ /**
+ * Reset the list of files
+ */
+ public void clear() {
+ files = new ArrayList<String>(1);
+ }
+
+ /**
+ * Add a mapped name
+ * @param mapping the value to use.
+ */
+ public void addMappedName(String mapping) {
+ files.add(mapping);
+ }
+
+ /**
+ * Returns an array containing the target filename(s) for the given source
+ * file.
+ * <p/>
+ * <p>if the given rule doesn't apply to the source file, implementation
+ * must return null. SourceFileScanner will then omit the source file in
+ * question.</p>
+ *
+ * @param sourceFileName the name of the source file relative to some given
+ * basedirectory.
+ * @return an array of strings if the rule applies to the source file, or
+ * null if it does not.
+ */
+
+ public String[] mapFileName(String sourceFileName) {
+ initScriptRunner();
+ getRunner().addBean("source", sourceFileName);
+ clear();
+ executeScript("ant_mapper");
+ if (files.size() == 0) {
+ return null;
+ } else {
+ return files.toArray(new String[files.size()]);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptSelector.java
new file mode 100644
index 00000000..ca28f69f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/ScriptSelector.java
@@ -0,0 +1,223 @@
+/*
+ * 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.tools.ant.types.optional;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.selectors.BaseSelector;
+import org.apache.tools.ant.util.ScriptRunnerBase;
+import org.apache.tools.ant.util.ScriptRunnerHelper;
+
+/**
+ * Selector that lets you run a script with selection logic inline
+ * @since Ant1.7
+ */
+public class ScriptSelector extends BaseSelector {
+
+ /**
+ * script runner helper
+ */
+ private ScriptRunnerHelper helper = new ScriptRunnerHelper();
+
+ /**
+ * script runner
+ */
+ private ScriptRunnerBase runner;
+
+ /**
+ * fields updated for every selection
+ */
+ private File basedir;
+ private String filename;
+ private File file;
+
+ /**
+ * selected flag
+ */
+ private boolean selected;
+
+ /**
+ * Set the project.
+ * @param project the owner of this component.
+ */
+ public void setProject(Project project) {
+ super.setProject(project);
+ helper.setProjectComponent(this);
+ }
+
+ /**
+ * Defines the manager.
+ *
+ * @param manager the scripting manager.
+ */
+ public void setManager(String manager) {
+ helper.setManager(manager);
+ }
+
+ /**
+ * Defines the language (required).
+ *
+ * @param language the scripting language name for the script.
+ */
+ public void setLanguage(String language) {
+ helper.setLanguage(language);
+ }
+
+ /**
+ * Initialize on demand.
+ *
+ * @throws org.apache.tools.ant.BuildException
+ * if someting goes wrong
+ */
+ private void init() throws BuildException {
+ if (runner != null) {
+ return;
+ }
+ runner = helper.getScriptRunner();
+ }
+
+ /**
+ * Load the script from an external file ; optional.
+ *
+ * @param file the file containing the script source.
+ */
+ public void setSrc(File file) {
+ helper.setSrc(file);
+ }
+
+ /**
+ * The script text.
+ *
+ * @param text a component of the script text to be added.
+ */
+ public void addText(String text) {
+ helper.addText(text);
+ }
+
+ /**
+ * Set the classpath to be used when searching for classes and resources.
+ *
+ * @param classpath an Ant Path object containing the search path.
+ */
+ public void setClasspath(Path classpath) {
+ helper.setClasspath(classpath);
+ }
+
+ /**
+ * Classpath to be used when searching for classes and resources.
+ *
+ * @return an empty Path instance to be configured by Ant.
+ */
+ public Path createClasspath() {
+ return helper.createClasspath();
+ }
+
+ /**
+ * Set the classpath by reference.
+ *
+ * @param r a Reference to a Path instance to be used as the classpath
+ * value.
+ */
+ public void setClasspathRef(Reference r) {
+ helper.setClasspathRef(r);
+ }
+
+ /**
+ * Set the setbeans attribute.
+ * If this is true, &lt;script&gt; will create variables in the
+ * script instance for all
+ * properties, targets and references of the current project.
+ * It this is false, only the project and self variables will
+ * be set.
+ * The default is true.
+ * @param setBeans the value to set.
+ */
+ public void setSetBeans(boolean setBeans) {
+ helper.setSetBeans(setBeans);
+ }
+
+ /**
+ * Method that each selector will implement to create their selection
+ * behaviour. If there is a problem with the setup of a selector, it can
+ * throw a BuildException to indicate the problem.
+ *
+ * @param basedir A java.io.File object for the base directory
+ * @param filename The name of the file to check
+ * @param file A File object for this filename
+ *
+ * @return whether the file should be selected or not
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+ init();
+ setSelected(true);
+ this.file = file;
+ this.basedir = basedir;
+ this.filename = filename;
+ runner.addBean("basedir", basedir);
+ runner.addBean("filename", filename);
+ runner.addBean("file", file);
+ runner.executeScript("ant_selector");
+ return isSelected();
+ }
+
+ /**
+ * get the base directory
+ * @return the base directory
+ */
+ public File getBasedir() {
+ return basedir;
+ }
+
+ /**
+ * get the filename of the file
+ * @return the filename of the file that is currently been tested
+ */
+ public String getFilename() {
+ return filename;
+ }
+
+ /**
+ * get the file that is currently to be tested
+ * @return the file that is currently been tested
+ */
+ public File getFile() {
+ return file;
+ }
+
+ /**
+ * get state of selected flag
+ * @return the selected flag
+ */
+ public boolean isSelected() {
+ return selected;
+ }
+
+ /**
+ * set the selected state
+ * Intended for script use, not as an Ant attribute
+ * @param selected the selected state
+ */
+ public void setSelected(boolean selected) {
+ this.selected = selected;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/depend/ClassfileSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/depend/ClassfileSet.java
new file mode 100644
index 00000000..f2fe69b1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/depend/ClassfileSet.java
@@ -0,0 +1,183 @@
+/*
+ * 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.tools.ant.types.optional.depend;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * A ClassfileSet is a FileSet that enlists all classes that depend on a
+ * certain set of root classes.
+ *
+ * ClassfileSet extends FileSet, its inherited properties
+ * defining the domain searched for dependent classes.
+ *
+ */
+public class ClassfileSet extends FileSet {
+ /**
+ * The list of root classes for this class file set. These are the
+ * classes which must be included in the fileset and which are the
+ * starting point for the dependency search.
+ */
+ private List<String> rootClasses = new ArrayList<String>();
+
+ /**
+ * The list of filesets which contain root classes.
+ */
+ private List<FileSet> rootFileSets = new ArrayList<FileSet>();
+
+ /**
+ * Inner class used to contain info about root classes.
+ */
+ public static class ClassRoot {
+ /** The name of the root class */
+ private String rootClass;
+
+ /**
+ * Set the root class name.
+ *
+ * @param name the name of the root class.
+ */
+ public void setClassname(String name) {
+ this.rootClass = name;
+ }
+
+ /**
+ * Get the name of the root class.
+ *
+ * @return the name of the root class.
+ */
+ public String getClassname() {
+ return rootClass;
+ }
+ }
+
+ /**
+ * Default constructor.
+ */
+ public ClassfileSet() {
+ }
+
+ /**
+ * Add a fileset to which contains a collection of root classes used to
+ * drive the search from classes.
+ *
+ * @param rootFileSet a root file set to be used to search for dependent
+ * classes.
+ */
+ public void addRootFileset(FileSet rootFileSet) {
+ rootFileSets.add(rootFileSet);
+ setChecked(false);
+ }
+
+ /**
+ * Create a ClassfileSet from another ClassfileSet.
+ *
+ * @param s the other classfileset.
+ */
+ protected ClassfileSet(ClassfileSet s) {
+ super(s);
+ rootClasses.addAll(s.rootClasses);
+ }
+
+ /**
+ * Set the root class attribute.
+ *
+ * @param rootClass the name of the root class.
+ */
+ public void setRootClass(String rootClass) {
+ rootClasses.add(rootClass);
+ }
+
+ /**
+ * Return the DirectoryScanner associated with this FileSet.
+ *
+ * @param p the project used to resolve dirs, etc.
+ *
+ * @return a dependency scanner.
+ */
+ public DirectoryScanner getDirectoryScanner(Project p) {
+ if (isReference()) {
+ return getRef(p).getDirectoryScanner(p);
+ }
+ dieOnCircularReference(p);
+ DirectoryScanner parentScanner = super.getDirectoryScanner(p);
+ DependScanner scanner = new DependScanner(parentScanner);
+ final Vector<String> allRootClasses = new Vector<String>(rootClasses);
+ for (FileSet additionalRootSet : rootFileSets) {
+ DirectoryScanner additionalScanner
+ = additionalRootSet.getDirectoryScanner(p);
+ String[] files = additionalScanner.getIncludedFiles();
+ for (int i = 0; i < files.length; ++i) {
+ if (files[i].endsWith(".class")) {
+ String classFilePath = StringUtils.removeSuffix(files[i], ".class");
+ String className
+ = classFilePath.replace('/', '.').replace('\\', '.');
+ allRootClasses.addElement(className);
+ }
+ }
+ scanner.addBasedir(additionalRootSet.getDir(p));
+ }
+ scanner.setBasedir(getDir(p));
+ scanner.setRootClasses(allRootClasses);
+ scanner.scan();
+ return scanner;
+ }
+
+ /**
+ * Add a nested root class definition to this class file set.
+ *
+ * @param root the configured class root.
+ */
+ public void addConfiguredRoot(ClassRoot root) {
+ rootClasses.add(root.getClassname());
+ }
+
+ /**
+ * Clone this data type.
+ *
+ * @return a clone of the class file set.
+ */
+ public Object clone() {
+ return new ClassfileSet(isReference()
+ ? (ClassfileSet) (getRef(getProject())) : this);
+ }
+
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p) {
+ if (isChecked()) {
+ return;
+ }
+
+ // takes care of nested selectors
+ super.dieOnCircularReference(stk, p);
+
+ if (!isReference()) {
+ for (FileSet additionalRootSet : rootFileSets) {
+ pushAndInvokeCircularReferenceCheck(additionalRootSet, stk, p);
+ }
+ setChecked(true);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/depend/DependScanner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/depend/DependScanner.java
new file mode 100644
index 00000000..bb3cf54b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/depend/DependScanner.java
@@ -0,0 +1,225 @@
+/*
+ * 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.tools.ant.types.optional.depend;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.depend.DependencyAnalyzer;
+
+
+/**
+ * DirectoryScanner for finding class dependencies.
+ */
+public class DependScanner extends DirectoryScanner {
+ /**
+ * The name of the analyzer to use by default.
+ */
+ public static final String DEFAULT_ANALYZER_CLASS
+ = "org.apache.tools.ant.util.depend.bcel.FullAnalyzer";
+
+ /**
+ * The root classes to drive the search for dependent classes.
+ */
+ private Vector<String> rootClasses;
+
+ /**
+ * The names of the classes to include in the fileset.
+ */
+ private Vector<String> included;
+
+ private Vector<File> additionalBaseDirs = new Vector<File>();
+
+ /**
+ * The parent scanner which gives the basic set of files. Only files which
+ * are in this set and which can be reached from a root class will end
+ * up being included in the result set.
+ */
+ private DirectoryScanner parentScanner;
+
+ /**
+ * Create a DependScanner, using the given scanner to provide the basic
+ * set of files from which class files come.
+ *
+ * @param parentScanner the DirectoryScanner which returns the files from
+ * which class files must come.
+ */
+ public DependScanner(DirectoryScanner parentScanner) {
+ this.parentScanner = parentScanner;
+ }
+
+ /**
+ * Sets the root classes to be used to drive the scan.
+ *
+ * @param rootClasses the rootClasses to be used for this scan.
+ */
+ public synchronized void setRootClasses(Vector<String> rootClasses) {
+ this.rootClasses = rootClasses;
+ }
+
+ /**
+ * Get the names of the class files on which baseClass depends.
+ *
+ * @return the names of the files.
+ */
+ public String[] getIncludedFiles() {
+ String[] files = new String[getIncludedFilesCount()];
+ for (int i = 0; i < files.length; i++) {
+ files[i] = (String) included.elementAt(i);
+ }
+ return files;
+ }
+
+ /** {@inheritDoc}. */
+ public synchronized int getIncludedFilesCount() {
+ if (included == null) {
+ throw new IllegalStateException();
+ }
+ return included.size();
+ }
+
+ /**
+ * Scans the base directory for files on which baseClass depends.
+ *
+ * @exception IllegalStateException when basedir was set incorrectly.
+ */
+ public synchronized void scan() throws IllegalStateException {
+ included = new Vector<String>();
+ String analyzerClassName = DEFAULT_ANALYZER_CLASS;
+ DependencyAnalyzer analyzer = null;
+ try {
+ Class<? extends DependencyAnalyzer> analyzerClass = Class.forName(analyzerClassName)
+ .asSubclass(DependencyAnalyzer.class);
+ analyzer = analyzerClass.newInstance();
+ } catch (Exception e) {
+ throw new BuildException("Unable to load dependency analyzer: "
+ + analyzerClassName, e);
+ }
+ analyzer.addClassPath(new Path(null, basedir.getPath()));
+ for (Enumeration<File> e = additionalBaseDirs.elements(); e.hasMoreElements();) {
+ File additionalBaseDir = e.nextElement();
+ analyzer.addClassPath(new Path(null, additionalBaseDir.getPath()));
+ }
+
+ for (Enumeration<String> e = rootClasses.elements(); e.hasMoreElements();) {
+ String rootClass = e.nextElement();
+ analyzer.addRootClass(rootClass);
+ }
+ Enumeration<String> e = analyzer.getClassDependencies();
+
+ String[] parentFiles = parentScanner.getIncludedFiles();
+ Hashtable<String, String> parentSet = new Hashtable<String, String>();
+ for (int i = 0; i < parentFiles.length; ++i) {
+ parentSet.put(parentFiles[i], parentFiles[i]);
+ }
+ while (e.hasMoreElements()) {
+ String classname = (String) e.nextElement();
+ String filename = classname.replace('.', File.separatorChar);
+ filename = filename + ".class";
+ File depFile = new File(basedir, filename);
+ if (depFile.exists() && parentSet.containsKey(filename)) {
+ // This is included
+ included.addElement(filename);
+ }
+ }
+ }
+
+ /**
+ * @see DirectoryScanner#addDefaultExcludes
+ */
+ public void addDefaultExcludes() {
+ }
+
+ /**
+ * @see DirectoryScanner#getExcludedDirectories
+ */
+ /** {@inheritDoc}. */
+ public String[] getExcludedDirectories() {
+ return null;
+ }
+
+ /**
+ * @see DirectoryScanner#getExcludedFiles
+ */
+ /** {@inheritDoc}. */
+ public String[] getExcludedFiles() {
+ return null;
+ }
+
+ /**
+ * @see DirectoryScanner#getIncludedDirectories
+ */
+ /** {@inheritDoc}. */
+ public String[] getIncludedDirectories() {
+ return new String[0];
+ }
+
+ /**
+ * @see DirectoryScanner#getIncludedDirsCount
+ */
+ /** {@inheritDoc}. */
+ public int getIncludedDirsCount() {
+ return 0;
+ }
+
+ /**
+ * @see DirectoryScanner#getNotIncludedDirectories
+ */
+ /** {@inheritDoc}. */
+ public String[] getNotIncludedDirectories() {
+ return null;
+ }
+
+ /**
+ * @see DirectoryScanner#getNotIncludedFiles
+ */
+ /** {@inheritDoc}. */
+ public String[] getNotIncludedFiles() {
+ return null;
+ }
+
+ /**
+ * @see DirectoryScanner#setExcludes
+ */
+ /** {@inheritDoc}. */
+ public void setExcludes(String[] excludes) {
+ }
+
+ /**
+ * @see DirectoryScanner#setIncludes
+ */
+ /** {@inheritDoc}. */
+ public void setIncludes(String[] includes) {
+ }
+
+ /**
+ * @see DirectoryScanner#setCaseSensitive
+ */
+ /** {@inheritDoc}. */
+ public void setCaseSensitive(boolean isCaseSensitive) {
+ }
+
+ public void addBasedir(File baseDir) {
+ additionalBaseDirs.addElement(baseDir);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Arc.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Arc.java
new file mode 100644
index 00000000..3d8b29b2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Arc.java
@@ -0,0 +1,123 @@
+/*
+ * 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.tools.ant.types.optional.image;
+
+import java.awt.BasicStroke;
+import java.awt.Graphics2D;
+import java.awt.geom.Arc2D;
+import java.awt.image.BufferedImage;
+
+import javax.media.jai.PlanarImage;
+
+/**
+ * Draw an arc.
+ */
+public class Arc extends BasicShape implements DrawOperation {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected int width = 0;
+ protected int height = 0;
+ protected int start = 0;
+ protected int stop = 0;
+ protected int type = Arc2D.OPEN;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Set the width.
+ * @param width the width of the arc.
+ */
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ /**
+ * Set the height.
+ * @param height the height of the arc.
+ */
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ /**
+ * Set the start of the arc.
+ * @param start the start of the arc.
+ */
+ public void setStart(int start) {
+ this.start = start;
+ }
+
+ /**
+ * Set the stop of the arc.
+ * @param stop the stop of the arc.
+ */
+ public void setStop(int stop) {
+ this.stop = stop;
+ }
+
+ /**
+ * Set the type of arc.
+ * @param strType the type to use - open, pie or chord.
+ * @todo refactor using an EnumeratedAttribute
+ */
+ public void setType(String strType) {
+ if (strType.equalsIgnoreCase("open")) {
+ type = Arc2D.OPEN;
+ } else if (strType.equalsIgnoreCase("pie")) {
+ type = Arc2D.PIE;
+ } else if (strType.equalsIgnoreCase("chord")) {
+ type = Arc2D.CHORD;
+ }
+ }
+
+ /** {@inheritDoc}. */
+ public PlanarImage executeDrawOperation() {
+ BufferedImage bi = new BufferedImage(width + (stroke_width * 2),
+ height + (stroke_width * 2), BufferedImage.TYPE_4BYTE_ABGR_PRE);
+
+ Graphics2D graphics = (Graphics2D) bi.getGraphics();
+
+ if (!stroke.equals("transparent")) {
+ BasicStroke bStroke = new BasicStroke(stroke_width);
+ graphics.setColor(ColorMapper.getColorByName(stroke));
+ graphics.setStroke(bStroke);
+ graphics.draw(new Arc2D.Double(stroke_width, stroke_width, width,
+ height, start, stop, type));
+ }
+
+ if (!fill.equals("transparent")) {
+ graphics.setColor(ColorMapper.getColorByName(fill));
+ graphics.fill(new Arc2D.Double(stroke_width, stroke_width,
+ width, height, start, stop, type));
+ }
+
+
+ final int size = instructions.size();
+ for (int i = 0; i < size; i++) {
+ ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+ if (instr instanceof DrawOperation) {
+ PlanarImage img = ((DrawOperation) instr).executeDrawOperation();
+ graphics.drawImage(img.getAsBufferedImage(), null, 0, 0);
+ } else if (instr instanceof TransformOperation) {
+ graphics = (Graphics2D) bi.getGraphics();
+ PlanarImage image = ((TransformOperation) instr)
+ .executeTransformOperation(PlanarImage.wrapRenderedImage(bi));
+ bi = image.getAsBufferedImage();
+ }
+ }
+ return PlanarImage.wrapRenderedImage(bi);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/BasicShape.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/BasicShape.java
new file mode 100644
index 00000000..ee2113a7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/BasicShape.java
@@ -0,0 +1,55 @@
+/*
+ * 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.tools.ant.types.optional.image;
+
+
+/** Draw a basic shape */
+public abstract class BasicShape extends ImageOperation implements DrawOperation {
+ // CheckStyle:VisibilityModifier OFF - bc
+ // CheckStyle:MemberNameCheck OFF - bc
+ protected int stroke_width = 0;
+ // CheckStyle:MemberNameCheck ON
+ protected String fill = "transparent";
+ protected String stroke = "black";
+ // CheckStyle:VisibilityModifier ON
+
+
+ /**
+ * Set the fill attribute.
+ * @param col the color value to use.
+ */
+ public void setFill(String col) {
+ fill = col;
+ }
+
+ /**
+ * Set the stroke attribute.
+ * @param col the color value to use.
+ */
+ public void setStroke(String col) {
+ stroke = col;
+ }
+
+ /**
+ * Set the stroke width attribute.
+ * @param width the value to use.
+ */
+ public void setStrokewidth(int width) {
+ stroke_width = width;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/ColorMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/ColorMapper.java
new file mode 100644
index 00000000..88e2871a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/ColorMapper.java
@@ -0,0 +1,102 @@
+/*
+ * 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.tools.ant.types.optional.image;
+
+import java.awt.Color;
+
+/**
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public final class ColorMapper {
+ /** private constructor for Utility class */
+ private ColorMapper() {
+ }
+
+ /** black string */
+ public static final String COLOR_BLACK = "black";
+ /** blue string */
+ public static final String COLOR_BLUE = "blue";
+ /** cyan string */
+ public static final String COLOR_CYAN = "cyan";
+ /** black string */
+ public static final String COLOR_DARKGRAY = "darkgray";
+ /** gray string */
+ public static final String COLOR_GRAY = "gray";
+ /** lightgray string */
+ public static final String COLOR_LIGHTGRAY = "lightgray";
+ // Gotta at least put in the proper spelling :-P
+ /** darkgrey string */
+ public static final String COLOR_DARKGREY = "darkgrey";
+ /** grey string */
+ public static final String COLOR_GREY = "grey";
+ /** lightgrey string */
+ public static final String COLOR_LIGHTGREY = "lightgrey";
+ /** green string */
+ public static final String COLOR_GREEN = "green";
+ /** magenta string */
+ public static final String COLOR_MAGENTA = "magenta";
+ /** orange string */
+ public static final String COLOR_ORANGE = "orange";
+ /** pink string */
+ public static final String COLOR_PINK = "pink";
+ /** reg string */
+ public static final String COLOR_RED = "red";
+ /** white string */
+ public static final String COLOR_WHITE = "white";
+ /** yellow string */
+ public static final String COLOR_YELLOW = "yellow";
+
+ /**
+ * Convert a color name to a color value.
+ * @param colorName a string repr of the color.
+ * @return the color value.
+ * @todo refactor to use an EnumeratedAttribute (maybe?)
+ */
+ public static Color getColorByName(String colorName) {
+ if (colorName.equalsIgnoreCase(COLOR_BLACK)) {
+ return Color.black;
+ } else if (colorName.equalsIgnoreCase(COLOR_BLUE)) {
+ return Color.blue;
+ } else if (colorName.equalsIgnoreCase(COLOR_CYAN)) {
+ return Color.cyan;
+ } else if (colorName.equalsIgnoreCase(COLOR_DARKGRAY) || colorName.equalsIgnoreCase(COLOR_DARKGREY)) {
+ return Color.darkGray;
+ } else if (colorName.equalsIgnoreCase(COLOR_GRAY) || colorName.equalsIgnoreCase(COLOR_GREY)) {
+ return Color.gray;
+ } else if (colorName.equalsIgnoreCase(COLOR_LIGHTGRAY) || colorName.equalsIgnoreCase(COLOR_LIGHTGREY)) {
+ return Color.lightGray;
+ } else if (colorName.equalsIgnoreCase(COLOR_GREEN)) {
+ return Color.green;
+ } else if (colorName.equalsIgnoreCase(COLOR_MAGENTA)) {
+ return Color.magenta;
+ } else if (colorName.equalsIgnoreCase(COLOR_ORANGE)) {
+ return Color.orange;
+ } else if (colorName.equalsIgnoreCase(COLOR_PINK)) {
+ return Color.pink;
+ } else if (colorName.equalsIgnoreCase(COLOR_RED)) {
+ return Color.red;
+ } else if (colorName.equalsIgnoreCase(COLOR_WHITE)) {
+ return Color.white;
+ } else if (colorName.equalsIgnoreCase(COLOR_YELLOW)) {
+ return Color.yellow;
+ }
+ return Color.black;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Draw.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Draw.java
new file mode 100644
index 00000000..2f097d5d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Draw.java
@@ -0,0 +1,102 @@
+/*
+ * 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.tools.ant.types.optional.image;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+import javax.media.jai.PlanarImage;
+
+/**
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public class Draw extends TransformOperation {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected int xloc = 0;
+ protected int yloc = 0;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Set the X location.
+ * @param x the value to use.
+ */
+ public void setXloc(int x) {
+ xloc = x;
+ }
+
+ /**
+ * Set the Y location.
+ * @param y the value to use.
+ */
+ public void setYloc(int y) {
+ yloc = y;
+ }
+
+ /** {@inheritDoc}. */
+ public void addRectangle(Rectangle rect) {
+ instructions.add(rect);
+ }
+
+ /** {@inheritDoc}. */
+ public void addText(Text text) {
+ instructions.add(text);
+ }
+
+ /**
+ * Add an ellipse.
+ * @param elip the ellipse to add.
+ */
+ public void addEllipse(Ellipse elip) {
+ instructions.add(elip);
+ }
+
+ /**
+ * Add an arc.
+ * @param arc the arc to add.
+ */
+ public void addArc(Arc arc) {
+ instructions.add(arc);
+ }
+
+ /** {@inheritDoc}. */
+ public PlanarImage executeTransformOperation(PlanarImage image) {
+ BufferedImage bi = image.getAsBufferedImage();
+ Graphics2D graphics = (Graphics2D) bi.getGraphics();
+
+ final int size = instructions.size();
+ for (int i = 0; i < size; i++) {
+ ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+ if (instr instanceof DrawOperation) {
+ PlanarImage op = ((DrawOperation) instr).executeDrawOperation();
+ log("\tDrawing to x=" + xloc + " y=" + yloc);
+ graphics.drawImage(op.getAsBufferedImage(), null, xloc, yloc);
+ } else if (instr instanceof TransformOperation) {
+ PlanarImage op
+ = ((TransformOperation) instr).executeTransformOperation(null);
+ BufferedImage child = op.getAsBufferedImage();
+ log("\tDrawing to x=" + xloc + " y=" + yloc);
+ graphics.drawImage(child, null, xloc, yloc);
+ PlanarImage.wrapRenderedImage(bi);
+ }
+ }
+ image = PlanarImage.wrapRenderedImage(bi);
+
+ return image;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/DrawOperation.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/DrawOperation.java
new file mode 100644
index 00000000..4f6410ca
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/DrawOperation.java
@@ -0,0 +1,40 @@
+/*
+ * 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.tools.ant.types.optional.image;
+
+import javax.media.jai.PlanarImage;
+
+
+/**
+ * Interface which represents an Operation which is "drawable", such
+ * as a Rectangle, Circle or Text. The Operation is responsible for
+ * creating its own image buffer and drawing itself into it, then
+ * wrapping and returning it as a PlanarImage. This allows multible
+ * "drawable" objects to be nested.
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public interface DrawOperation {
+ /**
+ * Abstract method which is intended to create an image buffer
+ * and return it so it can be drawn into another object. Use
+ * an Alpha channel for a "transparent" background.
+ * @return a planar image
+ */
+ PlanarImage executeDrawOperation();
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Ellipse.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Ellipse.java
new file mode 100644
index 00000000..9924d906
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Ellipse.java
@@ -0,0 +1,87 @@
+/*
+ * 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.tools.ant.types.optional.image;
+
+import java.awt.BasicStroke;
+import java.awt.Graphics2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.image.BufferedImage;
+
+import javax.media.jai.PlanarImage;
+
+/**
+ * Draw an ellipse.
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public class Ellipse extends BasicShape implements DrawOperation {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected int width = 0;
+ protected int height = 0;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Set the width.
+ * @param width the width of the elipse.
+ */
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ /**
+ * Set the height.
+ * @param height the height of the ellipse.
+ */
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ /** {@inheritDoc}. */
+ public PlanarImage executeDrawOperation() {
+ BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR_PRE);
+
+ Graphics2D graphics = (Graphics2D) bi.getGraphics();
+
+ if (!stroke.equals("transparent")) {
+ BasicStroke bStroke = new BasicStroke(stroke_width);
+ graphics.setColor(ColorMapper.getColorByName(stroke));
+ graphics.setStroke(bStroke);
+ graphics.draw(new Ellipse2D.Double(0, 0, width, height));
+ }
+
+ if (!fill.equals("transparent")) {
+ graphics.setColor(ColorMapper.getColorByName(fill));
+ graphics.fill(new Ellipse2D.Double(0, 0, width, height));
+ }
+
+
+ final int size = instructions.size();
+ for (int i = 0; i < size; i++) {
+ ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+ if (instr instanceof DrawOperation) {
+ PlanarImage img = ((DrawOperation) instr).executeDrawOperation();
+ graphics.drawImage(img.getAsBufferedImage(), null, 0, 0);
+ } else if (instr instanceof TransformOperation) {
+ graphics = (Graphics2D) bi.getGraphics();
+ PlanarImage image = ((TransformOperation) instr)
+ .executeTransformOperation(PlanarImage.wrapRenderedImage(bi));
+ bi = image.getAsBufferedImage();
+ }
+ }
+ return PlanarImage.wrapRenderedImage(bi);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/ImageOperation.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/ImageOperation.java
new file mode 100644
index 00000000..d72fe049
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/ImageOperation.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.tools.ant.types.optional.image;
+
+import java.util.Vector;
+
+import org.apache.tools.ant.types.DataType;
+
+/**
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public abstract class ImageOperation extends DataType {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected Vector<ImageOperation> instructions = new Vector<ImageOperation>();
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Add a rotate to the operation.
+ * @param instr the rotate to add.
+ */
+ public void addRotate(Rotate instr) {
+ instructions.add(instr);
+ }
+
+ /**
+ * Add a draw to the operation.
+ * @param instr the draw to add.
+ */
+ public void addDraw(Draw instr) {
+ instructions.add(instr);
+ }
+
+ /**
+ * Add a rectangle to the operation.
+ * @param instr the rectangle to add.
+ */
+ public void addRectangle(Rectangle instr) {
+ instructions.add(instr);
+ }
+
+ /**
+ * Add text to the operation.
+ * @param instr the text to add.
+ */
+ public void addText(Text instr) {
+ instructions.add(instr);
+ }
+
+ /**
+ * Add a scale to the operation.
+ * @param instr the scale to add.
+ */
+ public void addScale(Scale instr) {
+ instructions.add(instr);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Rectangle.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Rectangle.java
new file mode 100644
index 00000000..e2d5bb1b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Rectangle.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.tools.ant.types.optional.image;
+
+import java.awt.BasicStroke;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+import javax.media.jai.PlanarImage;
+
+/**
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public class Rectangle extends BasicShape implements DrawOperation {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected int width = 0;
+ protected int height = 0;
+ protected int arcwidth = 0;
+ protected int archeight = 0;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Set the width.
+ * @param w the value to use.
+ */
+ public void setWidth(int w) {
+ width = w;
+ }
+
+ /**
+ * Set the height.
+ * @param h the value to use.
+ */
+ public void setHeight(int h) {
+ height = h;
+ }
+
+ /**
+ * Set the arc width.
+ * @param w the value to use.
+ */
+ public void setArcwidth(int w) {
+ arcwidth = w;
+ }
+
+ /**
+ * Set the arc height.
+ * @param h the value to use.
+ */
+ public void setArcheight(int h) {
+ archeight = h;
+ }
+
+ /** {@inheritDoc}. */
+ public PlanarImage executeDrawOperation() {
+ log("\tCreating Rectangle w=" + width + " h=" + height + " arcw="
+ + arcwidth + " arch=" + archeight);
+ BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR_PRE);
+
+ Graphics2D graphics = (Graphics2D) bi.getGraphics();
+
+ if (!stroke.equals("transparent")) {
+ BasicStroke bStroke = new BasicStroke(stroke_width);
+ graphics.setColor(ColorMapper.getColorByName(stroke));
+ graphics.setStroke(bStroke);
+
+ if ((arcwidth != 0) || (archeight != 0)) {
+ graphics.drawRoundRect(0, 0, width, height, arcwidth, archeight);
+ } else {
+ graphics.drawRect(0, 0, width, height);
+ }
+ }
+
+ if (!fill.equals("transparent")) {
+ graphics.setColor(ColorMapper.getColorByName(fill));
+ if ((arcwidth != 0) || (archeight != 0)) {
+ graphics.fillRoundRect(stroke_width, stroke_width,
+ width - (stroke_width * 2), height - (stroke_width * 2),
+ arcwidth, archeight);
+ } else {
+ graphics.fillRect(stroke_width, stroke_width,
+ width - (stroke_width * 2), height - (stroke_width * 2));
+ }
+ }
+
+
+ final int size = instructions.size();
+ for (int i = 0; i < size; i++) {
+ ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+ if (instr instanceof DrawOperation) {
+ PlanarImage img = ((DrawOperation) instr).executeDrawOperation();
+ graphics.drawImage(img.getAsBufferedImage(), null, 0, 0);
+ } else if (instr instanceof TransformOperation) {
+ graphics = (Graphics2D) bi.getGraphics();
+ PlanarImage image
+ = ((TransformOperation) instr)
+ .executeTransformOperation(PlanarImage.wrapRenderedImage(bi));
+ bi = image.getAsBufferedImage();
+ }
+ }
+ return PlanarImage.wrapRenderedImage(bi);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Rotate.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Rotate.java
new file mode 100644
index 00000000..3013bde4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Rotate.java
@@ -0,0 +1,118 @@
+/*
+ * 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.tools.ant.types.optional.image;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.renderable.ParameterBlock;
+
+import javax.media.jai.InterpolationNearest;
+import javax.media.jai.JAI;
+import javax.media.jai.PlanarImage;
+
+/**
+ * ImageOperation to rotate an image by a certain degree
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public class Rotate extends TransformOperation implements DrawOperation {
+ private static final float HALF_CIRCLE = 180.0F;
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected float angle = 0.0F;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Sets the angle of rotation in degrees.
+ * @param ang The angle at which to rotate the image
+ */
+ public void setAngle(String ang) {
+ angle = Float.parseFloat(ang);
+ }
+
+
+ /**
+ * Rotate an image.
+ * @param image the image to rotate.
+ * @return the rotated image.
+ */
+ public PlanarImage performRotate(PlanarImage image) {
+ float tAngle = (float) (angle * (Math.PI / HALF_CIRCLE));
+ ParameterBlock pb = new ParameterBlock();
+ pb.addSource(image);
+ pb.add(0.0F);
+ pb.add(0.0F);
+ pb.add(tAngle);
+ pb.add(new InterpolationNearest());
+ return JAI.create("Rotate", pb, null);
+ }
+
+
+ /**
+ * Performs the image rotation when being handled as a TransformOperation.
+ * @param image The image to perform the transformation on.
+ * @return the transformed image.
+ */
+ public PlanarImage executeTransformOperation(PlanarImage image) {
+ BufferedImage bi = null;
+ final int size = instructions.size();
+ for (int i = 0; i < size; i++) {
+ ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+ if (instr instanceof DrawOperation) {
+ // If this TransformOperation has DrawOperation children
+ // then Rotate the first child and return.
+ System.out.println("Execing Draws");
+ PlanarImage op = ((DrawOperation) instr).executeDrawOperation();
+ image = performRotate(op);
+ return image;
+ } else if (instr instanceof TransformOperation) {
+ bi = image.getAsBufferedImage();
+ System.out.println("Execing Transforms");
+ image = ((TransformOperation) instr)
+ .executeTransformOperation(PlanarImage.wrapRenderedImage(bi));
+ bi = image.getAsBufferedImage();
+ }
+ }
+ System.out.println("Execing as TransformOperation");
+ image = performRotate(image);
+ System.out.println(image);
+ return image;
+ }
+
+ /**
+ * Performs the image rotation when being handled as a DrawOperation.
+ * It absolutely requires that there be a DrawOperation nested beneath it,
+ * but only the FIRST DrawOperation will be handled since it can only return
+ * ONE image.
+ * @return the image.
+ */
+ public PlanarImage executeDrawOperation() {
+ final int size = instructions.size();
+ for (int i = 0; i < size; i++) {
+ ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+ if (instr instanceof DrawOperation) {
+ // If this TransformOperation has DrawOperation children
+ // then Rotate the first child and return.
+ PlanarImage op = ((DrawOperation) instr).executeDrawOperation();
+ op = performRotate(op);
+ return op;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Scale.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Scale.java
new file mode 100644
index 00000000..532694d6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Scale.java
@@ -0,0 +1,181 @@
+/*
+ * 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.tools.ant.types.optional.image;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.renderable.ParameterBlock;
+
+import javax.media.jai.JAI;
+import javax.media.jai.PlanarImage;
+
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public class Scale extends TransformOperation implements DrawOperation {
+ private static final int HUNDRED = 100;
+
+ private String widthStr = "100%";
+ private String heightStr = "100%";
+ private boolean xPercent = true;
+ private boolean yPercent = true;
+ private String proportions = "ignore";
+
+ /** Enumerated class for proportions attribute. */
+ public static class ProportionsAttribute extends EnumeratedAttribute {
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return new String[] {"ignore", "width", "height", "cover", "fit"};
+ }
+ }
+
+ /**
+ * Sets the behaviour regarding the image proportions.
+ * @param pa the enumerated value.
+ */
+ public void setProportions(ProportionsAttribute pa) {
+ proportions = pa.getValue();
+ }
+
+ /**
+ * Sets the width of the image, either as an integer or a %.
+ * Defaults to 100%.
+ * @param width the value to use.
+ */
+ public void setWidth(String width) {
+ widthStr = width;
+ }
+
+ /**
+ * Sets the height of the image, either as an integer or a %. Defaults to 100%.
+ * @param height the value to use.
+ */
+ public void setHeight(String height) {
+ heightStr = height;
+ }
+
+ /**
+ * Get the width.
+ * @return the value converted from the width string.
+ */
+ public float getWidth() {
+ float width = 0.0F;
+ int percIndex = widthStr.indexOf('%');
+ if (percIndex > 0) {
+ width = Float.parseFloat(widthStr.substring(0, percIndex));
+ xPercent = true;
+ return width / HUNDRED;
+ } else {
+ xPercent = false;
+ return Float.parseFloat(widthStr);
+ }
+ }
+
+ /**
+ * Get the height.
+ * @return the value converted from the height string.
+ */
+ public float getHeight() {
+ int percIndex = heightStr.indexOf('%');
+ if (percIndex > 0) {
+ float height = Float.parseFloat(heightStr.substring(0, percIndex));
+ yPercent = true;
+ return height / HUNDRED;
+ } else {
+ yPercent = false;
+ return Float.parseFloat(heightStr);
+ }
+ }
+
+ /**
+ * Scale an image.
+ * @param image the image to scale.
+ * @return the scaled image.
+ */
+ public PlanarImage performScale(PlanarImage image) {
+ ParameterBlock pb = new ParameterBlock();
+ pb.addSource(image);
+ float xFl = getWidth();
+ float yFl = getHeight();
+
+ if (!xPercent) {
+ xFl = (xFl / image.getWidth());
+ }
+ if (!yPercent) {
+ yFl = (yFl / image.getHeight());
+ }
+
+ if ("width".equals(proportions)) {
+ yFl = xFl;
+ } else if ("height".equals(proportions)) {
+ xFl = yFl;
+ } else if ("fit".equals(proportions)) {
+ yFl = Math.min(xFl, yFl);
+ xFl = yFl;
+ } else if ("cover".equals(proportions)) {
+ yFl = Math.max(xFl, yFl);
+ xFl = yFl;
+ }
+
+ pb.add(new Float(xFl));
+ pb.add(new Float(yFl));
+
+ log("\tScaling to " + (xFl * HUNDRED) + "% x "
+ + (yFl * HUNDRED) + "%");
+
+ return JAI.create("scale", pb);
+ }
+
+
+ /** {@inheritDoc}. */
+ public PlanarImage executeTransformOperation(PlanarImage image) {
+ BufferedImage bi = null;
+ final int size = instructions.size();
+ for (int i = 0; i < size; i++) {
+ ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+ if (instr instanceof DrawOperation) {
+ return performScale(image);
+ } else if (instr instanceof TransformOperation) {
+ bi = image.getAsBufferedImage();
+ image = ((TransformOperation) instr)
+ .executeTransformOperation(PlanarImage.wrapRenderedImage(bi));
+ bi = image.getAsBufferedImage();
+ }
+ }
+ return performScale(image);
+ }
+
+
+ /** {@inheritDoc}. */
+ public PlanarImage executeDrawOperation() {
+ final int size = instructions.size();
+ for (int i = 0; i < size; i++) {
+ ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+ if (instr instanceof DrawOperation) {
+ PlanarImage image = null;
+ // If this TransformOperation has DrawOperation children
+ // then Rotate the first child and return.
+ performScale(image);
+ return image;
+ }
+ }
+ return null;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Text.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Text.java
new file mode 100644
index 00000000..869fbac1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/Text.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.tools.ant.types.optional.image;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+
+import javax.media.jai.PlanarImage;
+
+/**
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public class Text extends ImageOperation implements DrawOperation {
+ private static final int DEFAULT_POINT = 10;
+
+ private String strText = "";
+ private String font = "Arial";
+ private int point = DEFAULT_POINT;
+ private boolean bold = false;
+ private boolean italic = false;
+ private String color = "black";
+
+ /**
+ * Set the string to be used as text.
+ * @param str the string to be used.
+ */
+ public void setString(String str) {
+ strText = str;
+ }
+
+ /**
+ * Set the font to be used to draw the text.
+ * @param f the font to be used.
+ */
+ public void setFont(String f) {
+ font = f;
+ }
+
+ /**
+ * Set the number of points to be used.
+ * @param p an integer value as a string.
+ */
+ public void setPoint(String p) {
+ point = Integer.parseInt(p);
+ }
+
+ /**
+ * Set the color of the text.
+ * @param c the color name.
+ */
+ public void setColor(String c) {
+ color = c;
+ }
+
+ /**
+ * @todo is this used?
+ * @param state not used at the moment.
+ */
+ public void setBold(boolean state) {
+ bold = state;
+ }
+
+ /**
+ * @todo is this used?
+ * @param state not used at the moment.
+ */
+ public void setItalic(boolean state) {
+ italic = state;
+ }
+
+ /**
+ * Draw the text.
+ * @return the resultant image.
+ */
+ public PlanarImage executeDrawOperation() {
+ log("\tCreating Text \"" + strText + "\"");
+
+ Color couloir = ColorMapper.getColorByName(color);
+ int width = 1;
+ int height = 1;
+
+ BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR_PRE);
+ Graphics2D graphics = (Graphics2D) bi.getGraphics();
+ graphics.setRenderingHint(
+ RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ graphics.setRenderingHint(
+ RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+ Font f = new Font(font, Font.PLAIN, point);
+ FontMetrics fmetrics = graphics.getFontMetrics(f);
+ height = fmetrics.getMaxAscent() + fmetrics.getMaxDescent();
+ width = fmetrics.stringWidth(strText);
+
+
+ bi = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR_PRE);
+ graphics = (Graphics2D) bi.getGraphics();
+
+ graphics.setRenderingHint(
+ RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ graphics.setRenderingHint(
+ RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+
+ graphics.setFont(f);
+ graphics.setColor(couloir);
+ graphics.drawString(strText, 0, height - fmetrics.getMaxDescent());
+ PlanarImage image = PlanarImage.wrapRenderedImage(bi);
+ return image;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/TransformOperation.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/TransformOperation.java
new file mode 100644
index 00000000..896e5d10
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/optional/image/TransformOperation.java
@@ -0,0 +1,38 @@
+/*
+ * 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.tools.ant.types.optional.image;
+
+import javax.media.jai.PlanarImage;
+
+/**
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public abstract class TransformOperation extends ImageOperation {
+ /**
+ * Performs the transformations.
+ * @param img The image to perform the transformation on.
+ * @return the transformed image.
+ */
+ public abstract PlanarImage executeTransformOperation(PlanarImage img);
+
+ /** {@inheritDoc}. */
+ public void addRectangle(Rectangle instr) {
+ instructions.add(instr);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resolver/ApacheCatalog.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resolver/ApacheCatalog.java
new file mode 100644
index 00000000..cbf3f3fe
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resolver/ApacheCatalog.java
@@ -0,0 +1,121 @@
+/*
+ * 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.tools.ant.types.resolver;
+
+import org.apache.xml.resolver.Catalog;
+import org.apache.xml.resolver.CatalogEntry;
+import org.apache.xml.resolver.helpers.PublicId;
+
+
+/**
+ * This class extends the Catalog class provided by Norman Walsh's
+ * resolver library in xml-commons in order to add classpath entity
+ * and URI resolution. Since XMLCatalog already does classpath
+ * resolution, we simply add all CatalogEntry instances back to the
+ * controlling XMLCatalog instance. This is done via a callback
+ * mechanism. ApacheCatalog is <em>only</em> used for external
+ * catalog files. Inline entries (currently <code>&lt;dtd&gt;</code>
+ * and <code>&lt;entity&gt;</code>) are not added to ApacheCatalog.
+ * See XMLCatalog.java for the details of the entity and URI
+ * resolution algorithms.
+ *
+ * @see org.apache.tools.ant.types.XMLCatalog.CatalogResolver
+ * @since Ant 1.6
+ */
+public class ApacheCatalog extends Catalog {
+
+ /** The resolver object to callback. */
+ private ApacheCatalogResolver resolver = null;
+
+ /**
+ * <p>Create a new ApacheCatalog instance.</p>
+ *
+ * <p>This method overrides the superclass method of the same name
+ * in order to set the resolver object for callbacks. The reason
+ * we have to do this is that internally Catalog creates a new
+ * instance of itself for each external catalog file processed.
+ * That is, if two external catalog files are processed, there
+ * will be a total of two ApacheCatalog instances, and so on.</p>
+ * @return the catalog.
+ */
+ protected Catalog newCatalog() {
+ final ApacheCatalog cat = (ApacheCatalog) super.newCatalog();
+ cat.setResolver(resolver);
+ return cat;
+ }
+
+ /**
+ * Set the resolver object to callback.
+ * @param resolver the apache catalog resolver.
+ */
+ public void setResolver(final ApacheCatalogResolver resolver) {
+ this.resolver = resolver;
+ }
+
+ /**
+ * <p>This method overrides the superclass method of the same name
+ * in order to add catalog entries back to the controlling
+ * XMLCatalog instance. In this way, we can add classpath lookup
+ * for these entries.</p>
+ *
+ * <p>When we add an external catalog file, the entries inside it
+ * get parsed by this method. Therefore, we override it to add
+ * each of them back to the controlling XMLCatalog instance. This
+ * is done by performing a callback to the ApacheCatalogResolver,
+ * which in turn calls the XMLCatalog.</p>
+ *
+ * <p>XMLCatalog currently only understands <code>PUBLIC</code>
+ * and <code>URI</code> entry types, so we ignore the other types.</p>
+ *
+ * @param entry The CatalogEntry to process.
+ */
+ public void addEntry(final CatalogEntry entry) {
+
+ final int type = entry.getEntryType();
+
+ if (type == PUBLIC) {
+
+ final String publicid = PublicId.normalize(entry.getEntryArg(0));
+ final String systemid = normalizeURI(entry.getEntryArg(1));
+
+ if (resolver == null) {
+ catalogManager.debug
+ .message(1, "Internal Error: null ApacheCatalogResolver");
+ } else {
+ resolver.addPublicEntry(publicid, systemid, base);
+ }
+
+ } else if (type == URI) {
+
+ final String uri = normalizeURI(entry.getEntryArg(0));
+ final String altURI = normalizeURI(entry.getEntryArg(1));
+
+ if (resolver == null) {
+ catalogManager.debug
+ .message(1, "Internal Error: null ApacheCatalogResolver");
+ } else {
+ resolver.addURIEntry(uri, altURI, base);
+ }
+
+ }
+
+ super.addEntry(entry);
+ }
+
+} //- ApacheCatalog
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resolver/ApacheCatalogResolver.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resolver/ApacheCatalogResolver.java
new file mode 100644
index 00000000..2312d3dc
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resolver/ApacheCatalogResolver.java
@@ -0,0 +1,174 @@
+/*
+ * 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.tools.ant.types.resolver;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.ResourceLocation;
+import org.apache.tools.ant.types.XMLCatalog;
+import org.apache.xml.resolver.Catalog;
+import org.apache.xml.resolver.CatalogManager;
+import org.apache.xml.resolver.tools.CatalogResolver;
+
+
+/**
+ * <p>This class extends the CatalogResolver class provided by Norman
+ * Walsh's resolver library in xml-commons. It provides the bridge
+ * between the Ant XMLCatalog datatype and the xml-commons Catalog
+ * class. XMLCatalog calls methods in this class using Reflection in
+ * order to avoid requiring the xml-commons resolver library in the
+ * path.</p>
+ *
+ * <p>The {@link org.apache.tools.ant.types.resolver.ApacheCatalog
+ * ApacheCatalog} class is used to parse external catalog files, which
+ * can be in either <a
+ * href="http://oasis-open.org/committees/entity/background/9401.html">
+ * plain text format</a> or <a
+ * href="http://www.oasis-open.org/committees/entity/spec-2001-08-06.html">
+ * XML format</a>.</p>
+ *
+ * <p>For each entry found in an external catalog file, if any, an
+ * instance of {@link org.apache.tools.ant.types.ResourceLocation
+ * ResourceLocation} is created and added to the controlling
+ * XMLCatalog datatype. In this way, these entries will be included
+ * in XMLCatalog's lookup algorithm. See XMLCatalog.java for more
+ * details.</p>
+ *
+ * @see org.apache.tools.ant.types.XMLCatalog.CatalogResolver
+ * @see org.apache.xml.resolver.CatalogManager
+ * @since Ant 1.6
+ */
+
+public class ApacheCatalogResolver extends CatalogResolver {
+
+ /** The XMLCatalog object to callback. */
+ private XMLCatalog xmlCatalog = null;
+
+ static {
+ //
+ // If you don't do this, you get all sorts of annoying
+ // warnings about a missing properties file. However, it
+ // seems to work just fine with default values. Ultimately,
+ // we should probably include a "CatalogManager.properties"
+ // file in the ant jarfile with some default property
+ // settings. See CatalogManager.java for more details.
+ //
+ CatalogManager.getStaticManager().setIgnoreMissingProperties(true);
+
+ //
+ // Make sure CatalogResolver instantiates ApacheCatalog,
+ // rather than a plain Catalog
+ //
+ System.getProperties().put("xml.catalog.className",
+ ApacheCatalog.class.getName());
+
+ CatalogManager.getStaticManager().setUseStaticCatalog(false);
+
+ // debug
+ // CatalogManager.getStaticManager().setVerbosity(4);
+ }
+
+ /**
+ * Set the XMLCatalog object to callback.
+ * @param xmlCatalog the XMLCatalog to use.
+ */
+ public void setXMLCatalog(final XMLCatalog xmlCatalog) {
+ this.xmlCatalog = xmlCatalog;
+ }
+
+ /**
+ * XMLCatalog calls this to add an external catalog file for each
+ * file within a <code>&lt;catalogfiles&gt;</code> fileset.
+ * @param file the external catalog file.
+ */
+ public void parseCatalog(final String file) {
+
+ final Catalog catalog = getCatalog();
+ if (!(catalog instanceof ApacheCatalog)) {
+ throw new BuildException("Wrong catalog type found: " + catalog.getClass().getName());
+ }
+ final ApacheCatalog apacheCatalog = (ApacheCatalog) catalog;
+
+ // Pass in reference to ourselves so we can be called back.
+ apacheCatalog.setResolver(this);
+
+ try {
+ apacheCatalog.parseCatalog(file);
+ } catch (final MalformedURLException ex) {
+ throw new BuildException(ex);
+ } catch (final IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+
+ /**
+ * <p>Add a PUBLIC catalog entry to the controlling XMLCatalog instance.
+ * ApacheCatalog calls this for each PUBLIC entry found in an external
+ * catalog file.</p>
+ *
+ * @param publicid The public ID of the resource
+ * @param systemid The system ID (aka location) of the resource
+ * @param base The base URL of the resource. If the systemid
+ * specifies a relative URL/pathname, it is resolved using the
+ * base. The default base for an external catalog file is the
+ * directory in which the catalog is located.
+ *
+ */
+ public void addPublicEntry(final String publicid,
+ final String systemid,
+ final URL base) {
+
+ final ResourceLocation dtd = new ResourceLocation();
+ dtd.setBase(base);
+ dtd.setPublicId(publicid);
+ dtd.setLocation(systemid);
+
+ xmlCatalog.addDTD(dtd);
+ }
+
+ /**
+ * <p>Add a URI catalog entry to the controlling XMLCatalog instance.
+ * ApacheCatalog calls this for each URI entry found in an external
+ * catalog file.</p>
+ *
+ * @param uri The URI of the resource
+ * @param altURI The URI to which the resource should be mapped
+ * (aka the location)
+ * @param base The base URL of the resource. If the altURI
+ * specifies a relative URL/pathname, it is resolved using the
+ * base. The default base for an external catalog file is the
+ * directory in which the catalog is located.
+ *
+ */
+ public void addURIEntry(final String uri,
+ final String altURI,
+ final URL base) {
+
+ final ResourceLocation entity = new ResourceLocation();
+ entity.setBase(base);
+ entity.setPublicId(uri);
+ entity.setLocation(altURI);
+
+ xmlCatalog.addEntity(entity);
+ }
+
+} //-- ApacheCatalogResolver
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resolver/package.html b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resolver/package.html
new file mode 100644
index 00000000..11b3c773
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resolver/package.html
@@ -0,0 +1,35 @@
+<!--
+ 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.
+-->
+<body>
+Ant integration with xml-commons resolver.
+
+<p>These classes enhance the <code>&lt;xmlcatalog&gt;</code> datatype
+to support external catalog files using the xml-commons resolver, in
+accordance with the
+<a href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
+OASIS "Open Catalog" standard</a>. They will be used if and only if
+the xml-commons resolver library is available on the classpath.</p>
+
+@see <A HREF="http://xml.apache.org/commons">Apache xml-commons Project</A>
+
+@see org.apache.tools.ant.types.XMLCatalog
+@see org.apache.tools.ant.types.resolver.ApacheCatalogResolver
+@see org.apache.tools.ant.types.resolver.ApacheCatalog
+
+@author <A HREF="mailto:cstrong@arielpartners.com">Craeg Strong</A>
+
+</body>
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AbstractClasspathResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AbstractClasspathResource.java
new file mode 100644
index 00000000..417da9a7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AbstractClasspathResource.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.tools.ant.types.resources;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Stack;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ *
+ * A Resource representation of anything that is accessed via a Java classloader.
+ * The core methods to set/resolve the classpath are provided.
+ * @since Ant 1.8.0
+ *
+ */
+
+public abstract class AbstractClasspathResource extends Resource {
+ private Path classpath;
+ private Reference loader;
+ private boolean parentFirst = true;
+
+ /**
+ * Set the classpath to use when looking up a resource.
+ * @param classpath to add to any existing classpath
+ */
+ public void setClasspath(Path classpath) {
+ checkAttributesAllowed();
+ if (this.classpath == null) {
+ this.classpath = classpath;
+ } else {
+ this.classpath.append(classpath);
+ }
+ setChecked(false);
+ }
+
+ /**
+ * Add a classpath to use when looking up a resource.
+ * @return The classpath to be configured
+ */
+ public Path createClasspath() {
+ checkChildrenAllowed();
+ if (classpath == null) {
+ classpath = new Path(getProject());
+ }
+ setChecked(false);
+ return classpath.createPath();
+ }
+
+ /**
+ * Set the classpath to use when looking up a resource,
+ * given as reference to a &lt;path&gt; defined elsewhere
+ * @param r The reference value
+ */
+ public void setClasspathRef(Reference r) {
+ checkAttributesAllowed();
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * get the classpath used by this <code>LoadProperties</code>.
+ * @return The classpath
+ */
+ public Path getClasspath() {
+ if (isReference()) {
+ return ((AbstractClasspathResource) getCheckedRef()).getClasspath();
+ }
+ dieOnCircularReference();
+ return classpath;
+ }
+
+ /**
+ * Get the loader.
+ * @return the loader.
+ */
+ public Reference getLoader() {
+ if (isReference()) {
+ return ((AbstractClasspathResource) getCheckedRef()).getLoader();
+ }
+ dieOnCircularReference();
+ return loader;
+ }
+
+ /**
+ * Use the reference to locate the loader. If the loader is not
+ * found, taskdef will use the specified classpath and register it
+ * with the specified name.
+ *
+ * This allow multiple taskdef/typedef to use the same class loader,
+ * so they can be used together. It eliminate the need to
+ * put them in the CLASSPATH.
+ *
+ * @param r the reference to locate the loader.
+ */
+ public void setLoaderRef(Reference r) {
+ checkAttributesAllowed();
+ loader = r;
+ }
+
+ /**
+ * Whether to consult the parent classloader first.
+ *
+ * <p>Only relevant if a classpath has been specified.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public void setParentFirst(boolean b) {
+ parentFirst = b;
+ }
+
+ /**
+ * Overrides the super version.
+ * @param r the Reference to set.
+ */
+ public void setRefid(Reference r) {
+ if (loader != null || classpath != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Learn whether this resource exists. This implementation opens the input stream
+ * as the test.
+ * @return true if this resource exists.
+ */
+ public boolean isExists() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).isExists();
+ }
+ dieOnCircularReference();
+ InputStream is = null;
+ try {
+ is = getInputStream();
+ return is != null;
+ } catch (IOException ex) {
+ return false;
+ } finally {
+ FileUtils.close(is);
+ }
+ }
+
+ /**
+ * Return an InputStream for reading the contents of this Resource.
+ * @return an InputStream object.
+ * @throws IOException if an error occurs.
+ */
+ public InputStream getInputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getInputStream();
+ }
+ dieOnCircularReference();
+
+ final ClassLoaderWithFlag classLoader = getClassLoader();
+ return !classLoader.needsCleanup()
+ ? openInputStream(classLoader.getLoader())
+ : new FilterInputStream(openInputStream(classLoader.getLoader())) {
+ public void close() throws IOException {
+ FileUtils.close(in);
+ classLoader.cleanup();
+ }
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+ };
+ }
+
+ /**
+ * combines the various ways that could specify a ClassLoader and
+ * potentially creates one that needs to be cleaned up when it is
+ * no longer needed so that classes can get garbage collected.
+ */
+ protected ClassLoaderWithFlag getClassLoader() {
+ ClassLoader cl = null;
+ boolean clNeedsCleanup = false;
+ if (loader != null) {
+ cl = (ClassLoader) loader.getReferencedObject();
+ }
+ if (cl == null) {
+ if (getClasspath() != null) {
+ Path p = getClasspath().concatSystemClasspath("ignore");
+ if (parentFirst) {
+ cl = getProject().createClassLoader(p);
+ } else {
+ cl = AntClassLoader.newAntClassLoader(getProject()
+ .getCoreLoader(),
+ getProject(),
+ p, false);
+ }
+ clNeedsCleanup = loader == null;
+ } else {
+ cl = JavaResource.class.getClassLoader();
+ }
+ if (loader != null && cl != null) {
+ getProject().addReference(loader.getRefId(), cl);
+ }
+ }
+ return new ClassLoaderWithFlag(cl, clNeedsCleanup);
+ }
+
+ /**
+ * open the input stream from a specific classloader
+ * @param cl the classloader to use. Will be null if the system classloader is used
+ * @return an open input stream for the resource
+ * @throws IOException if an error occurs.
+ */
+ protected abstract InputStream openInputStream(ClassLoader cl) throws IOException;
+
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p) {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ if (classpath != null) {
+ pushAndInvokeCircularReferenceCheck(classpath, stk, p);
+ }
+ setChecked(true);
+ }
+ }
+
+ public static class ClassLoaderWithFlag {
+ private final ClassLoader loader;
+ private final boolean cleanup;
+
+ ClassLoaderWithFlag(ClassLoader l, boolean needsCleanup) {
+ loader = l;
+ cleanup = needsCleanup && l instanceof AntClassLoader;
+ }
+ public ClassLoader getLoader() { return loader; }
+ public boolean needsCleanup() { return cleanup; }
+ public void cleanup() {
+ if (cleanup) {
+ ((AntClassLoader) loader).cleanup();
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AbstractResourceCollectionWrapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AbstractResourceCollectionWrapper.java
new file mode 100644
index 00000000..5e4c3a3f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AbstractResourceCollectionWrapper.java
@@ -0,0 +1,209 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * Base class for a ResourceCollection that wraps a single nested
+ * ResourceCollection.
+ * @since Ant 1.8.2
+ */
+public abstract class AbstractResourceCollectionWrapper
+ extends DataType implements ResourceCollection, Cloneable {
+ private static final String ONE_NESTED_MESSAGE
+ = " expects exactly one nested resource collection.";
+
+ private ResourceCollection rc;
+ private boolean cache = true;
+
+ /**
+ * Set whether to cache collections.
+ * @param b boolean cache flag.
+ */
+ public synchronized void setCache(boolean b) {
+ cache = b;
+ }
+
+ /**
+ * Learn whether to cache collections. Default is <code>true</code>.
+ * @return boolean cache flag.
+ */
+ public synchronized boolean isCache() {
+ return cache;
+ }
+
+ /**
+ * Add a ResourceCollection to the container.
+ * @param c the ResourceCollection to add.
+ * @throws BuildException on error.
+ */
+ public synchronized void add(ResourceCollection c) throws BuildException {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (c == null) {
+ return;
+ }
+ if (rc != null) {
+ throw oneNested();
+ }
+ rc = c;
+ if (Project.getProject(rc) == null) {
+ Project p = getProject();
+ if (p != null) {
+ p.setProjectReference(rc);
+ }
+ }
+ setChecked(false);
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return an Iterator of Resources.
+ */
+ public final synchronized Iterator<Resource> iterator() {
+ if (isReference()) {
+ return ((AbstractResourceCollectionWrapper) getCheckedRef()).iterator();
+ }
+ dieOnCircularReference();
+ return new FailFast(this, createIterator());
+ }
+
+ /**
+ * Do create an iterator on the resource collection. The creation
+ * of the iterator is allowed to not be thread safe whereas the iterator
+ * itself should. The returned iterator will be wrapped into the FailFast
+ * one.
+ *
+ * @return the iterator on the resource collection
+ */
+ protected abstract Iterator<Resource> createIterator();
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ */
+ public synchronized int size() {
+ if (isReference()) {
+ return ((AbstractResourceCollectionWrapper) getCheckedRef()).size();
+ }
+ dieOnCircularReference();
+ return getSize();
+ }
+
+ /**
+ * Do compute the size of the resource collection. The implementation of
+ * this function is allowed to be not thread safe.
+ *
+ * @return size of resource collection.
+ */
+ protected abstract int getSize();
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return whether this is a filesystem-only resource collection.
+ */
+ public synchronized boolean isFilesystemOnly() {
+ if (isReference()) {
+ return ((BaseResourceCollectionContainer) getCheckedRef()).isFilesystemOnly();
+ }
+ dieOnCircularReference();
+
+ if (rc == null || rc.isFilesystemOnly()) {
+ return true;
+ }
+ /* now check each Resource in case the child only
+ lets through files from any children IT may have: */
+ for (Resource r : this) {
+ if (r.as(FileProvider.class) == null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Overrides the version of DataType to recurse on all DataType
+ * child elements that may have been added.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ if (rc instanceof DataType) {
+ pushAndInvokeCircularReferenceCheck((DataType) rc, stk, p);
+ }
+ setChecked(true);
+ }
+ }
+
+ /**
+ * Get the nested ResourceCollection.
+ * @return a ResourceCollection.
+ * @throws BuildException if no nested ResourceCollection has been provided.
+ */
+ protected final synchronized ResourceCollection getResourceCollection() {
+ dieOnCircularReference();
+ if (rc == null) {
+ throw oneNested();
+ }
+ return rc;
+ }
+
+ /**
+ * Format this BaseResourceCollectionWrapper as a String.
+ * @return a descriptive <code>String</code>.
+ */
+ public synchronized String toString() {
+ if (isReference()) {
+ return getCheckedRef().toString();
+ }
+ if (getSize() == 0) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ for (Resource resource : this) {
+ if (sb.length() > 0) {
+ sb.append(File.pathSeparatorChar);
+ }
+ sb.append(resource);
+ }
+ return sb.toString();
+ }
+
+ private BuildException oneNested() {
+ return new BuildException(super.toString() + ONE_NESTED_MESSAGE);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AllButFirst.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AllButFirst.java
new file mode 100644
index 00000000..ffa665f8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AllButFirst.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.tools.ant.types.resources;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * ResourceCollection that contains all resources of another
+ * collection except for the first <code>count</code> elements, a la
+ * the UNIX tail command with parameter <code>-n +count</code>.
+ * @since Ant 1.9.5
+ */
+public class AllButFirst extends SizeLimitCollection {
+
+ /**
+ * Take all elements except for the first <code>count</code> elements.
+ * @return a Collection of Resources.
+ */
+ protected Collection<Resource> getCollection() {
+ int ct = getValidCount();
+ Iterator<Resource> iter = getResourceCollection().iterator();
+ List<Resource> al = new ArrayList<Resource>();
+ for (int i = 0; i < ct && iter.hasNext(); i++) {
+ // discard
+ iter.next();
+ }
+ while (iter.hasNext()) {
+ al.add(iter.next());
+ }
+ return al;
+ }
+
+ @Override
+ public synchronized int size() {
+ int sz = getResourceCollection().size();
+ int ct = getValidCount();
+ return sz > ct ? sz - ct : 0;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AllButLast.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AllButLast.java
new file mode 100644
index 00000000..a1e6a984
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/AllButLast.java
@@ -0,0 +1,53 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.util.CollectionUtils;
+
+/**
+ * ResourceCollection that contains all resources of another
+ * collection except for the last <code>count</code> elements, a la
+ * the UNIX head command with parameter <code>-n -count</code>.
+ * @since Ant 1.9.5
+ */
+public class AllButLast extends SizeLimitCollection {
+
+ /**
+ * Take all elements except for the last <code>count</code> elements.
+ * @return a Collection of Resources.
+ */
+ protected Collection<Resource> getCollection() {
+ int ct = getValidCount();
+ List<Resource> result =
+ (List<Resource>) CollectionUtils.asCollection(getResourceCollection()
+ .iterator());
+ return result.subList(0, result.size() - ct);
+ }
+
+ @Override
+ public synchronized int size() {
+ int sz = getResourceCollection().size();
+ int ct = getValidCount();
+ return sz > ct ? sz - ct : 0;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Appendable.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Appendable.java
new file mode 100644
index 00000000..14f4711a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Appendable.java
@@ -0,0 +1,35 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Interface to be implemented by "appendable" resources.
+ * @since Ant 1.8
+ */
+public interface Appendable {
+
+ /**
+ * Get an appending OutputStream.
+ * @return OutputStream
+ * @throws IOException if anything goes wrong
+ */
+ OutputStream getAppendOutputStream() throws IOException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ArchiveResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ArchiveResource.java
new file mode 100644
index 00000000..308de03e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ArchiveResource.java
@@ -0,0 +1,293 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.File;
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * A Resource representation of an entry inside an archive.
+ * @since Ant 1.7
+ */
+public abstract class ArchiveResource extends Resource {
+ private static final int NULL_ARCHIVE
+ = Resource.getMagicNumber("null archive".getBytes());
+
+ private Resource archive;
+ private boolean haveEntry = false;
+ private boolean modeSet = false;
+ private int mode = 0;
+
+ /**
+ * Default constructor.
+ */
+ protected ArchiveResource() {
+ }
+
+ /**
+ * Construct a ArchiveResource representing the specified
+ * entry in the specified archive.
+ * @param a the archive as File.
+ */
+ protected ArchiveResource(File a) {
+ this(a, false);
+ }
+
+ /**
+ * Construct a ArchiveResource representing the specified
+ * entry in the specified archive.
+ * @param a the archive as File.
+ * @param withEntry if the entry has been specified.
+ */
+ protected ArchiveResource(File a, boolean withEntry) {
+ setArchive(a);
+ haveEntry = withEntry;
+ }
+
+ /**
+ * Construct a ArchiveResource representing the specified
+ * entry in the specified archive.
+ * @param a the archive as Resource.
+ * @param withEntry if the entry has been specified.
+ */
+ protected ArchiveResource(Resource a, boolean withEntry) {
+ addConfigured(a);
+ haveEntry = withEntry;
+ }
+
+ /**
+ * Set the archive that holds this Resource.
+ * @param a the archive as a File.
+ */
+ public void setArchive(File a) {
+ checkAttributesAllowed();
+ archive = new FileResource(a);
+ }
+
+ /**
+ * Sets the file or dir mode for this resource.
+ * @param mode integer representation of Unix permission mask.
+ */
+ public void setMode(int mode) {
+ checkAttributesAllowed();
+ this.mode = mode;
+ modeSet = true;
+ }
+
+ /**
+ * Sets the archive that holds this as a single element Resource
+ * collection.
+ * @param a the archive as a single element Resource collection.
+ */
+ public void addConfigured(ResourceCollection a) {
+ checkChildrenAllowed();
+ if (archive != null) {
+ throw new BuildException("you must not specify more than one"
+ + " archive");
+ }
+ if (a.size() != 1) {
+ throw new BuildException("only single argument resource collections"
+ + " are supported as archives");
+ }
+ archive = a.iterator().next();
+ }
+
+ /**
+ * Get the archive that holds this Resource.
+ * @return the archive as a Resource.
+ */
+ public Resource getArchive() {
+ return isReference()
+ ? ((ArchiveResource) getCheckedRef()).getArchive() : archive;
+ }
+
+ /**
+ * Get the last modified date of this Resource.
+ * @return the last modification date.
+ */
+ public long getLastModified() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getLastModified();
+ }
+ checkEntry();
+ return super.getLastModified();
+ }
+
+ /**
+ * Get the size of this Resource.
+ * @return the long size of this Resource.
+ */
+ public long getSize() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getSize();
+ }
+ checkEntry();
+ return super.getSize();
+ }
+
+ /**
+ * Learn whether this Resource represents a directory.
+ * @return boolean flag indicating whether the entry is a directory.
+ */
+ public boolean isDirectory() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).isDirectory();
+ }
+ checkEntry();
+ return super.isDirectory();
+ }
+
+ /**
+ * Find out whether this Resource represents an existing Resource.
+ * @return boolean existence flag.
+ */
+ public boolean isExists() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).isExists();
+ }
+ checkEntry();
+ return super.isExists();
+ }
+
+ /**
+ * Get the file or dir mode for this Resource.
+ * @return integer representation of Unix permission mask.
+ */
+ public int getMode() {
+ if (isReference()) {
+ return ((ArchiveResource) getCheckedRef()).getMode();
+ }
+ checkEntry();
+ return mode;
+ }
+
+ /**
+ * Overrides the super version.
+ * @param r the Reference to set.
+ */
+ public void setRefid(Reference r) {
+ if (archive != null || modeSet) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Compare this ArchiveResource to another Resource.
+ * @param another the other Resource against which to compare.
+ * @return a negative integer, zero, or a positive integer as this Resource
+ * is less than, equal to, or greater than the specified Resource.
+ */
+ public int compareTo(Resource another) {
+ return this.equals(another) ? 0 : super.compareTo(another);
+ }
+
+ /**
+ * Compare another Object to this ArchiveResource for equality.
+ * @param another the other Object to compare.
+ * @return true if another is a Resource representing
+ * the same entry in the same archive.
+ */
+ public boolean equals(Object another) {
+ if (this == another) {
+ return true;
+ }
+ if (isReference()) {
+ return getCheckedRef().equals(another);
+ }
+ if (another == null || !(another.getClass().equals(getClass()))) {
+ return false;
+ }
+ ArchiveResource r = (ArchiveResource) another;
+ return getArchive().equals(r.getArchive())
+ && getName().equals(r.getName());
+ }
+
+ /**
+ * Get the hash code for this Resource.
+ * @return hash code as int.
+ */
+ public int hashCode() {
+ return super.hashCode()
+ * (getArchive() == null ? NULL_ARCHIVE : getArchive().hashCode());
+ }
+
+ /**
+ * Format this Resource as a String.
+ * @return String representatation of this Resource.
+ */
+ public String toString() {
+ return isReference() ? getCheckedRef().toString()
+ : getArchive().toString() + ':' + getName();
+ }
+
+ /**
+ * Validate settings and ensure that the represented "archive entry"
+ * has been established.
+ */
+ protected final synchronized void checkEntry() throws BuildException {
+ dieOnCircularReference();
+ if (haveEntry) {
+ return;
+ }
+ String name = getName();
+ if (name == null) {
+ throw new BuildException("entry name not set");
+ }
+ Resource r = getArchive();
+ if (r == null) {
+ throw new BuildException("archive attribute not set");
+ }
+ if (!r.isExists()) {
+ throw new BuildException(r.toString() + " does not exist.");
+ }
+ if (r.isDirectory()) {
+ throw new BuildException(r + " denotes a directory.");
+ }
+ fetchEntry();
+ haveEntry = true;
+ }
+
+ /**
+ * Fetch information from the named entry inside the archive.
+ */
+ protected abstract void fetchEntry();
+
+ /**
+ * {@inheritDoc}
+ */
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p) {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ if (archive != null) {
+ pushAndInvokeCircularReferenceCheck(archive, stk, p);
+ }
+ setChecked(true);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Archives.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Archives.java
new file mode 100644
index 00000000..4b0d51c6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Archives.java
@@ -0,0 +1,194 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.ArchiveFileSet;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.TarFileSet;
+import org.apache.tools.ant.types.ZipFileSet;
+import org.apache.tools.ant.util.CollectionUtils;
+
+/**
+ * A resource collection that treats all nested resources as archives
+ * and returns the contents of the archives as its content.
+ *
+ * @since Ant 1.8.0
+ */
+public class Archives extends DataType
+ implements ResourceCollection, Cloneable {
+
+ private Union zips = new Union();
+ private Union tars = new Union();
+
+ /**
+ * Wrapper to identify nested resource collections as ZIP
+ * archives.
+ */
+ public Union createZips() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ return zips;
+ }
+
+ /**
+ * Wrapper to identify nested resource collections as ZIP
+ * archives.
+ */
+ public Union createTars() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ setChecked(false);
+ return tars;
+ }
+
+ /**
+ * Sums the sizes of nested archives.
+ */
+ public int size() {
+ if (isReference()) {
+ return ((Archives) getCheckedRef()).size();
+ }
+ dieOnCircularReference();
+ int total = 0;
+ for (final Iterator<ArchiveFileSet> i = grabArchives(); i.hasNext();) {
+ total += i.next().size();
+ }
+ return total;
+ }
+
+ /**
+ * Merges the nested collections.
+ */
+ public Iterator<Resource> iterator() {
+ if (isReference()) {
+ return ((Archives) getCheckedRef()).iterator();
+ }
+ dieOnCircularReference();
+ final List<Resource> l = new LinkedList<Resource>();
+ for (final Iterator<ArchiveFileSet> i = grabArchives(); i.hasNext();) {
+ l.addAll(CollectionUtils
+ .asCollection(i.next().iterator()));
+ }
+ return l.iterator();
+ }
+
+ /**
+ * @return false
+ */
+ public boolean isFilesystemOnly() {
+ if (isReference()) {
+ return ((Archives) getCheckedRef()).isFilesystemOnly();
+ }
+ dieOnCircularReference();
+ return false;
+ }
+
+ /**
+ * Overrides the base version.
+ * @param r the Reference to set.
+ */
+ @Override
+ public void setRefid(final Reference r) {
+ if (zips.getResourceCollections().size() > 0
+ || tars.getResourceCollections().size() > 0) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Implement clone. The nested resource collections are cloned as
+ * well.
+ * @return a cloned instance.
+ */
+ @Override
+ public Object clone() {
+ try {
+ final Archives a = (Archives) super.clone();
+ a.zips = (Union) zips.clone();
+ a.tars = (Union) tars.clone();
+ return a;
+ } catch (final CloneNotSupportedException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ // TODO this is a pretty expensive operation and so the result
+ // should be cached.
+ /**
+ * Turns all nested resources into corresponding ArchiveFileSets
+ * and returns an iterator over the collected archives.
+ */
+ protected Iterator<ArchiveFileSet> grabArchives() {
+ final List<ArchiveFileSet> l = new LinkedList<ArchiveFileSet>();
+ for (final Resource r : zips) {
+ l.add(configureArchive(new ZipFileSet(), r));
+ }
+ for (final Resource r : tars) {
+ l.add(configureArchive(new TarFileSet(), r));
+ }
+ return l.iterator();
+ }
+
+ /**
+ * Configures the archivefileset based on this type's settings,
+ * set the source.
+ */
+ protected ArchiveFileSet configureArchive(final ArchiveFileSet afs,
+ final Resource src) {
+ afs.setProject(getProject());
+ afs.setSrcResource(src);
+ return afs;
+ }
+
+ /**
+ * Overrides the version of DataType to recurse on all DataType
+ * child elements that may have been added.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ @Override
+ protected synchronized void dieOnCircularReference(final Stack<Object> stk, final Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ pushAndInvokeCircularReferenceCheck(zips, stk, p);
+ pushAndInvokeCircularReferenceCheck(tars, stk, p);
+ setChecked(true);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BCFileSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BCFileSet.java
new file mode 100644
index 00000000..aa99a4a3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BCFileSet.java
@@ -0,0 +1,73 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.util.Iterator;
+
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Utility FileSet that includes directories for backwards-compatibility
+ * with certain tasks e.g. Delete.
+ * @since Ant 1.7
+ */
+public class BCFileSet extends FileSet {
+ /**
+ * Default constructor.
+ */
+ public BCFileSet() {
+ }
+
+ /**
+ * Construct a new BCFileSet from the specified FileSet.
+ * @param fs the FileSet from which to inherit config.
+ */
+ public BCFileSet(FileSet fs) {
+ super(fs);
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return an Iterator of Resources.
+ * @since Ant 1.7
+ */
+ public Iterator<Resource> iterator() {
+ if (isReference()) {
+ return ((FileSet) getRef(getProject())).iterator();
+ }
+ FileResourceIterator result = new FileResourceIterator(getProject(), getDir());
+ result.addFiles(getDirectoryScanner().getIncludedFiles());
+ result.addFiles(getDirectoryScanner().getIncludedDirectories());
+ return result;
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ * @since Ant 1.7
+ */
+ public int size() {
+ if (isReference()) {
+ return ((FileSet) getRef(getProject())).size();
+ }
+ return getDirectoryScanner().getIncludedFilesCount()
+ + getDirectoryScanner().getIncludedDirsCount();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BZip2Resource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BZip2Resource.java
new file mode 100644
index 00000000..0c2dd4be
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BZip2Resource.java
@@ -0,0 +1,85 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.tools.bzip2.CBZip2InputStream;
+import org.apache.tools.bzip2.CBZip2OutputStream;
+
+/**
+ * A Bzip2 compressed resource.
+ *
+ * <p>Wraps around another resource, delegates all queries to that
+ * other resource but uncompresses/compresses streams on the fly.</p>
+ *
+ * @since Ant 1.7
+ */
+public class BZip2Resource extends CompressedResource {
+ private static final char[] MAGIC = new char[] {'B', 'Z'};
+
+ /** A no-arg constructor */
+ public BZip2Resource() {
+ }
+
+ /**
+ * Constructor with another resource to wrap.
+ * @param other the resource to wrap.
+ */
+ public BZip2Resource(org.apache.tools.ant.types.ResourceCollection other) {
+ super(other);
+ }
+
+ /**
+ * Decompress on the fly using {@link CBZip2InputStream}.
+ * @param in the stream to wrap.
+ * @return the wrapped stream.
+ * @throws IOException if there is a problem.
+ */
+ protected InputStream wrapStream(InputStream in) throws IOException {
+ for (int i = 0; i < MAGIC.length; i++) {
+ if (in.read() != MAGIC[i]) {
+ throw new IOException("Invalid bz2 stream.");
+ }
+ }
+ return new CBZip2InputStream(in);
+ }
+
+ /**
+ * Compress on the fly using {@link CBZip2OutputStream}.
+ * @param out the stream to wrap.
+ * @return the wrapped stream.
+ * @throws IOException if there is a problem.
+ */
+ protected OutputStream wrapStream(OutputStream out) throws IOException {
+ for (int i = 0; i < MAGIC.length; i++) {
+ out.write(MAGIC[i]);
+ }
+ return new CBZip2OutputStream(out);
+ }
+
+ /**
+ * Get the name of the compression method.
+ * @return the string "Bzip2".
+ */
+ protected String getCompressionName() {
+ return "Bzip2";
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BaseResourceCollectionContainer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BaseResourceCollectionContainer.java
new file mode 100644
index 00000000..281fa0f1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BaseResourceCollectionContainer.java
@@ -0,0 +1,268 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * Base class for ResourceCollections that nest multiple ResourceCollections.
+ * @since Ant 1.7
+ */
+public abstract class BaseResourceCollectionContainer
+ extends DataType implements ResourceCollection, Cloneable {
+ private List<ResourceCollection> rc = new ArrayList<ResourceCollection>();
+ private Collection<Resource> coll = null;
+ private boolean cache = true;
+
+ /**
+ * Create a new BaseResourceCollectionContainer.
+ */
+ public BaseResourceCollectionContainer() {
+ // TODO Auto-generated constructor stub
+ }
+
+ /**
+ * Create a new BaseResourceCollectionContainer.
+ * @since Ant 1.8
+ */
+ public BaseResourceCollectionContainer(Project project) {
+ setProject(project);
+ }
+
+ /**
+ * Set whether to cache collections.
+ * @param b boolean cache flag.
+ */
+ public synchronized void setCache(boolean b) {
+ cache = b;
+ }
+
+ /**
+ * Learn whether to cache collections. Default is <code>true</code>.
+ * @return boolean cache flag.
+ */
+ public synchronized boolean isCache() {
+ return cache;
+ }
+
+ /**
+ * Clear the container.
+ * @throws BuildException on error.
+ */
+ public synchronized void clear() throws BuildException {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ rc.clear();
+ FailFast.invalidate(this);
+ coll = null;
+ setChecked(false);
+ }
+
+ /**
+ * Add a ResourceCollection to the container.
+ * @param c the ResourceCollection to add.
+ * @throws BuildException on error.
+ */
+ public synchronized void add(ResourceCollection c) throws BuildException {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (c == null) {
+ return;
+ }
+ if (Project.getProject(c) == null) {
+ Project p = getProject();
+ if (p != null) {
+ p.setProjectReference(c);
+ }
+ }
+ rc.add(c);
+ FailFast.invalidate(this);
+ coll = null;
+ setChecked(false);
+ }
+
+ /**
+ * Add the Collection of ResourceCollections to the container.
+ * @param c the Collection whose elements to add.
+ * @throws BuildException on error.
+ */
+ public synchronized void addAll(Collection<? extends ResourceCollection> c) throws BuildException {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ try {
+ for (ResourceCollection resourceCollection : c) {
+ add(resourceCollection);
+ }
+ } catch (ClassCastException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract. The Iterator returned
+ * will throw ConcurrentModificationExceptions if ResourceCollections
+ * are added to this container while the Iterator is in use.
+ * @return a "fail-fast" Iterator.
+ */
+ public final synchronized Iterator<Resource> iterator() {
+ if (isReference()) {
+ return ((BaseResourceCollectionContainer) getCheckedRef()).iterator();
+ }
+ dieOnCircularReference();
+ return new FailFast(this, cacheCollection().iterator());
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ */
+ public synchronized int size() {
+ if (isReference()) {
+ return getCheckedRef(BaseResourceCollectionContainer.class, getDataTypeName()).size();
+ }
+ dieOnCircularReference();
+ return cacheCollection().size();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return whether this is a filesystem-only resource collection.
+ */
+ public synchronized boolean isFilesystemOnly() {
+ if (isReference()) {
+ return ((BaseResourceCollectionContainer) getCheckedRef()).isFilesystemOnly();
+ }
+ dieOnCircularReference();
+ //first the easy way, if all children are filesystem-only, return true:
+ boolean goEarly = true;
+ for (Iterator<ResourceCollection> i = rc.iterator(); goEarly && i.hasNext();) {
+ goEarly = i.next().isFilesystemOnly();
+ }
+ if (goEarly) {
+ return true;
+ }
+ /* now check each Resource in case the child only
+ lets through files from any children IT may have: */
+ for (Resource r : cacheCollection()) {
+ if (r.as(FileProvider.class) == null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Overrides the version of DataType to recurse on all DataType
+ * child elements that may have been added.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ for (ResourceCollection resourceCollection : rc) {
+ if (resourceCollection instanceof DataType) {
+ pushAndInvokeCircularReferenceCheck((DataType) resourceCollection, stk, p);
+ }
+ }
+ setChecked(true);
+ }
+ }
+
+ /**
+ * Get the nested ResourceCollections.
+ * @return List.
+ */
+ public final synchronized List<ResourceCollection> getResourceCollections() {
+ dieOnCircularReference();
+ return Collections.unmodifiableList(rc);
+ }
+
+ /**
+ * Template method for subclasses to return a Collection object of Resources.
+ * @return Collection.
+ */
+ protected abstract Collection<Resource> getCollection();
+
+ /**
+ * Implement clone. The set of nested resource
+ * collections is shallowly cloned.
+ * @return a cloned instance.
+ */
+ public Object clone() {
+ try {
+ BaseResourceCollectionContainer c
+ = (BaseResourceCollectionContainer) super.clone();
+ c.rc = new ArrayList<ResourceCollection>(rc);
+ c.coll = null;
+ return c;
+ } catch (CloneNotSupportedException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Format this BaseResourceCollectionContainer as a String.
+ * @return a descriptive <code>String</code>.
+ */
+ public synchronized String toString() {
+ if (isReference()) {
+ return getCheckedRef().toString();
+ }
+ if (cacheCollection().size() == 0) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ for (Resource resource : coll) {
+ if (sb.length() > 0) {
+ sb.append(File.pathSeparatorChar);
+ }
+ sb.append(resource);
+ }
+ return sb.toString();
+ }
+
+ private synchronized Collection<Resource> cacheCollection() {
+ if (coll == null || !isCache()) {
+ coll = getCollection();
+ }
+ return coll;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BaseResourceCollectionWrapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BaseResourceCollectionWrapper.java
new file mode 100644
index 00000000..78ba95ee
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/BaseResourceCollectionWrapper.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.tools.ant.types.resources;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Base class for a ResourceCollection that wraps a single nested
+ * ResourceCollection.
+ * @since Ant 1.7
+ */
+public abstract class BaseResourceCollectionWrapper
+ extends AbstractResourceCollectionWrapper {
+
+ private Collection<Resource> coll = null;
+
+ protected Iterator<Resource> createIterator() {
+ return cacheCollection().iterator();
+ }
+
+ protected int getSize() {
+ return cacheCollection().size();
+ }
+
+ /**
+ * Template method for subclasses to return a Collection of Resources.
+ * @return Collection.
+ */
+ protected abstract Collection<Resource> getCollection();
+
+ private synchronized Collection<Resource> cacheCollection() {
+ if (coll == null || !isCache()) {
+ coll = getCollection();
+ }
+ return coll;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/CompressedResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/CompressedResource.java
new file mode 100644
index 00000000..2c72c26f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/CompressedResource.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.tools.ant.types.resources;
+
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * A compressed resource.
+ *
+ * <p>Wraps around another resource, delegates all queries (except
+ * getSize) to that other resource but uncompresses/compresses streams
+ * on the fly.</p>
+ *
+ * @since Ant 1.7
+ */
+public abstract class CompressedResource extends ContentTransformingResource {
+
+ /** no arg constructor */
+ protected CompressedResource() {
+ }
+
+ /**
+ * Constructor with another resource to wrap.
+ * @param other the resource to wrap.
+ */
+ protected CompressedResource(ResourceCollection other) {
+ addConfigured(other);
+ }
+
+ /**
+ * Get the string representation of this Resource.
+ * @return this Resource formatted as a String.
+ * @since Ant 1.7
+ */
+ public String toString() {
+ return getCompressionName() + " compressed " + super.toString();
+ }
+
+ /**
+ * Get the name of the compression method used.
+ * @return the name of the compression method.
+ */
+ protected abstract String getCompressionName();
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ContentTransformingResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ContentTransformingResource.java
new file mode 100644
index 00000000..79445bfd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ContentTransformingResource.java
@@ -0,0 +1,177 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * A resource that transforms the content of another resource.
+ *
+ * <p>Wraps around another resource, delegates all queries (except
+ * getSize) to that other resource but transforms stream content
+ * on the fly.</p>
+ *
+ * @since Ant 1.8
+ */
+public abstract class ContentTransformingResource extends ResourceDecorator {
+
+ private static final int BUFFER_SIZE = 8192;
+
+ /** no arg constructor */
+ protected ContentTransformingResource() {
+ }
+
+ /**
+ * Constructor with another resource to wrap.
+ * @param other the resource to wrap.
+ */
+ protected ContentTransformingResource(final ResourceCollection other) {
+ super(other);
+ }
+
+ /**
+ * Get the size of this Resource.
+ * @return the size, as a long, 0 if the Resource does not exist (for
+ * compatibility with java.io.File), or UNKNOWN_SIZE if not known.
+ */
+ @Override
+ public long getSize() {
+ if (isExists()) {
+ InputStream in = null;
+ try {
+ in = getInputStream();
+ final byte[] buf = new byte[BUFFER_SIZE];
+ int size = 0;
+ int readNow;
+ while ((readNow = in.read(buf, 0, buf.length)) > 0) {
+ size += readNow;
+ }
+ return size;
+ } catch (final IOException ex) {
+ throw new BuildException("caught exception while reading "
+ + getName(), ex);
+ } finally {
+ FileUtils.close(in);
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Get an InputStream for the Resource.
+ * @return an InputStream containing this Resource's content.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if InputStreams are not
+ * supported for this Resource type.
+ */
+ @Override
+ public InputStream getInputStream() throws IOException {
+ InputStream in = getResource().getInputStream();
+ if (in != null) {
+ in = wrapStream(in);
+ }
+ return in;
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ */
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ OutputStream out = getResource().getOutputStream();
+ if (out != null) {
+ out = wrapStream(out);
+ }
+ return out;
+ }
+
+ /**
+ * Suppress FileProvider, re-implement Appendable
+ */
+ @Override
+ public <T> T as(final Class<T> clazz) {
+ if (Appendable.class.isAssignableFrom(clazz)) {
+ if (isAppendSupported()) {
+ final Appendable a =
+ getResource().as(Appendable.class);
+ if (a != null) {
+ return clazz.cast(new Appendable() {
+ public OutputStream getAppendOutputStream()
+ throws IOException {
+ OutputStream out = a.getAppendOutputStream();
+ if (out != null) {
+ out = wrapStream(out);
+ }
+ return out;
+ }
+ });
+ }
+ }
+ return null;
+ }
+
+ return FileProvider.class.isAssignableFrom(clazz)
+ ? null : getResource().as(clazz);
+ }
+
+ /**
+ * Learn whether the transformation performed allows appends.
+ *
+ * <p>In general compressed outputs will become invalid if they
+ * are appended to, for example.</p>
+ *
+ * <p>This implementations returns false.</p>
+ */
+ protected boolean isAppendSupported() {
+ return false;
+ }
+
+ /**
+ * Get a content-filtering/transforming InputStream.
+ *
+ * @param in InputStream to wrap, will never be null.
+ * @return a compressed inputstream.
+ * @throws IOException if there is a problem.
+ */
+ protected abstract InputStream wrapStream(InputStream in)
+ throws IOException;
+
+ /**
+ * Get a content-filtering/transforming OutputStream.
+ *
+ * @param out OutputStream to wrap, will never be null.
+ * @return a compressed outputstream.
+ * @throws IOException if there is a problem.
+ */
+ protected abstract OutputStream wrapStream(OutputStream out)
+ throws IOException;
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Difference.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Difference.java
new file mode 100644
index 00000000..3f3c983c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Difference.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.tools.ant.types.resources;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * ResourceCollection representing the difference between
+ * two or more nested ResourceCollections.
+ * @since Ant 1.7
+ */
+public class Difference extends BaseResourceCollectionContainer {
+
+ /**
+ * Calculate the difference of the nested ResourceCollections.
+ * @return a Collection of Resources.
+ */
+ protected Collection<Resource> getCollection() {
+ List<ResourceCollection> rcs = getResourceCollections();
+ int size = rcs.size();
+ if (size < 2) {
+ throw new BuildException("The difference of " + size
+ + " resource collection" + ((size == 1) ? "" : "s")
+ + " is undefined.");
+ }
+ Set<Resource> hs = new HashSet<Resource>();
+ List<Resource> al = new ArrayList<Resource>();
+ for (ResourceCollection rc : rcs) {
+ for (Resource r : rc) {
+ if (hs.add(r)) {
+ al.add(r);
+ } else {
+ al.remove(r);
+ }
+ }
+ }
+ return al;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FailFast.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FailFast.java
new file mode 100644
index 00000000..dc962bb4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FailFast.java
@@ -0,0 +1,135 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.util.ConcurrentModificationException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Helper class for ResourceCollections to return Iterators
+ * that fail on changes to the object.
+ * @since Ant 1.7
+ */
+/*package-private*/ class FailFast implements Iterator<Resource> {
+ private static final WeakHashMap<Object, Set<FailFast>> MAP = new WeakHashMap<Object, Set<FailFast>>();
+
+ /**
+ * Invalidate any in-use Iterators from the specified Object.
+ * @param o the parent Object.
+ */
+ static synchronized void invalidate(Object o) {
+ Set<FailFast> s = MAP.get(o);
+ if (s != null) {
+ s.clear();
+ }
+ }
+
+ private static synchronized void add(FailFast f) {
+ Set<FailFast> s = MAP.get(f.parent);
+ if (s == null) {
+ s = new HashSet<FailFast>();
+ MAP.put(f.parent, s);
+ }
+ s.add(f);
+ }
+
+ private static synchronized void remove(FailFast f) {
+ Set<FailFast> s = MAP.get(f.parent);
+ if (s != null) {
+ s.remove(f);
+ }
+ }
+
+ private static synchronized void failFast(FailFast f) {
+ Set<FailFast> s = MAP.get(f.parent);
+ if (!s.contains(f)) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ private final Object parent;
+ private Iterator<Resource> wrapped;
+
+ /**
+ * Construct a new FailFast Iterator wrapping the specified Iterator
+ * and dependent upon the specified parent Object.
+ * @param o the parent Object.
+ * @param i the wrapped Iterator.
+ */
+ FailFast(Object o, Iterator<Resource> i) {
+ if (o == null) {
+ throw new IllegalArgumentException("parent object is null");
+ }
+ if (i == null) {
+ throw new IllegalArgumentException("cannot wrap null iterator");
+ }
+ parent = o;
+ if (i.hasNext()) {
+ wrapped = i;
+ add(this);
+ }
+ }
+
+ /**
+ * Fulfill the Iterator contract.
+ * @return true if there are more elements.
+ */
+ public boolean hasNext() {
+ if (wrapped == null) {
+ return false;
+ }
+ failFast(this);
+ return wrapped.hasNext();
+ }
+
+ /**
+ * Fulfill the Iterator contract.
+ * @return the next element.
+ * @throws NoSuchElementException if no more elements.
+ */
+ public Resource next() {
+ if (wrapped == null || !wrapped.hasNext()) {
+ throw new NoSuchElementException();
+ }
+ failFast(this);
+ try {
+ return wrapped.next();
+ } finally {
+ if (!wrapped.hasNext()) {
+ wrapped = null;
+ remove(this);
+ }
+ }
+ }
+
+ /**
+ * Fulfill the Iterator contract.
+ * @throws UnsupportedOperationException always.
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FileProvider.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FileProvider.java
new file mode 100644
index 00000000..aa283004
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FileProvider.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.tools.ant.types.resources;
+
+import java.io.File;
+
+/**
+ * This is an interface that resources that can provide a file should implement.
+ * This is a refactoring of {@link FileResource}, to allow other resources
+ * to act as sources of files (and to make components that only support
+ * file-based resources from only support FileResource resources.
+ * @since Ant 1.8
+ */
+public interface FileProvider {
+ /**
+ * Get the file represented by this Resource.
+ * @return the file.
+ */
+ File getFile();
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FileResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FileResource.java
new file mode 100644
index 00000000..3ed49b81
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FileResource.java
@@ -0,0 +1,392 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * A Resource representation of a File.
+ * @since Ant 1.7
+ */
+public class FileResource extends Resource implements Touchable, FileProvider,
+ ResourceFactory, Appendable {
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+ private static final int NULL_FILE
+ = Resource.getMagicNumber("null file".getBytes());
+
+ private File file;
+ private File baseDir;
+
+ /**
+ * Default constructor.
+ */
+ public FileResource() {
+ }
+
+ /**
+ * Construct a new FileResource using the specified basedir and relative name.
+ * @param b the basedir as File.
+ * @param name the relative filename.
+ */
+ public FileResource(File b, String name) {
+ this.baseDir = b;
+ this.file = FILE_UTILS.resolveFile(b, name);
+ }
+
+ /**
+ * Construct a new FileResource from a File.
+ * @param f the File represented.
+ */
+ public FileResource(File f) {
+ setFile(f);
+ }
+
+ /**
+ * Create a new FileResource.
+ * @param p Project
+ * @param f File represented
+ * @since Ant 1.8
+ */
+ public FileResource(Project p, File f) {
+ this(f);
+ setProject(p);
+ }
+
+ /**
+ * Constructor for Ant attribute introspection.
+ * @param p the Project against which to resolve <code>s</code>.
+ * @param s the absolute or Project-relative filename as a String.
+ * @see org.apache.tools.ant.IntrospectionHelper
+ */
+ public FileResource(Project p, String s) {
+ this(p, p.resolveFile(s));
+ }
+
+ /**
+ * Set the File for this FileResource.
+ * @param f the File to be represented.
+ */
+ public void setFile(File f) {
+ checkAttributesAllowed();
+ file = f;
+ if (f != null && (getBaseDir() == null || !FILE_UTILS.isLeadingPath(getBaseDir(), f))) {
+ setBaseDir(f.getParentFile());
+ }
+ }
+
+ /**
+ * Get the file represented by this FileResource.
+ * @return the File.
+ */
+ public File getFile() {
+ if (isReference()) {
+ return ((FileResource) getCheckedRef()).getFile();
+ }
+ dieOnCircularReference();
+ synchronized (this) {
+ if (file == null) {
+ //try to resolve file set via basedir/name property setters:
+ File d = getBaseDir();
+ String n = super.getName();
+ if (n != null) {
+ setFile(FILE_UTILS.resolveFile(d, n));
+ }
+ }
+ }
+ return file;
+ }
+
+ /**
+ * Set the basedir for this FileResource.
+ * @param b the basedir as File.
+ */
+ public void setBaseDir(File b) {
+ checkAttributesAllowed();
+ baseDir = b;
+ }
+
+ /**
+ * Return the basedir to which the name is relative.
+ * @return the basedir as File.
+ */
+ public File getBaseDir() {
+ if (isReference()) {
+ return ((FileResource) getCheckedRef()).getBaseDir();
+ }
+ dieOnCircularReference();
+ return baseDir;
+ }
+
+ /**
+ * Overrides the super version.
+ * @param r the Reference to set.
+ */
+ public void setRefid(Reference r) {
+ if (file != null || baseDir != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Get the name of this FileResource. If the basedir is set,
+ * the name will be relative to that. Otherwise the basename
+ * only will be returned.
+ * @return the name of this resource.
+ */
+ public String getName() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getName();
+ }
+ File b = getBaseDir();
+ return b == null ? getNotNullFile().getName()
+ : FILE_UTILS.removeLeadingPath(b, getNotNullFile());
+ }
+
+ /**
+ * Learn whether this file exists.
+ * @return true if this resource exists.
+ */
+ public boolean isExists() {
+ return isReference() ? ((Resource) getCheckedRef()).isExists()
+ : getNotNullFile().exists();
+ }
+
+ /**
+ * Get the modification time in milliseconds since 01.01.1970 .
+ * @return 0 if the resource does not exist.
+ */
+ public long getLastModified() {
+ return isReference()
+ ? ((Resource) getCheckedRef()).getLastModified()
+ : getNotNullFile().lastModified();
+ }
+
+ /**
+ * Learn whether the resource is a directory.
+ * @return boolean flag indicating if the resource is a directory.
+ */
+ public boolean isDirectory() {
+ return isReference() ? ((Resource) getCheckedRef()).isDirectory()
+ : getNotNullFile().isDirectory();
+ }
+
+ /**
+ * Get the size of this Resource.
+ * @return the size, as a long, 0 if the Resource does not exist.
+ */
+ public long getSize() {
+ return isReference() ? ((Resource) getCheckedRef()).getSize()
+ : getNotNullFile().length();
+ }
+
+ /**
+ * Return an InputStream for reading the contents of this Resource.
+ * @return an InputStream object.
+ * @throws IOException if an error occurs.
+ */
+ public InputStream getInputStream() throws IOException {
+ return isReference()
+ ? ((Resource) getCheckedRef()).getInputStream()
+ : new FileInputStream(getNotNullFile());
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ */
+ public OutputStream getOutputStream() throws IOException {
+ if (isReference()) {
+ return ((FileResource) getCheckedRef()).getOutputStream();
+ }
+ return getOutputStream(false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public OutputStream getAppendOutputStream() throws IOException {
+ if (isReference()) {
+ return ((FileResource) getCheckedRef()).getAppendOutputStream();
+ }
+ return getOutputStream(true);
+ }
+
+ private OutputStream getOutputStream(boolean append) throws IOException {
+ File f = getNotNullFile();
+ if (f.exists()) {
+ if (f.isFile() && !append) {
+ f.delete();
+ }
+ } else {
+ File p = f.getParentFile();
+ if (p != null && !(p.exists())) {
+ p.mkdirs();
+ }
+ }
+ return append ? new FileOutputStream(f.getAbsolutePath(), true) : new FileOutputStream(f);
+ }
+
+ /**
+ * Compare this FileResource to another Resource.
+ * @param another the other Resource against which to compare.
+ * @return a negative integer, zero, or a positive integer as this FileResource
+ * is less than, equal to, or greater than the specified Resource.
+ */
+ public int compareTo(Resource another) {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).compareTo(another);
+ }
+ if (this.equals(another)) {
+ return 0;
+ }
+ FileProvider otherFP = another.as(FileProvider.class);
+ if (otherFP != null) {
+ File f = getFile();
+ if (f == null) {
+ return -1;
+ }
+ File of = otherFP.getFile();
+ if (of == null) {
+ return 1;
+ }
+ return f.compareTo(of);
+ }
+ return super.compareTo(another);
+ }
+
+ /**
+ * Compare another Object to this FileResource for equality.
+ * @param another the other Object to compare.
+ * @return true if another is a FileResource representing the same file.
+ */
+ public boolean equals(Object another) {
+ if (this == another) {
+ return true;
+ }
+ if (isReference()) {
+ return getCheckedRef().equals(another);
+ }
+ if (another == null || !(another.getClass().equals(getClass()))) {
+ return false;
+ }
+ FileResource otherfr = (FileResource) another;
+ return getFile() == null
+ ? otherfr.getFile() == null
+ : getFile().equals(otherfr.getFile());
+ }
+
+ /**
+ * Get the hash code for this Resource.
+ * @return hash code as int.
+ */
+ public int hashCode() {
+ if (isReference()) {
+ return getCheckedRef().hashCode();
+ }
+ return MAGIC * (getFile() == null ? NULL_FILE : getFile().hashCode());
+ }
+
+ /**
+ * Get the string representation of this Resource.
+ * @return this FileResource formatted as a String.
+ */
+ public String toString() {
+ if (isReference()) {
+ return getCheckedRef().toString();
+ }
+ if (file == null) {
+ return "(unbound file resource)";
+ }
+ String absolutePath = file.getAbsolutePath();
+ return FILE_UTILS.normalize(absolutePath).getAbsolutePath();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return whether this Resource is a FileResource.
+ */
+ public boolean isFilesystemOnly() {
+ if (isReference()) {
+ return ((FileResource) getCheckedRef()).isFilesystemOnly();
+ }
+ dieOnCircularReference();
+ return true;
+ }
+
+ /**
+ * Implement the Touchable interface.
+ * @param modTime new last modification time.
+ */
+ public void touch(long modTime) {
+ if (isReference()) {
+ ((FileResource) getCheckedRef()).touch(modTime);
+ return;
+ }
+ if (!getNotNullFile().setLastModified(modTime)) {
+ log("Failed to change file modification time", Project.MSG_WARN);
+ }
+ }
+
+ /**
+ * Get the file represented by this FileResource, ensuring it is not null.
+ * @return the not-null File.
+ * @throws BuildException if file is null.
+ */
+ protected File getNotNullFile() {
+ if (getFile() == null) {
+ throw new BuildException("file attribute is null!");
+ }
+ dieOnCircularReference();
+ return getFile();
+ }
+
+ /**
+ * Create a new resource that matches a relative or absolute path.
+ * If the current instance has a compatible baseDir attribute, it is copied.
+ * @param path relative/absolute path to a resource
+ * @return a new resource of type FileResource
+ * @throws BuildException if desired
+ * @since Ant1.8
+ */
+ public Resource getResource(String path) {
+ File newfile = FILE_UTILS.resolveFile(getFile(), path);
+ FileResource fileResource = new FileResource(newfile);
+ if (FILE_UTILS.isLeadingPath(getBaseDir(), newfile)) {
+ fileResource.setBaseDir(getBaseDir());
+ }
+ return fileResource;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FileResourceIterator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FileResourceIterator.java
new file mode 100644
index 00000000..6d8849ce
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/FileResourceIterator.java
@@ -0,0 +1,149 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Iterator of FileResources from filenames.
+ * @since Ant 1.7
+ */
+public class FileResourceIterator implements Iterator<Resource> {
+ private Project project;
+ private File basedir;
+ private String[] files;
+ private int pos = 0;
+
+ /**
+ * Construct a new FileResourceIterator.
+ * @deprecated in favor of {@link FileResourceIterator#FileResourceIterator(Project)}
+ */
+ public FileResourceIterator() {
+ }
+
+ /**
+ * Create a new FileResourceIterator.
+ * @param project associated Project instance
+ * @since Ant 1.8
+ */
+ public FileResourceIterator(Project project) {
+ this.project = project;
+ }
+
+ /**
+ * Construct a new FileResourceIterator relative to the specified
+ * base directory.
+ * @param basedir the base directory of this instance.
+ * @deprecated in favor of {@link FileResourceIterator#FileResourceIterator(Project, File)}
+ */
+ public FileResourceIterator(File basedir) {
+ this(null, basedir);
+ }
+
+ /**
+ * Construct a new FileResourceIterator relative to the specified
+ * base directory.
+ * @param project associated Project instance
+ * @param basedir the base directory of this instance.
+ * @since Ant 1.8
+ */
+ public FileResourceIterator(Project project, File basedir) {
+ this(project);
+ this.basedir = basedir;
+ }
+
+ /**
+ * Construct a new FileResourceIterator over the specified filenames,
+ * relative to the specified base directory.
+ * @param basedir the base directory of this instance.
+ * @param filenames the String[] of filenames.
+ * @deprecated in favor of {@link FileResourceIterator#FileResourceIterator(Project, File, String[])}
+ */
+ public FileResourceIterator(File basedir, String[] filenames) {
+ this(null, basedir, filenames);
+ }
+
+ /**
+ * Construct a new FileResourceIterator over the specified filenames,
+ * relative to the specified base directory.
+ * @param project associated Project instance
+ * @param basedir the base directory of this instance.
+ * @param filenames the String[] of filenames.
+ * @since Ant 1.8
+ */
+ public FileResourceIterator(Project project, File basedir, String[] filenames) {
+ this(project, basedir);
+ addFiles(filenames);
+ }
+
+ /**
+ * Add an array of filenames to this FileResourceIterator.
+ * @param s the filenames to add.
+ */
+ public void addFiles(String[] s) {
+ int start = (files == null) ? 0 : files.length;
+ String[] newfiles = new String[start + s.length];
+ if (start > 0) {
+ System.arraycopy(files, 0, newfiles, 0, start);
+ }
+ files = newfiles;
+ System.arraycopy(s, 0, files, start, s.length);
+ }
+
+ /**
+ * Find out whether this FileResourceIterator has more elements.
+ * @return whether there are more Resources to iterate over.
+ */
+ public boolean hasNext() {
+ return pos < files.length;
+ }
+
+ /**
+ * Get the next element from this FileResourceIterator.
+ * @return the next Object.
+ */
+ public Resource next() {
+ return nextResource();
+ }
+
+ /**
+ * Not implemented.
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Convenience method to return the next resource.
+ * @return the next File.
+ */
+ public FileResource nextResource() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ FileResource result = new FileResource(basedir, files[pos++]);
+ result.setProject(project);
+ return result;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Files.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Files.java
new file mode 100644
index 00000000..521bcc8d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Files.java
@@ -0,0 +1,503 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.selectors.AbstractSelectorContainer;
+import org.apache.tools.ant.types.selectors.FileSelector;
+
+/**
+ * ResourceCollection implementation; like AbstractFileSet with absolute paths.
+ * @since Ant 1.7
+ */
+public class Files extends AbstractSelectorContainer
+ implements ResourceCollection {
+
+ private static final Iterator<Resource> EMPTY_ITERATOR
+ = Collections.<Resource>emptySet().iterator();
+
+ private PatternSet defaultPatterns = new PatternSet();
+ private Vector<PatternSet> additionalPatterns = new Vector<PatternSet>();
+
+ private boolean useDefaultExcludes = true;
+ private boolean caseSensitive = true;
+ private boolean followSymlinks = true;
+
+ /* cached DirectoryScanner instance */
+ private DirectoryScanner ds = null;
+
+ /**
+ * Construct a new <code>Files</code> collection.
+ */
+ public Files() {
+ super();
+ }
+
+ /**
+ * Construct a new <code>Files</code> collection, shallowly cloned
+ * from the specified <code>Files</code>.
+ * @param f the <code>Files</code> to use as a template.
+ */
+ protected Files(Files f) {
+ this.defaultPatterns = f.defaultPatterns;
+ this.additionalPatterns = f.additionalPatterns;
+ this.useDefaultExcludes = f.useDefaultExcludes;
+ this.caseSensitive = f.caseSensitive;
+ this.followSymlinks = f.followSymlinks;
+ this.ds = f.ds;
+ setProject(f.getProject());
+ }
+
+ /**
+ * Make this instance in effect a reference to another instance.
+ *
+ * <p>You must not set another attribute or nest elements inside
+ * this element if you make it a reference.</p>
+ * @param r the <code>Reference</code> to use.
+ * @throws BuildException if there is a problem.
+ */
+ public void setRefid(Reference r) throws BuildException {
+ if (hasPatterns(defaultPatterns)) {
+ throw tooManyAttributes();
+ }
+ if (!additionalPatterns.isEmpty()) {
+ throw noChildrenAllowed();
+ }
+ if (hasSelectors()) {
+ throw noChildrenAllowed();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Create a nested patternset.
+ * @return <code>PatternSet</code>.
+ */
+ public synchronized PatternSet createPatternSet() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ PatternSet patterns = new PatternSet();
+ additionalPatterns.addElement(patterns);
+ ds = null;
+ setChecked(false);
+ return patterns;
+ }
+
+ /**
+ * Add a name entry to the include list.
+ * @return <code>PatternSet.NameEntry</code>.
+ */
+ public synchronized PatternSet.NameEntry createInclude() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ ds = null;
+ return defaultPatterns.createInclude();
+ }
+
+ /**
+ * Add a name entry to the include files list.
+ * @return <code>PatternSet.NameEntry</code>.
+ */
+ public synchronized PatternSet.NameEntry createIncludesFile() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ ds = null;
+ return defaultPatterns.createIncludesFile();
+ }
+
+ /**
+ * Add a name entry to the exclude list.
+ * @return <code>PatternSet.NameEntry</code>.
+ */
+ public synchronized PatternSet.NameEntry createExclude() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ ds = null;
+ return defaultPatterns.createExclude();
+ }
+
+ /**
+ * Add a name entry to the excludes files list.
+ * @return <code>PatternSet.NameEntry</code>.
+ */
+ public synchronized PatternSet.NameEntry createExcludesFile() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ ds = null;
+ return defaultPatterns.createExcludesFile();
+ }
+
+ /**
+ * Append <code>includes</code> to the current list of include
+ * patterns.
+ *
+ * <p>Patterns may be separated by a comma or a space.</p>
+ *
+ * @param includes the <code>String</code> containing the include patterns.
+ */
+ public synchronized void setIncludes(String includes) {
+ checkAttributesAllowed();
+ defaultPatterns.setIncludes(includes);
+ ds = null;
+ }
+
+ /**
+ * Append <code>includes</code> to the current list of include
+ * patterns.
+ *
+ * @param includes array containing the include patterns.
+ */
+ public synchronized void appendIncludes(String[] includes) {
+ checkAttributesAllowed();
+ if (includes != null) {
+ for (int i = 0; i < includes.length; i++) {
+ defaultPatterns.createInclude().setName(includes[i]);
+ }
+ ds = null;
+ }
+ }
+
+ /**
+ * Append <code>excludes</code> to the current list of exclude
+ * patterns.
+ *
+ * <p>Patterns may be separated by a comma or a space.</p>
+ *
+ * @param excludes the <code>String</code> containing the exclude patterns.
+ */
+ public synchronized void setExcludes(String excludes) {
+ checkAttributesAllowed();
+ defaultPatterns.setExcludes(excludes);
+ ds = null;
+ }
+
+ /**
+ * Append <code>excludes</code> to the current list of include
+ * patterns.
+ *
+ * @param excludes array containing the exclude patterns.
+ */
+ public synchronized void appendExcludes(String[] excludes) {
+ checkAttributesAllowed();
+ if (excludes != null) {
+ for (int i = 0; i < excludes.length; i++) {
+ defaultPatterns.createExclude().setName(excludes[i]);
+ }
+ ds = null;
+ }
+ }
+
+ /**
+ * Set the <code>File</code> containing the includes patterns.
+ *
+ * @param incl <code>File</code> instance.
+ * @throws BuildException if there is a problem.
+ */
+ public synchronized void setIncludesfile(File incl) throws BuildException {
+ checkAttributesAllowed();
+ defaultPatterns.setIncludesfile(incl);
+ ds = null;
+ }
+
+ /**
+ * Set the <code>File</code> containing the excludes patterns.
+ *
+ * @param excl <code>File</code> instance.
+ * @throws BuildException if there is a problem.
+ */
+ public synchronized void setExcludesfile(File excl) throws BuildException {
+ checkAttributesAllowed();
+ defaultPatterns.setExcludesfile(excl);
+ ds = null;
+ }
+
+ /**
+ * Set whether default exclusions should be used or not.
+ *
+ * @param useDefaultExcludes <code>boolean</code>.
+ */
+ public synchronized void setDefaultexcludes(boolean useDefaultExcludes) {
+ checkAttributesAllowed();
+ this.useDefaultExcludes = useDefaultExcludes;
+ ds = null;
+ }
+
+ /**
+ * Get whether default exclusions should be used or not.
+ * @return the defaultexclusions value.
+ */
+ public synchronized boolean getDefaultexcludes() {
+ return (isReference())
+ ? getRef().getDefaultexcludes() : useDefaultExcludes;
+ }
+
+ /**
+ * Set case-sensitivity of the Files collection.
+ *
+ * @param caseSensitive <code>boolean</code>.
+ */
+ public synchronized void setCaseSensitive(boolean caseSensitive) {
+ checkAttributesAllowed();
+ this.caseSensitive = caseSensitive;
+ ds = null;
+ }
+
+ /**
+ * Find out if this Files collection is case-sensitive.
+ *
+ * @return <code>boolean</code> indicating whether the Files
+ * collection is case-sensitive.
+ */
+ public synchronized boolean isCaseSensitive() {
+ return (isReference())
+ ? getRef().isCaseSensitive() : caseSensitive;
+ }
+
+ /**
+ * Set whether or not symbolic links should be followed.
+ *
+ * @param followSymlinks whether or not symbolic links should be followed.
+ */
+ public synchronized void setFollowSymlinks(boolean followSymlinks) {
+ checkAttributesAllowed();
+ this.followSymlinks = followSymlinks;
+ ds = null;
+ }
+
+ /**
+ * Find out whether symbolic links should be followed.
+ *
+ * @return <code>boolean</code> indicating whether symbolic links
+ * should be followed.
+ */
+ public synchronized boolean isFollowSymlinks() {
+ return (isReference())
+ ? getRef().isFollowSymlinks() : followSymlinks;
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return an Iterator of Resources.
+ */
+ public synchronized Iterator<Resource> iterator() {
+ if (isReference()) {
+ return getRef().iterator();
+ }
+ ensureDirectoryScannerSetup();
+ ds.scan();
+ int fct = ds.getIncludedFilesCount();
+ int dct = ds.getIncludedDirsCount();
+ if (fct + dct == 0) {
+ return EMPTY_ITERATOR;
+ }
+ FileResourceIterator result = new FileResourceIterator(getProject());
+ if (fct > 0) {
+ result.addFiles(ds.getIncludedFiles());
+ }
+ if (dct > 0) {
+ result.addFiles(ds.getIncludedDirectories());
+ }
+ return result;
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ */
+ public synchronized int size() {
+ if (isReference()) {
+ return getRef().size();
+ }
+ ensureDirectoryScannerSetup();
+ ds.scan();
+ return ds.getIncludedFilesCount() + ds.getIncludedDirsCount();
+ }
+
+ /**
+ * Find out whether this Files collection has patterns.
+ *
+ * @return whether any patterns are in this container.
+ */
+ public synchronized boolean hasPatterns() {
+ if (isReference()) {
+ return getRef().hasPatterns();
+ }
+ dieOnCircularReference();
+ if (hasPatterns(defaultPatterns)) {
+ return true;
+ }
+ for (PatternSet patternSet : additionalPatterns) {
+ if (hasPatterns(patternSet)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add a new selector into this container.
+ *
+ * @param selector the new <code>FileSelector</code> to add.
+ */
+ public synchronized void appendSelector(FileSelector selector) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ super.appendSelector(selector);
+ ds = null;
+ }
+
+ /**
+ * Format this Files collection as a String.
+ * @return a descriptive <code>String</code>.
+ */
+ public String toString() {
+ if (isReference()) {
+ return getRef().toString();
+ }
+ Iterator<Resource> i = iterator();
+ if (!i.hasNext()) {
+ return "";
+ }
+ StringBuffer sb = new StringBuffer();
+ while (i.hasNext()) {
+ if (sb.length() > 0) {
+ sb.append(File.pathSeparatorChar);
+ }
+ sb.append(i.next());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Create a deep clone of this instance, except for the nested selectors
+ * (the list of selectors is a shallow clone of this instance's list).
+ * @return a cloned Object.
+ */
+ public synchronized Object clone() {
+ if (isReference()) {
+ return getRef().clone();
+ }
+ Files f = (Files) super.clone();
+ f.defaultPatterns = (PatternSet) defaultPatterns.clone();
+ f.additionalPatterns = new Vector<PatternSet>(additionalPatterns.size());
+ for (PatternSet ps : additionalPatterns) {
+ f.additionalPatterns.add((PatternSet) ps.clone());
+ }
+ return f;
+ }
+
+ /**
+ * Get the merged include patterns for this Files collection.
+ * @param p Project instance.
+ * @return the include patterns of the default pattern set and all
+ * nested patternsets.
+ */
+ public String[] mergeIncludes(Project p) {
+ return mergePatterns(p).getIncludePatterns(p);
+ }
+
+ /**
+ * Get the merged exclude patterns for this Files collection.
+ * @param p Project instance.
+ * @return the exclude patterns of the default pattern set and all
+ * nested patternsets.
+ */
+ public String[] mergeExcludes(Project p) {
+ return mergePatterns(p).getExcludePatterns(p);
+ }
+
+ /**
+ * Get the merged patterns for this Files collection.
+ * @param p Project instance.
+ * @return the default patternset merged with the additional sets
+ * in a new PatternSet instance.
+ */
+ public synchronized PatternSet mergePatterns(Project p) {
+ if (isReference()) {
+ return getRef().mergePatterns(p);
+ }
+ dieOnCircularReference();
+ PatternSet ps = new PatternSet();
+ ps.append(defaultPatterns, p);
+ final int count = additionalPatterns.size();
+ for (int i = 0; i < count; i++) {
+ Object o = additionalPatterns.elementAt(i);
+ ps.append((PatternSet) o, p);
+ }
+ return ps;
+ }
+
+ /**
+ * Always returns true.
+ * @return true indicating that all elements of a Files collection
+ * will be FileResources.
+ */
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+
+ /**
+ * Perform the check for circular references and return the
+ * referenced Files collection.
+ * @return <code>FileCollection</code>.
+ */
+ protected Files getRef() {
+ return (Files) getCheckedRef();
+ }
+
+ private synchronized void ensureDirectoryScannerSetup() {
+ dieOnCircularReference();
+ if (ds == null) {
+ ds = new DirectoryScanner();
+ PatternSet ps = mergePatterns(getProject());
+ ds.setIncludes(ps.getIncludePatterns(getProject()));
+ ds.setExcludes(ps.getExcludePatterns(getProject()));
+ ds.setSelectors(getSelectors(getProject()));
+ if (useDefaultExcludes) {
+ ds.addDefaultExcludes();
+ }
+ ds.setCaseSensitive(caseSensitive);
+ ds.setFollowSymlinks(followSymlinks);
+ }
+ }
+
+ private boolean hasPatterns(PatternSet ps) {
+ String[] includePatterns = ps.getIncludePatterns(getProject());
+ String[] excludePatterns = ps.getExcludePatterns(getProject());
+ return (includePatterns != null && includePatterns.length > 0)
+ || (includePatterns != null && excludePatterns.length > 0);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/First.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/First.java
new file mode 100644
index 00000000..ea9e7d0a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/First.java
@@ -0,0 +1,48 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * ResourceCollection that contains the first <code>count</code> elements of
+ * another ResourceCollection, a la the UNIX head command.
+ * @since Ant 1.7
+ */
+public class First extends SizeLimitCollection {
+
+ /**
+ * Take the first <code>count</code> elements.
+ * @return a Collection of Resources.
+ */
+ protected Collection<Resource> getCollection() {
+ int ct = getValidCount();
+ Iterator<Resource> iter = getResourceCollection().iterator();
+ List<Resource> al = new ArrayList<Resource>(ct);
+ for (int i = 0; i < ct && iter.hasNext(); i++) {
+ al.add(iter.next());
+ }
+ return al;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/GZipResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/GZipResource.java
new file mode 100644
index 00000000..3f95a698
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/GZipResource.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.tools.ant.types.resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * A GZip compressed resource.
+ *
+ * <p>Wraps around another resource, delegates all queries to that
+ * other resource but uncompresses/compresses streams on the fly.</p>
+ *
+ * @since Ant 1.7
+ */
+public class GZipResource extends CompressedResource {
+
+ /** A no-arg constructor */
+ public GZipResource() {
+ }
+
+ /**
+ * Constructor with another resource to wrap.
+ * @param other the resource to wrap.
+ */
+ public GZipResource(org.apache.tools.ant.types.ResourceCollection other) {
+ super(other);
+ }
+
+ /**
+ * Decompress on the fly using java.util.zip.GZIPInputStream.
+ * @param in the stream to wrap.
+ * @return the wrapped stream.
+ * @throws IOException if there is a problem.
+ */
+ protected InputStream wrapStream(InputStream in) throws IOException {
+ return new GZIPInputStream(in);
+ }
+
+ /**
+ * Compress on the fly using java.util.zip.GZIPOutStream.
+ * @param out the stream to wrap.
+ * @return the wrapped stream.
+ * @throws IOException if there is a problem.
+ */
+ protected OutputStream wrapStream(OutputStream out) throws IOException {
+ return new GZIPOutputStream(out);
+ }
+
+ /**
+ * Get the name of the compression method.
+ * @return the string "GZip".
+ */
+ protected String getCompressionName() {
+ return "GZip";
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ImmutableResourceException.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ImmutableResourceException.java
new file mode 100644
index 00000000..9dce7154
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ImmutableResourceException.java
@@ -0,0 +1,46 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when an attempt is made to get an OutputStream
+ * from an immutable Resource.
+ * @since Ant 1.7
+ */
+public class ImmutableResourceException extends IOException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Default constructor.
+ */
+ public ImmutableResourceException() {
+ super();
+ }
+
+ /**
+ * Construct a new ImmutableResourceException with the specified message.
+ * @param s the message String.
+ */
+ public ImmutableResourceException(String s) {
+ super(s);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Intersect.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Intersect.java
new file mode 100644
index 00000000..cdbeed0f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Intersect.java
@@ -0,0 +1,64 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * ResourceCollection representing the intersection
+ * of multiple nested ResourceCollections.
+ * @since Ant 1.7
+ */
+public class Intersect extends BaseResourceCollectionContainer {
+
+ /**
+ * Calculate the intersection of the nested ResourceCollections.
+ * @return a Collection of Resources.
+ */
+ protected Collection<Resource> getCollection() {
+ List<ResourceCollection> rcs = getResourceCollections();
+ int size = rcs.size();
+ if (size < 2) {
+ throw new BuildException("The intersection of " + size
+ + " resource collection" + ((size == 1) ? "" : "s")
+ + " is undefined.");
+ }
+ Iterator<ResourceCollection> rc = rcs.iterator();
+ Set<Resource> s = new LinkedHashSet<Resource>(collect(rc.next()));
+ while (rc.hasNext()) {
+ s.retainAll(collect(rc.next()));
+ }
+ return s;
+ }
+
+ private Set<Resource> collect(ResourceCollection rc) {
+ Set<Resource> result = new LinkedHashSet<Resource>();
+ for (Resource r : rc) {
+ result.add(r);
+ }
+ return result;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/JavaConstantResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/JavaConstantResource.java
new file mode 100644
index 00000000..e8c8f02c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/JavaConstantResource.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.tools.ant.types.resources;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+
+/**
+ * A resource that is a java constant.
+ * This lets you extract values off the classpath and use them elsewhere
+ * @since Ant 1.7
+ */
+public class JavaConstantResource extends AbstractClasspathResource {
+
+ /**
+ * open the input stream from a specific classloader
+ *
+ * @param cl the classloader to use. Will be null if the system classloader is used
+ * @return an open input stream for the resource
+ * @throws IOException if an error occurs.
+ */
+ protected InputStream openInputStream(ClassLoader cl) throws IOException {
+ String constant = getName();
+ if (constant == null) {
+ throw new IOException("Attribute 'name' must be set.");
+ }
+ int index = constant.lastIndexOf('.');
+ if (index < 0) {
+ throw new IOException("No class name in " + constant);
+ }
+ String classname = constant.substring(0, index);
+ String fieldname = constant.substring(index + 1, constant.length());
+ try {
+ Class<?> clazz =
+ cl != null
+ ? Class.forName(classname, true, cl)
+ : Class.forName(classname);
+ Field field = clazz.getField(fieldname);
+ String value = field.get(null).toString();
+ return new ByteArrayInputStream(value.getBytes("UTF-8"));
+ } catch (ClassNotFoundException e) {
+ throw new IOException("Class not found:" + classname);
+ } catch (NoSuchFieldException e) {
+ throw new IOException(
+ "Field not found:" + fieldname + " in " + classname);
+ } catch (IllegalAccessException e) {
+ throw new IOException("Illegal access to :" + fieldname + " in " + classname);
+ } catch (NullPointerException npe) {
+ throw new IOException("Not a static field: " + fieldname + " in " + classname);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/JavaResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/JavaResource.java
new file mode 100644
index 00000000..a927d3fb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/JavaResource.java
@@ -0,0 +1,141 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * A Resource representation of something loadable via a Java classloader.
+ * @since Ant 1.7
+ */
+public class JavaResource extends AbstractClasspathResource
+ implements URLProvider {
+
+ /**
+ * Default constructor.
+ */
+ public JavaResource() {
+ }
+
+ /**
+ * Construct a new JavaResource using the specified name and
+ * classpath.
+ *
+ * @param name the resource name.
+ * @param path the classpath.
+ */
+ public JavaResource(String name, Path path) {
+ setName(name);
+ setClasspath(path);
+ }
+
+ /**
+ * open the input stream from a specific classloader
+ * @param cl the classloader to use. Will be null if the system
+ * classloader is used
+ * @return an open input stream for the resource
+ * @throws IOException if an error occurs.
+ */
+ protected InputStream openInputStream(ClassLoader cl) throws IOException {
+ InputStream inputStream;
+ if (cl == null) {
+ inputStream = ClassLoader.getSystemResourceAsStream(getName());
+ if (inputStream == null) {
+ throw new FileNotFoundException("No resource " + getName()
+ + " on Ant's classpath");
+ }
+ } else {
+ inputStream = cl.getResourceAsStream(getName());
+ if (inputStream == null) {
+ throw new FileNotFoundException("No resource " + getName()
+ + " on the classpath " + cl);
+ }
+ }
+ return inputStream;
+ }
+
+ /**
+ * Get the URL represented by this Resource.
+ * @since Ant 1.8.0
+ */
+ public URL getURL() {
+ if (isReference()) {
+ return ((JavaResource) getCheckedRef()).getURL();
+ }
+ AbstractClasspathResource.ClassLoaderWithFlag classLoader =
+ getClassLoader();
+ if (classLoader.getLoader() == null) {
+ return ClassLoader.getSystemResource(getName());
+ } else {
+ try {
+ return classLoader.getLoader().getResource(getName());
+ } finally {
+ classLoader.cleanup();
+ }
+ }
+ }
+
+ /**
+ * Compare this JavaResource to another Resource.
+ * @param another the other Resource against which to compare.
+ * @return a negative integer, zero, or a positive integer as this
+ * JavaResource is less than, equal to, or greater than the
+ * specified Resource.
+ */
+ public int compareTo(Resource another) {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).compareTo(another);
+ }
+ if (another.getClass().equals(getClass())) {
+ JavaResource otherjr = (JavaResource) another;
+ if (!getName().equals(otherjr.getName())) {
+ return getName().compareTo(otherjr.getName());
+ }
+ if (getLoader() != otherjr.getLoader()) {
+ if (getLoader() == null) {
+ return -1;
+ }
+ if (otherjr.getLoader() == null) {
+ return 1;
+ }
+ return getLoader().getRefId()
+ .compareTo(otherjr.getLoader().getRefId());
+ }
+ Path p = getClasspath();
+ Path op = otherjr.getClasspath();
+ if (p != op) {
+ if (p == null) {
+ return -1;
+ }
+ if (op == null) {
+ return 1;
+ }
+ return p.toString().compareTo(op.toString());
+ }
+ return 0;
+ }
+ return super.compareTo(another);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Last.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Last.java
new file mode 100644
index 00000000..312271b2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Last.java
@@ -0,0 +1,73 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * ResourceCollection that contains the last <code>count</code> elements of
+ * another ResourceCollection, a la the UNIX tail command.
+ * @since Ant 1.7.1
+ */
+public class Last extends SizeLimitCollection {
+
+ /**
+ * Take the last <code>count</code> elements.
+ * @return a Collection of Resources.
+ */
+ protected Collection<Resource> getCollection() {
+ int count = getValidCount();
+ ResourceCollection rc = getResourceCollection();
+ int i = count;
+ Iterator<Resource> iter = rc.iterator();
+ int size = rc.size();
+ for (; i < size; i++) {
+ iter.next();
+ }
+
+ List<Resource> al = new ArrayList<Resource>(count);
+ for (; iter.hasNext(); i++) {
+ al.add(iter.next());
+ }
+ int found = al.size();
+ if (found == count || (size < count && found == size)) {
+ return al;
+ }
+
+ //mismatch:
+ String msg = "Resource collection " + rc + " reports size " + size
+ + " but returns " + i + " elements.";
+
+ //size was understated -> too many results; warn and continue:
+ if (found > count) {
+ log(msg, Project.MSG_WARN);
+ return al.subList(found - count, found);
+ }
+ //size was overstated; we missed some and are now in error-land:
+ throw new BuildException(msg);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/LazyResourceCollectionWrapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/LazyResourceCollectionWrapper.java
new file mode 100644
index 00000000..4f9acd32
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/LazyResourceCollectionWrapper.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.tools.ant.types.resources;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Resource collection which load underlying resource collection only on demand
+ * with support for caching
+ */
+public class LazyResourceCollectionWrapper extends
+ AbstractResourceCollectionWrapper {
+
+ /** List of cached resources */
+ private final List<Resource> cachedResources = new ArrayList<Resource>();
+
+ private FilteringIterator filteringIterator;
+
+ @Override
+ protected Iterator<Resource> createIterator() {
+ Iterator<Resource> iterator;
+ if (isCache()) {
+ if (filteringIterator == null) {
+ // no worry of thread safety here, see function's contract
+ filteringIterator = new FilteringIterator(
+ getResourceCollection().iterator());
+ }
+ iterator = new CachedIterator(filteringIterator);
+ } else {
+ iterator = new FilteringIterator(getResourceCollection().iterator());
+ }
+ return iterator;
+ }
+
+ @Override
+ protected int getSize() {
+ // to compute the size, just iterate: the iterator will take care of
+ // caching
+ final Iterator<Resource> it = createIterator();
+ int size = 0;
+ while (it.hasNext()) {
+ it.next();
+ size++;
+ }
+ return size;
+ }
+
+ /**
+ * Specify if the resource should be filtered or not. This function should
+ * be overrided in order to define the filtering algorithm
+ *
+ * @param r resource considered for filtration
+ * @return whether the resource should be filtered or not
+ */
+ protected boolean filterResource(final Resource r) {
+ return false;
+ }
+
+ private class FilteringIterator implements Iterator<Resource> {
+
+ Resource next = null;
+
+ boolean ended = false;
+
+ protected final Iterator<Resource> it;
+
+ public FilteringIterator(final Iterator<Resource> it) {
+ this.it = it;
+ }
+
+ public boolean hasNext() {
+ if (ended) {
+ return false;
+ }
+ while (next == null) {
+ if (!it.hasNext()) {
+ ended = true;
+ return false;
+ }
+ next = it.next();
+ if (filterResource(next)) {
+ next = null;
+ }
+ }
+ return true;
+ }
+
+ public Resource next() {
+ if (!hasNext()) {
+ throw new UnsupportedOperationException();
+ }
+ final Resource r = next;
+ next = null;
+ return r;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Iterator that will put in the shared cache array list the selected
+ * resources
+ */
+ private class CachedIterator implements Iterator<Resource> {
+
+ int cusrsor = 0;
+
+ private final Iterator<Resource> it;
+
+ /**
+ * Default constructor
+ *
+ * @param it
+ * the iterator which will provide the resources to put in
+ * cache
+ */
+ public CachedIterator(final Iterator<Resource> it) {
+ this.it = it;
+ }
+
+ public boolean hasNext() {
+ synchronized (cachedResources) {
+ // have we already cached the next entry ?
+ if (cachedResources.size() > cusrsor) {
+ return true;
+ }
+ // does the wrapped iterator any more resource ?
+ if (!it.hasNext()) {
+ return false;
+ }
+ // put in cache the next resource
+ final Resource r = it.next();
+ cachedResources.add(r);
+ }
+ return true;
+ }
+
+ public Resource next() {
+ // first check that we have some to deliver
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ synchronized (cachedResources) {
+ // return the cached entry as hasNext should have put one for
+ // this iterator
+ return cachedResources.get(cusrsor++);
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/LogOutputResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/LogOutputResource.java
new file mode 100644
index 00000000..cd19c9c7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/LogOutputResource.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.tools.ant.types.resources;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Output-only Resource that always appends to Ant's log.
+ * @since Ant 1.8
+ */
+public class LogOutputResource extends Resource implements Appendable {
+ private static final String NAME = "[Ant log]";
+
+ private LogOutputStream outputStream;
+
+ /**
+ * Create a new LogOutputResource.
+ * @param managingComponent
+ */
+ public LogOutputResource(ProjectComponent managingComponent) {
+ super(NAME);
+ outputStream = new LogOutputStream(managingComponent);
+ }
+
+ /**
+ * Create a new LogOutputResource.
+ * @param managingComponent owning log content
+ * @param level log level
+ */
+ public LogOutputResource(ProjectComponent managingComponent, int level) {
+ super(NAME);
+ outputStream = new LogOutputStream(managingComponent, level);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public OutputStream getAppendOutputStream() throws IOException {
+ return outputStream;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public OutputStream getOutputStream() throws IOException {
+ return outputStream;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/MappedResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/MappedResource.java
new file mode 100644
index 00000000..339c88bf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/MappedResource.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.tools.ant.types.resources;
+
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.util.FileNameMapper;
+
+/**
+ * A decorator around a different resource that uses a mapper to
+ * dynamically remap the resource's name.
+ *
+ * <p>Strips the FileProvider interface from decorated resources since
+ * it may be used to circumvent name mapping.</p>
+ *
+ * @since Ant 1.8.0
+ */
+public class MappedResource extends ResourceDecorator {
+ private final FileNameMapper mapper;
+
+ /**
+ * Wraps an existing resource.
+ * @param r Resource to wrap
+ * @param m FileNameMapper that handles mapping
+ */
+ public MappedResource(Resource r, FileNameMapper m) {
+ super(r);
+ mapper = m;
+ }
+
+ /**
+ * Maps the name.
+ */
+ @Override
+ public String getName() {
+ String name = getResource().getName();
+ if (isReference()) {
+ return name;
+ }
+ String[] mapped = mapper.mapFileName(name);
+ return mapped != null && mapped.length > 0 ? mapped[0] : null;
+ }
+
+ /**
+ * Not really supported since mapper is never null.
+ * @param r reference to set
+ */
+ @Override
+ public void setRefid(Reference r) {
+ if (mapper != null) {
+ throw noChildrenAllowed();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Suppress FileProvider
+ * @param clazz the type to implement
+ */
+ @Override
+ public <T> T as(Class<T> clazz) {
+ return FileProvider.class.isAssignableFrom(clazz)
+ ? null : getResource().as(clazz);
+ }
+
+ /**
+ * Get the hash code for this Resource.
+ * @since Ant 1.8.1
+ */
+ @Override
+ public int hashCode() {
+ String n = getName();
+ return n == null ? super.hashCode() : n.hashCode();
+ }
+
+ /**
+ * Equality check based on the resource's name in addition to the
+ * resource itself.
+ * @since Ant 1.8.1
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || !other.getClass().equals(getClass())) {
+ return false;
+ }
+ MappedResource m = (MappedResource) other;
+ String myName = getName();
+ String otherName = m.getName();
+ return (myName == null ? otherName == null : myName.equals(otherName))
+ && getResource().equals(m.getResource());
+ }
+
+ @Override
+ public String toString() {
+ if (isReference()) {
+ return getCheckedRef().toString();
+ }
+ return getName();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/MappedResourceCollection.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/MappedResourceCollection.java
new file mode 100644
index 00000000..2f1a926b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/MappedResourceCollection.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.tools.ant.types.resources;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.ant.util.MergingMapper;
+
+/**
+ * Wrapper around a resource collections that maps the names of the
+ * other collection using a configured mapper.
+ * @since Ant 1.8.0
+ */
+public class MappedResourceCollection
+ extends DataType implements ResourceCollection, Cloneable {
+
+ private ResourceCollection nested = null;
+ private Mapper mapper = null;
+ private boolean enableMultipleMappings = false;
+ private boolean cache = false;
+ private Collection<Resource> cachedColl = null;
+
+ /**
+ * Adds the required nested ResourceCollection.
+ * @param c the ResourceCollection to add.
+ * @throws BuildException on error.
+ */
+ public synchronized void add(ResourceCollection c) throws BuildException {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (nested != null) {
+ throw new BuildException("Only one resource collection can be"
+ + " nested into mappedresources",
+ getLocation());
+ }
+ setChecked(false);
+ cachedColl = null;
+ nested = c;
+ }
+
+ /**
+ * Define the mapper to map source to destination files.
+ * @return a mapper to be configured.
+ * @exception BuildException if more than one mapper is defined.
+ */
+ public Mapper createMapper() throws BuildException {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (mapper != null) {
+ throw new BuildException("Cannot define more than one mapper",
+ getLocation());
+ }
+ setChecked(false);
+ mapper = new Mapper(getProject());
+ cachedColl = null;
+ return mapper;
+ }
+
+ /**
+ * Add a nested filenamemapper.
+ * @param fileNameMapper the mapper to add.
+ * @since Ant 1.6.3
+ */
+ public void add(FileNameMapper fileNameMapper) {
+ createMapper().add(fileNameMapper);
+ }
+
+ /**
+ * Set method of handling mappers that return multiple
+ * mappings for a given source path.
+ * @param enableMultipleMappings If true the type will
+ * use all the mappings for a given source path, if
+ * false, only the first mapped name is
+ * processed.
+ * By default, this setting is false to provide backward
+ * compatibility with earlier releases.
+ * @since Ant 1.8.1
+ */
+ public void setEnableMultipleMappings(boolean enableMultipleMappings) {
+ this.enableMultipleMappings = enableMultipleMappings;
+ }
+
+ /**
+ * Set whether to cache collections.
+ * @since Ant 1.8.1
+ */
+ public void setCache(boolean cache) {
+ this.cache = cache;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isFilesystemOnly() {
+ if (isReference()) {
+ return ((MappedResourceCollection) getCheckedRef())
+ .isFilesystemOnly();
+ }
+ checkInitialized();
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int size() {
+ if (isReference()) {
+ return ((MappedResourceCollection) getCheckedRef()).size();
+ }
+ checkInitialized();
+ return cacheCollection().size();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Iterator<Resource> iterator() {
+ if (isReference()) {
+ return ((MappedResourceCollection) getCheckedRef()).iterator();
+ }
+ checkInitialized();
+ return cacheCollection().iterator();
+ }
+
+ /**
+ * Overrides the base version.
+ * @param r the Reference to set.
+ */
+ public void setRefid(Reference r) {
+ if (nested != null || mapper != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Implement clone. The nested resource collection and mapper are copied.
+ * @return a cloned instance.
+ */
+ public Object clone() {
+ try {
+ MappedResourceCollection c =
+ (MappedResourceCollection) super.clone();
+ c.nested = nested;
+ c.mapper = mapper;
+ c.cachedColl = null;
+ return c;
+ } catch (CloneNotSupportedException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Overrides the version of DataType to recurse on all DataType
+ * child elements that may have been added.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ checkInitialized();
+ if (mapper != null) {
+ pushAndInvokeCircularReferenceCheck(mapper, stk, p);
+ }
+ if (nested instanceof DataType) {
+ pushAndInvokeCircularReferenceCheck((DataType) nested, stk, p);
+ }
+ setChecked(true);
+ }
+ }
+
+ private void checkInitialized() {
+ if (nested == null) {
+ throw new BuildException("A nested resource collection element is"
+ + " required", getLocation());
+ }
+ dieOnCircularReference();
+ }
+
+ private synchronized Collection<Resource> cacheCollection() {
+ if (cachedColl == null || !cache) {
+ cachedColl = getCollection();
+ }
+ return cachedColl;
+ }
+
+ private Collection<Resource> getCollection() {
+ Collection<Resource> collected = new ArrayList<Resource>();
+ FileNameMapper m =
+ mapper != null ? mapper.getImplementation() : new IdentityMapper();
+ for (Resource r : nested) {
+ if (enableMultipleMappings) {
+ String[] n = m.mapFileName(r.getName());
+ if (n != null) {
+ for (int i = 0; i < n.length; i++) {
+ collected.add(new MappedResource(r,
+ new MergingMapper(n[i]))
+ );
+ }
+ }
+ } else {
+ collected.add(new MappedResource(r, m));
+ }
+ }
+ return collected;
+ }
+
+ /**
+ * Format this resource collection as a String.
+ * @return a descriptive <code>String</code>.
+ */
+ public String toString() {
+ if (isReference()) {
+ return getCheckedRef().toString();
+ }
+ Iterator<Resource> i = iterator();
+ if (!i.hasNext()) {
+ return "";
+ }
+ StringBuffer sb = new StringBuffer();
+ while (i.hasNext()) {
+ if (sb.length() > 0) {
+ sb.append(File.pathSeparatorChar);
+ }
+ sb.append(i.next());
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/MultiRootFileSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/MultiRootFileSet.java
new file mode 100644
index 00000000..d7938908
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/MultiRootFileSet.java
@@ -0,0 +1,237 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.AbstractFileSet;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * Union of file/dirsets that share the same patterns and selectors
+ * but have different roots.
+ * @since Ant 1.9.4
+ */
+public class MultiRootFileSet extends AbstractFileSet
+ implements ResourceCollection {
+
+ private SetType type = SetType.file;
+ private boolean cache = true;
+ private List<File> baseDirs = new ArrayList<File>();
+ private Union union;
+
+ @Override
+ public void setDir(final File dir) {
+ throw new BuildException(getDataTypeName()
+ + " doesn't support the dir attribute");
+ }
+
+ /**
+ * Determines the types of resources to return.
+ * @param type the types of resources to return
+ */
+ public void setType(final SetType type) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.type = type;
+ }
+
+ /**
+ * Set whether to cache collections.
+ * @param b boolean cache flag.
+ */
+ public synchronized void setCache(final boolean b) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ cache = b;
+ }
+
+ /**
+ * Adds basedirs as a comma separated list.
+ * @param dirs directories as CSV
+ */
+ public void setBaseDirs(final String dirs) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (dirs != null && dirs.length() > 0) {
+ final String[] ds = dirs.split(",");
+ for (final String d : ds) {
+ baseDirs.add(getProject().resolveFile(d));
+ }
+ }
+ }
+
+ /**
+ * Adds a basedir as nested element.
+ * @param r basedir
+ */
+ public void addConfiguredBaseDir(final FileResource r) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ baseDirs.add(r.getFile());
+ }
+
+ @Override
+ public void setRefid(final Reference r) {
+ if (!baseDirs.isEmpty()) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Return a MultiRootFileSet that has the same basedirs and same patternsets
+ * as this one.
+ * @return the cloned MultiRootFileSet.
+ */
+ @Override
+ public Object clone() {
+ if (isReference()) {
+ return ((MultiRootFileSet) getRef(getProject())).clone();
+ } else {
+ final MultiRootFileSet fs = (MultiRootFileSet) super.clone();
+ fs.baseDirs = new ArrayList<File>(baseDirs);
+ fs.union = null;
+ return fs;
+ }
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return an Iterator of Resources.
+ */
+ public Iterator<Resource> iterator() {
+ if (isReference()) {
+ return ((MultiRootFileSet) getRef(getProject())).iterator();
+ }
+ return merge().iterator();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ */
+ public int size() {
+ if (isReference()) {
+ return ((MultiRootFileSet) getRef(getProject())).size();
+ }
+ return merge().size();
+ }
+
+ /**
+ * Always returns true.
+ * @return true indicating that all elements will be FileResources.
+ */
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+
+ /**
+ * Returns included directories as a list of semicolon-separated paths.
+ *
+ * @return a <code>String</code> of included directories.
+ */
+ @Override
+ public String toString() {
+ if (isReference()) {
+ return ((MultiRootFileSet) getRef(getProject())).toString();
+ }
+ return merge().toString();
+ }
+
+ private synchronized Union merge() {
+ if (cache && union != null) {
+ return union;
+ }
+ final Union u = new Union();
+ setup(u);
+ if (cache) {
+ union = u;
+ }
+ return u;
+ }
+
+ private void setup(final Union u) {
+ for (final File d : baseDirs) {
+ u.add(new Worker(this, type, d));
+ }
+ }
+
+ /**
+ * What to return from the set: files, directories or both.
+ */
+ public static enum SetType {
+ file, dir, both
+ }
+
+ private static class Worker extends AbstractFileSet
+ implements ResourceCollection {
+
+ private final SetType type;
+
+ private Worker(final MultiRootFileSet fs, final SetType type, final File dir) {
+ super(fs);
+ this.type = type;
+ setDir(dir);
+ }
+
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+
+ public Iterator<Resource> iterator() {
+ final DirectoryScanner ds = getDirectoryScanner(getProject());
+ String[] names = type == SetType.file
+ ? ds.getIncludedFiles()
+ : ds.getIncludedDirectories();
+ if (type == SetType.both) {
+ final String[] files = ds.getIncludedFiles();
+ final String[] merged = new String[names.length + files.length];
+ System.arraycopy(names, 0, merged, 0, names.length);
+ System.arraycopy(files, 0, merged, names.length, files.length);
+ names = merged;
+ }
+ return new FileResourceIterator(getProject(), getDir(getProject()),
+ names);
+ }
+
+ public int size() {
+ final DirectoryScanner ds = getDirectoryScanner(getProject());
+ int count = type == SetType.file
+ ? ds.getIncludedFilesCount()
+ : ds.getIncludedDirsCount();
+ if (type == SetType.both) {
+ count += ds.getIncludedFilesCount();
+ }
+ return count;
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/PropertyResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/PropertyResource.java
new file mode 100644
index 00000000..a7cecb4b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/PropertyResource.java
@@ -0,0 +1,206 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.util.PropertyOutputStream;
+
+/**
+ * Exposes an Ant property as a Resource.
+ * @since Ant 1.7
+ */
+public class PropertyResource extends Resource {
+
+ /** Magic number */
+ private static final int PROPERTY_MAGIC
+ = Resource.getMagicNumber("PropertyResource".getBytes());
+
+ private static final InputStream UNSET = new InputStream() {
+ public int read() {
+ return -1;
+ }
+ };
+
+ /**
+ * Default constructor.
+ */
+ public PropertyResource() {
+ }
+
+ /**
+ * Construct a new PropertyResource with the specified name.
+ * @param p the project to use.
+ * @param n the String name of this PropertyResource (Ant property name/key).
+ */
+ public PropertyResource(Project p, String n) {
+ super(n);
+ setProject(p);
+ }
+
+ /**
+ * Get the value of this PropertyResource.
+ * @return the value of the specified Property.
+ */
+ public String getValue() {
+ if (isReference()) {
+ return ((PropertyResource) getCheckedRef()).getValue();
+ }
+ Project p = getProject();
+ return p == null ? null : p.getProperty(getName());
+ }
+
+ /**
+ * Get the Object value of this PropertyResource.
+ * @return the Object value of the specified Property.
+ * @since Ant 1.8.1
+ */
+ public Object getObjectValue() {
+ if (isReference()) {
+ return ((PropertyResource) getCheckedRef()).getObjectValue();
+ }
+ Project p = getProject();
+ return p == null ? null : PropertyHelper.getProperty(p, getName());
+ }
+
+ /**
+ * Find out whether this Resource exists.
+ * @return true if the Property is set, false otherwise.
+ */
+ public boolean isExists() {
+ if (isReferenceOrProxy()) {
+ return getReferencedOrProxied().isExists();
+ }
+ return getObjectValue() != null;
+ }
+
+ /**
+ * Get the size of this Resource.
+ * @return the size, as a long, 0 if the Resource does not exist (for
+ * compatibility with java.io.File), or UNKNOWN_SIZE if not known.
+ */
+ public long getSize() {
+ if (isReferenceOrProxy()) {
+ return getReferencedOrProxied().getSize();
+ }
+ Object o = getObjectValue();
+ return o == null ? 0L : (long) String.valueOf(o).length();
+ }
+
+ /**
+ * Override to implement equality with equivalent Resources,
+ * since we are capable of proxying them.
+ * @param o object to compare
+ * @return true if equal to o
+ */
+ public boolean equals(Object o) {
+ if (super.equals(o)) {
+ return true;
+ }
+ return isReferenceOrProxy() && getReferencedOrProxied().equals(o);
+ }
+
+ /**
+ * Get the hash code for this Resource.
+ * @return hash code as int.
+ */
+ public int hashCode() {
+ if (isReferenceOrProxy()) {
+ return getReferencedOrProxied().hashCode();
+ }
+ return super.hashCode() * PROPERTY_MAGIC;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ if (isReferenceOrProxy()) {
+ return getReferencedOrProxied().toString();
+ }
+ return getValue();
+ }
+
+ /**
+ * Get an InputStream for the Resource.
+ * @return an InputStream containing this Resource's content.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if InputStreams are not
+ * supported for this Resource type.
+ */
+ public InputStream getInputStream() throws IOException {
+ if (isReferenceOrProxy()) {
+ return getReferencedOrProxied().getInputStream();
+ }
+ Object o = getObjectValue();
+ return o == null ? UNSET : new ByteArrayInputStream(String.valueOf(o).getBytes());
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ */
+ public OutputStream getOutputStream() throws IOException {
+ if (isReferenceOrProxy()) {
+ return getReferencedOrProxied().getOutputStream();
+ }
+ if (isExists()) {
+ throw new ImmutableResourceException();
+ }
+ return new PropertyOutputStream(getProject(), getName());
+ }
+
+ /**
+ * Learn whether this PropertyResource either refers to another Resource
+ * or proxies another Resource due to its object property value being said Resource.
+ * @return boolean
+ */
+ protected boolean isReferenceOrProxy() {
+ return isReference() || getObjectValue() instanceof Resource;
+ }
+
+ /**
+ * Get the referenced or proxied Resource, if applicable.
+ * @return Resource
+ * @throws IllegalStateException if this PropertyResource neither proxies nor
+ * references another Resource.
+ */
+ protected Resource getReferencedOrProxied() {
+ if (isReference()) {
+ return (Resource) getCheckedRef(Resource.class, "resource");
+ }
+ Object o = getObjectValue();
+ if (o instanceof Resource) {
+ return (Resource) o;
+ }
+ throw new IllegalStateException(
+ "This PropertyResource does not reference or proxy another Resource");
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ResourceDecorator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ResourceDecorator.java
new file mode 100644
index 00000000..3806cf2c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ResourceDecorator.java
@@ -0,0 +1,270 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * Abstract class that delegates all reading methods of Resource to
+ * its wrapped resource and deals with reference handling.
+ *
+ * <p>Overwrites all setters to throw exceptions.</p>
+ *
+ * @since Ant 1.8.0
+ */
+public abstract class ResourceDecorator extends Resource {
+
+ private Resource resource;
+
+ /** no arg constructor */
+ protected ResourceDecorator() {
+ }
+
+ /**
+ * Constructor with another resource to wrap.
+ * @param other the resource to wrap.
+ */
+ protected ResourceDecorator(ResourceCollection other) {
+ addConfigured(other);
+ }
+
+ /**
+ * Sets the resource to wrap using a single-element collection.
+ * @param a the resource to wrap as a single element Resource collection.
+ */
+ public final void addConfigured(ResourceCollection a) {
+ checkChildrenAllowed();
+ if (resource != null) {
+ throw new BuildException("you must not specify more than one"
+ + " resource");
+ }
+ if (a.size() != 1) {
+ throw new BuildException("only single argument resource collections"
+ + " are supported");
+ }
+ setChecked(false);
+ resource = a.iterator().next();
+ }
+
+ /**
+ * Get the name of the resource.
+ * @return the name of the wrapped resource.
+ */
+ public String getName() {
+ return getResource().getName();
+ }
+
+ /**
+ * The exists attribute tells whether a file exists.
+ * @return true if this resource exists.
+ */
+ public boolean isExists() {
+ return getResource().isExists();
+ }
+
+ /**
+ * Tells the modification time in milliseconds since 01.01.1970 .
+ *
+ * @return 0 if the resource does not exist to mirror the behavior
+ * of {@link java.io.File File}.
+ */
+ public long getLastModified() {
+ return getResource().getLastModified();
+ }
+
+ /**
+ * Tells if the resource is a directory.
+ * @return boolean flag indicating if the resource is a directory.
+ */
+ public boolean isDirectory() {
+ return getResource().isDirectory();
+ }
+
+ /**
+ * Get the size of this Resource.
+ * @return the size, as a long, 0 if the Resource does not exist (for
+ * compatibility with java.io.File), or UNKNOWN_SIZE if not known.
+ */
+ public long getSize() {
+ return getResource().getSize();
+ }
+
+ /**
+ * Get an InputStream for the Resource.
+ * @return an InputStream containing this Resource's content.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if InputStreams are not
+ * supported for this Resource type.
+ */
+ public InputStream getInputStream() throws IOException {
+ return getResource().getInputStream();
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ */
+ public OutputStream getOutputStream() throws IOException {
+ return getResource().getOutputStream();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return whether this Resource is a FileProvider.
+ */
+ public boolean isFilesystemOnly() {
+ return as(FileProvider.class) != null;
+ }
+
+ /**
+ * Overrides the base version.
+ * @param r the Reference to set.
+ */
+ public void setRefid(Reference r) {
+ if (resource != null) {
+ throw noChildrenAllowed();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T> T as(Class<T> clazz) {
+ return getResource().as(clazz);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int compareTo(Resource other) {
+ if (other == this) {
+ return 0;
+ }
+ if (other instanceof ResourceDecorator) {
+ return getResource().compareTo(
+ ((ResourceDecorator) other).getResource());
+ }
+ return getResource().compareTo(other);
+ }
+
+ /**
+ * Get the hash code for this Resource.
+ * @return hash code as int.
+ */
+ public int hashCode() {
+ return (getClass().hashCode() << 4) | getResource().hashCode();
+ }
+
+ /**
+ * De-references refids if any, ensures a wrapped resource has
+ * been specified.
+ */
+ protected final Resource getResource() {
+ if (isReference()) {
+ return (Resource) getCheckedRef();
+ }
+ if (resource == null) {
+ throw new BuildException("no resource specified");
+ }
+ dieOnCircularReference();
+ return resource;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected void dieOnCircularReference(final Stack<Object> stack,
+ final Project project)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stack, project);
+ } else {
+ pushAndInvokeCircularReferenceCheck(resource, stack, project);
+ setChecked(true);
+ }
+ }
+
+ // disable modification
+
+ /**
+ * Overridden, not allowed to set the name of the resource.
+ * @param name not used.
+ * @throws BuildException always.
+ */
+ public void setName(String name) throws BuildException {
+ throw new BuildException("you can't change the name of a "
+ + getDataTypeName());
+ }
+
+ /**
+ * Set the exists attribute.
+ * @param exists if true, this resource exists.
+ */
+ public void setExists(boolean exists) {
+ throw new BuildException("you can't change the exists state of a "
+ + getDataTypeName());
+ }
+
+ /**
+ * Override setLastModified.
+ * @param lastmodified not used.
+ * @throws BuildException always.
+ */
+ public void setLastModified(long lastmodified) throws BuildException {
+ throw new BuildException("you can't change the timestamp of a "
+ + getDataTypeName());
+ }
+
+ /**
+ * Override setDirectory.
+ * @param directory not used.
+ * @throws BuildException always.
+ */
+ public void setDirectory(boolean directory) throws BuildException {
+ throw new BuildException("you can't change the directory state of a "
+ + getDataTypeName());
+ }
+
+ /**
+ * Override setSize.
+ * @param size not used.
+ * @throws BuildException always.
+ */
+ public void setSize(long size) throws BuildException {
+ throw new BuildException("you can't change the size of a "
+ + getDataTypeName());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ResourceList.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ResourceList.java
new file mode 100644
index 00000000..8b77e1bb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ResourceList.java
@@ -0,0 +1,239 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Reads a resource as text document and creates a resource for each
+ * line.
+ * @since Ant 1.8.0
+ */
+public class ResourceList extends DataType implements ResourceCollection {
+ private final Vector<FilterChain> filterChains = new Vector<FilterChain>();
+ private final ArrayList<ResourceCollection> textDocuments = new ArrayList<ResourceCollection>();
+ private final Union cachedResources = new Union();
+ private volatile boolean cached = false;
+ private String encoding = null;
+
+ public ResourceList() {
+ cachedResources.setCache(true);
+ }
+
+ /**
+ * Adds a source.
+ */
+ public void add(ResourceCollection rc) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ textDocuments.add(rc);
+ setChecked(false);
+ }
+
+ /**
+ * Adds a FilterChain.
+ */
+ public final void addFilterChain(FilterChain filter) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ filterChains.add(filter);
+ setChecked(false);
+ }
+
+ /**
+ * Encoding to use for input, defaults to the platform's default
+ * encoding. <p>
+ *
+ * For a list of possible values see
+ * <a href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">
+ * http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html
+ * </a>.</p>
+ */
+ public final void setEncoding(String encoding) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.encoding = encoding;
+ }
+
+ /**
+ * Makes this instance in effect a reference to another ResourceList
+ * instance.
+ */
+ public void setRefid(Reference r) throws BuildException {
+ if (encoding != null) {
+ throw tooManyAttributes();
+ }
+ if (filterChains.size() > 0 || textDocuments.size() > 0) {
+ throw noChildrenAllowed();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract. The Iterator returned
+ * will throw ConcurrentModificationExceptions if ResourceCollections
+ * are added to this container while the Iterator is in use.
+ * @return a "fail-fast" Iterator.
+ */
+ public final synchronized Iterator<Resource> iterator() {
+ if (isReference()) {
+ return ((ResourceList) getCheckedRef()).iterator();
+ }
+ return cache().iterator();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ */
+ public synchronized int size() {
+ if (isReference()) {
+ return ((ResourceList) getCheckedRef()).size();
+ }
+ return cache().size();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return whether this is a filesystem-only resource collection.
+ */
+ public synchronized boolean isFilesystemOnly() {
+ if (isReference()) {
+ return ((ResourceList) getCheckedRef()).isFilesystemOnly();
+ }
+ return cache().isFilesystemOnly();
+ }
+
+ /**
+ * Overrides the version of DataType to recurse on all DataType
+ * child elements that may have been added.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ for (ResourceCollection resourceCollection : textDocuments) {
+ if (resourceCollection instanceof DataType) {
+ pushAndInvokeCircularReferenceCheck((DataType) resourceCollection, stk, p);
+ }
+ }
+ for (FilterChain filterChain : filterChains) {
+ pushAndInvokeCircularReferenceCheck(filterChain, stk, p);
+ }
+ setChecked(true);
+ }
+ }
+
+ private synchronized ResourceCollection cache() {
+ if (!cached) {
+ dieOnCircularReference();
+ for (ResourceCollection rc : textDocuments) {
+ for (Resource r : rc) {
+ cachedResources.add(read(r));
+ }
+ }
+ cached = true;
+ }
+ return cachedResources;
+ }
+
+ private ResourceCollection read(Resource r) {
+ BufferedInputStream bis = null;
+ try {
+ bis = new BufferedInputStream(r.getInputStream());
+ Reader input = null;
+ if (encoding == null) {
+ input = new InputStreamReader(bis);
+ } else {
+ input = new InputStreamReader(bis, encoding);
+ }
+ ChainReaderHelper crh = new ChainReaderHelper();
+ crh.setPrimaryReader(input);
+ crh.setFilterChains(filterChains);
+ crh.setProject(getProject());
+ BufferedReader reader = new BufferedReader(crh.getAssembledReader());
+
+ Union streamResources = new Union();
+ streamResources.setCache(true);
+
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ streamResources.add(parse(line));
+ }
+
+ return streamResources;
+ } catch (final IOException ioe) {
+ throw new BuildException("Unable to read resource " + r.getName()
+ + ": " + ioe, ioe, getLocation());
+ } finally {
+ FileUtils.close(bis);
+ }
+ }
+
+ private Resource parse(final String line) {
+ PropertyHelper propertyHelper
+ = (PropertyHelper) PropertyHelper.getPropertyHelper(getProject());
+ Object expanded = propertyHelper.parseProperties(line);
+ if (expanded instanceof Resource) {
+ return (Resource) expanded;
+ }
+ String expandedLine = expanded.toString();
+ int colon = expandedLine.indexOf(":");
+ if (colon != -1) {
+ // could be an URL or an absolute file on an OS with drives
+ try {
+ return new URLResource(expandedLine);
+ } catch (BuildException mfe) {
+ // a translated MalformedURLException
+
+ // probably it's an absolute path fall back to file
+ // resource
+ }
+ }
+ return new FileResource(getProject(), expandedLine);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Resources.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Resources.java
new file mode 100644
index 00000000..1dd888d4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Resources.java
@@ -0,0 +1,274 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.File;
+import java.util.AbstractCollection;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.util.CollectionUtils;
+
+/**
+ * Generic ResourceCollection: Either stores nested ResourceCollections,
+ * making no attempt to remove duplicates, or references another ResourceCollection.
+ * @since Ant 1.7
+ */
+public class Resources extends DataType implements ResourceCollection {
+ /** static empty ResourceCollection */
+ public static final ResourceCollection NONE = new ResourceCollection() {
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+ public Iterator<Resource> iterator() {
+ return EMPTY_ITERATOR;
+ }
+ public int size() {
+ return 0;
+ }
+ };
+
+ /** static empty Iterator */
+ public static final Iterator<Resource> EMPTY_ITERATOR = new Iterator<Resource>() {
+ public Resource next() {
+ throw new NoSuchElementException();
+ }
+ public boolean hasNext() {
+ return false;
+ }
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ private class MyCollection extends AbstractCollection<Resource> {
+ private Collection<Resource> cached;
+
+ MyCollection() {
+ }
+ public int size() {
+ return getCache().size();
+ }
+ public Iterator<Resource> iterator() {
+ return getCache().iterator();
+ }
+ private synchronized Collection<Resource> getCache() {
+ Collection<Resource> coll = cached;
+ if (coll == null) {
+ coll = CollectionUtils.asCollection(new MyIterator());
+ if (cache) {
+ cached = coll;
+ }
+ }
+ return coll;
+ }
+ private class MyIterator implements Iterator<Resource> {
+ private Iterator<ResourceCollection> rci = getNested().iterator();
+ private Iterator<Resource> ri = null;
+
+ public boolean hasNext() {
+ boolean result = ri != null && ri.hasNext();
+ while (!result && rci.hasNext()) {
+ ri = ((ResourceCollection) rci.next()).iterator();
+ result = ri.hasNext();
+ }
+ return result;
+ }
+ public Resource next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return ri.next();
+ }
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+
+ private Vector<ResourceCollection> rc;
+ private Collection<Resource> coll;
+ private boolean cache = false;
+
+ /**
+ * Create a new Resources.
+ */
+ public Resources() {
+ }
+
+ /**
+ * Create a new Resources.
+ * @since Ant 1.8
+ */
+ public Resources(Project project) {
+ setProject(project);
+ }
+
+ /**
+ * Set whether to cache collections.
+ * @param b boolean cache flag.
+ * @since Ant 1.8.0
+ */
+ public synchronized void setCache(boolean b) {
+ cache = b;
+ }
+
+ /**
+ * Add a ResourceCollection.
+ * @param c the ResourceCollection to add.
+ */
+ public synchronized void add(ResourceCollection c) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (c == null) {
+ return;
+ }
+ if (rc == null) {
+ rc = new Vector<ResourceCollection>();
+ }
+ rc.add(c);
+ invalidateExistingIterators();
+ coll = null;
+ setChecked(false);
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return an Iterator of Resources.
+ */
+ public synchronized Iterator<Resource> iterator() {
+ if (isReference()) {
+ return getRef().iterator();
+ }
+ validate();
+ return new FailFast(this, coll.iterator());
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ */
+ public synchronized int size() {
+ if (isReference()) {
+ return getRef().size();
+ }
+ validate();
+ return coll.size();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return true if all Resources represent files.
+ */
+ public boolean isFilesystemOnly() {
+ if (isReference()) {
+ return getRef().isFilesystemOnly();
+ }
+ validate();
+
+ for (Iterator<ResourceCollection> i = getNested().iterator(); i.hasNext();) {
+ if (!i.next().isFilesystemOnly()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Format this <code>Resources</code> as a String.
+ * @return a descriptive <code>String</code>.
+ */
+ public synchronized String toString() {
+ if (isReference()) {
+ return getCheckedRef().toString();
+ }
+ validate();
+ if (coll == null || coll.isEmpty()) {
+ return "";
+ }
+ StringBuffer sb = new StringBuffer();
+ for (Resource r : coll) {
+ if (sb.length() > 0) {
+ sb.append(File.pathSeparatorChar);
+ }
+ sb.append(r);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Overrides the version of DataType to recurse on all DataType
+ * child elements that may have been added.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ for (ResourceCollection resourceCollection : getNested()) {
+ if (resourceCollection instanceof DataType) {
+ pushAndInvokeCircularReferenceCheck((DataType) resourceCollection, stk, p);
+ }
+ }
+ setChecked(true);
+ }
+ }
+
+ /**
+ * Allow subclasses to notify existing Iterators they have experienced concurrent modification.
+ */
+ protected void invalidateExistingIterators() {
+ FailFast.invalidate(this);
+ }
+
+ /**
+ * Resolves references, allowing any ResourceCollection.
+ * @return the referenced ResourceCollection.
+ */
+ private ResourceCollection getRef() {
+ return (ResourceCollection) getCheckedRef(
+ ResourceCollection.class, "ResourceCollection");
+ }
+
+ private synchronized void validate() {
+ dieOnCircularReference();
+ coll = (coll == null) ? new MyCollection() : coll;
+ }
+
+ private synchronized List<ResourceCollection> getNested() {
+ return rc == null ? Collections.<ResourceCollection> emptyList() : rc;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Restrict.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Restrict.java
new file mode 100644
index 00000000..2ea1a861
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Restrict.java
@@ -0,0 +1,156 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.util.Iterator;
+import java.util.Stack;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelectorContainer;
+
+/**
+ * ResourceCollection that allows a number of selectors to be
+ * applied to a single ResourceCollection for the purposes of
+ * restricting or narrowing results.
+ * @since Ant 1.7
+ */
+public class Restrict
+ extends ResourceSelectorContainer implements ResourceCollection {
+
+ private LazyResourceCollectionWrapper w = new LazyResourceCollectionWrapper() {
+ /**
+ * Restrict the nested ResourceCollection based on the nested selectors.
+ */
+ protected boolean filterResource(Resource r) {
+ for (Iterator<ResourceSelector> i = getSelectors(); i.hasNext();) {
+ if (!i.next().isSelected(r)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ /**
+ * Add the ResourceCollection.
+ * @param c the ResourceCollection to add.
+ */
+ public synchronized void add(ResourceCollection c) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (c == null) {
+ return;
+ }
+ w.add(c);
+ setChecked(false);
+ }
+
+ /**
+ * Set whether to cache collections.
+ * @param b boolean cache flag.
+ */
+ public synchronized void setCache(boolean b) {
+ w.setCache(b);
+ }
+
+ /**
+ * Learn whether to cache collections. Default is <code>true</code>.
+ * @return boolean cache flag.
+ */
+ public synchronized boolean isCache() {
+ return w.isCache();
+ }
+
+ /**
+ * Add a ResourceSelector.
+ * @param s the ResourceSelector to add.
+ */
+ public synchronized void add(ResourceSelector s) {
+ if (s == null) {
+ return;
+ }
+ super.add(s);
+ FailFast.invalidate(this);
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return an Iterator of Resources.
+ */
+ public final synchronized Iterator<Resource> iterator() {
+ if (isReference()) {
+ return ((Restrict) getCheckedRef()).iterator();
+ }
+ dieOnCircularReference();
+ return w.iterator();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ */
+ public synchronized int size() {
+ if (isReference()) {
+ return ((Restrict) getCheckedRef()).size();
+ }
+ dieOnCircularReference();
+ return w.size();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return whether this is a filesystem-only resource collection.
+ */
+ public synchronized boolean isFilesystemOnly() {
+ if (isReference()) {
+ return ((Restrict) getCheckedRef()).isFilesystemOnly();
+ }
+ dieOnCircularReference();
+ return w.isFilesystemOnly();
+ }
+
+ /**
+ * Format this Restrict collection as a String.
+ * @return the String value of this collection.
+ */
+ public synchronized String toString() {
+ if (isReference()) {
+ return getCheckedRef().toString();
+ }
+ dieOnCircularReference();
+ return w.toString();
+ }
+
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p) {
+ if (isChecked()) {
+ return;
+ }
+
+ // takes care of Selectors
+ super.dieOnCircularReference(stk, p);
+
+ if (!isReference()) {
+ pushAndInvokeCircularReferenceCheck(w, stk, p);
+ setChecked(true);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/SizeLimitCollection.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/SizeLimitCollection.java
new file mode 100644
index 00000000..c8e772be
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/SizeLimitCollection.java
@@ -0,0 +1,71 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * ResourceCollection that imposes a size limit on another ResourceCollection.
+ * @since Ant 1.7.1
+ */
+public abstract class SizeLimitCollection extends BaseResourceCollectionWrapper {
+ private static final String BAD_COUNT
+ = "size-limited collection count should be set to an int >= 0";
+
+ private int count = 1;
+
+ /**
+ * Set the number of resources to be included.
+ * @param i the count as <code>int</count>.
+ */
+ public synchronized void setCount(int i) {
+ checkAttributesAllowed();
+ count = i;
+ }
+
+ /**
+ * Get the number of resources to be included. Default is 1.
+ * @return the count as <code>int</count>.
+ */
+ public synchronized int getCount() {
+ return count;
+ }
+
+ /**
+ * Efficient size implementation.
+ * @return int size
+ */
+ public synchronized int size() {
+ int sz = getResourceCollection().size();
+ int ct = getValidCount();
+ return sz < ct ? sz : ct;
+ }
+
+ /**
+ * Get the count, verifying it is >= 0.
+ * @return int count
+ */
+ protected int getValidCount() {
+ int ct = getCount();
+ if (ct < 0) {
+ throw new BuildException(BAD_COUNT);
+ }
+ return ct;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Sort.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Sort.java
new file mode 100644
index 00000000..b4dc88c2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Sort.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.tools.ant.types.resources;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.comparators.DelegatedResourceComparator;
+import org.apache.tools.ant.types.resources.comparators.ResourceComparator;
+import org.apache.tools.ant.util.CollectionUtils;
+
+/**
+ * ResourceCollection that sorts another ResourceCollection.
+ *
+ * Note that Sort must not be used in cases where the ordering of the objects
+ * being sorted might change during the sorting process.
+ *
+ * @since Ant 1.7
+ */
+public class Sort extends BaseResourceCollectionWrapper {
+
+ private DelegatedResourceComparator comp = new DelegatedResourceComparator();
+
+ /**
+ * Sort the contained elements.
+ * @return a Collection of Resources.
+ */
+ protected synchronized Collection<Resource> getCollection() {
+ ResourceCollection rc = getResourceCollection();
+ Iterator<Resource> iter = rc.iterator();
+ if (!(iter.hasNext())) {
+ return Collections.emptySet();
+ }
+ List<Resource> result = (List<Resource>) CollectionUtils.asCollection(iter);
+ Collections.sort(result, comp);
+ return result;
+ }
+
+ /**
+ * Add a ResourceComparator to this Sort ResourceCollection.
+ * If multiple ResourceComparators are added, they will be processed in LIFO order.
+ * @param c the ResourceComparator to add.
+ */
+ public synchronized void add(ResourceComparator c) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ comp.add(c);
+ FailFast.invalidate(this);
+ setChecked(false);
+ }
+
+ /**
+ * Overrides the BaseResourceCollectionContainer version
+ * to recurse on nested ResourceComparators.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+
+ // check nested collection
+ super.dieOnCircularReference(stk, p);
+
+ if (!isReference()) {
+ DataType.pushAndInvokeCircularReferenceCheck(comp, stk, p);
+ setChecked(true);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/StringResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/StringResource.java
new file mode 100644
index 00000000..b12a2e14
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/StringResource.java
@@ -0,0 +1,261 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Exposes a string as a Resource.
+ * @since Ant 1.7
+ */
+public class StringResource extends Resource {
+
+ /** Magic number */
+ private static final int STRING_MAGIC
+ = Resource.getMagicNumber("StringResource".getBytes());
+
+ private static final String DEFAULT_ENCODING = "UTF-8";
+ private String encoding = DEFAULT_ENCODING;
+
+ /**
+ * Default constructor.
+ */
+ public StringResource() {
+ }
+
+ /**
+ * Construct a StringResource with the supplied value.
+ * @param value the value of this StringResource.
+ */
+ public StringResource(String value) {
+ this(null, value);
+ }
+
+ /**
+ * Construct a StringResource with the supplied project and value,
+ * doing property replacement against the project if non-null.
+ * @param project the owning Project.
+ * @param value the value of this StringResource.
+ */
+ public StringResource(Project project, String value) {
+ setProject(project);
+ setValue(project == null ? value : project.replaceProperties(value));
+ }
+
+ /**
+ * Enforce String immutability.
+ * @param s the new name/value for this StringResource.
+ */
+ public synchronized void setName(String s) {
+ if (getName() != null) {
+ throw new BuildException(new ImmutableResourceException());
+ }
+ super.setName(s);
+ }
+
+ /**
+ * The value attribute is a semantically superior alias for the name attribute.
+ * @param s the String's value.
+ */
+ public synchronized void setValue(String s) {
+ setName(s);
+ }
+
+ /**
+ * Synchronize access.
+ * @return the name/value of this StringResource.
+ */
+ public synchronized String getName() {
+ return super.getName();
+ }
+
+ /**
+ * Get the value of this StringResource, resolving to the root reference if needed.
+ * @return the represented String.
+ */
+ public synchronized String getValue() {
+ return getName();
+ }
+
+ /**
+ * The exists attribute tells whether a resource exists.
+ *
+ * @return true if this resource exists.
+ */
+ public boolean isExists() {
+ return getValue() != null;
+ }
+
+ /**
+ * Add nested text to this resource.
+ * Properties will be expanded during this process.
+ * @since Ant 1.7.1
+ * @param text text to use as the string resource
+ */
+ public void addText(String text) {
+ checkChildrenAllowed();
+ setValue(getProject().replaceProperties(text));
+ }
+
+ /**
+ * Set the encoding to be used for this StringResource.
+ * @param s the encoding name.
+ */
+ public synchronized void setEncoding(String s) {
+ checkAttributesAllowed();
+ encoding = s;
+ }
+
+ /**
+ * Get the encoding used by this StringResource.
+ * @return the encoding name.
+ */
+ public synchronized String getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * Get the size of this Resource.
+ * @return the size, as a long, 0 if the Resource does not exist (for
+ * compatibility with java.io.File), or UNKNOWN_SIZE if not known.
+ */
+ public synchronized long getSize() {
+ return isReference() ? ((Resource) getCheckedRef()).getSize()
+ : getContent().length();
+ }
+
+ /**
+ * Get the hash code for this Resource.
+ * @return hash code as int.
+ */
+ public synchronized int hashCode() {
+ if (isReference()) {
+ return getCheckedRef().hashCode();
+ }
+ return super.hashCode() * STRING_MAGIC;
+ }
+
+ /**
+ * Get the string. See {@link #getContent()}
+ *
+ * @return the string contents of the resource.
+ * @since Ant 1.7
+ */
+ public String toString() {
+ return String.valueOf(getContent());
+ }
+
+ /**
+ * Get an InputStream for the Resource.
+ * @return an InputStream containing this Resource's content.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if InputStreams are not
+ * supported for this Resource type.
+ */
+ public synchronized InputStream getInputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getInputStream();
+ }
+ String content = getContent();
+ if (content == null) {
+ throw new IllegalStateException("unset string value");
+ }
+ return new ByteArrayInputStream(encoding == null
+ ? content.getBytes() : content.getBytes(encoding));
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ */
+ public synchronized OutputStream getOutputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getOutputStream();
+ }
+ if (getValue() != null) {
+ throw new ImmutableResourceException();
+ }
+ return new StringResourceFilterOutputStream();
+ }
+
+ /**
+ * Overrides the super version.
+ * @param r the Reference to set.
+ */
+ public void setRefid(Reference r) {
+ if (encoding != DEFAULT_ENCODING) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Get the content of this StringResource. See {@link #getValue()}
+ * @return a String or null if there is no value.
+ */
+ protected synchronized String getContent() {
+ return getValue();
+ }
+
+ /**
+ * This method is only for use by our private helper output stream.
+ * It contains specific logic for expanding properties.
+ * @param output the output
+ */
+ private void setValueFromOutputStream(String output) {
+ String value;
+ if (getProject() != null) {
+ value = getProject().replaceProperties(output);
+ } else {
+ value = output;
+ }
+ setValue(value);
+ }
+
+ private class StringResourceFilterOutputStream extends FilterOutputStream {
+ private final ByteArrayOutputStream baos;
+
+ public StringResourceFilterOutputStream() {
+ super(new ByteArrayOutputStream());
+ baos = (ByteArrayOutputStream) out;
+ }
+
+ public void close() throws IOException {
+ super.close();
+ String result = encoding == null
+ ? baos.toString() : baos.toString(encoding);
+
+ StringResource.this.setValueFromOutputStream(result);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/TarResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/TarResource.java
new file mode 100644
index 00000000..62f1f267
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/TarResource.java
@@ -0,0 +1,198 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.tar.TarEntry;
+import org.apache.tools.tar.TarInputStream;
+
+/**
+ * A Resource representation of an entry in a tar archive.
+ * @since Ant 1.7
+ */
+public class TarResource extends ArchiveResource {
+
+ private String userName = "";
+ private String groupName = "";
+ private int uid;
+ private int gid;
+
+ /**
+ * Default constructor.
+ */
+ public TarResource() {
+ }
+
+ /**
+ * Construct a TarResource representing the specified
+ * entry in the specified archive.
+ * @param a the archive as File.
+ * @param e the TarEntry.
+ */
+ public TarResource(File a, TarEntry e) {
+ super(a, true);
+ setEntry(e);
+ }
+
+ /**
+ * Construct a TarResource representing the specified
+ * entry in the specified archive.
+ * @param a the archive as Resource.
+ * @param e the TarEntry.
+ */
+ public TarResource(Resource a, TarEntry e) {
+ super(a, true);
+ setEntry(e);
+ }
+
+ /**
+ * Return an InputStream for reading the contents of this Resource.
+ * @return an InputStream object.
+ * @throws IOException if the tar file cannot be opened,
+ * or the entry cannot be read.
+ */
+ public InputStream getInputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getInputStream();
+ }
+ Resource archive = getArchive();
+ final TarInputStream i = new TarInputStream(archive.getInputStream());
+ TarEntry te = null;
+ while ((te = i.getNextEntry()) != null) {
+ if (te.getName().equals(getName())) {
+ return i;
+ }
+ }
+
+ FileUtils.close(i);
+ throw new BuildException("no entry " + getName() + " in "
+ + getArchive());
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ */
+ public OutputStream getOutputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getOutputStream();
+ }
+ throw new UnsupportedOperationException(
+ "Use the tar task for tar output.");
+ }
+
+ /**
+ * @return the user name for the tar entry
+ */
+ public String getUserName() {
+ if (isReference()) {
+ return ((TarResource) getCheckedRef()).getUserName();
+ }
+ checkEntry();
+ return userName;
+ }
+
+ /**
+ * @return the group name for the tar entry
+ */
+ public String getGroup() {
+ if (isReference()) {
+ return ((TarResource) getCheckedRef()).getGroup();
+ }
+ checkEntry();
+ return groupName;
+ }
+
+ /**
+ * @return the uid for the tar entry
+ */
+ public int getUid() {
+ if (isReference()) {
+ return ((TarResource) getCheckedRef()).getUid();
+ }
+ checkEntry();
+ return uid;
+ }
+
+ /**
+ * @return the gid for the tar entry
+ */
+ public int getGid() {
+ if (isReference()) {
+ return ((TarResource) getCheckedRef()).getGid();
+ }
+ checkEntry();
+ return gid;
+ }
+
+ /**
+ * fetches information from the named entry inside the archive.
+ */
+ protected void fetchEntry() {
+ Resource archive = getArchive();
+ TarInputStream i = null;
+ try {
+ i = new TarInputStream(archive.getInputStream());
+ TarEntry te = null;
+ while ((te = i.getNextEntry()) != null) {
+ if (te.getName().equals(getName())) {
+ setEntry(te);
+ return;
+ }
+ }
+ } catch (IOException e) {
+ log(e.getMessage(), Project.MSG_DEBUG);
+ throw new BuildException(e);
+ } finally {
+ if (i != null) {
+ FileUtils.close(i);
+ }
+ }
+ setEntry(null);
+ }
+
+ private void setEntry(TarEntry e) {
+ if (e == null) {
+ setExists(false);
+ return;
+ }
+ setName(e.getName());
+ setExists(true);
+ setLastModified(e.getModTime().getTime());
+ setDirectory(e.isDirectory());
+ setSize(e.getSize());
+ setMode(e.getMode());
+ userName = e.getUserName();
+ groupName = e.getGroupName();
+ uid = e.getUserId();
+ gid = e.getGroupId();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Tokens.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Tokens.java
new file mode 100644
index 00000000..0a518c3d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Tokens.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.tools.ant.types.resources;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.util.ConcatResourceInputStream;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.LineTokenizer;
+import org.apache.tools.ant.util.Tokenizer;
+
+/**
+ * ResourceCollection consisting of StringResources gathered from tokenizing
+ * another ResourceCollection with a Tokenizer implementation.
+ * @since Ant 1.7
+ */
+public class Tokens extends BaseResourceCollectionWrapper {
+
+ private Tokenizer tokenizer;
+ private String encoding;
+
+ /**
+ * Sort the contained elements.
+ * @return a Collection of Resources.
+ */
+ protected synchronized Collection<Resource> getCollection() {
+ ResourceCollection rc = getResourceCollection();
+ if (rc.size() == 0) {
+ return Collections.emptySet();
+ }
+ if (tokenizer == null) {
+ tokenizer = new LineTokenizer();
+ }
+ ConcatResourceInputStream cat = new ConcatResourceInputStream(rc);
+ cat.setManagingComponent(this);
+
+ try {
+ InputStreamReader rdr = null;
+ if (encoding == null) {
+ rdr = new InputStreamReader(cat);
+ } else {
+ try {
+ rdr = new InputStreamReader(cat, encoding);
+ } catch (UnsupportedEncodingException e) {
+ throw new BuildException(e);
+ }
+ }
+ ArrayList<Resource> result = new ArrayList<Resource>();
+ for (String s = tokenizer.getToken(rdr); s != null; s = tokenizer.getToken(rdr)) {
+ StringResource resource = new StringResource(s);
+ resource.setProject(getProject());
+ result.add(resource);
+ }
+ return result;
+ } catch (IOException e) {
+ throw new BuildException("Error reading tokens", e);
+ } finally {
+ FileUtils.close(cat);
+ }
+ }
+
+ /**
+ * Set the encoding used to create the tokens.
+ * @param encoding the encoding to use.
+ */
+ public synchronized void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Add the nested Tokenizer to this Tokens ResourceCollection.
+ * A LineTokenizer will be used by default.
+ * @param tokenizer the tokenizer to add.
+ */
+ public synchronized void add(Tokenizer tokenizer) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (this.tokenizer != null) {
+ throw new BuildException("Only one nested tokenizer allowed.");
+ }
+ this.tokenizer = tokenizer;
+ setChecked(false);
+ }
+
+ /**
+ * Overrides the BaseResourceCollectionContainer version
+ * to check the nested Tokenizer.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+
+ // check nested collection
+ super.dieOnCircularReference(stk, p);
+
+ if (!isReference()) {
+ if (tokenizer instanceof DataType) {
+ pushAndInvokeCircularReferenceCheck((DataType) tokenizer, stk,
+ p);
+ }
+ setChecked(true);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Touchable.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Touchable.java
new file mode 100644
index 00000000..3a54a693
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Touchable.java
@@ -0,0 +1,32 @@
+/*
+ * 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.tools.ant.types.resources;
+
+/**
+ * Interface to be implemented by "touchable" resources;
+ * that is, those whose modification time can be altered.
+ * @since Ant 1.7
+ */
+public interface Touchable {
+ /**
+ * Method called to "touch" the resource.
+ * @param modTime the time to set the modified "field" of the resource,
+ * measured in milliseconds since the epoch.
+ */
+ void touch(long modTime);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/URLProvider.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/URLProvider.java
new file mode 100644
index 00000000..81bb6697
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/URLProvider.java
@@ -0,0 +1,35 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.net.URL;
+
+/**
+ * This is an interface that resources that can provide an URL should implement.
+ * This is a refactoring of {@link URLResource}, to allow other resources
+ * to act as sources of URLs.
+ * @since Ant 1.8
+ */
+public interface URLProvider {
+ /**
+ * Get the URL represented by this Resource.
+ * @return the file.
+ */
+ URL getURL();
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/URLResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/URLResource.java
new file mode 100644
index 00000000..70d6c9ba
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/URLResource.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.tools.ant.types.resources;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Exposes a URL as a Resource.
+ * @since Ant 1.7
+ */
+public class URLResource extends Resource implements URLProvider {
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+ private static final int NULL_URL
+ = Resource.getMagicNumber("null URL".getBytes());
+
+ private URL url;
+ private URLConnection conn;
+ private URL baseURL;
+ private String relPath;
+
+ /**
+ * Default constructor.
+ */
+ public URLResource() {
+ }
+
+ /**
+ * Convenience constructor.
+ * @param u the URL to expose.
+ */
+ public URLResource(URL u) {
+ setURL(u);
+ }
+
+ /**
+ * Convenience constructor.
+ * @param u holds the URL to expose.
+ */
+ public URLResource(URLProvider u) {
+ setURL(u.getURL());
+ }
+
+ /**
+ * Convenience constructor.
+ * @param f the File to set as a URL.
+ */
+ public URLResource(File f) {
+ setFile(f);
+ }
+
+ /**
+ * String constructor for Ant attribute introspection.
+ * @param u String representation of this URL.
+ * @see org.apache.tools.ant.IntrospectionHelper
+ */
+ public URLResource(String u) {
+ this(newURL(u));
+ }
+
+ /**
+ * Set the URL for this URLResource.
+ * @param u the URL to expose.
+ */
+ public synchronized void setURL(URL u) {
+ checkAttributesAllowed();
+ url = u;
+ }
+
+ /**
+ * Set the URL from a File.
+ * @param f the File to set as a URL.
+ */
+ public synchronized void setFile(File f) {
+ try {
+ setURL(FILE_UTILS.getFileURL(f));
+ } catch (MalformedURLException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Base URL which combined with the relativePath attribute defines
+ * the URL.
+ * @since Ant 1.8.0
+ */
+ public synchronized void setBaseURL(URL base) {
+ checkAttributesAllowed();
+ if (url != null) {
+ throw new BuildException("can't define URL and baseURL attribute");
+ }
+ baseURL = base;
+ }
+
+ /**
+ * Relative path which combined with the baseURL attribute defines
+ * the URL.
+ * @since Ant 1.8.0
+ */
+ public synchronized void setRelativePath(String r) {
+ checkAttributesAllowed();
+ if (url != null) {
+ throw new BuildException("can't define URL and relativePath"
+ + " attribute");
+ }
+ relPath = r;
+ }
+
+
+ /**
+ * Get the URL used by this URLResource.
+ * @return a URL object.
+ */
+ public synchronized URL getURL() {
+ if (isReference()) {
+ return ((URLResource) getCheckedRef()).getURL();
+ }
+ if (url == null) {
+ if (baseURL != null) {
+ if (relPath == null) {
+ throw new BuildException("must provide relativePath"
+ + " attribute when using baseURL.");
+ }
+ try {
+ url = new URL(baseURL, relPath);
+ } catch (MalformedURLException e) {
+ throw new BuildException(e);
+ }
+ }
+ }
+ return url;
+ }
+
+ /**
+ * Overrides the super version.
+ * @param r the Reference to set.
+ */
+ public synchronized void setRefid(Reference r) {
+ //not using the accessor in this case to avoid side effects
+ if (url != null || baseURL != null || relPath != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Get the name of this URLResource
+ * (its file component minus the leading separator).
+ * @return the name of this resource.
+ */
+ public synchronized String getName() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getName();
+ }
+ String name = getURL().getFile();
+ return "".equals(name) ? name : name.substring(1);
+ }
+
+ /**
+ * Return this URLResource formatted as a String.
+ * @return a String representation of this URLResource.
+ */
+ public synchronized String toString() {
+ return isReference()
+ ? getCheckedRef().toString() : String.valueOf(getURL());
+ }
+
+ /**
+ * Find out whether the URL exists .
+ * @return true if this resource exists.
+ */
+ public synchronized boolean isExists() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).isExists();
+ }
+ return isExists(false);
+ }
+
+ /**
+ * Find out whether the URL exists, and close the connection
+ * opened to the URL if closeConnection is true.
+ *
+ * Note that this method does ensure that if:
+ * - the resource exists (if it returns true)
+ * - and if the current object is not a reference
+ * (isReference() returns false)
+ * - and if it was called with closeConnection to false,
+ *
+ * then the connection to the URL (stored in the conn
+ * private field) will be opened, and require to be closed
+ * by the caller.
+ *
+ * @param closeConnection true if the connection should be closed
+ * after the call, false if it should stay open.
+ * @return true if this resource exists.
+ */
+ private synchronized boolean isExists(boolean closeConnection) {
+ if (getURL() == null) {
+ return false;
+ }
+ try {
+ connect(Project.MSG_VERBOSE);
+ if (conn instanceof HttpURLConnection) {
+ int sc = ((HttpURLConnection) conn).getResponseCode();
+ // treating inaccessible resources as non-existent
+ return sc < 400;
+ } else if (url.getProtocol().startsWith("ftp")) {
+ closeConnection = true;
+ InputStream in = null;
+ try {
+ in = conn.getInputStream();
+ } finally {
+ FileUtils.close(in);
+ }
+ }
+ return true;
+ } catch (IOException e) {
+ return false;
+ } finally {
+ if (closeConnection) {
+ close();
+ }
+ }
+ }
+
+
+ /**
+ * Tells the modification time in milliseconds since 01.01.1970 .
+ *
+ * @return 0 if the resource does not exist to mirror the behavior
+ * of {@link java.io.File File}.
+ */
+ public synchronized long getLastModified() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getLastModified();
+ }
+ if (!isExists(false)) {
+ return UNKNOWN_DATETIME;
+ }
+ return withConnection(new ConnectionUser() {
+ public long useConnection(URLConnection c) {
+ return conn.getLastModified();
+ }
+ }, UNKNOWN_DATETIME);
+ }
+
+ /**
+ * Tells if the resource is a directory.
+ * @return boolean whether the resource is a directory.
+ */
+ public synchronized boolean isDirectory() {
+ return isReference()
+ ? ((Resource) getCheckedRef()).isDirectory()
+ : getName().endsWith("/");
+ }
+
+ /**
+ * Get the size of this Resource.
+ * @return the size, as a long, 0 if the Resource does not exist (for
+ * compatibility with java.io.File), or UNKNOWN_SIZE if not known.
+ */
+ public synchronized long getSize() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getSize();
+ }
+ if (!isExists(false)) {
+ return 0L;
+ }
+ return withConnection(new ConnectionUser() {
+ public long useConnection(URLConnection c) {
+ return conn.getContentLength();
+ }
+ }, UNKNOWN_SIZE);
+ }
+
+ /**
+ * Test whether an Object equals this URLResource.
+ * @param another the other Object to compare.
+ * @return true if the specified Object is equal to this Resource.
+ */
+ public synchronized boolean equals(Object another) {
+ if (this == another) {
+ return true;
+ }
+ if (isReference()) {
+ return getCheckedRef().equals(another);
+ }
+ if (another == null || !(another.getClass().equals(getClass()))) {
+ return false;
+ }
+ URLResource otheru = (URLResource) another;
+ return getURL() == null
+ ? otheru.getURL() == null
+ : getURL().equals(otheru.getURL());
+ }
+
+ /**
+ * Get the hash code for this Resource.
+ * @return hash code as int.
+ */
+ public synchronized int hashCode() {
+ if (isReference()) {
+ return getCheckedRef().hashCode();
+ }
+ return MAGIC * ((getURL() == null) ? NULL_URL : getURL().hashCode());
+ }
+
+ /**
+ * Get an InputStream for the Resource.
+ * @return an InputStream containing this Resource's content.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if InputStreams are not
+ * supported for this Resource type.
+ */
+ public synchronized InputStream getInputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getInputStream();
+ }
+ connect();
+ try {
+ return conn.getInputStream();
+ } finally {
+ conn = null;
+ }
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ * @throws IOException if the URL cannot be opened.
+ */
+ public synchronized OutputStream getOutputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getOutputStream();
+ }
+ connect();
+ try {
+ return conn.getOutputStream();
+ } finally {
+ conn = null;
+ }
+ }
+
+ /**
+ * Ensure that we have a connection.
+ * @throws IOException if the connection cannot be established.
+ */
+ protected void connect() throws IOException {
+ connect(Project.MSG_ERR);
+ }
+
+ /**
+ * Ensure that we have a connection.
+ * @param logLevel severity to use when logging connection errors.
+ * Should be one of the <code>MSG_</code> constants in {@link
+ * Project Project}.
+ * @throws IOException if the connection cannot be established.
+ * @since Ant 1.8.2
+ */
+ protected synchronized void connect(int logLevel) throws IOException {
+ URL u = getURL();
+ if (u == null) {
+ throw new BuildException("URL not set");
+ }
+ if (conn == null) {
+ try {
+ conn = u.openConnection();
+ conn.connect();
+ } catch (IOException e) {
+ log(e.toString(), logLevel);
+ conn = null;
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * Closes the URL connection if:
+ * - it is opened (i.e. the field conn is not null)
+ * - this type of URLConnection supports some sort of close mechanism
+ *
+ * This method ensures the field conn will be null after the call.
+ *
+ */
+ private synchronized void close() {
+ try {
+ FileUtils.close(conn);
+ } finally {
+ conn = null;
+ }
+ }
+
+ private static URL newURL(String u) {
+ try {
+ return new URL(u);
+ } catch (MalformedURLException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ private interface ConnectionUser {
+ long useConnection(URLConnection c);
+ }
+
+ private long withConnection(ConnectionUser u, long defaultValue) {
+ try {
+ if (conn != null) {
+ return u.useConnection(conn);
+ } else {
+ try {
+ connect();
+ return u.useConnection(conn);
+ } finally {
+ close();
+ }
+ }
+ } catch (IOException ex) {
+ return defaultValue;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Union.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Union.java
new file mode 100644
index 00000000..e2f2f9f2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/Union.java
@@ -0,0 +1,156 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * ResourceCollection representing the union of multiple nested ResourceCollections.
+ * @since Ant 1.7
+ */
+public class Union extends BaseResourceCollectionContainer {
+
+ /**
+ * Static convenience method to union an arbitrary set of Resources.
+ * @param rc a ResourceCollection.
+ * @return a Union.
+ */
+ public static Union getInstance(ResourceCollection rc) {
+ return rc instanceof Union ? (Union) rc : new Union(rc);
+ }
+
+ /**
+ * Default constructor.
+ */
+ public Union() {
+ }
+
+ /**
+ * Create a new Union.
+ * @param project owning Project
+ */
+ public Union(Project project) {
+ super(project);
+ }
+
+ /**
+ * Convenience constructor.
+ * @param rc the ResourceCollection to add.
+ */
+ public Union(ResourceCollection rc) {
+ this(Project.getProject(rc), rc);
+ }
+
+ /**
+ * Convenience constructor.
+ * @param project owning Project
+ * @param rc the ResourceCollection to add.
+ */
+ public Union(Project project, ResourceCollection rc) {
+ super(project);
+ add(rc);
+ }
+
+ /**
+ * Returns all Resources in String format. Provided for
+ * convenience in implementing Path.
+ * @return String array of Resources.
+ */
+ public String[] list() {
+ if (isReference()) {
+ return getCheckedRef(Union.class, getDataTypeName()).list();
+ }
+ final Collection<String> result = getAllToStrings();
+ return result.toArray(new String[result.size()]);
+ }
+
+ /**
+ * Convenience method.
+ * @return Resource[]
+ */
+ public Resource[] listResources() {
+ if (isReference()) {
+ return getCheckedRef(Union.class, getDataTypeName()).listResources();
+ }
+ final Collection<Resource> result = getAllResources();
+ return result.toArray(new Resource[result.size()]);
+ }
+
+ /**
+ * Unify the contained Resources.
+ * @return a Collection of Resources.
+ */
+ protected Collection<Resource> getCollection() {
+ return getAllResources();
+ }
+
+ /**
+ * Unify the contained Resources.
+ * @param asString indicates whether the resulting Collection
+ * should contain Strings instead of Resources.
+ * @return a Collection of Resources.
+ */
+ @Deprecated
+ @SuppressWarnings("unchecked")
+ protected <T> Collection<T> getCollection(boolean asString) { // TODO untypable
+ return asString ? (Collection<T>) getAllToStrings() : (Collection<T>) getAllResources();
+ }
+
+ /**
+ * Get a collection of strings representing the unified resource set (strings may duplicate).
+ * @return Collection<String>
+ */
+ protected Collection<String> getAllToStrings() {
+ final Set<Resource> allResources = getAllResources();
+ final ArrayList<String> result = new ArrayList<String>(allResources.size());
+ for (Resource r : allResources) {
+ result.add(r.toString());
+ }
+ return result;
+ }
+
+ /**
+ * Get the unified set of contained Resources.
+ * @return Set<Resource>
+ */
+ protected Set<Resource> getAllResources() {
+ final List<ResourceCollection> resourceCollections = getResourceCollections();
+ if (resourceCollections.isEmpty()) {
+ return Collections.emptySet();
+ }
+ final LinkedHashSet<Resource> result = new LinkedHashSet<Resource>(
+ resourceCollections.size() * 2);
+ for (ResourceCollection resourceCollection : resourceCollections) {
+ for (Resource r : resourceCollection) {
+ result.add(r);
+ }
+ }
+ return result;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ZipResource.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ZipResource.java
new file mode 100644
index 00000000..37fc98ec
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/ZipResource.java
@@ -0,0 +1,226 @@
+/*
+ * 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.tools.ant.types.resources;
+
+import java.io.File;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.zip.ZipEntry;
+import org.apache.tools.zip.ZipExtraField;
+import org.apache.tools.zip.ZipFile;
+
+/**
+ * A Resource representation of an entry in a zipfile.
+ * @since Ant 1.7
+ */
+public class ZipResource extends ArchiveResource {
+
+ private String encoding;
+ private ZipExtraField[] extras;
+ private int method;
+
+ /**
+ * Default constructor.
+ */
+ public ZipResource() {
+ }
+
+ /**
+ * Construct a ZipResource representing the specified
+ * entry in the specified zipfile.
+ * @param z the zipfile as File.
+ * @param enc the encoding used for filenames.
+ * @param e the ZipEntry.
+ */
+ public ZipResource(File z, String enc, ZipEntry e) {
+ super(z, true);
+ setEncoding(enc);
+ setEntry(e);
+ }
+
+ /**
+ * Set the zipfile that holds this ZipResource.
+ * @param z the zipfile as a File.
+ */
+ public void setZipfile(File z) {
+ setArchive(z);
+ }
+
+ /**
+ * Get the zipfile that holds this ZipResource.
+ * @return the zipfile as a File.
+ */
+ public File getZipfile() {
+ FileProvider fp = getArchive().as(FileProvider.class);
+ return fp.getFile();
+ }
+
+ /**
+ * Sets the archive that holds this as a single element Resource
+ * collection.
+ * @param a the archive as a single element Resource collection.
+ */
+ public void addConfigured(ResourceCollection a) {
+ super.addConfigured(a);
+ if (!a.isFilesystemOnly()) {
+ throw new BuildException("only filesystem resources are supported");
+ }
+ }
+
+ /**
+ * Set the encoding to use with the zipfile.
+ * @param enc the String encoding.
+ */
+ public void setEncoding(String enc) {
+ checkAttributesAllowed();
+ encoding = enc;
+ }
+
+ /**
+ * Get the encoding to use with the zipfile.
+ * @return String encoding.
+ */
+ public String getEncoding() {
+ return isReference()
+ ? ((ZipResource) getCheckedRef()).getEncoding() : encoding;
+ }
+
+ /**
+ * Overrides the super version.
+ * @param r the Reference to set.
+ */
+ public void setRefid(Reference r) {
+ if (encoding != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Return an InputStream for reading the contents of this Resource.
+ * @return an InputStream object.
+ * @throws IOException if the zip file cannot be opened,
+ * or the entry cannot be read.
+ */
+ public InputStream getInputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getInputStream();
+ }
+ final ZipFile z = new ZipFile(getZipfile(), getEncoding());
+ ZipEntry ze = z.getEntry(getName());
+ if (ze == null) {
+ z.close();
+ throw new BuildException("no entry " + getName() + " in "
+ + getArchive());
+ }
+ return new FilterInputStream(z.getInputStream(ze)) {
+ public void close() throws IOException {
+ FileUtils.close(in);
+ z.close();
+ }
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+ };
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ */
+ public OutputStream getOutputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getOutputStream();
+ }
+ throw new UnsupportedOperationException(
+ "Use the zip task for zip output.");
+ }
+
+ /**
+ * Retrieves extra fields.
+ * @return an array of the extra fields
+ * @since Ant 1.8.0
+ */
+ public ZipExtraField[] getExtraFields() {
+ if (isReference()) {
+ return ((ZipResource) getCheckedRef()).getExtraFields();
+ }
+ checkEntry();
+ if (extras == null) {
+ return new ZipExtraField[0];
+ }
+ return extras;
+ }
+
+ /**
+ * The compression method that has been used.
+ * @since Ant 1.8.0
+ */
+ public int getMethod() {
+ return method;
+ }
+
+ /**
+ * fetches information from the named entry inside the archive.
+ */
+ protected void fetchEntry() {
+ ZipFile z = null;
+ try {
+ z = new ZipFile(getZipfile(), getEncoding());
+ setEntry(z.getEntry(getName()));
+ } catch (IOException e) {
+ log(e.getMessage(), Project.MSG_DEBUG);
+ throw new BuildException(e);
+ } finally {
+ ZipFile.closeQuietly(z);
+ }
+ }
+
+ private void setEntry(ZipEntry e) {
+ if (e == null) {
+ setExists(false);
+ return;
+ }
+ setName(e.getName());
+ setExists(true);
+ setLastModified(e.getTime());
+ setDirectory(e.isDirectory());
+ setSize(e.getSize());
+ setMode(e.getUnixMode());
+ extras = e.getExtraFields(true);
+ method = e.getMethod();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Content.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Content.java
new file mode 100644
index 00000000..1810b640
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Content.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.tools.ant.types.resources.comparators;
+
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.util.ResourceUtils;
+
+/**
+ * Compares Resources by content.
+ * @since Ant 1.7
+ */
+public class Content extends ResourceComparator {
+
+ private boolean binary = true;
+
+ /**
+ * Set binary mode for this Content ResourceComparator. If this
+ * attribute is set to false, Resource content will be compared
+ * ignoring platform line-ending conventions.
+ * Default is <code>true</code>.
+ * @param b whether to compare content in binary mode.
+ */
+ public void setBinary(boolean b) {
+ binary = b;
+ }
+
+ /**
+ * Learn whether this Content ResourceComparator is operating in binary mode.
+ * @return boolean binary flag.
+ */
+ public boolean isBinary() {
+ return binary;
+ }
+
+ /**
+ * Compare two Resources by content.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ * @throws BuildException if I/O errors occur.
+ * @see org.apache.tools.ant.util.ResourceUtils#compareContent(Resource, Resource, boolean).
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ try {
+ return ResourceUtils.compareContent(foo, bar, !binary);
+ } catch (IOException e) {
+ throw new BuildException(e);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Date.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Date.java
new file mode 100644
index 00000000..b6be66bb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Date.java
@@ -0,0 +1,45 @@
+/*
+ * 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.tools.ant.types.resources.comparators;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Compares Resources by last modification date.
+ * @since Ant 1.7
+ */
+public class Date extends ResourceComparator {
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ long diff = foo.getLastModified() - bar.getLastModified();
+ if (diff > 0) {
+ return +1;
+ } else if (diff < 0) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/DelegatedResourceComparator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/DelegatedResourceComparator.java
new file mode 100644
index 00000000..aa2f55a8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/DelegatedResourceComparator.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.tools.ant.types.resources.comparators;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Delegates to other ResourceComparators or, if none specified,
+ * uses Resources' natural ordering.
+ * @since Ant 1.7
+ */
+public class DelegatedResourceComparator extends ResourceComparator {
+
+ private List<ResourceComparator> resourceComparators = null;
+
+ /**
+ * Add a delegate ResourceComparator.
+ * @param c the next delegate ResourceComparator.
+ */
+ public synchronized void add(ResourceComparator c) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (c == null) {
+ return;
+ }
+ resourceComparators = (resourceComparators == null) ? new Vector<ResourceComparator>() : resourceComparators;
+ resourceComparators.add(c);
+ setChecked(false);
+ }
+
+ /**
+ * Equality method based on the vector of resources,
+ * or if a reference, the referredto object.
+ * @param o the object to check against.
+ * @return true if there is equality.
+ */
+ public synchronized boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (isReference()) {
+ return getCheckedRef().equals(o);
+ }
+ if (!(o instanceof DelegatedResourceComparator)) {
+ return false;
+ }
+ List<ResourceComparator> ov = ((DelegatedResourceComparator) o).resourceComparators;
+ return resourceComparators == null ? ov == null : resourceComparators.equals(ov);
+ }
+
+ /**
+ * Hashcode based on the rules for equality.
+ * @return a hashcode.
+ */
+ public synchronized int hashCode() {
+ if (isReference()) {
+ return getCheckedRef().hashCode();
+ }
+ return resourceComparators == null ? 0 : resourceComparators.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ protected synchronized int resourceCompare(Resource foo, Resource bar) {
+ //if no nested, natural order:
+ if (resourceComparators == null || resourceComparators.isEmpty()) {
+ return foo.compareTo(bar);
+ }
+ int result = 0;
+ for (Iterator<ResourceComparator> i = resourceComparators.iterator(); result == 0 && i.hasNext();) {
+ result = i.next().resourceCompare(foo, bar);
+ }
+ return result;
+ }
+
+ /**
+ * Overrides the version from DataType to recurse on nested ResourceSelector
+s.
+ * @param stk the Stack of references.
+ * @param p the Project to resolve against.
+ * @throws BuildException on error.
+ */
+ protected void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ if (!(resourceComparators == null || resourceComparators.isEmpty())) {
+ for (ResourceComparator resourceComparator : resourceComparators) {
+ if (resourceComparator instanceof DataType) {
+ pushAndInvokeCircularReferenceCheck((DataType) resourceComparator, stk,
+ p);
+ }
+ }
+ }
+ setChecked(true);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Exists.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Exists.java
new file mode 100644
index 00000000..58321502
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Exists.java
@@ -0,0 +1,43 @@
+/*
+ * 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.tools.ant.types.resources.comparators;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Compares Resources by existence. Not existing is "less than" existing.
+ * @since Ant 1.7
+ */
+public class Exists extends ResourceComparator {
+
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ boolean f = foo.isExists();
+ if (f == bar.isExists()) {
+ return 0;
+ }
+ return f ? 1 : -1;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/FileSystem.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/FileSystem.java
new file mode 100644
index 00000000..7eafeb94
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/FileSystem.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.tools.ant.types.resources.comparators;
+
+import java.io.File;
+
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Compares filesystem Resources.
+ * @since Ant 1.7
+ */
+public class FileSystem extends ResourceComparator {
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ * @throws ClassCastException if either resource is not an instance of FileResource.
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ FileProvider fooFP = foo.as(FileProvider.class);
+ if (fooFP == null) {
+ throw new ClassCastException(foo.getClass()
+ + " doesn't provide files");
+ }
+ File foofile = fooFP.getFile();
+ FileProvider barFP = bar.as(FileProvider.class);
+ if (barFP == null) {
+ throw new ClassCastException(bar.getClass()
+ + " doesn't provide files");
+ }
+ File barfile = barFP.getFile();
+ return foofile.equals(barfile) ? 0
+ : FILE_UTILS.isLeadingPath(foofile, barfile) ? -1
+ : FILE_UTILS.normalize(foofile.getAbsolutePath()).compareTo(
+ FILE_UTILS.normalize(barfile.getAbsolutePath()));
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Name.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Name.java
new file mode 100644
index 00000000..d048ac01
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Name.java
@@ -0,0 +1,38 @@
+/*
+ * 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.tools.ant.types.resources.comparators;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Compares Resources by name.
+ * @since Ant 1.7
+ */
+public class Name extends ResourceComparator {
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ return foo.getName().compareTo(bar.getName());
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/ResourceComparator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/ResourceComparator.java
new file mode 100644
index 00000000..3bfc9c70
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/ResourceComparator.java
@@ -0,0 +1,81 @@
+/*
+ * 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.tools.ant.types.resources.comparators;
+
+import java.util.Comparator;
+
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Abstract Resource Comparator.
+ * @since Ant 1.7
+ */
+public abstract class ResourceComparator extends DataType implements Comparator<Resource> {
+
+ /**
+ * Compare two objects.
+ * @param foo the first Object.
+ * @param bar the second Object.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ * @throws ClassCastException if either argument is null.
+ */
+ public final int compare(Resource foo, Resource bar) {
+ dieOnCircularReference();
+ ResourceComparator c =
+ isReference() ? (ResourceComparator) getCheckedRef() : this;
+ return c.resourceCompare(foo, bar);
+ }
+
+ /**
+ * Test for equality with this ResourceComparator.
+ * @param o the Object to compare against.
+ * @return true if the specified Object equals this one.
+ */
+ public boolean equals(Object o) {
+ if (isReference()) {
+ return getCheckedRef().equals(o);
+ }
+ if (o == null) {
+ return false;
+ }
+ return o == this || o.getClass().equals(getClass());
+ }
+
+ /**
+ * Hashcode based on the rules for equality.
+ * @return a hashcode.
+ */
+ public synchronized int hashCode() {
+ if (isReference()) {
+ return getCheckedRef().hashCode();
+ }
+ return getClass().hashCode();
+ }
+
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ */
+ protected abstract int resourceCompare(Resource foo, Resource bar);
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Reverse.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Reverse.java
new file mode 100644
index 00000000..c787a765
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Reverse.java
@@ -0,0 +1,91 @@
+/*
+ * 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.tools.ant.types.resources.comparators;
+
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Reverses another ResourceComparator. If no nested ResourceComparator
+ * is supplied, the compared Resources' natural order will be reversed.
+ * @since Ant 1.7
+ */
+public class Reverse extends ResourceComparator {
+ private static final String ONE_NESTED
+ = "You must not nest more than one ResourceComparator for reversal.";
+
+ private ResourceComparator nested;
+
+ /**
+ * Default constructor.
+ */
+ public Reverse() {
+ }
+
+ /**
+ * Construct a new Reverse, supplying the ResourceComparator to be reversed.
+ * @param c the ResourceComparator to reverse.
+ */
+ public Reverse(ResourceComparator c) {
+ add(c);
+ }
+
+ /**
+ * Add the ResourceComparator to reverse.
+ * @param c the ResourceComparator to add.
+ */
+ public void add(ResourceComparator c) {
+ if (nested != null) {
+ throw new BuildException(ONE_NESTED);
+ }
+ nested = c;
+ setChecked(false);
+ }
+
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is greater than, equal to, or less than the second.
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ return -1 * (nested == null
+ ? foo.compareTo(bar) : nested.compare(foo, bar));
+ }
+
+ protected void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ if (nested instanceof DataType) {
+ pushAndInvokeCircularReferenceCheck((DataType) nested, stk,
+ p);
+ }
+ setChecked(true);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Size.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Size.java
new file mode 100644
index 00000000..b94f250b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Size.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.tools.ant.types.resources.comparators;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Compares Resources by size.
+ * @since Ant 1.7
+ */
+public class Size extends ResourceComparator {
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ long diff = foo.getSize() - bar.getSize();
+ return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Type.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Type.java
new file mode 100644
index 00000000..6c082ef6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/comparators/Type.java
@@ -0,0 +1,44 @@
+/*
+ * 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.tools.ant.types.resources.comparators;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Compares Resources by is-directory status. As a container
+ * of files, a directory is deemed "greater" than a file.
+ * @since Ant 1.7
+ */
+public class Type extends ResourceComparator {
+
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ boolean f = foo.isDirectory();
+ if (f == bar.isDirectory()) {
+ return 0;
+ }
+ return f ? 1 : -1;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/And.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/And.java
new file mode 100644
index 00000000..409ed661
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/And.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.tools.ant.types.resources.selectors;
+
+import java.util.Iterator;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * And ResourceSelector.
+ * @since Ant 1.7
+ */
+public class And extends ResourceSelectorContainer implements ResourceSelector {
+
+ /**
+ * Default constructor.
+ */
+ public And() {
+ }
+
+ /**
+ * Convenience constructor.
+ * @param r the ResourceSelector[] to add.
+ */
+ public And(ResourceSelector[] r) {
+ super(r);
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ for (Iterator<ResourceSelector> i = getSelectors(); i.hasNext();) {
+ if (!i.next().isSelected(r)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Compare.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Compare.java
new file mode 100644
index 00000000..f345c278
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Compare.java
@@ -0,0 +1,149 @@
+/*
+ * 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.tools.ant.types.resources.selectors;
+
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Comparison;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Quantifier;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.types.resources.comparators.DelegatedResourceComparator;
+import org.apache.tools.ant.types.resources.comparators.ResourceComparator;
+
+/**
+ * ResourceSelector that compares against "control" Resource(s)
+ * using ResourceComparators.
+ * @since Ant 1.7
+ */
+public class Compare extends DataType implements ResourceSelector {
+
+ private static final String ONE_CONTROL_MESSAGE
+ = " the <control> element should be specified exactly once.";
+
+ private DelegatedResourceComparator comp = new DelegatedResourceComparator();
+ private Quantifier against = Quantifier.ALL;
+
+ private Comparison when = Comparison.EQUAL;
+
+ private Union control;
+
+ /**
+ * Add a ResourceComparator to this Compare selector.
+ * If multiple ResourceComparators are added, they will be processed in LIFO order.
+ * @param c the ResourceComparator to add.
+ */
+ public synchronized void add(ResourceComparator c) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ comp.add(c);
+ setChecked(false);
+ }
+
+ /**
+ * Set the quantifier to be used. Default "all".
+ * @param against the Quantifier EnumeratedAttribute to use.
+ */
+ public synchronized void setAgainst(Quantifier against) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.against = against;
+ }
+
+ /**
+ * Set the comparison to be used. Default "equal".
+ * @param when the Comparison EnumeratedAttribute to use.
+ */
+ public synchronized void setWhen(Comparison when) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ this.when = when;
+ }
+
+ /**
+ * Create the nested control element. These are the
+ * resources to compare against.
+ * @return ResourceCollection.
+ */
+ public synchronized ResourceCollection createControl() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (control != null) {
+ throw oneControl();
+ }
+ control = new Union();
+ setChecked(false);
+ return control;
+ }
+
+ //implement ResourceSelector; inherit doc
+ /** {@inheritDoc} */
+ public synchronized boolean isSelected(Resource r) {
+ if (isReference()) {
+ return ((ResourceSelector) getCheckedRef()).isSelected(r);
+ }
+ if (control == null) {
+ throw oneControl();
+ }
+ dieOnCircularReference();
+ int t = 0, f = 0;
+ for (Resource res : control) {
+ if (when.evaluate(comp.compare(r, res))) {
+ t++;
+ } else {
+ f++;
+ }
+ }
+ return against.evaluate(t, f);
+ }
+
+ /**
+ * Overrides the version from DataType
+ * to recurse on nested ResourceComparators.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ if (control != null) {
+ DataType.pushAndInvokeCircularReferenceCheck(control, stk, p);
+ }
+ DataType.pushAndInvokeCircularReferenceCheck(comp, stk, p);
+ setChecked(true);
+ }
+ }
+
+ private BuildException oneControl() {
+ return new BuildException(super.toString() + ONE_CONTROL_MESSAGE);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Date.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Date.java
new file mode 100644
index 00000000..8541e858
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Date.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.tools.ant.types.resources.selectors;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.TimeComparison;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Date ResourceSelector. Based on the date FileSelector, with the most
+ * notable difference being the lack of support for the includedirs attribute.
+ * It is recommended that the effect of includeDirs = "false" be achieved for
+ * resources by enclosing a "dir" Type ResourceSelector and a Date
+ * ResourceSelector in an Or ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Date implements ResourceSelector {
+ private static final String MILLIS_OR_DATETIME
+ = "Either the millis or the datetime attribute must be set.";
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private Long millis = null;
+ private String dateTime = null;
+ private String pattern = null;
+ private TimeComparison when = TimeComparison.EQUAL;
+ private long granularity = FILE_UTILS.getFileTimestampGranularity();
+
+ /**
+ * Set the date/time in milliseconds since 1970.
+ * @param m the number of millis.
+ */
+ public synchronized void setMillis(long m) {
+ millis = new Long(m);
+ }
+
+ /**
+ * Get the date/time in ms.
+ * @return long number of millis since 1970.
+ */
+ public synchronized long getMillis() {
+ return millis == null ? -1L : millis.longValue();
+ }
+
+ /**
+ * Set the date and time as a String.
+ * @param s the date and time to use.
+ */
+ public synchronized void setDateTime(String s) {
+ dateTime = s;
+ millis = null;
+ }
+
+ /**
+ * Get the date and time in String format.
+ * @return a String representing a date and time.
+ */
+ public synchronized String getDatetime() {
+ return dateTime;
+ }
+
+ /**
+ * Set the granularity to use for this ResourceSelector.
+ * @param g the timestamp granularity.
+ */
+ public synchronized void setGranularity(long g) {
+ granularity = g;
+ }
+
+ /**
+ * Get the timestamp granularity used by this ResourceSelector.
+ * @return the long granularity.
+ */
+ public synchronized long getGranularity() {
+ return granularity;
+ }
+
+ /**
+ * Set the optional pattern to use with the datetime attribute.
+ * @param p the SimpleDateFormat-compatible pattern string.
+ */
+ public synchronized void setPattern(String p) {
+ pattern = p;
+ }
+
+ /**
+ * Get the pattern for use with the datetime attribute.
+ * @return a SimpleDateFormat-compatible pattern string.
+ */
+ public synchronized String getPattern() {
+ return pattern;
+ }
+
+ /**
+ * Set the comparison mode.
+ * @param c a TimeComparison object.
+ */
+ public synchronized void setWhen(TimeComparison c) {
+ when = c;
+ }
+
+ /**
+ * Get the comparison mode.
+ * @return a TimeComparison object.
+ */
+ public synchronized TimeComparison getWhen() {
+ return when;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public synchronized boolean isSelected(Resource r) {
+ if (dateTime == null && millis == null) {
+ throw new BuildException(MILLIS_OR_DATETIME);
+ }
+ if (millis == null) {
+ DateFormat df = ((pattern == null)
+ ? DateFormat.getDateTimeInstance(
+ DateFormat.SHORT, DateFormat.SHORT, Locale.US)
+ : new SimpleDateFormat(pattern));
+ try {
+ long m = df.parse(dateTime).getTime();
+ if (m < 0) {
+ throw new BuildException("Date of " + dateTime
+ + " results in negative milliseconds value"
+ + " relative to epoch (January 1, 1970, 00:00:00 GMT).");
+ }
+ setMillis(m);
+ } catch (ParseException pe) {
+ throw new BuildException("Date of " + dateTime
+ + " Cannot be parsed correctly. It should be in"
+ + (pattern == null
+ ? " MM/DD/YYYY HH:MM AM_PM" : pattern) + " format.");
+ }
+ }
+ return when.evaluate(r.getLastModified(), millis.longValue(), granularity);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Exists.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Exists.java
new file mode 100644
index 00000000..1b498bca
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Exists.java
@@ -0,0 +1,37 @@
+/*
+ * 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.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Exists ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Exists implements ResourceSelector {
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ return r.isExists();
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/InstanceOf.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/InstanceOf.java
new file mode 100644
index 00000000..39b31088
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/InstanceOf.java
@@ -0,0 +1,130 @@
+/*
+ * 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.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.AntTypeDefinition;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * InstanceOf ResourceSelector.
+ * @since Ant 1.7
+ */
+public class InstanceOf implements ResourceSelector {
+ private static final String ONE_ONLY = "Exactly one of class|type must be set.";
+
+ private Project project;
+ private Class<?> clazz;
+ private String type;
+ private String uri;
+
+ /**
+ * Set the Project instance for this InstanceOf selector.
+ * @param p the Project instance used for type comparisons.
+ */
+ public void setProject(Project p) {
+ project = p;
+ }
+
+ /**
+ * Set the class to compare against.
+ * @param c the class.
+ */
+ public void setClass(Class<?> c) {
+ if (clazz != null) {
+ throw new BuildException("The class attribute has already been set.");
+ }
+ clazz = c;
+ }
+
+ /**
+ * Set the Ant type to compare against.
+ * @param s the type name.
+ */
+ public void setType(String s) {
+ type = s;
+ }
+
+ /**
+ * Set the URI in which the Ant type, if specified, should be defined.
+ * @param u the URI.
+ */
+ public void setURI(String u) {
+ uri = u;
+ }
+
+ /**
+ * Get the comparison class.
+ * @return the Class object.
+ */
+ public Class<?> getCheckClass() {
+ return clazz;
+ }
+
+ /**
+ * Get the comparison type.
+ * @return the String typename.
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Get the type's URI.
+ * @return the String URI.
+ */
+ public String getURI() {
+ return uri;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ * @throws BuildException if an error occurs.
+ */
+ public boolean isSelected(Resource r) {
+ if ((clazz == null) == (type == null)) {
+ throw new BuildException(ONE_ONLY);
+ }
+ Class<?> c = clazz;
+ if (type != null) {
+ if (project == null) {
+ throw new BuildException(
+ "No project set for InstanceOf ResourceSelector; "
+ + "the type attribute is invalid.");
+ }
+ AntTypeDefinition d = ComponentHelper.getComponentHelper(
+ project).getDefinition(ProjectHelper.genComponentName(uri, type));
+ if (d == null) {
+ throw new BuildException("type " + type + " not found.");
+ }
+ try {
+ c = d.innerGetTypeClass();
+ } catch (ClassNotFoundException e) {
+ throw new BuildException(e);
+ }
+ }
+ return c.isAssignableFrom(r.getClass());
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Majority.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Majority.java
new file mode 100644
index 00000000..5a7a95c0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Majority.java
@@ -0,0 +1,84 @@
+/*
+ * 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.tools.ant.types.resources.selectors;
+
+import java.util.Iterator;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Majority ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Majority
+ extends ResourceSelectorContainer implements ResourceSelector {
+
+ private boolean tie = true;
+
+ /**
+ * Default constructor.
+ */
+ public Majority() {
+ }
+
+ /**
+ * Convenience constructor.
+ * @param r the ResourceSelector[] to add.
+ */
+ public Majority(ResourceSelector[] r) {
+ super(r);
+ }
+
+ /**
+ * Set whether ties are allowed.
+ * @param b whether a tie is a pass.
+ */
+ public synchronized void setAllowtie(boolean b) {
+ tie = b;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public synchronized boolean isSelected(Resource r) {
+ int passed = 0;
+ int failed = 0;
+ int count = selectorCount();
+ boolean even = count % 2 == 0;
+ int threshold = count / 2;
+
+ for (Iterator<ResourceSelector> i = getSelectors(); i.hasNext();) {
+ if (i.next().isSelected(r)) {
+ ++passed;
+ if (passed > threshold || (even && tie && passed == threshold)) {
+ return true;
+ }
+ } else {
+ ++failed;
+ if (failed > threshold || (even && !tie && failed == threshold)) {
+ return false;
+ }
+ }
+ }
+ //dummy
+ return false;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Name.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Name.java
new file mode 100644
index 00000000..50c242a2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Name.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.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.RegularExpression;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+import org.apache.tools.ant.util.regexp.Regexp;
+import org.apache.tools.ant.util.regexp.RegexpUtil;
+
+/**
+ * Name ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Name implements ResourceSelector {
+ private String regex = null;
+ private String pattern;
+ private boolean cs = true;
+ private boolean handleDirSep = false;
+
+ // caches for performance reasons
+ private RegularExpression reg;
+ private Regexp expression;
+
+ private Project project;
+
+ public void setProject(Project p) {
+ project = p;
+ }
+
+ /**
+ * Set the pattern to compare names against.
+ * @param n the pattern String to set.
+ */
+ public void setName(String n) {
+ pattern = n;
+ }
+
+ /**
+ * Get the pattern used by this Name ResourceSelector.
+ * @return the String selection pattern.
+ */
+ public String getName() {
+ return pattern;
+ }
+
+ /**
+ * Set the regular expression to compare names against.
+ * @param r the regex to set.
+ * @since Ant 1.8.0
+ */
+ public void setRegex(String r) {
+ regex = r;
+ reg = null;
+ }
+
+ /**
+ * Get the regular expression used by this Name ResourceSelector.
+ * @return the String selection pattern.
+ * @since Ant 1.8.0
+ */
+ public String getRegex() {
+ return regex;
+ }
+
+ /**
+ * Set whether the name comparisons are case-sensitive.
+ * @param b boolean case-sensitivity flag.
+ */
+ public void setCaseSensitive(boolean b) {
+ cs = b;
+ }
+
+ /**
+ * Learn whether this Name ResourceSelector is case-sensitive.
+ * @return boolean case-sensitivity flag.
+ */
+ public boolean isCaseSensitive() {
+ return cs;
+ }
+
+ /**
+ * Attribute specifying whether to ignore the difference
+ * between / and \ (the two common directory characters).
+ * @param handleDirSep a boolean, default is false.
+ * @since Ant 1.8.0
+ */
+ public void setHandleDirSep(boolean handleDirSep) {
+ this.handleDirSep = handleDirSep;
+ }
+
+ /**
+ * Whether the difference between / and \ (the two common
+ * directory characters) is ignored.
+ *
+ * @since Ant 1.8.0
+ */
+ public boolean doesHandledirSep() {
+ return handleDirSep;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ String n = r.getName();
+ if (matches(n)) {
+ return true;
+ }
+ String s = r.toString();
+ return s.equals(n) ? false : matches(s);
+ }
+
+ private boolean matches(String name) {
+ if (pattern != null) {
+ return SelectorUtils.match(modify(pattern), modify(name), cs);
+ } else {
+ if (reg == null) {
+ reg = new RegularExpression();
+ reg.setPattern(regex);
+ expression = reg.getRegexp(project);
+ }
+ return expression.matches(modify(name), RegexpUtil.asOptions(cs));
+ }
+ }
+
+ private String modify(String s) {
+ if (s == null || !handleDirSep || s.indexOf("\\") == -1) {
+ return s;
+ }
+ return s.replace('\\', '/');
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/None.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/None.java
new file mode 100644
index 00000000..0de86236
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/None.java
@@ -0,0 +1,59 @@
+/*
+ * 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.tools.ant.types.resources.selectors;
+
+import java.util.Iterator;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * None ResourceSelector.
+ * @since Ant 1.7
+ */
+public class None
+ extends ResourceSelectorContainer implements ResourceSelector {
+
+ /**
+ * Default constructor.
+ */
+ public None() {
+ }
+
+ /**
+ * Convenience constructor.
+ * @param r the ResourceSelector[] to add.
+ */
+ public None(ResourceSelector[] r) {
+ super(r);
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ for (Iterator<ResourceSelector> i = getSelectors(); i.hasNext();) {
+ if (i.next().isSelected(r)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Not.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Not.java
new file mode 100644
index 00000000..dc67da1f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Not.java
@@ -0,0 +1,66 @@
+/*
+ * 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.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Not ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Not implements ResourceSelector {
+
+ private ResourceSelector sel;
+
+ /**
+ * Default constructor.
+ */
+ public Not() {
+ }
+
+ /**
+ * Convenience constructor.
+ * @param s the ResourceSelector to negate.
+ */
+ public Not(ResourceSelector s) {
+ add(s);
+ }
+
+ /**
+ * Set the ResourceSelector.
+ * @param s the ResourceSelector to negate.
+ * @throws IllegalStateException if already set.
+ */
+ public void add(ResourceSelector s) {
+ if (sel != null) {
+ throw new IllegalStateException(
+ "The Not ResourceSelector accepts a single nested ResourceSelector");
+ }
+ sel = s;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ return !(sel.isSelected(r));
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Or.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Or.java
new file mode 100644
index 00000000..b22303a4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Or.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.tools.ant.types.resources.selectors;
+
+import java.util.Iterator;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Or ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Or extends ResourceSelectorContainer implements ResourceSelector {
+
+ /**
+ * Default constructor.
+ */
+ public Or() {
+ }
+
+ /**
+ * Convenience constructor.
+ * @param r the ResourceSelector[] to add.
+ */
+ public Or(ResourceSelector[] r) {
+ super(r);
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ for (Iterator<ResourceSelector> i = getSelectors(); i.hasNext();) {
+ if (i.next().isSelected(r)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelector.java
new file mode 100644
index 00000000..37151ecd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelector.java
@@ -0,0 +1,35 @@
+/*
+ * 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.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Interface for a Resource selector.
+ * @since Ant 1.7
+ */
+public interface ResourceSelector {
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ boolean isSelected(Resource r);
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelectorContainer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelectorContainer.java
new file mode 100644
index 00000000..6b1c8002
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelectorContainer.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.tools.ant.types.resources.selectors;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+
+/**
+ * ResourceSelector container.
+ * @since Ant 1.7
+ */
+public class ResourceSelectorContainer extends DataType {
+
+ private final List<ResourceSelector> resourceSelectors = new ArrayList<ResourceSelector>();
+
+ /**
+ * Default constructor.
+ */
+ public ResourceSelectorContainer() {
+ }
+
+ /**
+ * Construct a new ResourceSelectorContainer with the specified array of selectors.
+ * @param r the ResourceSelector[] to add.
+ */
+ public ResourceSelectorContainer(ResourceSelector[] r) {
+ for (int i = 0; i < r.length; i++) {
+ add(r[i]);
+ }
+ }
+
+ /**
+ * Add a ResourceSelector to the container.
+ * @param s the ResourceSelector to add.
+ */
+ public void add(ResourceSelector s) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (s == null) {
+ return;
+ }
+ resourceSelectors.add(s);
+ setChecked(false);
+ }
+
+ /**
+ * Learn whether this ResourceSelectorContainer has selectors.
+ * @return boolean indicating whether selectors have been added to the container.
+ */
+ public boolean hasSelectors() {
+ if (isReference()) {
+ return ((ResourceSelectorContainer) getCheckedRef()).hasSelectors();
+ }
+ dieOnCircularReference();
+ return !resourceSelectors.isEmpty();
+ }
+
+ /**
+ * Get the count of nested selectors.
+ * @return the selector count as int.
+ */
+ public int selectorCount() {
+ if (isReference()) {
+ return ((ResourceSelectorContainer) getCheckedRef()).selectorCount();
+ }
+ dieOnCircularReference();
+ return resourceSelectors.size();
+ }
+
+ /**
+ * Return an Iterator over the nested selectors.
+ * @return Iterator of ResourceSelectors.
+ */
+ public Iterator<ResourceSelector> getSelectors() {
+ if (isReference()) {
+ return ((ResourceSelectorContainer) getCheckedRef()).getSelectors();
+ }
+ dieOnCircularReference();
+ return Collections.unmodifiableList(resourceSelectors).iterator();
+ }
+
+ /**
+ * Overrides the version from DataType to recurse on nested ResourceSelectors.
+ * @param stk the Stack of references.
+ * @param p the Project to resolve against.
+ * @throws BuildException on error.
+ */
+ protected void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ for (ResourceSelector resourceSelector : resourceSelectors) {
+ if (resourceSelector instanceof DataType) {
+ pushAndInvokeCircularReferenceCheck((DataType) resourceSelector, stk, p);
+ }
+ }
+ setChecked(true);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Size.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Size.java
new file mode 100644
index 00000000..4d6c87e2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Size.java
@@ -0,0 +1,73 @@
+/*
+ * 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.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.types.Comparison;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Size ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Size implements ResourceSelector {
+ private long size = -1;
+ private Comparison when = Comparison.EQUAL;
+
+ /**
+ * Set the size to compare against.
+ * @param l the long resource size.
+ */
+ public void setSize(long l) {
+ size = l;
+ }
+
+ /**
+ * Get the size compared to by this Size ResourceSelector.
+ * @return the long resource size.
+ */
+ public long getSize() {
+ return size;
+ }
+
+ /**
+ * Set the comparison mode.
+ * @param c a Comparison object.
+ */
+ public void setWhen(Comparison c) {
+ when = c;
+ }
+
+ /**
+ * Get the comparison mode.
+ * @return a Comparison object.
+ */
+ public Comparison getWhen() {
+ return when;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ long diff = r.getSize() - size;
+ return when.evaluate(diff == 0 ? 0 : (int) (diff / Math.abs(diff)));
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Type.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Type.java
new file mode 100644
index 00000000..65d4a577
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/resources/selectors/Type.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.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Type file/dir ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Type implements ResourceSelector {
+
+ private static final String FILE_ATTR = "file";
+ private static final String DIR_ATTR = "dir";
+ private static final String ANY_ATTR = "any";
+
+ /** Static file type selector. */
+ public static final Type FILE = new Type(new FileDir(FILE_ATTR));
+
+ /** Static dir type selector. */
+ public static final Type DIR = new Type(new FileDir(DIR_ATTR));
+
+ /** Static any type selector. Since Ant 1.8. */
+ public static final Type ANY = new Type(new FileDir(ANY_ATTR));
+
+ /**
+ * Implements the type attribute.
+ */
+ public static class FileDir extends EnumeratedAttribute {
+ private static final String[] VALUES = new String[] {FILE_ATTR, DIR_ATTR, ANY_ATTR};
+
+ /**
+ * Default constructor.
+ */
+ public FileDir() {
+ }
+
+ /**
+ * Convenience constructor.
+ * @param value the String EnumeratedAttribute value.
+ */
+ public FileDir(final String value) {
+ setValue(value);
+ }
+
+ /**
+ * Return the possible values.
+ * @return a String array.
+ */
+ @Override
+ public String[] getValues() {
+ return VALUES;
+ }
+ }
+
+ private FileDir type = null;
+
+ /**
+ * Default constructor.
+ */
+ public Type() {
+ }
+
+ /**
+ * Convenience constructor.
+ * @param fd the FileDir type.
+ */
+ public Type(final FileDir fd) {
+ setType(fd);
+ }
+
+ /**
+ * Set type; file|dir.
+ * @param fd a FileDir object.
+ */
+ public void setType(final FileDir fd) {
+ type = fd;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(final Resource r) {
+ if (type == null) {
+ throw new BuildException("The type attribute is required.");
+ }
+ final int i = type.getIndex();
+ return i == 2 || (r.isDirectory() ? i == 1 : i == 0);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/AbstractSelectorContainer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/AbstractSelectorContainer.java
new file mode 100644
index 00000000..b80816da
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/AbstractSelectorContainer.java
@@ -0,0 +1,353 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.util.Enumeration;
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector;
+
+/**
+ * This is the a base class a container of selectors - it does
+ * not need do be a selector itself.
+ *
+ * @since 1.7
+ */
+public abstract class AbstractSelectorContainer extends DataType
+ implements Cloneable, SelectorContainer {
+
+ private Vector<FileSelector> selectorsList = new Vector<FileSelector>();
+
+ /**
+ * Indicates whether there are any selectors here.
+ * @return true if there are selectors
+ */
+ public boolean hasSelectors() {
+ if (isReference()) {
+ return ((AbstractSelectorContainer) getCheckedRef()).hasSelectors();
+ }
+ dieOnCircularReference();
+ return !(selectorsList.isEmpty());
+ }
+
+ /**
+ * Gives the count of the number of selectors in this container
+ * @return the number of selectors
+ */
+ public int selectorCount() {
+ if (isReference()) {
+ return ((AbstractSelectorContainer) getCheckedRef()).selectorCount();
+ }
+ dieOnCircularReference();
+ return selectorsList.size();
+ }
+
+ /**
+ * Returns the set of selectors as an array.
+ * @param p the current project
+ * @return an array of selectors
+ */
+ public FileSelector[] getSelectors(Project p) {
+ if (isReference()) {
+ return ((AbstractSelectorContainer) getCheckedRef(p))
+ .getSelectors(p);
+ }
+ dieOnCircularReference(p);
+ FileSelector[] result = new FileSelector[selectorsList.size()];
+ selectorsList.copyInto(result);
+ return result;
+ }
+
+ /**
+ * Returns an enumerator for accessing the set of selectors.
+ * @return an enumerator for the selectors
+ */
+ public Enumeration<FileSelector> selectorElements() {
+ if (isReference()) {
+ return ((AbstractSelectorContainer) getCheckedRef())
+ .selectorElements();
+ }
+ dieOnCircularReference();
+ return selectorsList.elements();
+ }
+
+ /**
+ * Convert the Selectors within this container to a string. This will
+ * just be a helper class for the subclasses that put their own name
+ * around the contents listed here.
+ *
+ * @return comma separated list of Selectors contained in this one
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ Enumeration<FileSelector> e = selectorElements();
+ if (e.hasMoreElements()) {
+ while (e.hasMoreElements()) {
+ buf.append(e.nextElement().toString());
+ if (e.hasMoreElements()) {
+ buf.append(", ");
+ }
+ }
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Add a new selector into this container.
+ *
+ * @param selector the new selector to add
+ */
+ public void appendSelector(FileSelector selector) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ selectorsList.addElement(selector);
+ setChecked(false);
+ }
+
+ /**
+ * <p>
+ * This validates each contained selector
+ * provided that the selector implements the validate interface.
+ * </p>
+ * <p>Ordinarily, this will validate all the elements of a selector
+ * container even if the isSelected() method of some elements is
+ * never called. This has two effects:</p>
+ * <ul>
+ * <li>Validation will often occur twice.
+ * <li>Since it is not required that selectors derive from
+ * BaseSelector, there could be selectors in the container whose
+ * error conditions are not detected if their isSelected() call
+ * is never made.
+ * </ul>
+ */
+ public void validate() {
+ if (isReference()) {
+ ((AbstractSelectorContainer) getCheckedRef()).validate();
+ }
+ dieOnCircularReference();
+ Enumeration<FileSelector> e = selectorElements();
+ while (e.hasMoreElements()) {
+ Object o = e.nextElement();
+ if (o instanceof BaseSelector) {
+ ((BaseSelector) o).validate();
+ }
+ }
+ }
+
+
+ /* Methods below all add specific selectors */
+
+ /**
+ * add a "Select" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addSelector(SelectSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add an "And" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addAnd(AndSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add an "Or" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addOr(OrSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a "Not" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addNot(NotSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a "None" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addNone(NoneSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a majority selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addMajority(MajoritySelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a selector date entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addDate(DateSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a selector size entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addSize(SizeSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a selector filename entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addFilename(FilenameSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add an extended selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addCustom(ExtendSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a contains selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addContains(ContainsSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a present selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addPresent(PresentSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a depth selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addDepth(DepthSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a depends selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addDepend(DependSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * adds a different selector to the selector list
+ * @param selector the selector to add
+ */
+ public void addDifferent(DifferentSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * adds a type selector to the selector list
+ * @param selector the selector to add
+ */
+ public void addType(TypeSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a regular expression selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addContainsRegexp(ContainsRegexpSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add the modified selector
+ * @param selector the selector to add
+ * @since ant 1.6
+ */
+ public void addModified(ModifiedSelector selector) {
+ appendSelector(selector);
+ }
+
+ public void addReadable(ReadableSelector r) {
+ appendSelector(r);
+ }
+
+ public void addWritable(WritableSelector w) {
+ appendSelector(w);
+ }
+
+ /**
+ * add an arbitrary selector
+ * @param selector the selector to add
+ * @since Ant 1.6
+ */
+ public void add(FileSelector selector) {
+ appendSelector(selector);
+ }
+
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p) {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ for (FileSelector fileSelector : selectorsList) {
+ if (fileSelector instanceof DataType) {
+ pushAndInvokeCircularReferenceCheck((DataType) fileSelector, stk, p);
+ }
+ }
+ setChecked(true);
+ }
+ }
+
+ public synchronized Object clone() {
+ if (isReference()) {
+ return ((AbstractSelectorContainer) getCheckedRef()).clone();
+ }
+ try {
+ AbstractSelectorContainer sc =
+ (AbstractSelectorContainer) super.clone();
+ sc.selectorsList = new Vector<FileSelector>(selectorsList);
+ return sc;
+ } catch (CloneNotSupportedException e) {
+ throw new BuildException(e);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/AndSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/AndSelector.java
new file mode 100644
index 00000000..c8e96a04
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/AndSelector.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.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.Enumeration;
+
+/**
+ * This selector has a collection of other selectors, all of which have to
+ * select a file in order for this selector to select it.
+ *
+ * @since 1.5
+ */
+public class AndSelector extends BaseSelectorContainer {
+
+ /**
+ * Default constructor.
+ */
+ public AndSelector() {
+ }
+
+ /**
+ * @return a string representation of the selector
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ if (hasSelectors()) {
+ buf.append("{andselect: ");
+ buf.append(super.toString());
+ buf.append("}");
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Returns true (the file is selected) only if all other selectors
+ * agree that the file should be selected.
+ *
+ * @param basedir the base directory the scan is being done from
+ * @param filename the name of the file to check
+ * @param file a java.io.File object for the filename that the selector
+ * can use
+ * @return whether the file should be selected or not
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+ validate();
+ Enumeration<FileSelector> e = selectorElements();
+
+ while (e.hasMoreElements()) {
+ if (!e.nextElement().isSelected(basedir, filename, file)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/BaseExtendSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/BaseExtendSelector.java
new file mode 100644
index 00000000..f17ca027
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/BaseExtendSelector.java
@@ -0,0 +1,87 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Convenience base class for all selectors accessed through ExtendSelector.
+ * It provides support for gathering the parameters together as well as for
+ * assigning an error message and throwing a build exception if an error is
+ * detected.
+ *
+ * @since 1.5
+ */
+public abstract class BaseExtendSelector
+ extends BaseSelector
+ implements ExtendFileSelector {
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /** The passed in parameter array. */
+ protected Parameter[] parameters = null;
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Default constructor.
+ */
+ public BaseExtendSelector() {
+ }
+
+ /**
+ * Set all the Parameters for this custom selector, collected by
+ * the ExtendSelector class.
+ *
+ * @param parameters the complete set of parameters for this selector
+ */
+ public void setParameters(Parameter[] parameters) {
+ this.parameters = parameters;
+ }
+
+ /**
+ * Allows access to the parameters gathered and set within the
+ * &lt;custom&gt; tag.
+ *
+ * @return the set of parameters defined for this selector
+ */
+ protected Parameter[] getParameters() {
+ return parameters;
+ }
+
+ /**
+ * Method that each selector will implement to create their
+ * selection behaviour. If there is a problem with the setup
+ * of a selector, it can throw a BuildException to indicate
+ * the problem.
+ *
+ * @param basedir A java.io.File object for the base directory
+ * @param filename The name of the file to check
+ * @param file A File object for this filename
+ * @return whether the file should be selected or not
+ * @exception BuildException if an error occurs
+ */
+ public abstract boolean isSelected(File basedir, String filename,
+ File file)
+ throws BuildException;
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/BaseSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/BaseSelector.java
new file mode 100644
index 00000000..61d7a1a2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/BaseSelector.java
@@ -0,0 +1,113 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.DataType;
+
+/**
+ * A convenience base class that you can subclass Selectors from. It
+ * provides some helpful common behaviour. Note that there is no need
+ * for Selectors to inherit from this class, it is only necessary that
+ * they implement FileSelector.
+ *
+ * @since 1.5
+ */
+public abstract class BaseSelector extends DataType implements FileSelector {
+
+ private String errmsg = null;
+
+
+ /**
+ * Do nothing constructor.
+ */
+ public BaseSelector() {
+ }
+
+ /**
+ * Allows all selectors to indicate a setup error. Note that only
+ * the first error message is recorded.
+ *
+ * @param msg The error message any BuildException should throw.
+ */
+ public void setError(String msg) {
+ if (errmsg == null) {
+ errmsg = msg;
+ }
+ }
+
+ /**
+ * Returns any error messages that have been set.
+ *
+ * @return the error condition
+ */
+ public String getError() {
+ return errmsg;
+ }
+
+
+ /**
+ * <p>Subclasses can override this method to provide checking of their
+ * state. So long as they call validate() from isSelected(), this will
+ * be called automatically (unless they override validate()).</p>
+ * <p>Implementations should check for incorrect settings and call
+ * setError() as necessary.</p>
+ */
+ public void verifySettings() {
+ if (isReference()) {
+ ((BaseSelector) getCheckedRef()).verifySettings();
+ }
+ }
+
+
+ /**
+ * Subclasses can use this to throw the requisite exception
+ * in isSelected() in the case of an error condition.
+ */
+ public void validate() {
+ if (getError() == null) {
+ verifySettings();
+ }
+ if (getError() != null) {
+ throw new BuildException(errmsg);
+ }
+ if (!isReference()) {
+ dieOnCircularReference();
+ }
+ }
+
+ /**
+ * Method that each selector will implement to create their
+ * selection behaviour. If there is a problem with the setup
+ * of a selector, it can throw a BuildException to indicate
+ * the problem.
+ *
+ * @param basedir A java.io.File object for the base directory
+ * @param filename The name of the file to check
+ * @param file A File object for this filename
+ * @return whether the file should be selected or not
+ */
+ public abstract boolean isSelected(File basedir, String filename,
+ File file);
+
+}
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/BaseSelectorContainer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/BaseSelectorContainer.java
new file mode 100644
index 00000000..1edf0857
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/BaseSelectorContainer.java
@@ -0,0 +1,343 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector;
+
+/**
+ * This is the base class for selectors that can contain other selectors.
+ *
+ * @since 1.5
+ */
+public abstract class BaseSelectorContainer extends BaseSelector
+ implements SelectorContainer {
+
+ private Vector<FileSelector> selectorsList = new Vector<FileSelector>();
+
+ /**
+ * Default constructor.
+ */
+ public BaseSelectorContainer() {
+ }
+
+ /**
+ * Indicates whether there are any selectors here.
+ * @return true if there are selectors
+ */
+ public boolean hasSelectors() {
+ dieOnCircularReference();
+ return !(selectorsList.isEmpty());
+ }
+
+ /**
+ * Gives the count of the number of selectors in this container
+ * @return the number of selectors
+ */
+ public int selectorCount() {
+ dieOnCircularReference();
+ return selectorsList.size();
+ }
+
+ /**
+ * Returns the set of selectors as an array.
+ * @param p the current project
+ * @return an array of selectors
+ */
+ public FileSelector[] getSelectors(Project p) {
+ dieOnCircularReference();
+ FileSelector[] result = new FileSelector[selectorsList.size()];
+ selectorsList.copyInto(result);
+ return result;
+ }
+
+ /**
+ * Returns an enumerator for accessing the set of selectors.
+ * @return an enumerator for the selectors
+ */
+ public Enumeration<FileSelector> selectorElements() {
+ dieOnCircularReference();
+ return selectorsList.elements();
+ }
+
+ /**
+ * Convert the Selectors within this container to a string. This will
+ * just be a helper class for the subclasses that put their own name
+ * around the contents listed here.
+ *
+ * @return comma separated list of Selectors contained in this one
+ */
+ public String toString() {
+ dieOnCircularReference();
+ StringBuilder buf = new StringBuilder();
+ Enumeration<FileSelector> e = selectorElements();
+ while (e.hasMoreElements()) {
+ buf.append(e.nextElement().toString());
+ if (e.hasMoreElements()) {
+ buf.append(", ");
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Add a new selector into this container.
+ *
+ * @param selector the new selector to add
+ */
+ public void appendSelector(FileSelector selector) {
+ selectorsList.addElement(selector);
+ setChecked(false);
+ }
+
+ /**
+ * <p>This implementation validates the container by calling
+ * verifySettings() and then validates each contained selector
+ * provided that the selector implements the validate interface.
+ * </p>
+ * <p>Ordinarily, this will validate all the elements of a selector
+ * container even if the isSelected() method of some elements is
+ * never called. This has two effects:</p>
+ * <ul>
+ * <li>Validation will often occur twice.
+ * <li>Since it is not required that selectors derive from
+ * BaseSelector, there could be selectors in the container whose
+ * error conditions are not detected if their isSelected() call
+ * is never made.
+ * </ul>
+ */
+ public void validate() {
+ verifySettings();
+ dieOnCircularReference();
+ String errmsg = getError();
+ if (errmsg != null) {
+ throw new BuildException(errmsg);
+ }
+ Enumeration<FileSelector> e = selectorElements();
+ while (e.hasMoreElements()) {
+ Object o = e.nextElement();
+ if (o instanceof BaseSelector) {
+ ((BaseSelector) o).validate();
+ }
+ }
+ }
+
+
+ /**
+ * Method that each selector will implement to create their selection
+ * behaviour. This is what makes SelectorContainer abstract.
+ *
+ * @param basedir the base directory the scan is being done from
+ * @param filename the name of the file to check
+ * @param file a java.io.File object for the filename that the selector
+ * can use
+ * @return whether the file should be selected or not
+ */
+ public abstract boolean isSelected(File basedir, String filename,
+ File file);
+
+
+ /* Methods below all add specific selectors */
+
+ /**
+ * add a "Select" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addSelector(SelectSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add an "And" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addAnd(AndSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add an "Or" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addOr(OrSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a "Not" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addNot(NotSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a "None" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addNone(NoneSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a majority selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addMajority(MajoritySelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a selector date entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addDate(DateSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a selector size entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addSize(SizeSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a selector filename entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addFilename(FilenameSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add an extended selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addCustom(ExtendSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a contains selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addContains(ContainsSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a present selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addPresent(PresentSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a depth selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addDepth(DepthSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a depends selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addDepend(DependSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * adds a different selector to the selector list
+ * @param selector the selector to add
+ */
+ public void addDifferent(DifferentSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * adds a type selector to the selector list
+ * @param selector the selector to add
+ */
+ public void addType(TypeSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a regular expression selector entry on the selector list
+ * @param selector the selector to add
+ */
+ public void addContainsRegexp(ContainsRegexpSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add the modified selector
+ * @param selector the selector to add
+ * @since ant 1.6
+ */
+ public void addModified(ModifiedSelector selector) {
+ appendSelector(selector);
+ }
+
+ public void addReadable(ReadableSelector r) {
+ appendSelector(r);
+ }
+
+ public void addWritable(WritableSelector w) {
+ appendSelector(w);
+ }
+
+ /**
+ * add an arbitrary selector
+ * @param selector the selector to add
+ * @since Ant 1.6
+ */
+ public void add(FileSelector selector) {
+ appendSelector(selector);
+ }
+
+ protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ for (FileSelector fileSelector : selectorsList) {
+ if (fileSelector instanceof DataType) {
+ pushAndInvokeCircularReferenceCheck((DataType) fileSelector, stk, p);
+ }
+ }
+ setChecked(true);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ContainsRegexpSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ContainsRegexpSelector.java
new file mode 100644
index 00000000..4da3b6ff
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ContainsRegexpSelector.java
@@ -0,0 +1,219 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.RegularExpression;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+import org.apache.tools.ant.util.regexp.Regexp;
+import org.apache.tools.ant.util.regexp.RegexpUtil;
+
+/**
+ * Selector that filters files based on a regular expression.
+ *
+ * @since Ant 1.6
+ */
+public class ContainsRegexpSelector extends BaseExtendSelector
+ implements ResourceSelector {
+
+ private String userProvidedExpression = null;
+ private RegularExpression myRegExp = null;
+ private Regexp myExpression = null;
+ private boolean caseSensitive = true;
+ private boolean multiLine = false;
+ private boolean singleLine = false;
+ /** Key to used for parameterized custom selector */
+ public static final String EXPRESSION_KEY = "expression";
+ /** Parameter name for the casesensitive attribute. */
+ private static final String CS_KEY = "casesensitive";
+ /** Parameter name for the multiline attribute. */
+ private static final String ML_KEY = "multiline";
+ /** Parameter name for the singleline attribute. */
+ private static final String SL_KEY = "singleline";
+
+ /**
+ * Creates a new <code>ContainsRegexpSelector</code> instance.
+ */
+ public ContainsRegexpSelector() {
+ }
+
+ /**
+ * @return a string describing this object
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder(
+ "{containsregexpselector expression: ");
+ buf.append(userProvidedExpression);
+ buf.append("}");
+ return buf.toString();
+ }
+
+ /**
+ * The regular expression used to search the file.
+ *
+ * @param theexpression this must match a line in the file to be selected.
+ */
+ public void setExpression(String theexpression) {
+ this.userProvidedExpression = theexpression;
+ }
+
+ /**
+ * Whether to ignore case or not.
+ * @param b if false, ignore case.
+ * @since Ant 1.8.2
+ */
+ public void setCaseSensitive(boolean b) {
+ caseSensitive = b;
+ }
+
+ /**
+ * Whether to match should be multiline.
+ * @param b the value to set.
+ * @since Ant 1.8.2
+ */
+ public void setMultiLine(boolean b) {
+ multiLine = b;
+ }
+
+ /**
+ * Whether to treat input as singleline ('.' matches newline).
+ * Corresponds to java.util.regex.Pattern.DOTALL.
+ * @param b the value to set.
+ * @since Ant 1.8.2
+ */
+ public void setSingleLine(boolean b) {
+ singleLine = b;
+ }
+
+ /**
+ * When using this as a custom selector, this method will be called.
+ * It translates each parameter into the appropriate setXXX() call.
+ *
+ * @param parameters the complete set of parameters for this selector
+ */
+ public void setParameters(Parameter[] parameters) {
+ super.setParameters(parameters);
+ if (parameters != null) {
+ for (int i = 0; i < parameters.length; i++) {
+ String paramname = parameters[i].getName();
+ if (EXPRESSION_KEY.equalsIgnoreCase(paramname)) {
+ setExpression(parameters[i].getValue());
+ } else if (CS_KEY.equalsIgnoreCase(paramname)) {
+ setCaseSensitive(Project
+ .toBoolean(parameters[i].getValue()));
+ } else if (ML_KEY.equalsIgnoreCase(paramname)) {
+ setMultiLine(Project.toBoolean(parameters[i].getValue()));
+ } else if (SL_KEY.equalsIgnoreCase(paramname)) {
+ setSingleLine(Project.toBoolean(parameters[i].getValue()));
+ } else {
+ setError("Invalid parameter " + paramname);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks that an expression was specified.
+ *
+ */
+ public void verifySettings() {
+ if (userProvidedExpression == null) {
+ setError("The expression attribute is required");
+ }
+ }
+
+ /**
+ * Tests a regular expression against each line of text in the file.
+ *
+ * @param basedir the base directory the scan is being done from
+ * @param filename is the name of the file to check
+ * @param file is a java.io.File object the selector can use
+ * @return whether the file should be selected or not
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+ return isSelected(new FileResource(file));
+ }
+
+ /**
+ * Tests a regular expression against each line of text in a Resource.
+ *
+ * @param r the Resource to check.
+ * @return whether the Resource is selected or not
+ */
+ public boolean isSelected(Resource r) {
+ String teststr = null;
+ BufferedReader in = null;
+
+ // throw BuildException on error
+
+ validate();
+
+ if (r.isDirectory()) {
+ return true;
+ }
+
+ if (myRegExp == null) {
+ myRegExp = new RegularExpression();
+ myRegExp.setPattern(userProvidedExpression);
+ myExpression = myRegExp.getRegexp(getProject());
+ }
+
+ try {
+ in = new BufferedReader(new InputStreamReader(r.getInputStream()));
+ } catch (Exception e) {
+ throw new BuildException("Could not get InputStream from "
+ + r.toLongString(), e);
+ }
+ try {
+ teststr = in.readLine();
+
+ while (teststr != null) {
+
+ if (myExpression.matches(teststr,
+ RegexpUtil.asOptions(caseSensitive,
+ multiLine,
+ singleLine))) {
+ return true;
+ }
+ teststr = in.readLine();
+ }
+
+ return false;
+ } catch (IOException ioe) {
+ throw new BuildException("Could not read " + r.toLongString());
+ } finally {
+ try {
+ in.close();
+ } catch (Exception e) {
+ throw new BuildException("Could not close "
+ + r.toLongString());
+ }
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ContainsSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ContainsSelector.java
new file mode 100644
index 00000000..6dabaf4c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ContainsSelector.java
@@ -0,0 +1,221 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Selector that filters files/resources based on whether they contain a
+ * particular string.
+ *
+ * @since 1.5
+ */
+public class ContainsSelector extends BaseExtendSelector implements ResourceSelector {
+
+ private String contains = null;
+ private boolean casesensitive = true;
+ private boolean ignorewhitespace = false;
+ private String encoding = null;
+ /** Key to used for parameterized custom selector */
+ public static final String EXPRESSION_KEY = "expression";
+ /** Used for parameterized custom selector */
+ public static final String CONTAINS_KEY = "text";
+ /** Used for parameterized custom selector */
+ public static final String CASE_KEY = "casesensitive";
+ /** Used for parameterized custom selector */
+ public static final String WHITESPACE_KEY = "ignorewhitespace";
+
+
+ /**
+ * Creates a new <code>ContainsSelector</code> instance.
+ *
+ */
+ public ContainsSelector() {
+ }
+
+ /**
+ * @return a string describing this object
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder("{containsselector text: ");
+ buf.append('"').append(contains).append('"');
+ buf.append(" casesensitive: ");
+ buf.append(casesensitive ? "true" : "false");
+ buf.append(" ignorewhitespace: ");
+ buf.append(ignorewhitespace ? "true" : "false");
+ buf.append("}");
+ return buf.toString();
+ }
+
+ /**
+ * The string to search for within a file.
+ *
+ * @param contains the string that a file must contain to be selected.
+ */
+ public void setText(String contains) {
+ this.contains = contains;
+ }
+
+ /**
+ * The encoding of the resources processed
+ * @since Ant 1.9.0
+ * @param encoding encoding of the resources processed
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Whether to ignore case in the string being searched.
+ *
+ * @param casesensitive whether to pay attention to case sensitivity
+ */
+ public void setCasesensitive(boolean casesensitive) {
+ this.casesensitive = casesensitive;
+ }
+
+ /**
+ * Whether to ignore whitespace in the string being searched.
+ *
+ * @param ignorewhitespace whether to ignore any whitespace
+ * (spaces, tabs, etc.) in the searchstring
+ */
+ public void setIgnorewhitespace(boolean ignorewhitespace) {
+ this.ignorewhitespace = ignorewhitespace;
+ }
+
+ /**
+ * When using this as a custom selector, this method will be called.
+ * It translates each parameter into the appropriate setXXX() call.
+ *
+ * @param parameters the complete set of parameters for this selector
+ */
+ public void setParameters(Parameter[] parameters) {
+ super.setParameters(parameters);
+ if (parameters != null) {
+ for (int i = 0; i < parameters.length; i++) {
+ String paramname = parameters[i].getName();
+ if (CONTAINS_KEY.equalsIgnoreCase(paramname)) {
+ setText(parameters[i].getValue());
+ } else if (CASE_KEY.equalsIgnoreCase(paramname)) {
+ setCasesensitive(Project.toBoolean(
+ parameters[i].getValue()));
+ } else if (WHITESPACE_KEY.equalsIgnoreCase(paramname)) {
+ setIgnorewhitespace(Project.toBoolean(
+ parameters[i].getValue()));
+ } else {
+ setError("Invalid parameter " + paramname);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks to make sure all settings are kosher. In this case, it
+ * means that the pattern attribute has been set.
+ *
+ */
+ public void verifySettings() {
+ if (contains == null) {
+ setError("The text attribute is required");
+ }
+ }
+
+ /**
+ * The heart of the matter. This is where the selector gets to decide
+ * on the inclusion of a file in a particular fileset.
+ *
+ * @param basedir the base directory the scan is being done from
+ * @param filename is the name of the file to check
+ * @param file is a java.io.File object the selector can use
+ * @return whether the file should be selected or not
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+ return isSelected(new FileResource(file));
+ }
+
+ /**
+ * The heart of the matter. This is where the selector gets to decide
+ * on the inclusion of a Resource.
+ *
+ * @param r the Resource to check.
+ * @return whether the Resource is selected.
+ */
+ public boolean isSelected(Resource r) {
+
+ // throw BuildException on error
+ validate();
+
+ if (r.isDirectory() || contains.length() == 0) {
+ return true;
+ }
+
+ String userstr = contains;
+ if (!casesensitive) {
+ userstr = contains.toLowerCase();
+ }
+ if (ignorewhitespace) {
+ userstr = SelectorUtils.removeWhitespace(userstr);
+ }
+ BufferedReader in = null;
+ try {
+ if (encoding != null) {
+ in = new BufferedReader(new InputStreamReader(r.getInputStream(), encoding));
+ } else {
+ in = new BufferedReader(new InputStreamReader(r.getInputStream()));
+ }
+ } catch (Exception e) {
+ throw new BuildException("Could not get InputStream from "
+ + r.toLongString(), e);
+ }
+ try {
+ String teststr = in.readLine();
+ while (teststr != null) {
+ if (!casesensitive) {
+ teststr = teststr.toLowerCase();
+ }
+ if (ignorewhitespace) {
+ teststr = SelectorUtils.removeWhitespace(teststr);
+ }
+ if (teststr.indexOf(userstr) > -1) {
+ return true;
+ }
+ teststr = in.readLine();
+ }
+ return false;
+ } catch (IOException ioe) {
+ throw new BuildException("Could not read " + r.toLongString());
+ } finally {
+ FileUtils.close(in);
+ }
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DateSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DateSelector.java
new file mode 100644
index 00000000..aea94a8b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DateSelector.java
@@ -0,0 +1,260 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.TimeComparison;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Selector that chooses files based on their last modified date.
+ *
+ * @since 1.5
+ */
+public class DateSelector extends BaseExtendSelector {
+
+ /** Utilities used for file operations */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private long millis = -1;
+ private String dateTime = null;
+ private boolean includeDirs = false;
+ private long granularity = 0;
+ private String pattern;
+ private TimeComparison when = TimeComparison.EQUAL;
+
+ /** Key to used for parameterized custom selector */
+ public static final String MILLIS_KEY = "millis";
+ /** Key to used for parameterized custom selector */
+ public static final String DATETIME_KEY = "datetime";
+ /** Key to used for parameterized custom selector */
+ public static final String CHECKDIRS_KEY = "checkdirs";
+ /** Key to used for parameterized custom selector */
+ public static final String GRANULARITY_KEY = "granularity";
+ /** Key to used for parameterized custom selector */
+ public static final String WHEN_KEY = "when";
+ /** Key to used for parameterized custom selector */
+ public static final String PATTERN_KEY = "pattern";
+
+ /**
+ * Creates a new <code>DateSelector</code> instance.
+ *
+ */
+ public DateSelector() {
+ granularity = FILE_UTILS.getFileTimestampGranularity();
+ }
+
+ /**
+ * @return a string describing this object
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder("{dateselector date: ");
+ buf.append(dateTime);
+ buf.append(" compare: ").append(when.getValue());
+ buf.append(" granularity: ");
+ buf.append(granularity);
+ if (pattern != null) {
+ buf.append(" pattern: ").append(pattern);
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+
+ /**
+ * Set the time; for users who prefer to express time in ms since 1970.
+ *
+ * @param millis the time to compare file's last modified date to,
+ * expressed in milliseconds.
+ */
+ public void setMillis(long millis) {
+ this.millis = millis;
+ }
+
+ /**
+ * Returns the millisecond value the selector is set for.
+ * @return the millisecond value.
+ */
+ public long getMillis() {
+ if (dateTime != null) {
+ validate();
+ }
+ return millis;
+ }
+
+ /**
+ * Sets the date. The user must supply it in MM/DD/YYYY HH:MM AM_PM format,
+ * unless an alternate pattern is specified via the pattern attribute.
+ *
+ * @param dateTime a formatted date <code>String</code>.
+ */
+ public void setDatetime(String dateTime) {
+ this.dateTime = dateTime;
+ millis = -1;
+ }
+
+ /**
+ * Set whether to check dates on directories.
+ *
+ * @param includeDirs whether to check the timestamp on directories.
+ */
+ public void setCheckdirs(boolean includeDirs) {
+ this.includeDirs = includeDirs;
+ }
+
+ /**
+ * Sets the number of milliseconds leeway we will give before we consider
+ * a file not to have matched a date.
+ * @param granularity the number of milliseconds leeway.
+ */
+ public void setGranularity(int granularity) {
+ this.granularity = granularity;
+ }
+
+ /**
+ * Sets the type of comparison to be done on the file's last modified
+ * date.
+ *
+ * @param tcmp The comparison to perform, an EnumeratedAttribute.
+ */
+ public void setWhen(TimeComparisons tcmp) {
+ setWhen((TimeComparison) tcmp);
+ }
+
+ /**
+ * Set the comparison type.
+ * @param t TimeComparison object.
+ */
+ public void setWhen(TimeComparison t) {
+ when = t;
+ }
+
+ /**
+ * Sets the pattern to be used for the SimpleDateFormat.
+ *
+ * @param pattern the pattern that defines the date format.
+ */
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+
+ /**
+ * When using this as a custom selector, this method will be called.
+ * It translates each parameter into the appropriate setXXX() call.
+ *
+ * @param parameters the complete set of parameters for this selector.
+ */
+ public void setParameters(Parameter[] parameters) {
+ super.setParameters(parameters);
+ if (parameters != null) {
+ for (int i = 0; i < parameters.length; i++) {
+ String paramname = parameters[i].getName();
+ if (MILLIS_KEY.equalsIgnoreCase(paramname)) {
+ try {
+ setMillis(Long.parseLong(parameters[i].getValue()));
+ } catch (NumberFormatException nfe) {
+ setError("Invalid millisecond setting "
+ + parameters[i].getValue());
+ }
+ } else if (DATETIME_KEY.equalsIgnoreCase(paramname)) {
+ setDatetime(parameters[i].getValue());
+ } else if (CHECKDIRS_KEY.equalsIgnoreCase(paramname)) {
+ setCheckdirs(Project.toBoolean(parameters[i].getValue()));
+ } else if (GRANULARITY_KEY.equalsIgnoreCase(paramname)) {
+ try {
+ setGranularity(Integer.parseInt(parameters[i].getValue()));
+ } catch (NumberFormatException nfe) {
+ setError("Invalid granularity setting "
+ + parameters[i].getValue());
+ }
+ } else if (WHEN_KEY.equalsIgnoreCase(paramname)) {
+ setWhen(new TimeComparison(parameters[i].getValue()));
+ } else if (PATTERN_KEY.equalsIgnoreCase(paramname)) {
+ setPattern(parameters[i].getValue());
+ } else {
+ setError("Invalid parameter " + paramname);
+ }
+ }
+ }
+ }
+
+ /**
+ * This is a consistency check to ensure the selector's required
+ * values have been set.
+ */
+ public void verifySettings() {
+ if (dateTime == null && millis < 0) {
+ setError("You must provide a datetime or the number of "
+ + "milliseconds.");
+ } else if (millis < 0 && dateTime != null) {
+ // check millis and only set it once.
+ DateFormat df = ((pattern == null)
+ ? DateFormat.getDateTimeInstance(
+ DateFormat.SHORT, DateFormat.SHORT, Locale.US)
+ : new SimpleDateFormat(pattern));
+
+ try {
+ setMillis(df.parse(dateTime).getTime());
+ if (millis < 0) {
+ setError("Date of " + dateTime
+ + " results in negative milliseconds value"
+ + " relative to epoch (January 1, 1970, 00:00:00 GMT).");
+ }
+ } catch (ParseException pe) {
+ setError("Date of " + dateTime
+ + " Cannot be parsed correctly. It should be in"
+ + ((pattern == null)
+ ? " MM/DD/YYYY HH:MM AM_PM" : pattern) + " format.");
+ }
+ }
+ }
+
+ /**
+ * The heart of the matter. This is where the selector gets to decide
+ * on the inclusion of a file in a particular fileset.
+ *
+ * @param basedir the base directory from which the scan is being performed.
+ * @param filename is the name of the file to check.
+ * @param file is a java.io.File object the selector can use.
+ * @return whether the file is selected.
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+
+ validate();
+
+ return (file.isDirectory() && !includeDirs)
+ || when.evaluate(file.lastModified(), millis, granularity);
+ }
+
+ /**
+ * Enumerated attribute with the values for time comparison.
+ * <p>
+ */
+ public static class TimeComparisons extends TimeComparison {
+ }
+
+}
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DependSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DependSelector.java
new file mode 100644
index 00000000..01ac2379
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DependSelector.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.tools.ant.types.selectors;
+
+import java.io.File;
+
+/**
+ * Selector that filters files based on whether they are newer than
+ * a matching file in another directory tree. It can contain a mapper
+ * element, so isn't available as an ExtendSelector (since those
+ * parameters can't hold other elements).
+ *
+ * @since 1.5
+ */
+public class DependSelector extends MappingSelector {
+
+ /**
+ * Creates a new <code>DependSelector</code> instance.
+ *
+ */
+ public DependSelector() {
+
+ }
+
+ /**
+ * @return a string describing this object
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder("{dependselector targetdir: ");
+ if (targetdir == null) {
+ buf.append("NOT YET SET");
+ } else {
+ buf.append(targetdir.getName());
+ }
+ buf.append(" granularity: ");
+ buf.append(granularity);
+ if (map != null) {
+ buf.append(" mapper: ");
+ buf.append(map.toString());
+ } else if (mapperElement != null) {
+ buf.append(" mapper: ");
+ buf.append(mapperElement.toString());
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+
+
+ /**
+ * this test is our selection test that compared the file with the destfile
+ * @param srcfile the source file
+ * @param destfile the destination file
+ * @return true if destination is out of date
+ */
+ public boolean selectionTest(File srcfile, File destfile) {
+ boolean selected = SelectorUtils.isOutOfDate(srcfile, destfile,
+ granularity);
+ return selected;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DepthSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DepthSelector.java
new file mode 100644
index 00000000..a80f9aa9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DepthSelector.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.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Selector that filters files based on the how deep in the directory
+ * tree they are.
+ *
+ * @since 1.5
+ */
+public class DepthSelector extends BaseExtendSelector {
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /** min attribute */
+ public int min = -1;
+ /** max attribute */
+ public int max = -1;
+
+ // CheckStyle:VisibilityModifier ON
+
+ /** Used for parameterized custom selector */
+ public static final String MIN_KEY = "min";
+ /** Used for parameterized custom selector */
+ public static final String MAX_KEY = "max";
+
+ /**
+ * Creates a new <code>DepthSelector</code> instance.
+ *
+ */
+ public DepthSelector() {
+ }
+
+ /**
+ * @return a string describing this object
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder("{depthselector min: ");
+ buf.append(min);
+ buf.append(" max: ");
+ buf.append(max);
+ buf.append("}");
+ return buf.toString();
+ }
+
+ /**
+ * The minimum depth below the basedir before a file is selected.
+ *
+ * @param min minimum directory levels below basedir to go
+ */
+ public void setMin(int min) {
+ this.min = min;
+ }
+
+ /**
+ * The minimum depth below the basedir before a file is selected.
+ *
+ * @param max maximum directory levels below basedir to go
+ */
+ public void setMax(int max) {
+ this.max = max;
+ }
+
+ /**
+ * When using this as a custom selector, this method will be called.
+ * It translates each parameter into the appropriate setXXX() call.
+ *
+ * @param parameters the complete set of parameters for this selector
+ */
+ public void setParameters(Parameter[] parameters) {
+ super.setParameters(parameters);
+ if (parameters != null) {
+ for (int i = 0; i < parameters.length; i++) {
+ String paramname = parameters[i].getName();
+ if (MIN_KEY.equalsIgnoreCase(paramname)) {
+ try {
+ setMin(Integer.parseInt(parameters[i].getValue()));
+ } catch (NumberFormatException nfe1) {
+ setError("Invalid minimum value "
+ + parameters[i].getValue());
+ }
+ } else if (MAX_KEY.equalsIgnoreCase(paramname)) {
+ try {
+ setMax(Integer.parseInt(parameters[i].getValue()));
+ } catch (NumberFormatException nfe1) {
+ setError("Invalid maximum value "
+ + parameters[i].getValue());
+ }
+ } else {
+ setError("Invalid parameter " + paramname);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks to make sure all settings are kosher. In this case, it
+ * means that the max depth is not lower than the min depth.
+ */
+ public void verifySettings() {
+ if (min < 0 && max < 0) {
+ setError("You must set at least one of the min or the "
+ + "max levels.");
+ }
+ if (max < min && max > -1) {
+ setError("The maximum depth is lower than the minimum.");
+ }
+ }
+
+ /**
+ * The heart of the matter. This is where the selector gets to decide
+ * on the inclusion of a file in a particular fileset. Most of the work
+ * for this selector is offloaded into SelectorUtils, a static class
+ * that provides the same services for both FilenameSelector and
+ * DirectoryScanner.
+ *
+ * @param basedir the base directory the scan is being done from
+ * @param filename is the name of the file to check
+ * @param file is a java.io.File object the selector can use
+ * @return whether the file should be selected or not
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+
+ // throw BuildException on error
+ validate();
+
+ int depth = -1;
+ // If you felt daring, you could cache the basedir absolute path
+ String absBase = basedir.getAbsolutePath();
+ String absFile = file.getAbsolutePath();
+ StringTokenizer tokBase = new StringTokenizer(absBase,
+ File.separator);
+ StringTokenizer tokFile = new StringTokenizer(absFile,
+ File.separator);
+ while (tokFile.hasMoreTokens()) {
+ String filetoken = tokFile.nextToken();
+ if (tokBase.hasMoreTokens()) {
+ String basetoken = tokBase.nextToken();
+ // Sanity check. Ditch it if you want faster performance
+ if (!basetoken.equals(filetoken)) {
+ throw new BuildException("File " + filename
+ + " does not appear within " + absBase
+ + "directory");
+ }
+ } else {
+ depth += 1;
+ if (max > -1 && depth > max) {
+ return false;
+ }
+ }
+ }
+ if (tokBase.hasMoreTokens()) {
+ throw new BuildException("File " + filename
+ + " is outside of " + absBase + "directory tree");
+ }
+ if (min > -1 && depth < min) {
+ return false;
+ }
+ return true;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DifferentSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DifferentSelector.java
new file mode 100644
index 00000000..c701fb84
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/DifferentSelector.java
@@ -0,0 +1,114 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * This selector selects files against a mapped set of target files, selecting
+ * all those files which are different.
+ * Files with different lengths are deemed different
+ * automatically
+ * Files with identical timestamps are viewed as matching by
+ * default, unless you specify otherwise.
+ * Contents are compared if the lengths are the same
+ * and the timestamps are ignored or the same,
+ * except if you decide to ignore contents to gain speed.
+ * <p>
+ * This is a useful selector to work with programs and tasks that don't handle
+ * dependency checking properly; Even if a predecessor task always creates its
+ * output files, followup tasks can be driven off copies made with a different
+ * selector, so their dependencies are driven on the absolute state of the
+ * files, not a timestamp.
+ * <p>
+ * Clearly, however, bulk file comparisons is inefficient; anything that can
+ * use timestamps is to be preferred. If this selector must be used, use it
+ * over as few files as possible, perhaps following it with an &lt;uptodate;&gt
+ * to keep the descendant routines conditional.
+ *
+ */
+public class DifferentSelector extends MappingSelector {
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private boolean ignoreFileTimes = true;
+ private boolean ignoreContents = false;
+
+
+ /**
+ * This flag tells the selector to ignore file times in the comparison
+ * @param ignoreFileTimes if true ignore file times
+ */
+ public void setIgnoreFileTimes(boolean ignoreFileTimes) {
+ this.ignoreFileTimes = ignoreFileTimes;
+ }
+ /**
+ * This flag tells the selector to ignore contents
+ * @param ignoreContents if true ignore contents
+ * @since ant 1.6.3
+ */
+ public void setIgnoreContents(boolean ignoreContents) {
+ this.ignoreContents = ignoreContents;
+ }
+ /**
+ * this test is our selection test that compared the file with the destfile
+ * @param srcfile the source file
+ * @param destfile the destination file
+ * @return true if the files are different
+ */
+ protected boolean selectionTest(File srcfile, File destfile) {
+
+ //if either of them is missing, they are different
+ if (srcfile.exists() != destfile.exists()) {
+ return true;
+ }
+
+ if (srcfile.length() != destfile.length()) {
+ // different size =>different files
+ return true;
+ }
+
+ if (!ignoreFileTimes) {
+ //same date if dest timestamp is within granularity of the srcfile
+ boolean sameDate;
+ sameDate = destfile.lastModified() >= srcfile.lastModified() - granularity
+ && destfile.lastModified() <= srcfile.lastModified() + granularity;
+
+ // different dates => different files
+ if (!sameDate) {
+ return true;
+ }
+ }
+ if (!ignoreContents) {
+ //here do a bulk comparison
+ try {
+ return !FILE_UTILS.contentEquals(srcfile, destfile);
+ } catch (IOException e) {
+ throw new BuildException("while comparing " + srcfile + " and "
+ + destfile, e);
+ }
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ExtendFileSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ExtendFileSelector.java
new file mode 100644
index 00000000..fe974065
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ExtendFileSelector.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.tools.ant.types.selectors;
+
+import org.apache.tools.ant.types.Parameterizable;
+
+/**
+ * This is the interface to be used by all custom selectors, those that are
+ * called through the &lt;custom&gt; tag. It is the amalgamation of two
+ * interfaces, the FileSelector and the Parameterizable interface. Note that
+ * you will almost certainly want the default behaviour for handling
+ * Parameters, so you probably want to use the BaseExtendSelector class
+ * as the base class for your custom selector rather than implementing
+ * this interface from scratch.
+ *
+ * @since 1.5
+ */
+public interface ExtendFileSelector extends FileSelector, Parameterizable {
+
+ // No further methods necessary. This is just an amalgamation of two other
+ // interfaces.
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ExtendSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ExtendSelector.java
new file mode 100644
index 00000000..af8c920f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ExtendSelector.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.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.Vector;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Selector that selects files by forwarding the request on to other classes.
+ *
+ * @since 1.5
+ */
+public class ExtendSelector extends BaseSelector {
+
+ private String classname = null;
+ private FileSelector dynselector = null;
+ private Vector<Parameter> paramVec = new Vector<Parameter>();
+ private Path classpath = null;
+
+ /**
+ * Default constructor.
+ */
+ public ExtendSelector() {
+ }
+
+ /**
+ * Sets the classname of the custom selector.
+ *
+ * @param classname is the class which implements this selector
+ */
+ public void setClassname(String classname) {
+ this.classname = classname;
+ }
+
+ /**
+ * Instantiates the identified custom selector class.
+ */
+ public void selectorCreate() {
+ if (classname != null && classname.length() > 0) {
+ try {
+ Class<?> c = null;
+ if (classpath == null) {
+ c = Class.forName(classname);
+ } else {
+ // Memory-Leak in line below
+ AntClassLoader al
+ = getProject().createClassLoader(classpath);
+ c = Class.forName(classname, true, al);
+ }
+ dynselector = c.asSubclass(FileSelector.class).newInstance();
+ final Project p = getProject();
+ if (p != null) {
+ p.setProjectReference(dynselector);
+ }
+ } catch (ClassNotFoundException cnfexcept) {
+ setError("Selector " + classname
+ + " not initialized, no such class");
+ } catch (InstantiationException iexcept) {
+ setError("Selector " + classname
+ + " not initialized, could not create class");
+ } catch (IllegalAccessException iaexcept) {
+ setError("Selector " + classname
+ + " not initialized, class not accessible");
+ }
+ } else {
+ setError("There is no classname specified");
+ }
+ }
+
+ /**
+ * Create new parameters to pass to custom selector.
+ *
+ * @param p The new Parameter object
+ */
+ public void addParam(Parameter p) {
+ paramVec.addElement(p);
+ }
+
+
+ /**
+ * Set the classpath to load the classname specified using an attribute.
+ * @param classpath the classpath to use
+ */
+ public final void setClasspath(Path classpath) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ if (this.classpath == null) {
+ this.classpath = classpath;
+ } else {
+ this.classpath.append(classpath);
+ }
+ }
+
+ /**
+ * Specify the classpath to use to load the Selector (nested element).
+ * @return a classpath to be configured
+ */
+ public final Path createClasspath() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (this.classpath == null) {
+ this.classpath = new Path(getProject());
+ }
+ return this.classpath.createPath();
+ }
+
+ /**
+ * Get the classpath
+ * @return the classpath
+ */
+ public final Path getClasspath() {
+ return classpath;
+ }
+
+ /**
+ * Set the classpath to use for loading a custom selector by using
+ * a reference.
+ * @param r a reference to the classpath
+ */
+ public void setClasspathref(Reference r) {
+ if (isReference()) {
+ throw tooManyAttributes();
+ }
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * These are errors specific to ExtendSelector only. If there are
+ * errors in the custom selector, it should throw a BuildException
+ * when isSelected() is called.
+ */
+ public void verifySettings() {
+ // Creation is done here rather than in isSelected() because some
+ // containers may do a validation pass before running isSelected(),
+ // but we need to check for the existence of the created class.
+ if (dynselector == null) {
+ selectorCreate();
+ }
+ if (classname == null || classname.length() < 1) {
+ setError("The classname attribute is required");
+ } else if (dynselector == null) {
+ setError("Internal Error: The custom selector was not created");
+ } else if (!(dynselector instanceof ExtendFileSelector)
+ && (paramVec.size() > 0)) {
+ setError("Cannot set parameters on custom selector that does not "
+ + "implement ExtendFileSelector");
+ }
+ }
+
+
+ /**
+ * Allows the custom selector to choose whether to select a file. This
+ * is also where the Parameters are passed to the custom selector,
+ * since we know we must have them all by now. And since we must know
+ * both classpath and classname, creating the class is deferred to here
+ * as well.
+ * @param basedir The the base directory.
+ * @param filename The name of the file to check.
+ * @param file A File object for this filename.
+ * @return whether the file should be selected or not.
+ * @exception BuildException if an error occurs.
+ */
+ public boolean isSelected(File basedir, String filename, File file)
+ throws BuildException {
+ validate();
+ if (paramVec.size() > 0 && dynselector instanceof ExtendFileSelector) {
+ Parameter[] paramArray = new Parameter[paramVec.size()];
+ paramVec.copyInto(paramArray);
+ // We know that dynselector must be non-null if no error message
+ ((ExtendFileSelector) dynselector).setParameters(paramArray);
+ }
+ return dynselector.isSelected(basedir, filename, file);
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/FileSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/FileSelector.java
new file mode 100644
index 00000000..614a9706
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/FileSelector.java
@@ -0,0 +1,48 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * This is the interface to be used by all selectors.
+ *
+ * @since 1.5
+ */
+public interface FileSelector {
+
+ /**
+ * Method that each selector will implement to create their
+ * selection behaviour. If there is a problem with the setup
+ * of a selector, it can throw a BuildException to indicate
+ * the problem.
+ *
+ * @param basedir A java.io.File object for the base directory
+ * @param filename The name of the file to check
+ * @param file A File object for this filename
+ * @return whether the file should be selected or not
+ * @exception BuildException if the selector was not configured correctly
+ */
+ boolean isSelected(File basedir, String filename, File file)
+ throws BuildException;
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/FilenameSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/FilenameSelector.java
new file mode 100644
index 00000000..1b998f9f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/FilenameSelector.java
@@ -0,0 +1,195 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.RegularExpression;
+import org.apache.tools.ant.util.regexp.Regexp;
+import org.apache.tools.ant.util.regexp.RegexpUtil;
+
+/**
+ * Selector that filters files based on the filename.
+ *
+ * @since 1.5
+ */
+public class FilenameSelector extends BaseExtendSelector {
+
+ private String pattern = null;
+ private String regex = null;
+ private boolean casesensitive = true;
+
+ private boolean negated = false;
+ /** Used for parameterized custom selector */
+ public static final String NAME_KEY = "name";
+ /** Used for parameterized custom selector */
+ public static final String CASE_KEY = "casesensitive";
+ /** Used for parameterized custom selector */
+ public static final String NEGATE_KEY = "negate";
+ /** Used for parameterized custom selector */
+ public static final String REGEX_KEY = "regex";
+
+ // caches for performance reasons
+ private RegularExpression reg;
+ private Regexp expression;
+
+ /**
+ * Creates a new <code>FilenameSelector</code> instance.
+ *
+ */
+ public FilenameSelector() {
+ }
+
+ /**
+ * @return a string describing this object
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder("{filenameselector name: ");
+ if (pattern != null) {
+ buf.append(pattern);
+ }
+ if (regex != null) {
+ buf.append(regex).append(" [as regular expression]");
+ }
+ buf.append(" negate: ").append(negated);
+ buf.append(" casesensitive: ").append(casesensitive);
+ buf.append("}");
+ return buf.toString();
+ }
+
+ /**
+ * The name of the file, or the pattern for the name, that
+ * should be used for selection.
+ *
+ * @param pattern the file pattern that any filename must match
+ * against in order to be selected.
+ */
+ public void setName(String pattern) {
+ pattern = pattern.replace('/', File.separatorChar).replace('\\',
+ File.separatorChar);
+ if (pattern.endsWith(File.separator)) {
+ pattern += "**";
+ }
+ this.pattern = pattern;
+ }
+
+ /**
+ * The regular expression the file name will be matched against.
+ *
+ * @param pattern the regular expression that any filename must match
+ * against in order to be selected.
+ */
+ public void setRegex(String pattern) {
+ this.regex = pattern;
+ this.reg = null;
+ }
+
+ /**
+ * Whether to ignore case when checking filenames.
+ *
+ * @param casesensitive whether to pay attention to case sensitivity
+ */
+ public void setCasesensitive(boolean casesensitive) {
+ this.casesensitive = casesensitive;
+ }
+
+ /**
+ * You can optionally reverse the selection of this selector,
+ * thereby emulating an &lt;exclude&gt; tag, by setting the attribute
+ * negate to true. This is identical to surrounding the selector
+ * with &lt;not&gt;&lt;/not&gt;.
+ *
+ * @param negated whether to negate this selection
+ */
+ public void setNegate(boolean negated) {
+ this.negated = negated;
+ }
+
+ /**
+ * When using this as a custom selector, this method will be called.
+ * It translates each parameter into the appropriate setXXX() call.
+ *
+ * @param parameters the complete set of parameters for this selector
+ */
+ public void setParameters(Parameter[] parameters) {
+ super.setParameters(parameters);
+ if (parameters != null) {
+ for (int i = 0; i < parameters.length; i++) {
+ String paramname = parameters[i].getName();
+ if (NAME_KEY.equalsIgnoreCase(paramname)) {
+ setName(parameters[i].getValue());
+ } else if (CASE_KEY.equalsIgnoreCase(paramname)) {
+ setCasesensitive(Project.toBoolean(
+ parameters[i].getValue()));
+ } else if (NEGATE_KEY.equalsIgnoreCase(paramname)) {
+ setNegate(Project.toBoolean(parameters[i].getValue()));
+ } else if (REGEX_KEY.equalsIgnoreCase(paramname)) {
+ setRegex(parameters[i].getValue());
+ } else {
+ setError("Invalid parameter " + paramname);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks to make sure all settings are kosher. In this case, it
+ * means that the name attribute has been set.
+ *
+ */
+ public void verifySettings() {
+ if (pattern == null && regex == null) {
+ setError("The name or regex attribute is required");
+ } else if (pattern != null && regex != null) {
+ setError("Only one of name and regex attribute is allowed");
+ }
+ }
+
+ /**
+ * The heart of the matter. This is where the selector gets to decide
+ * on the inclusion of a file in a particular fileset. Most of the work
+ * for this selector is offloaded into SelectorUtils, a static class
+ * that provides the same services for both FilenameSelector and
+ * DirectoryScanner.
+ *
+ * @param basedir the base directory the scan is being done from
+ * @param filename is the name of the file to check
+ * @param file is a java.io.File object the selector can use
+ * @return whether the file should be selected or not
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+ validate();
+ if (pattern != null) {
+ return (SelectorUtils.matchPath(pattern, filename,
+ casesensitive) == !(negated));
+ } else {
+ if (reg == null) {
+ reg = new RegularExpression();
+ reg.setPattern(regex);
+ expression = reg.getRegexp(getProject());
+ }
+ int options = RegexpUtil.asOptions(casesensitive);
+ return expression.matches(filename, options) == !negated;
+ }
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/MajoritySelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/MajoritySelector.java
new file mode 100644
index 00000000..842258fb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/MajoritySelector.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.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.Enumeration;
+
+/**
+ * This selector is here just to shake up your thinking a bit. Don't get
+ * too caught up in boolean, there are other ways you can evaluate a
+ * collection of selectors. This one takes a vote of the selectors it
+ * contains, and majority wins. You could also have an "all-but-one"
+ * selector, a "weighted-average" selector, and so on. These are left
+ * as exercises for the reader (as are the usecases where this would
+ * be necessary).
+ *
+ * @since 1.5
+ */
+public class MajoritySelector extends BaseSelectorContainer {
+
+ private boolean allowtie = true;
+
+ /**
+ * Default constructor.
+ */
+ public MajoritySelector() {
+ }
+
+ /**
+ * @return a string describing this object
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ if (hasSelectors()) {
+ buf.append("{majorityselect: ");
+ buf.append(super.toString());
+ buf.append("}");
+ }
+ return buf.toString();
+ }
+
+ /**
+ * A attribute to specify what will happen if number
+ * of yes votes is the same as the number of no votes
+ * defaults to true
+ *
+ * @param tiebreaker the value to give if there is a tie
+ */
+ public void setAllowtie(boolean tiebreaker) {
+ allowtie = tiebreaker;
+ }
+
+ /**
+ * Returns true (the file is selected) if most of the other selectors
+ * agree. In case of a tie, go by the allowtie setting. That defaults
+ * to true, meaning in case of a tie, the file is selected.
+ *
+ * @param basedir the base directory the scan is being done from
+ * @param filename is the name of the file to check
+ * @param file is a java.io.File object for the filename that the selector
+ * can use
+ * @return whether the file should be selected or not
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+ validate();
+ int yesvotes = 0;
+ int novotes = 0;
+ Enumeration<FileSelector> e = selectorElements();
+
+ while (e.hasMoreElements()) {
+ if (e.nextElement().isSelected(basedir,
+ filename, file)) {
+ yesvotes++;
+ } else {
+ novotes++;
+ }
+ }
+ if (yesvotes > novotes) {
+ return true;
+ } else if (novotes > yesvotes) {
+ return false;
+ }
+ // At this point, we know we have a tie.
+ return allowtie;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/MappingSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/MappingSelector.java
new file mode 100644
index 00000000..1a274949
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/MappingSelector.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.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.IdentityMapper;
+
+/**
+ * A mapping selector is an abstract class adding mapping support to the base
+ * selector
+ */
+public abstract class MappingSelector extends BaseSelector {
+
+ /** Utilities used for file operations */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ protected File targetdir = null;
+ protected Mapper mapperElement = null;
+ protected FileNameMapper map = null;
+ protected int granularity = 0;
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Creates a new <code>MappingSelector</code> instance.
+ *
+ */
+ public MappingSelector() {
+ granularity = (int) FILE_UTILS.getFileTimestampGranularity();
+ }
+
+
+ /**
+ * The name of the file or directory which is checked for out-of-date
+ * files.
+ *
+ * @param targetdir the directory to scan looking for files.
+ */
+ public void setTargetdir(File targetdir) {
+ this.targetdir = targetdir;
+ }
+
+ /**
+ * Defines the FileNameMapper to use (nested mapper element).
+ * @return a mapper to be configured
+ * @throws BuildException if more than one mapper defined
+ */
+ public Mapper createMapper() throws BuildException {
+ if (map != null || mapperElement != null) {
+ throw new BuildException("Cannot define more than one mapper");
+ }
+ mapperElement = new Mapper(getProject());
+ return mapperElement;
+ }
+
+ /**
+ * Add a configured FileNameMapper instance.
+ * @param fileNameMapper the FileNameMapper to add
+ * @throws BuildException if more than one mapper defined
+ * @since Ant 1.8.0
+ */
+ public void addConfigured(FileNameMapper fileNameMapper) {
+ if (map != null || mapperElement != null) {
+ throw new BuildException("Cannot define more than one mapper");
+ }
+ this.map = fileNameMapper;
+ }
+
+ /**
+ * Checks to make sure all settings are kosher. In this case, it
+ * means that the dest attribute has been set and we have a mapper.
+ */
+ @Override
+ public void verifySettings() {
+ if (targetdir == null) {
+ setError("The targetdir attribute is required.");
+ }
+ if (map == null) {
+ if (mapperElement == null) {
+ map = new IdentityMapper();
+ } else {
+ map = mapperElement.getImplementation();
+ if (map == null) {
+ setError("Could not set <mapper> element.");
+ }
+ }
+ }
+ }
+
+ /**
+ * The heart of the matter. This is where the selector gets to decide
+ * on the inclusion of a file in a particular fileset.
+ *
+ * @param basedir the base directory the scan is being done from
+ * @param filename is the name of the file to check
+ * @param file is a java.io.File object the selector can use
+ * @return whether the file should be selected or not
+ */
+ @Override
+ public boolean isSelected(File basedir, String filename, File file) {
+
+ // throw BuildException on error
+ validate();
+
+ // Determine file whose out-of-dateness is to be checked
+ String[] destfiles = map.mapFileName(filename);
+ // If filename does not match the To attribute of the mapper
+ // then filter it out of the files we are considering
+ if (destfiles == null) {
+ return false;
+ }
+ // Sanity check
+ if (destfiles.length != 1 || destfiles[0] == null) {
+ throw new BuildException("Invalid destination file results for "
+ + targetdir.getName() + " with filename " + filename);
+ }
+ String destname = destfiles[0];
+ File destfile = FILE_UTILS.resolveFile(targetdir, destname);
+
+ boolean selected = selectionTest(file, destfile);
+ return selected;
+ }
+
+ /**
+ * this test is our selection test that compared the file with the destfile
+ * @param srcfile file to test; may be null
+ * @param destfile destination file
+ * @return true if source file compares with destination file
+ */
+ protected abstract boolean selectionTest(File srcfile, File destfile);
+
+ /**
+ * Sets the number of milliseconds leeway we will give before we consider
+ * a file out of date. Defaults to 2000 on MS-DOS derivatives and 1000 on
+ * others.
+ * @param granularity the leeway in milliseconds
+ */
+ public void setGranularity(int granularity) {
+ this.granularity = granularity;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/NoneSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/NoneSelector.java
new file mode 100644
index 00000000..536b5b5f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/NoneSelector.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.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.Enumeration;
+
+/**
+ * This selector has a collection of other selectors. All of those selectors
+ * must refuse to select a file before the file is considered selected by
+ * this selector.
+ *
+ * @since 1.5
+ */
+public class NoneSelector extends BaseSelectorContainer {
+
+ /**
+ * Default constructor.
+ */
+ public NoneSelector() {
+ }
+
+ /**
+ * @return a string representation of the selector
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ if (hasSelectors()) {
+ buf.append("{noneselect: ");
+ buf.append(super.toString());
+ buf.append("}");
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Returns true (the file is selected) only if all other selectors
+ * agree that the file should not be selected.
+ *
+ * @param basedir the base directory the scan is being done from
+ * @param filename is the name of the file to check
+ * @param file is a java.io.File object for the filename that the selector
+ * can use
+ * @return whether the file should be selected or not
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+ validate();
+ Enumeration<FileSelector> e = selectorElements();
+
+ while (e.hasMoreElements()) {
+ if (e.nextElement().isSelected(basedir, filename, file)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/NotSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/NotSelector.java
new file mode 100644
index 00000000..71c3940f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/NotSelector.java
@@ -0,0 +1,73 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+
+/**
+ * This selector has one other selectors whose meaning it inverts. It
+ * actually relies on NoneSelector for its implementation of the
+ * isSelected() method, but it adds a check to ensure there is only one
+ * other selector contained within.
+ *
+ * @since 1.5
+ */
+public class NotSelector extends NoneSelector {
+
+ /**
+ * Default constructor.
+ */
+ public NotSelector() {
+ }
+
+ /**
+ * Constructor that inverts the meaning of its argument.
+ * @param other the selector to invert
+ * @since Ant 1.7
+ */
+ public NotSelector(FileSelector other) {
+ this();
+ appendSelector(other);
+ }
+
+ /**
+ * @return a string representation of the selector
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ if (hasSelectors()) {
+ buf.append("{notselect: ");
+ buf.append(super.toString());
+ buf.append("}");
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Makes sure that there is only one entry, sets an error message if
+ * not.
+ */
+ public void verifySettings() {
+ if (selectorCount() != 1) {
+ setError("One and only one selector is allowed within the "
+ + "<not> tag");
+ }
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/OrSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/OrSelector.java
new file mode 100644
index 00000000..b0777445
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/OrSelector.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.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.Enumeration;
+
+/**
+ * This selector has a collection of other selectors, any of which have to
+ * select a file in order for this selector to select it.
+ *
+ * @since 1.5
+ */
+public class OrSelector extends BaseSelectorContainer {
+
+ /**
+ * Default constructor.
+ */
+ public OrSelector() {
+ }
+
+ /**
+ * @return a string representation of the selector
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ if (hasSelectors()) {
+ buf.append("{orselect: ");
+ buf.append(super.toString());
+ buf.append("}");
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Returns true (the file is selected) if any of the other selectors
+ * agree that the file should be selected.
+ *
+ * @param basedir the base directory the scan is being done from
+ * @param filename the name of the file to check
+ * @param file a java.io.File object for the filename that the selector
+ * can use
+ * @return whether the file should be selected or not
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+ validate();
+ Enumeration<FileSelector> e = selectorElements();
+
+ // First, check that all elements are correctly configured
+ while (e.hasMoreElements()) {
+ if (e.nextElement().isSelected(basedir, filename, file)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/PresentSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/PresentSelector.java
new file mode 100644
index 00000000..1c0c000c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/PresentSelector.java
@@ -0,0 +1,198 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.IdentityMapper;
+
+/**
+ * Selector that filters files based on whether they appear in another
+ * directory tree. It can contain a mapper element, so isn't available
+ * as an ExtendSelector (since those parameters can't hold other
+ * elements).
+ *
+ * @since 1.5
+ */
+public class PresentSelector extends BaseSelector {
+ private File targetdir = null;
+ private Mapper mapperElement = null;
+ private FileNameMapper map = null;
+ private boolean destmustexist = true;
+
+ /**
+ * Creates a new <code>PresentSelector</code> instance.
+ *
+ */
+ public PresentSelector() {
+ }
+
+ /**
+ * @return a string describing this object
+ */
+ @Override
+ public String toString() {
+ final StringBuilder buf = new StringBuilder("{presentselector targetdir: ");
+ if (targetdir == null) {
+ buf.append("NOT YET SET");
+ } else {
+ buf.append(targetdir.getName());
+ }
+ buf.append(" present: ");
+ if (destmustexist) {
+ buf.append("both");
+ } else {
+ buf.append("srconly");
+ }
+ if (map != null) {
+ buf.append(map.toString());
+ } else if (mapperElement != null) {
+ buf.append(mapperElement.toString());
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+
+ /**
+ * The name of the file or directory which is checked for matching
+ * files.
+ *
+ * @param targetdir the directory to scan looking for matching files.
+ */
+ public void setTargetdir(final File targetdir) {
+ this.targetdir = targetdir;
+ }
+
+ /**
+ * Defines the FileNameMapper to use (nested mapper element).
+ * @return a mapper to be configured
+ * @throws BuildException if more than one mapper defined
+ */
+ public Mapper createMapper() throws BuildException {
+ if (map != null || mapperElement != null) {
+ throw new BuildException("Cannot define more than one mapper");
+ }
+ mapperElement = new Mapper(getProject());
+ return mapperElement;
+ }
+
+ /**
+ * Add a configured FileNameMapper instance.
+ * @param fileNameMapper the FileNameMapper to add
+ * @throws BuildException if more than one mapper defined
+ * @since Ant 1.8.0
+ */
+ public void addConfigured(final FileNameMapper fileNameMapper) {
+ if (map != null || mapperElement != null) {
+ throw new BuildException("Cannot define more than one mapper");
+ }
+ this.map = fileNameMapper;
+ }
+
+ /**
+ * This sets whether to select a file if its dest file is present.
+ * It could be a <code>negate</code> boolean, but by doing things
+ * this way, we get some documentation on how the system works.
+ * A user looking at the documentation should clearly understand
+ * that the ONLY files whose presence is being tested are those
+ * that already exist in the source directory, hence the lack of
+ * a <code>destonly</code> option.
+ *
+ * @param fp An attribute set to either <code>srconly</code or
+ * <code>both</code>.
+ */
+ public void setPresent(final FilePresence fp) {
+ if (fp.getIndex() == 0) {
+ destmustexist = false;
+ }
+ }
+
+ /**
+ * Checks to make sure all settings are kosher. In this case, it
+ * means that the targetdir attribute has been set and we have a mapper.
+ */
+ @Override
+ public void verifySettings() {
+ if (targetdir == null) {
+ setError("The targetdir attribute is required.");
+ }
+ if (map == null) {
+ if (mapperElement == null) {
+ map = new IdentityMapper();
+ } else {
+ map = mapperElement.getImplementation();
+ if (map == null) {
+ setError("Could not set <mapper> element.");
+ }
+ }
+ }
+ }
+
+ /**
+ * The heart of the matter. This is where the selector gets to decide
+ * on the inclusion of a file in a particular fileset.
+ *
+ * @param basedir the base directory the scan is being done from
+ * @param filename is the name of the file to check
+ * @param file is a java.io.File object the selector can use
+ * @return whether the file should be selected or not
+ */
+ @Override
+ public boolean isSelected(final File basedir, final String filename, final File file) {
+
+ // throw BuildException on error
+ validate();
+
+ // Determine file whose existence is to be checked
+ final String[] destfiles = map.mapFileName(filename);
+ // If filename does not match the To attribute of the mapper
+ // then filter it out of the files we are considering
+ if (destfiles == null) {
+ return false;
+ }
+ // Sanity check
+ if (destfiles.length != 1 || destfiles[0] == null) {
+ throw new BuildException("Invalid destination file results for "
+ + targetdir + " with filename " + filename);
+ }
+ final String destname = destfiles[0];
+ final File destfile = FileUtils.getFileUtils().resolveFile(targetdir, destname);
+ return destfile.exists() == destmustexist;
+ }
+
+ /**
+ * Enumerated attribute with the values for indicating where a file's
+ * presence is allowed and required.
+ */
+ public static class FilePresence extends EnumeratedAttribute {
+ /**
+ * @return the values as an array of strings
+ */
+ @Override
+ public String[] getValues() {
+ return new String[] {"srconly", "both"};
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ReadableSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ReadableSelector.java
new file mode 100644
index 00000000..20471188
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/ReadableSelector.java
@@ -0,0 +1,49 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+
+/**
+ * A selector that selects readable files.
+ *
+ * <p>Readable is defined in terms of java.io.File#canRead, this
+ * means the selector will accept any file that exists and is readable
+ * by the application.</p>
+ *
+ * @since Ant 1.8.0
+ */
+public class ReadableSelector implements FileSelector, ResourceSelector {
+
+ public boolean isSelected(File basedir, String filename, File file) {
+ return file != null && file.canRead();
+ }
+
+ public boolean isSelected(Resource r) {
+ FileProvider fp = r.as(FileProvider.class);
+ if (fp != null) {
+ return isSelected(null, null, fp.getFile());
+ }
+ return false;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectSelector.java
new file mode 100644
index 00000000..2089012c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectSelector.java
@@ -0,0 +1,231 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.Enumeration;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+
+/**
+ * This selector just holds one other selector and forwards all
+ * requests to it. It exists so that there is a single selector
+ * type that can exist outside of any targets, as an element of
+ * project. It overrides all of the reference stuff so that it
+ * works as expected. Note that this is the only selector you
+ * can reference.
+ *
+ * @since 1.5
+ */
+public class SelectSelector extends BaseSelectorContainer {
+
+ private Object ifCondition;
+ private Object unlessCondition;
+
+ /**
+ * Default constructor.
+ */
+ public SelectSelector() {
+ }
+
+ /**
+ * @return a string describing this object
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ if (hasSelectors()) {
+ buf.append("{select");
+ if (ifCondition != null) {
+ buf.append(" if: ");
+ buf.append(ifCondition);
+ }
+ if (unlessCondition != null) {
+ buf.append(" unless: ");
+ buf.append(unlessCondition);
+ }
+ buf.append(" ");
+ buf.append(super.toString());
+ buf.append("}");
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Performs the check for circular references and returns the
+ * referenced Selector.
+ */
+ private SelectSelector getRef() {
+ Object o = getCheckedRef(this.getClass(), "SelectSelector");
+ return (SelectSelector) o;
+ }
+
+ /**
+ * Indicates whether there are any selectors here.
+ * @return whether any selectors are in this container
+ */
+ public boolean hasSelectors() {
+ if (isReference()) {
+ return getRef().hasSelectors();
+ }
+ return super.hasSelectors();
+ }
+
+ /**
+ * Gives the count of the number of selectors in this container
+ * @return the number of selectors in this container
+ */
+ public int selectorCount() {
+ if (isReference()) {
+ return getRef().selectorCount();
+ }
+ return super.selectorCount();
+ }
+
+ /**
+ * Returns the set of selectors as an array.
+ * @param p the current project
+ * @return an array of selectors in this container
+ */
+ public FileSelector[] getSelectors(Project p) {
+ if (isReference()) {
+ return getRef().getSelectors(p);
+ }
+ return super.getSelectors(p);
+ }
+
+ /**
+ * Returns an enumerator for accessing the set of selectors.
+ * @return an enumerator that goes through each of the selectors
+ */
+ public Enumeration<FileSelector> selectorElements() {
+ if (isReference()) {
+ return getRef().selectorElements();
+ }
+ return super.selectorElements();
+ }
+
+ /**
+ * Add a new selector into this container.
+ *
+ * @param selector the new selector to add
+ */
+ public void appendSelector(FileSelector selector) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ super.appendSelector(selector);
+ }
+
+
+ /**
+ * Makes sure that there is only one entry, sets an error message if
+ * not.
+ */
+ public void verifySettings() {
+ int cnt = selectorCount();
+ if (cnt < 0 || cnt > 1) {
+ setError("Only one selector is allowed within the "
+ + "<selector> tag");
+ }
+ }
+
+ /**
+ * Ensures that the selector passes the conditions placed
+ * on it with <code>if</code> and <code>unless</code>.
+ * @return true if conditions are passed
+ */
+ public boolean passesConditions() {
+ PropertyHelper ph = PropertyHelper.getPropertyHelper(getProject());
+ return ph.testIfCondition(ifCondition)
+ && ph.testUnlessCondition(unlessCondition);
+ }
+
+ /**
+ * Sets the if attribute to an expression which must evaluate to
+ * true or the name of an existing property for the
+ * selector to select any files.
+ * @param ifProperty the expression to check
+ * @since Ant 1.8.0
+ */
+ public void setIf(Object ifProperty) {
+ this.ifCondition = ifProperty;
+ }
+
+ /**
+ * Sets the if attribute to an expression which must evaluate to
+ * true or the name of an existing property for the
+ * selector to select any files.
+ * @param ifProperty the expression to check
+ */
+ public void setIf(String ifProperty) {
+ setIf((Object) ifProperty);
+ }
+
+ /**
+ * Sets the unless attribute to an expression which must evaluate to
+ * false or the name of a property which cannot exist for the
+ * selector to select any files.
+ * @param unlessProperty the expression to check
+ * @since Ant 1.8.0
+ */
+ public void setUnless(Object unlessProperty) {
+ this.unlessCondition = unlessProperty;
+ }
+
+ /**
+ * Sets the unless attribute to an expression which must evaluate to
+ * false or the name of a property which cannot exist for the
+ * selector to select any files.
+ * @param unlessProperty the expression to check
+ */
+ public void setUnless(String unlessProperty) {
+ setUnless((Object) unlessProperty);
+ }
+
+ /**
+ * Returns true (the file is selected) only if the if property (if any)
+ * exists, the unless property (if any) doesn't exist, and the
+ * contained selector (if any) selects the file. If there is no contained
+ * selector, return true (because we assume that the point was to test
+ * the if and unless conditions).
+ *
+ * @param basedir the base directory the scan is being done from
+ * @param filename the name of the file to check
+ * @param file a java.io.File object for the filename that the selector
+ * can use
+ * @return whether the file should be selected or not
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+ validate();
+
+ // Deal with if and unless properties first
+ if (!(passesConditions())) {
+ return false;
+ }
+
+ Enumeration<FileSelector> e = selectorElements();
+ if (!e.hasMoreElements()) {
+ return true;
+ }
+ FileSelector f = e.nextElement();
+ return f.isSelected(basedir, filename, file);
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectorContainer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectorContainer.java
new file mode 100644
index 00000000..47e4e4fd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectorContainer.java
@@ -0,0 +1,187 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.util.Enumeration;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector;
+
+/**
+ * This is the interface for selectors that can contain other selectors.
+ *
+ * @since 1.5
+ */
+public interface SelectorContainer {
+
+ /**
+ * Indicates whether there are any selectors here.
+ *
+ * @return whether any selectors are in this container
+ */
+ boolean hasSelectors();
+
+ /**
+ * Gives the count of the number of selectors in this container
+ *
+ * @return the number of selectors in this container
+ */
+ int selectorCount();
+
+ /**
+ * Returns the set of selectors as an array.
+ * @param p the current project
+ * @return an array of selectors in this container
+ */
+ FileSelector[] getSelectors(Project p);
+
+ /**
+ * Returns an enumerator for accessing the set of selectors.
+ *
+ * @return an enumerator that goes through each of the selectors
+ */
+ Enumeration<FileSelector> selectorElements();
+
+ /**
+ * Add a new selector into this container.
+ *
+ * @param selector the new selector to add
+ */
+ void appendSelector(FileSelector selector);
+
+ /* Methods below all add specific selectors */
+
+ /**
+ * add a "Select" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ void addSelector(SelectSelector selector);
+
+ /**
+ * add an "And" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ void addAnd(AndSelector selector);
+
+ /**
+ * add an "Or" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ void addOr(OrSelector selector);
+
+ /**
+ * add a "Not" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ void addNot(NotSelector selector);
+
+ /**
+ * add a "None" selector entry on the selector list
+ * @param selector the selector to add
+ */
+ void addNone(NoneSelector selector);
+
+ /**
+ * add a majority selector entry on the selector list
+ * @param selector the selector to add
+ */
+ void addMajority(MajoritySelector selector);
+
+ /**
+ * add a selector date entry on the selector list
+ * @param selector the selector to add
+ */
+ void addDate(DateSelector selector);
+
+ /**
+ * add a selector size entry on the selector list
+ * @param selector the selector to add
+ */
+ void addSize(SizeSelector selector);
+
+ /**
+ * add a selector filename entry on the selector list
+ * @param selector the selector to add
+ */
+ void addFilename(FilenameSelector selector);
+
+ /**
+ * add an extended selector entry on the selector list
+ * @param selector the selector to add
+ */
+ void addCustom(ExtendSelector selector);
+
+ /**
+ * add a contains selector entry on the selector list
+ * @param selector the selector to add
+ */
+ void addContains(ContainsSelector selector);
+
+ /**
+ * add a present selector entry on the selector list
+ * @param selector the selector to add
+ */
+ void addPresent(PresentSelector selector);
+
+ /**
+ * add a depth selector entry on the selector list
+ * @param selector the selector to add
+ */
+ void addDepth(DepthSelector selector);
+
+ /**
+ * add a depends selector entry on the selector list
+ * @param selector the selector to add
+ */
+ void addDepend(DependSelector selector);
+
+ /**
+ * add a regular expression selector entry on the selector list
+ * @param selector the selector to add
+ */
+ void addContainsRegexp(ContainsRegexpSelector selector);
+
+ /**
+ * add the type selector
+ * @param selector the selector to add
+ * @since ant 1.6
+ */
+ void addType(TypeSelector selector);
+
+ /**
+ * add the different selector
+ * @param selector the selector to add
+ * @since ant 1.6
+ */
+ void addDifferent(DifferentSelector selector);
+
+ /**
+ * add the modified selector
+ * @param selector the selector to add
+ * @since ant 1.6
+ */
+ void addModified(ModifiedSelector selector);
+
+ /**
+ * add an arbitrary selector
+ * @param selector the selector to add
+ * @since Ant 1.6
+ */
+ void add(FileSelector selector);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectorScanner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectorScanner.java
new file mode 100644
index 00000000..df9f8a40
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectorScanner.java
@@ -0,0 +1,49 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+/**
+ * An interface used to describe the actions required by any type of
+ * directory scanner that supports Selectors.
+ *
+ * @since 1.5
+ */
+public interface SelectorScanner {
+ /**
+ * Sets the selectors the scanner should use.
+ *
+ * @param selectors the list of selectors
+ */
+ void setSelectors(FileSelector[] selectors);
+
+ /**
+ * Directories which were selected out of a scan.
+ *
+ * @return list of directories not selected
+ */
+ String[] getDeselectedDirectories();
+
+ /**
+ * Files which were selected out of a scan.
+ *
+ * @return list of files not selected
+ */
+ String[] getDeselectedFiles();
+
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectorUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectorUtils.java
new file mode 100644
index 00000000..277470b7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SelectorUtils.java
@@ -0,0 +1,695 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * <p>This is a utility class used by selectors and DirectoryScanner. The
+ * functionality more properly belongs just to selectors, but unfortunately
+ * DirectoryScanner exposed these as protected methods. Thus we have to
+ * support any subclasses of DirectoryScanner that may access these methods.
+ * </p>
+ * <p>This is a Singleton.</p>
+ *
+ * @since 1.5
+ */
+public final class SelectorUtils {
+
+ /**
+ * The pattern that matches an arbitrary number of directories.
+ * @since Ant 1.8.0
+ */
+ public static final String DEEP_TREE_MATCH = "**";
+
+ private static final SelectorUtils instance = new SelectorUtils();
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Private Constructor
+ */
+ private SelectorUtils() {
+ }
+
+ /**
+ * Retrieves the instance of the Singleton.
+ * @return singleton instance
+ */
+ public static SelectorUtils getInstance() {
+ return instance;
+ }
+
+ /**
+ * Tests whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ * <p>
+ * This is not a general purpose test and should only be used if you
+ * can live with false positives. For example, <code>pattern=**\a</code>
+ * and <code>str=b</code> will yield <code>true</code>.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * <code>null</code>.
+ * @param str The path to match, as a String. Must not be
+ * <code>null</code>.
+ *
+ * @return whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ */
+ public static boolean matchPatternStart(String pattern, String str) {
+ return matchPatternStart(pattern, str, true);
+ }
+
+ /**
+ * Tests whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ * <p>
+ * This is not a general purpose test and should only be used if you
+ * can live with false positives. For example, <code>pattern=**\a</code>
+ * and <code>str=b</code> will yield <code>true</code>.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * <code>null</code>.
+ * @param str The path to match, as a String. Must not be
+ * <code>null</code>.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ *
+ * @return whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ */
+ public static boolean matchPatternStart(String pattern, String str,
+ boolean isCaseSensitive) {
+ // When str starts with a File.separator, pattern has to start with a
+ // File.separator.
+ // When pattern starts with a File.separator, str has to start with a
+ // File.separator.
+ if (str.startsWith(File.separator)
+ != pattern.startsWith(File.separator)) {
+ return false;
+ }
+
+ String[] patDirs = tokenizePathAsArray(pattern);
+ String[] strDirs = tokenizePathAsArray(str);
+ return matchPatternStart(patDirs, strDirs, isCaseSensitive);
+ }
+
+
+ /**
+ * Tests whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ * <p>
+ * This is not a general purpose test and should only be used if you
+ * can live with false positives. For example, <code>pattern=**\a</code>
+ * and <code>str=b</code> will yield <code>true</code>.
+ *
+ * @param patDirs The tokenized pattern to match against. Must not be
+ * <code>null</code>.
+ * @param strDirs The tokenized path to match. Must not be
+ * <code>null</code>.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ *
+ * @return whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ */
+ static boolean matchPatternStart(String[] patDirs, String[] strDirs,
+ boolean isCaseSensitive) {
+ int patIdxStart = 0;
+ int patIdxEnd = patDirs.length - 1;
+ int strIdxStart = 0;
+ int strIdxEnd = strDirs.length - 1;
+
+ // up to first '**'
+ while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+ String patDir = patDirs[patIdxStart];
+ if (patDir.equals(DEEP_TREE_MATCH)) {
+ break;
+ }
+ if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
+ return false;
+ }
+ patIdxStart++;
+ strIdxStart++;
+ }
+
+ // CheckStyle:SimplifyBooleanReturnCheck OFF
+ // Check turned off as the code needs the comments for the various
+ // code paths.
+ if (strIdxStart > strIdxEnd) {
+ // String is exhausted
+ return true;
+ } else if (patIdxStart > patIdxEnd) {
+ // String not exhausted, but pattern is. Failure.
+ return false;
+ } else {
+ // pattern now holds ** while string is not exhausted
+ // this will generate false positives but we can live with that.
+ return true;
+ }
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * If you need to call this method multiple times with the same
+ * pattern you should rather use TokenizedPath
+ *
+ * @see TokenizedPath
+ *
+ * @param pattern The pattern to match against. Must not be
+ * <code>null</code>.
+ * @param str The path to match, as a String. Must not be
+ * <code>null</code>.
+ *
+ * @return <code>true</code> if the pattern matches against the string,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean matchPath(String pattern, String str) {
+ String[] patDirs = tokenizePathAsArray(pattern);
+ return matchPath(patDirs, tokenizePathAsArray(str), true);
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * If you need to call this method multiple times with the same
+ * pattern you should rather use TokenizedPattern
+ *
+ * @see TokenizedPattern
+ *
+ * @param pattern The pattern to match against. Must not be
+ * <code>null</code>.
+ * @param str The path to match, as a String. Must not be
+ * <code>null</code>.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ *
+ * @return <code>true</code> if the pattern matches against the string,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean matchPath(String pattern, String str,
+ boolean isCaseSensitive) {
+ String[] patDirs = tokenizePathAsArray(pattern);
+ return matchPath(patDirs, tokenizePathAsArray(str), isCaseSensitive);
+ }
+
+ /**
+ * Core implementation of matchPath. It is isolated so that it
+ * can be called from TokenizedPattern.
+ */
+ static boolean matchPath(String[] tokenizedPattern, String[] strDirs,
+ boolean isCaseSensitive) {
+ int patIdxStart = 0;
+ int patIdxEnd = tokenizedPattern.length - 1;
+ int strIdxStart = 0;
+ int strIdxEnd = strDirs.length - 1;
+
+ // up to first '**'
+ while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+ String patDir = tokenizedPattern[patIdxStart];
+ if (patDir.equals(DEEP_TREE_MATCH)) {
+ break;
+ }
+ if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
+ return false;
+ }
+ patIdxStart++;
+ strIdxStart++;
+ }
+ if (strIdxStart > strIdxEnd) {
+ // String is exhausted
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ if (patIdxStart > patIdxEnd) {
+ // String not exhausted, but pattern is. Failure.
+ return false;
+ }
+ }
+
+ // up to last '**'
+ while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+ String patDir = tokenizedPattern[patIdxEnd];
+ if (patDir.equals(DEEP_TREE_MATCH)) {
+ break;
+ }
+ if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) {
+ return false;
+ }
+ patIdxEnd--;
+ strIdxEnd--;
+ }
+ if (strIdxStart > strIdxEnd) {
+ // String is exhausted
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+ int patIdxTmp = -1;
+ for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+ if (tokenizedPattern[i].equals(DEEP_TREE_MATCH)) {
+ patIdxTmp = i;
+ break;
+ }
+ }
+ if (patIdxTmp == patIdxStart + 1) {
+ // '**/**' situation, so skip one
+ patIdxStart++;
+ continue;
+ }
+ // Find the pattern between padIdxStart & padIdxTmp in str between
+ // strIdxStart & strIdxEnd
+ int patLength = (patIdxTmp - patIdxStart - 1);
+ int strLength = (strIdxEnd - strIdxStart + 1);
+ int foundIdx = -1;
+ strLoop:
+ for (int i = 0; i <= strLength - patLength; i++) {
+ for (int j = 0; j < patLength; j++) {
+ String subPat = tokenizedPattern[patIdxStart + j + 1];
+ String subStr = strDirs[strIdxStart + i + j];
+ if (!match(subPat, subStr, isCaseSensitive)) {
+ continue strLoop;
+ }
+ }
+
+ foundIdx = strIdxStart + i;
+ break;
+ }
+
+ if (foundIdx == -1) {
+ return false;
+ }
+
+ patIdxStart = patIdxTmp;
+ strIdxStart = foundIdx + patLength;
+ }
+
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Tests whether or not a string matches against a pattern.
+ * The pattern may contain two special characters:<br>
+ * '*' means zero or more characters<br>
+ * '?' means one and only one character
+ *
+ * @param pattern The pattern to match against.
+ * Must not be <code>null</code>.
+ * @param str The string which must be matched against the pattern.
+ * Must not be <code>null</code>.
+ *
+ * @return <code>true</code> if the string matches against the pattern,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean match(String pattern, String str) {
+ return match(pattern, str, true);
+ }
+
+ /**
+ * Tests whether or not a string matches against a pattern.
+ * The pattern may contain two special characters:<br>
+ * '*' means zero or more characters<br>
+ * '?' means one and only one character
+ *
+ * @param pattern The pattern to match against.
+ * Must not be <code>null</code>.
+ * @param str The string which must be matched against the pattern.
+ * Must not be <code>null</code>.
+ * @param caseSensitive Whether or not matching should be performed
+ * case sensitively.
+ *
+ *
+ * @return <code>true</code> if the string matches against the pattern,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean match(String pattern, String str,
+ boolean caseSensitive) {
+ char[] patArr = pattern.toCharArray();
+ char[] strArr = str.toCharArray();
+ int patIdxStart = 0;
+ int patIdxEnd = patArr.length - 1;
+ int strIdxStart = 0;
+ int strIdxEnd = strArr.length - 1;
+ char ch;
+
+ boolean containsStar = false;
+ for (int i = 0; i < patArr.length; i++) {
+ if (patArr[i] == '*') {
+ containsStar = true;
+ break;
+ }
+ }
+
+ if (!containsStar) {
+ // No '*'s, so we make a shortcut
+ if (patIdxEnd != strIdxEnd) {
+ return false; // Pattern and string do not have the same size
+ }
+ for (int i = 0; i <= patIdxEnd; i++) {
+ ch = patArr[i];
+ if (ch != '?') {
+ if (different(caseSensitive, ch, strArr[i])) {
+ return false; // Character mismatch
+ }
+ }
+ }
+ return true; // String matches against pattern
+ }
+
+ if (patIdxEnd == 0) {
+ return true; // Pattern contains only '*', which matches anything
+ }
+
+ // Process characters before first star
+ while (true) {
+ ch = patArr[patIdxStart];
+ if (ch == '*' || strIdxStart > strIdxEnd) {
+ break;
+ }
+ if (ch != '?') {
+ if (different(caseSensitive, ch, strArr[strIdxStart])) {
+ return false; // Character mismatch
+ }
+ }
+ patIdxStart++;
+ strIdxStart++;
+ }
+ if (strIdxStart > strIdxEnd) {
+ // All characters in the string are used. Check if only '*'s are
+ // left in the pattern. If so, we succeeded. Otherwise failure.
+ return allStars(patArr, patIdxStart, patIdxEnd);
+ }
+
+ // Process characters after last star
+ while (true) {
+ ch = patArr[patIdxEnd];
+ if (ch == '*' || strIdxStart > strIdxEnd) {
+ break;
+ }
+ if (ch != '?') {
+ if (different(caseSensitive, ch, strArr[strIdxEnd])) {
+ return false; // Character mismatch
+ }
+ }
+ patIdxEnd--;
+ strIdxEnd--;
+ }
+ if (strIdxStart > strIdxEnd) {
+ // All characters in the string are used. Check if only '*'s are
+ // left in the pattern. If so, we succeeded. Otherwise failure.
+ return allStars(patArr, patIdxStart, patIdxEnd);
+ }
+
+ // process pattern between stars. padIdxStart and patIdxEnd point
+ // always to a '*'.
+ while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+ int patIdxTmp = -1;
+ for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+ if (patArr[i] == '*') {
+ patIdxTmp = i;
+ break;
+ }
+ }
+ if (patIdxTmp == patIdxStart + 1) {
+ // Two stars next to each other, skip the first one.
+ patIdxStart++;
+ continue;
+ }
+ // Find the pattern between padIdxStart & padIdxTmp in str between
+ // strIdxStart & strIdxEnd
+ int patLength = (patIdxTmp - patIdxStart - 1);
+ int strLength = (strIdxEnd - strIdxStart + 1);
+ int foundIdx = -1;
+ strLoop:
+ for (int i = 0; i <= strLength - patLength; i++) {
+ for (int j = 0; j < patLength; j++) {
+ ch = patArr[patIdxStart + j + 1];
+ if (ch != '?') {
+ if (different(caseSensitive, ch,
+ strArr[strIdxStart + i + j])) {
+ continue strLoop;
+ }
+ }
+ }
+
+ foundIdx = strIdxStart + i;
+ break;
+ }
+
+ if (foundIdx == -1) {
+ return false;
+ }
+
+ patIdxStart = patIdxTmp;
+ strIdxStart = foundIdx + patLength;
+ }
+
+ // All characters in the string are used. Check if only '*'s are left
+ // in the pattern. If so, we succeeded. Otherwise failure.
+ return allStars(patArr, patIdxStart, patIdxEnd);
+ }
+
+ private static boolean allStars(char[] chars, int start, int end) {
+ for (int i = start; i <= end; ++i) {
+ if (chars[i] != '*') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean different(
+ boolean caseSensitive, char ch, char other) {
+ return caseSensitive
+ ? ch != other
+ : Character.toUpperCase(ch) != Character.toUpperCase(other);
+ }
+
+ /**
+ * Breaks a path up into a Vector of path elements, tokenizing on
+ * <code>File.separator</code>.
+ *
+ * @param path Path to tokenize. Must not be <code>null</code>.
+ *
+ * @return a Vector of path elements from the tokenized path
+ */
+ public static Vector<String> tokenizePath(String path) {
+ return tokenizePath(path, File.separator);
+ }
+
+ /**
+ * Breaks a path up into a Vector of path elements, tokenizing on
+ *
+ * @param path Path to tokenize. Must not be <code>null</code>.
+ * @param separator the separator against which to tokenize.
+ *
+ * @return a Vector of path elements from the tokenized path
+ * @since Ant 1.6
+ */
+ public static Vector<String> tokenizePath(String path, String separator) {
+ Vector<String> ret = new Vector<String>();
+ if (FileUtils.isAbsolutePath(path)) {
+ String[] s = FILE_UTILS.dissect(path);
+ ret.add(s[0]);
+ path = s[1];
+ }
+ StringTokenizer st = new StringTokenizer(path, separator);
+ while (st.hasMoreTokens()) {
+ ret.addElement(st.nextToken());
+ }
+ return ret;
+ }
+
+ /**
+ * Same as {@link #tokenizePath tokenizePath} but hopefully faster.
+ */
+ /*package*/ static String[] tokenizePathAsArray(String path) {
+ String root = null;
+ if (FileUtils.isAbsolutePath(path)) {
+ String[] s = FILE_UTILS.dissect(path);
+ root = s[0];
+ path = s[1];
+ }
+ char sep = File.separatorChar;
+ int start = 0;
+ int len = path.length();
+ int count = 0;
+ for (int pos = 0; pos < len; pos++) {
+ if (path.charAt(pos) == sep) {
+ if (pos != start) {
+ count++;
+ }
+ start = pos + 1;
+ }
+ }
+ if (len != start) {
+ count++;
+ }
+ String[] l = new String[count + ((root == null) ? 0 : 1)];
+
+ if (root != null) {
+ l[0] = root;
+ count = 1;
+ } else {
+ count = 0;
+ }
+ start = 0;
+ for (int pos = 0; pos < len; pos++) {
+ if (path.charAt(pos) == sep) {
+ if (pos != start) {
+ String tok = path.substring(start, pos);
+ l[count++] = tok;
+ }
+ start = pos + 1;
+ }
+ }
+ if (len != start) {
+ String tok = path.substring(start);
+ l[count/*++*/] = tok;
+ }
+ return l;
+ }
+
+ /**
+ * Returns dependency information on these two files. If src has been
+ * modified later than target, it returns true. If target doesn't exist,
+ * it likewise returns true. Otherwise, target is newer than src and
+ * is not out of date, thus the method returns false. It also returns
+ * false if the src file doesn't even exist, since how could the
+ * target then be out of date.
+ *
+ * @param src the original file
+ * @param target the file being compared against
+ * @param granularity the amount in seconds of slack we will give in
+ * determining out of dateness
+ * @return whether the target is out of date
+ */
+ public static boolean isOutOfDate(File src, File target, int granularity) {
+ if (!src.exists()) {
+ return false;
+ }
+ if (!target.exists()) {
+ return true;
+ }
+ if ((src.lastModified() - granularity) > target.lastModified()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns dependency information on these two resources. If src has been
+ * modified later than target, it returns true. If target doesn't exist,
+ * it likewise returns true. Otherwise, target is newer than src and
+ * is not out of date, thus the method returns false. It also returns
+ * false if the src file doesn't even exist, since how could the
+ * target then be out of date.
+ *
+ * @param src the original resource
+ * @param target the resource being compared against
+ * @param granularity the int amount in seconds of slack we will give in
+ * determining out of dateness
+ * @return whether the target is out of date
+ */
+ public static boolean isOutOfDate(Resource src, Resource target,
+ int granularity) {
+ return isOutOfDate(src, target, (long) granularity);
+ }
+
+ /**
+ * Returns dependency information on these two resources. If src has been
+ * modified later than target, it returns true. If target doesn't exist,
+ * it likewise returns true. Otherwise, target is newer than src and
+ * is not out of date, thus the method returns false. It also returns
+ * false if the src file doesn't even exist, since how could the
+ * target then be out of date.
+ *
+ * @param src the original resource
+ * @param target the resource being compared against
+ * @param granularity the long amount in seconds of slack we will give in
+ * determining out of dateness
+ * @return whether the target is out of date
+ */
+ public static boolean isOutOfDate(Resource src, Resource target, long granularity) {
+ long sourceLastModified = src.getLastModified();
+ long targetLastModified = target.getLastModified();
+ return src.isExists()
+ && (sourceLastModified == Resource.UNKNOWN_DATETIME
+ || targetLastModified == Resource.UNKNOWN_DATETIME
+ || (sourceLastModified - granularity) > targetLastModified);
+ }
+
+ /**
+ * "Flattens" a string by removing all whitespace (space, tab, linefeed,
+ * carriage return, and formfeed). This uses StringTokenizer and the
+ * default set of tokens as documented in the single argument constructor.
+ *
+ * @param input a String to remove all whitespace.
+ * @return a String that has had all whitespace removed.
+ */
+ public static String removeWhitespace(String input) {
+ StringBuffer result = new StringBuffer();
+ if (input != null) {
+ StringTokenizer st = new StringTokenizer(input);
+ while (st.hasMoreTokens()) {
+ result.append(st.nextToken());
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Tests if a string contains stars or question marks
+ * @param input a String which one wants to test for containing wildcard
+ * @return true if the string contains at least a star or a question mark
+ */
+ public static boolean hasWildcards(String input) {
+ return (input.indexOf('*') != -1 || input.indexOf('?') != -1);
+ }
+
+ /**
+ * removes from a pattern all tokens to the right containing wildcards
+ * @param input the input string
+ * @return the leftmost part of the pattern without wildcards
+ */
+ public static String rtrimWildcardTokens(String input) {
+ return new TokenizedPattern(input).rtrimWildcardTokens().toString();
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SignedSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SignedSelector.java
new file mode 100644
index 00000000..cd515022
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SignedSelector.java
@@ -0,0 +1,59 @@
+/*
+ * 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.tools.ant.types.selectors;
+import java.io.File;
+
+import org.apache.tools.ant.taskdefs.condition.IsSigned;
+import org.apache.tools.ant.types.DataType;
+
+/**
+ * Selector that chooses files based on whether they are signed or not.
+ *
+ * @since 1.7
+ */
+public class SignedSelector extends DataType implements FileSelector {
+ private IsSigned isSigned = new IsSigned();
+
+ /**
+ * The signature name to check jarfile for.
+ *
+ * @param name signature to look for.
+ */
+ public void setName(String name) {
+ isSigned.setName(name);
+ }
+
+ /**
+ * The heart of the matter. This is where the selector gets to decide
+ * on the inclusion of a file in a particular fileset.
+ *
+ * @param basedir not used by this selector
+ * @param filename not used by this selector
+ * @param file path to file to be selected
+ * @return whether the file should be selected or not
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+ if (file.isDirectory()) {
+ return false; // Quick return: directories cannot be signed
+ }
+ isSigned.setProject(getProject());
+ isSigned.setFile(file);
+ return isSigned.eval();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SizeSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SizeSelector.java
new file mode 100644
index 00000000..9ff91ad9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/SizeSelector.java
@@ -0,0 +1,279 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.types.Comparison;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Selector that filters files based on their size.
+ *
+ * @since 1.5
+ */
+public class SizeSelector extends BaseExtendSelector {
+
+ /** Constants for kilo, kibi etc */
+ private static final int KILO = 1000;
+ private static final int KIBI = 1024;
+ private static final int KIBI_POS = 4;
+ private static final int MEGA = 1000000;
+ private static final int MEGA_POS = 9;
+ private static final int MEBI = 1048576;
+ private static final int MEBI_POS = 13;
+ private static final long GIGA = 1000000000L;
+ private static final int GIGA_POS = 18;
+ private static final long GIBI = 1073741824L;
+ private static final int GIBI_POS = 22;
+ private static final long TERA = 1000000000000L;
+ private static final int TERA_POS = 27;
+ private static final long TEBI = 1099511627776L;
+ private static final int TEBI_POS = 31;
+ private static final int END_POS = 36;
+
+ /** Used for parameterized custom selector */
+ public static final String SIZE_KEY = "value";
+ /** Used for parameterized custom selector */
+ public static final String UNITS_KEY = "units";
+ /** Used for parameterized custom selector */
+ public static final String WHEN_KEY = "when";
+
+ private long size = -1;
+ private long multiplier = 1;
+ private long sizelimit = -1;
+ private Comparison when = Comparison.EQUAL;
+
+ /**
+ * Creates a new <code>SizeSelector</code> instance.
+ *
+ */
+ public SizeSelector() {
+ }
+
+ /**
+ * Returns a <code>String</code> object representing the specified
+ * SizeSelector. This is "{sizeselector value: " + <"compare",
+ * "less", "more", "equal"> + "}".
+ * @return a string describing this object
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder("{sizeselector value: ");
+ buf.append(sizelimit);
+ buf.append("compare: ").append(when.getValue());
+ buf.append("}");
+ return buf.toString();
+ }
+
+ /**
+ * A size selector needs to know what size to base its selecting on.
+ * This will be further modified by the multiplier to get an
+ * actual size limit.
+ *
+ * @param size the size to select against expressed in units.
+ */
+ public void setValue(long size) {
+ this.size = size;
+ if (multiplier != 0 && size > -1) {
+ sizelimit = size * multiplier;
+ }
+ }
+
+ /**
+ * Sets the units to use for the comparison. This is a little
+ * complicated because common usage has created standards that
+ * play havoc with capitalization rules. Thus, some people will
+ * use "K" for indicating 1000's, when the SI standard calls for
+ * "k". Others have tried to introduce "K" as a multiple of 1024,
+ * but that falls down when you reach "M", since "m" is already
+ * defined as 0.001.
+ * <p>
+ * To get around this complexity, a number of standards bodies
+ * have proposed the 2^10 standard, and at least one has adopted
+ * it. But we are still left with a populace that isn't clear on
+ * how capitalization should work.
+ * <p>
+ * We therefore ignore capitalization as much as possible.
+ * Completely mixed case is not possible, but all upper and lower
+ * forms are accepted for all long and short forms. Since we have
+ * no need to work with the 0.001 case, this practice works here.
+ * <p>
+ * This function translates all the long and short forms that a
+ * unit prefix can occur in and translates them into a single
+ * multiplier.
+ *
+ * @param units The units to compare the size to, using an
+ * EnumeratedAttribute.
+ */
+ public void setUnits(ByteUnits units) {
+ int i = units.getIndex();
+ multiplier = 0;
+ if (i > -1 && i < KIBI_POS) {
+ multiplier = KILO;
+ } else if (i < MEGA_POS) {
+ multiplier = KIBI;
+ } else if (i < MEBI_POS) {
+ multiplier = MEGA;
+ } else if (i < GIGA_POS) {
+ multiplier = MEBI;
+ } else if (i < GIBI_POS) {
+ multiplier = GIGA;
+ } else if (i < TERA_POS) {
+ multiplier = GIBI;
+ } else if (i < TEBI_POS) {
+ multiplier = TERA;
+ } else if (i < END_POS) {
+ multiplier = TEBI;
+ }
+ if (multiplier > 0 && size > -1) {
+ sizelimit = size * multiplier;
+ }
+ }
+
+ /**
+ * This specifies when the file should be selected, whether it be
+ * when the file matches a particular size, when it is smaller,
+ * or whether it is larger.
+ *
+ * @param when The comparison to perform, an EnumeratedAttribute.
+ */
+ public void setWhen(SizeComparisons when) {
+ this.when = when;
+ }
+
+ /**
+ * When using this as a custom selector, this method will be called.
+ * It translates each parameter into the appropriate setXXX() call.
+ *
+ * @param parameters the complete set of parameters for this selector.
+ */
+ public void setParameters(Parameter[] parameters) {
+ super.setParameters(parameters);
+ if (parameters != null) {
+ for (int i = 0; i < parameters.length; i++) {
+ String paramname = parameters[i].getName();
+ if (SIZE_KEY.equalsIgnoreCase(paramname)) {
+ try {
+ setValue(Long.parseLong(parameters[i].getValue()));
+ } catch (NumberFormatException nfe) {
+ setError("Invalid size setting "
+ + parameters[i].getValue());
+ }
+ } else if (UNITS_KEY.equalsIgnoreCase(paramname)) {
+ ByteUnits units = new ByteUnits();
+ units.setValue(parameters[i].getValue());
+ setUnits(units);
+ } else if (WHEN_KEY.equalsIgnoreCase(paramname)) {
+ SizeComparisons scmp = new SizeComparisons();
+ scmp.setValue(parameters[i].getValue());
+ setWhen(scmp);
+ } else {
+ setError("Invalid parameter " + paramname);
+ }
+ }
+ }
+ }
+
+ /**
+ * <p>Checks to make sure all settings are kosher. In this case, it
+ * means that the size attribute has been set (to a positive value),
+ * that the multiplier has a valid setting, and that the size limit
+ * is valid. Since the latter is a calculated value, this can only
+ * fail due to a programming error.
+ * </p>
+ * <p>If a problem is detected, the setError() method is called.
+ * </p>
+ */
+ public void verifySettings() {
+ if (size < 0) {
+ setError("The value attribute is required, and must be positive");
+ } else if (multiplier < 1) {
+ setError("Invalid Units supplied, must be K,Ki,M,Mi,G,Gi,T,or Ti");
+ } else if (sizelimit < 0) {
+ setError("Internal error: Code is not setting sizelimit correctly");
+ }
+ }
+
+ /**
+ * The heart of the matter. This is where the selector gets to decide
+ * on the inclusion of a file in a particular fileset.
+ *
+ * @param basedir A java.io.File object for the base directory.
+ * @param filename The name of the file to check.
+ * @param file A File object for this filename.
+ * @return whether the file should be selected or not.
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+
+ // throw BuildException on error
+ validate();
+
+ // Directory size never selected for
+ if (file.isDirectory()) {
+ return true;
+ }
+ long diff = file.length() - sizelimit;
+ return when.evaluate(diff == 0 ? 0 : (int) (diff / Math.abs(diff)));
+ }
+
+
+ /**
+ * Enumerated attribute with the values for units.
+ * <p>
+ * This treats the standard SI units as representing powers of ten,
+ * as they should. If you want the powers of 2 that approximate
+ * the SI units, use the first two characters followed by a
+ * <code>bi</code>. So 1024 (2^10) becomes <code>kibi</code>,
+ * 1048576 (2^20) becomes <code>mebi</code>, 1073741824 (2^30)
+ * becomes <code>gibi</code>, and so on. The symbols are also
+ * accepted, and these are the first letter capitalized followed
+ * by an <code>i</code>. <code>Ki</code>, <code>Mi</code>,
+ * <code>Gi</code>, and so on. Capitalization variations on these
+ * are also accepted.
+ * <p>
+ * This binary prefix system is approved by the IEC and appears on
+ * its way for approval by other agencies, but it is not an SI
+ * standard. It disambiguates things for us, though.
+ */
+ public static class ByteUnits extends EnumeratedAttribute {
+ /**
+ * @return the values as an array of strings
+ */
+ public String[] getValues() {
+ return new String[]{"K", "k", "kilo", "KILO",
+ "Ki", "KI", "ki", "kibi", "KIBI",
+ "M", "m", "mega", "MEGA",
+ "Mi", "MI", "mi", "mebi", "MEBI",
+ "G", "g", "giga", "GIGA",
+ "Gi", "GI", "gi", "gibi", "GIBI",
+ "T", "t", "tera", "TERA",
+ /* You wish! */ "Ti", "TI", "ti", "tebi", "TEBI"
+ };
+ }
+ }
+
+ /**
+ * Enumerated attribute with the values for size comparison.
+ */
+ public static class SizeComparisons extends Comparison {
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/TokenizedPath.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/TokenizedPath.java
new file mode 100644
index 00000000..a712759c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/TokenizedPath.java
@@ -0,0 +1,222 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.SymbolicLinkUtils;
+
+/**
+ * Container for a path that has been split into its components.
+ * @since 1.8.0
+ */
+public class TokenizedPath {
+
+ /**
+ * Instance that holds no tokens at all.
+ */
+ public static final TokenizedPath EMPTY_PATH =
+ new TokenizedPath("", new String[0]);
+
+ /** Helper. */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+ /** Helper. */
+ private static final SymbolicLinkUtils SYMLINK_UTILS =
+ SymbolicLinkUtils.getSymbolicLinkUtils();
+ /** iterations for case-sensitive scanning. */
+ private static final boolean[] CS_SCAN_ONLY = new boolean[] {true};
+ /** iterations for non-case-sensitive scanning. */
+ private static final boolean[] CS_THEN_NON_CS = new boolean[] {true, false};
+
+ private final String path;
+ private final String[] tokenizedPath;
+
+ /**
+ * Initialize the TokenizedPath by parsing it.
+ * @param path The path to tokenize. Must not be
+ * <code>null</code>.
+ */
+ public TokenizedPath(String path) {
+ this(path, SelectorUtils.tokenizePathAsArray(path));
+ }
+
+ /**
+ * Creates a new path as a child of another path.
+ *
+ * @param parent the parent path
+ * @param child the child, must not contain the file separator
+ */
+ public TokenizedPath(TokenizedPath parent, String child) {
+ if (parent.path.length() > 0
+ && parent.path.charAt(parent.path.length() - 1)
+ != File.separatorChar) {
+ path = parent.path + File.separatorChar + child;
+ } else {
+ path = parent.path + child;
+ }
+ tokenizedPath = new String[parent.tokenizedPath.length + 1];
+ System.arraycopy(parent.tokenizedPath, 0, tokenizedPath, 0,
+ parent.tokenizedPath.length);
+ tokenizedPath[parent.tokenizedPath.length] = child;
+ }
+
+ /* package */ TokenizedPath(String path, String[] tokens) {
+ this.path = path;
+ this.tokenizedPath = tokens;
+ }
+
+ /**
+ * @return The original path String
+ */
+ @Override
+ public String toString() {
+ return path;
+ }
+
+ /**
+ * The depth (or length) of a path.
+ */
+ public int depth() {
+ return tokenizedPath.length;
+ }
+
+ /* package */ String[] getTokens() {
+ return tokenizedPath;
+ }
+
+ /**
+ * From <code>base</code> traverse the filesystem in order to find
+ * a file that matches the given name.
+ *
+ * @param base base File (dir).
+ * @param cs whether to scan case-sensitively.
+ * @return File object that points to the file in question or null.
+ */
+ public File findFile(File base, final boolean cs) {
+ String[] tokens = tokenizedPath;
+ if (FileUtils.isAbsolutePath(path)) {
+ if (base == null) {
+ String[] s = FILE_UTILS.dissect(path);
+ base = new File(s[0]);
+ tokens = SelectorUtils.tokenizePathAsArray(s[1]);
+ } else {
+ File f = FILE_UTILS.normalize(path);
+ String s = FILE_UTILS.removeLeadingPath(base, f);
+ if (s.equals(f.getAbsolutePath())) {
+ //removing base from path yields no change; path
+ //not child of base
+ return null;
+ }
+ tokens = SelectorUtils.tokenizePathAsArray(s);
+ }
+ }
+ return findFile(base, tokens, cs);
+ }
+
+ /**
+ * Do we have to traverse a symlink when trying to reach path from
+ * basedir?
+ * @param base base File (dir).
+ */
+ public boolean isSymlink(File base) {
+ for (int i = 0; i < tokenizedPath.length; i++) {
+ try {
+ if ((base != null
+ && SYMLINK_UTILS.isSymbolicLink(base, tokenizedPath[i]))
+ ||
+ (base == null
+ && SYMLINK_UTILS.isSymbolicLink(tokenizedPath[i]))
+ ) {
+ return true;
+ }
+ base = new File(base, tokenizedPath[i]);
+ } catch (java.io.IOException ioe) {
+ String msg = "IOException caught while checking "
+ + "for links, couldn't get canonical path!";
+ // will be caught and redirected to Ant's logging system
+ System.err.println(msg);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * true if the original paths are equal.
+ */
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof TokenizedPath
+ && path.equals(((TokenizedPath) o).path);
+ }
+
+ @Override
+ public int hashCode() {
+ return path.hashCode();
+ }
+
+ /**
+ * From <code>base</code> traverse the filesystem in order to find
+ * a file that matches the given stack of names.
+ *
+ * @param base base File (dir) - must not be null.
+ * @param pathElements array of path elements (dirs...file).
+ * @param cs whether to scan case-sensitively.
+ * @return File object that points to the file in question or null.
+ */
+ private static File findFile(File base, final String[] pathElements,
+ final boolean cs) {
+ for (int current = 0; current < pathElements.length; current++) {
+ if (!base.isDirectory()) {
+ return null;
+ }
+ String[] files = base.list();
+ if (files == null) {
+ throw new BuildException("IO error scanning directory "
+ + base.getAbsolutePath());
+ }
+ boolean found = false;
+ boolean[] matchCase = cs ? CS_SCAN_ONLY : CS_THEN_NON_CS;
+ for (int i = 0; !found && i < matchCase.length; i++) {
+ for (int j = 0; !found && j < files.length; j++) {
+ if (matchCase[i]
+ ? files[j].equals(pathElements[current])
+ : files[j].equalsIgnoreCase(pathElements[current])) {
+ base = new File(base, files[j]);
+ found = true;
+ }
+ }
+ }
+ if (!found) {
+ return null;
+ }
+ }
+ return pathElements.length == 0 && !base.isDirectory() ? null : base;
+ }
+
+ /**
+ * Creates a TokenizedPattern from the same tokens that make up
+ * this path.
+ */
+ public TokenizedPattern toPattern() {
+ return new TokenizedPattern(path, tokenizedPath);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/TokenizedPattern.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/TokenizedPattern.java
new file mode 100644
index 00000000..dbe7ec81
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/TokenizedPattern.java
@@ -0,0 +1,177 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+
+/**
+ * Provides reusable path pattern matching. PathPattern is preferable
+ * to equivalent SelectorUtils methods if you need to execute multiple
+ * matching with the same pattern because here the pattern itself will
+ * be parsed only once.
+ * @see SelectorUtils#matchPath(String, String)
+ * @see SelectorUtils#matchPath(String, String, boolean)
+ * @since 1.8.0
+ */
+public class TokenizedPattern {
+
+ /**
+ * Instance that holds no tokens at all.
+ */
+ public static final TokenizedPattern EMPTY_PATTERN =
+ new TokenizedPattern("", new String[0]);
+
+ private final String pattern;
+ private final String[] tokenizedPattern;
+
+ /**
+ * Initialize the PathPattern by parsing it.
+ * @param pattern The pattern to match against. Must not be
+ * <code>null</code>.
+ */
+ public TokenizedPattern(String pattern) {
+ this(pattern, SelectorUtils.tokenizePathAsArray(pattern));
+ }
+
+ TokenizedPattern(String pattern, String[] tokens) {
+ this.pattern = pattern;
+ this.tokenizedPattern = tokens;
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * @param path The path to match, as a String. Must not be
+ * <code>null</code>.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ *
+ * @return <code>true</code> if the pattern matches against the string,
+ * or <code>false</code> otherwise.
+ */
+ public boolean matchPath(TokenizedPath path, boolean isCaseSensitive) {
+ return SelectorUtils.matchPath(tokenizedPattern, path.getTokens(),
+ isCaseSensitive);
+ }
+
+ /**
+ * Tests whether or not this pattern matches the start of
+ * a path.
+ */
+ public boolean matchStartOf(TokenizedPath path,
+ boolean caseSensitive) {
+ return SelectorUtils.matchPatternStart(tokenizedPattern,
+ path.getTokens(), caseSensitive);
+ }
+
+ /**
+ * @return The pattern String
+ */
+ public String toString() {
+ return pattern;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ /**
+ * true if the original patterns are equal.
+ */
+ public boolean equals(Object o) {
+ return o instanceof TokenizedPattern
+ && pattern.equals(((TokenizedPattern) o).pattern);
+ }
+
+ public int hashCode() {
+ return pattern.hashCode();
+ }
+
+ /**
+ * The depth (or length) of a pattern.
+ */
+ public int depth() {
+ return tokenizedPattern.length;
+ }
+
+ /**
+ * Does the tokenized pattern contain the given string?
+ */
+ public boolean containsPattern(String pat) {
+ for (int i = 0; i < tokenizedPattern.length; i++) {
+ if (tokenizedPattern[i].equals(pat)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns a new TokenizedPath where all tokens of this pattern to
+ * the right containing wildcards have been removed
+ * @return the leftmost part of the pattern without wildcards
+ */
+ public TokenizedPath rtrimWildcardTokens() {
+ StringBuilder sb = new StringBuilder();
+ int newLen = 0;
+ for (; newLen < tokenizedPattern.length; newLen++) {
+ if (SelectorUtils.hasWildcards(tokenizedPattern[newLen])) {
+ break;
+ }
+ if (newLen > 0
+ && sb.charAt(sb.length() - 1) != File.separatorChar) {
+ sb.append(File.separator);
+ }
+ sb.append(tokenizedPattern[newLen]);
+ }
+ if (newLen == 0) {
+ return TokenizedPath.EMPTY_PATH;
+ }
+ String[] newPats = new String[newLen];
+ System.arraycopy(tokenizedPattern, 0, newPats, 0, newLen);
+ return new TokenizedPath(sb.toString(), newPats);
+ }
+
+ /**
+ * true if the last token equals the given string.
+ */
+ public boolean endsWith(String s) {
+ return tokenizedPattern.length > 0
+ && tokenizedPattern[tokenizedPattern.length - 1].equals(s);
+ }
+
+ /**
+ * Returns a new pattern without the last token of this pattern.
+ */
+ public TokenizedPattern withoutLastToken() {
+ if (tokenizedPattern.length == 0) {
+ throw new IllegalStateException("can't strip a token from nothing");
+ } else if (tokenizedPattern.length == 1) {
+ return EMPTY_PATTERN;
+ } else {
+ String toStrip = tokenizedPattern[tokenizedPattern.length - 1];
+ int index = pattern.lastIndexOf(toStrip);
+ String[] tokens = new String[tokenizedPattern.length - 1];
+ System.arraycopy(tokenizedPattern, 0, tokens, 0,
+ tokenizedPattern.length - 1);
+ return new TokenizedPattern(pattern.substring(0, index), tokens);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/TypeSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/TypeSelector.java
new file mode 100644
index 00000000..fd3684d6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/TypeSelector.java
@@ -0,0 +1,135 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Selector that selects a certain kind of file: directory or regular.
+ *
+ * @since 1.6
+ */
+public class TypeSelector extends BaseExtendSelector {
+
+ private String type = null;
+
+ /** Key to used for parameterized custom selector */
+ public static final String TYPE_KEY = "type";
+
+ /**
+ * Creates a new <code>TypeSelector</code> instance.
+ *
+ */
+ public TypeSelector() {
+ }
+
+ /**
+ * @return a string describing this object
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder("{typeselector type: ");
+ buf.append(type);
+ buf.append("}");
+ return buf.toString();
+ }
+
+ /**
+ * Set the type of file to require.
+ * @param fileTypes the type of file - file or dir
+ */
+ public void setType(FileType fileTypes) {
+ this.type = fileTypes.getValue();
+ }
+
+ /**
+ * When using this as a custom selector, this method will be called.
+ * It translates each parameter into the appropriate setXXX() call.
+ *
+ * @param parameters the complete set of parameters for this selector
+ */
+ public void setParameters(Parameter[] parameters) {
+ super.setParameters(parameters);
+ if (parameters != null) {
+ for (int i = 0; i < parameters.length; i++) {
+ String paramname = parameters[i].getName();
+ if (TYPE_KEY.equalsIgnoreCase(paramname)) {
+ FileType t = new FileType();
+ t.setValue(parameters[i].getValue());
+ setType(t);
+ } else {
+ setError("Invalid parameter " + paramname);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks to make sure all settings are kosher. In this case, it
+ * means that the pattern attribute has been set.
+ *
+ */
+ public void verifySettings() {
+ if (type == null) {
+ setError("The type attribute is required");
+ }
+ }
+
+ /**
+ * The heart of the matter. This is where the selector gets to decide
+ * on the inclusion of a file in a particular fileset.
+ *
+ * @param basedir the base directory the scan is being done from
+ * @param filename is the name of the file to check
+ * @param file is a java.io.File object the selector can use
+ * @return whether the file should be selected or not
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+
+ // throw BuildException on error
+ validate();
+
+ if (file.isDirectory()) {
+ return type.equals(FileType.DIR);
+ } else {
+ return type.equals(FileType.FILE);
+ }
+ }
+
+ /**
+ * Enumerated attribute with the values for types of file
+ */
+ public static class FileType extends EnumeratedAttribute {
+ /** the string value for file */
+ public static final String FILE = "file";
+ /** the string value for dir */
+ public static final String DIR = "dir";
+
+ /**
+ * @return the values as an array of strings
+ */
+ public String[] getValues() {
+ return new String[]{FILE, DIR};
+ }
+ }
+
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/WritableSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/WritableSelector.java
new file mode 100644
index 00000000..c7391f02
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/WritableSelector.java
@@ -0,0 +1,46 @@
+/*
+ * 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.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+
+/**
+ * A selector that selects writable files.
+ *
+ * <p>Writable is defined in terms of java.io.File#canWrite, this
+ * means the selector will accept any file that exists and is
+ * writable by the application.</p>
+ *
+ * @since Ant 1.8.0
+ */
+public class WritableSelector implements FileSelector, ResourceSelector {
+
+ public boolean isSelected(File basedir, String filename, File file) {
+ return file != null && file.canWrite();
+ }
+
+ public boolean isSelected(Resource r) {
+ FileProvider fp = r.as(FileProvider.class);
+ return fp != null && isSelected(null, null, fp.getFile());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Algorithm.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Algorithm.java
new file mode 100644
index 00000000..82e043b5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Algorithm.java
@@ -0,0 +1,48 @@
+/*
+ * 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.tools.ant.types.selectors.modifiedselector;
+
+
+import java.io.File;
+
+
+/**
+ * The <i>Algorithm</i> defines how a value for a file is computed.
+ * It must be sure that multiple calls for the same file results in the
+ * same value.
+ * The implementing class should implement a useful toString() method.
+ *
+ * @version 2003-09-13
+ * @since Ant 1.6
+ */
+public interface Algorithm {
+
+ /**
+ * Checks its prerequisites.
+ * @return <i>true</i> if all is ok, otherwise <i>false</i>.
+ */
+ boolean isValid();
+
+ /**
+ * Get the value for a file.
+ * @param file File object for which the value should be evaluated.
+ * @return The value for that file
+ */
+ String getValue(File file);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Cache.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Cache.java
new file mode 100644
index 00000000..13c74c2b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Cache.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.tools.ant.types.selectors.modifiedselector;
+
+
+import java.util.Iterator;
+
+
+/**
+ * A Cache let the user store key-value-pairs in a permanent manner and access
+ * them.
+ * It is possible that a client uses get() before load() therefore the
+ * implementation must ensure that no error occurred because of the wrong
+ * <i>order</i>.
+ * The implementing class should implement a useful toString() method.
+ *
+ * @version 2003-09-13
+ * @since Ant 1.6
+ */
+public interface Cache {
+
+ /**
+ * Checks its prerequisites.
+ * @return <i>true</i> if all is ok, otherwise <i>false</i>.
+ */
+ boolean isValid();
+
+ /** Deletes the cache. If file based the file has to be deleted also. */
+ void delete();
+
+ /** Loads the cache, must handle not existing cache. */
+ void load();
+
+ /** Saves modification of the cache. */
+ void save();
+
+ /**
+ * Returns a value for a given key from the cache.
+ * @param key the key
+ * @return the stored value
+ */
+ Object get(Object key);
+
+ /**
+ * Saves a key-value-pair in the cache.
+ * @param key the key
+ * @param value the value
+ */
+ void put(Object key, Object value);
+
+ /**
+ * Returns an iterator over the keys in the cache.
+ * @return An iterator over the keys.
+ */
+ Iterator<String> iterator();
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ChecksumAlgorithm.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ChecksumAlgorithm.java
new file mode 100644
index 00000000..210d5dc9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ChecksumAlgorithm.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.tools.ant.types.selectors.modifiedselector;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.security.NoSuchAlgorithmException;
+import java.util.Locale;
+import java.util.zip.Adler32;
+import java.util.zip.CRC32;
+import java.util.zip.CheckedInputStream;
+import java.util.zip.Checksum;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Computes a 'checksum' for the content of file using
+ * java.util.zip.CRC32 and java.util.zip.Adler32.
+ * Use of this algorithm doesn't require any additional nested <param>s.
+ * Supported <param>s are:
+ * <table>
+ * <tr>
+ * <th>name</th><th>values</th><th>description</th><th>required</th>
+ * </tr>
+ * <tr>
+ * <td> algorithm.algorithm </td>
+ * <td> ADLER | CRC ( default ) </td>
+ * <td> name of the algorithm the checksum should use </td>
+ * <td> no, defaults to CRC </td>
+ * </tr>
+ * </table>
+ *
+ * @version 2004-06-17
+ * @since Ant 1.7
+ */
+public class ChecksumAlgorithm implements Algorithm {
+
+
+ // ----- member variables -----
+
+
+ /**
+ * Checksum algorithm to be used.
+ */
+ private String algorithm = "CRC";
+
+ /**
+ * Checksum interface instance
+ */
+ private Checksum checksum = null;
+
+
+ // ----- Algorithm-Configuration -----
+
+
+ /**
+ * Specifies the algorithm to be used to compute the checksum.
+ * Defaults to "CRC". Other popular algorithms like "ADLER" may be used as well.
+ * @param algorithm the digest algorithm to use
+ */
+ public void setAlgorithm(String algorithm) {
+ this.algorithm =
+ algorithm != null ? algorithm.toUpperCase(Locale.ENGLISH) : null;
+ }
+
+
+ /** Initialize the checksum interface. */
+ public void initChecksum() {
+ if (checksum != null) {
+ return;
+ }
+ if ("CRC".equals(algorithm)) {
+ checksum = new CRC32();
+ } else if ("ADLER".equals(algorithm)) {
+ checksum = new Adler32();
+ } else {
+ throw new BuildException(new NoSuchAlgorithmException());
+ }
+ }
+
+
+ // ----- Logic -----
+
+
+ /**
+ * This algorithm supports only CRC and Adler.
+ * @return <i>true</i> if all is ok, otherwise <i>false</i>.
+ */
+ public boolean isValid() {
+ return "CRC".equals(algorithm) || "ADLER".equals(algorithm);
+ }
+
+
+ /**
+ * Computes a value for a file content with the specified checksum algorithm.
+ * @param file File object for which the value should be evaluated.
+ * @return The value for that file
+ */
+ public String getValue(File file) {
+ initChecksum();
+ String rval = null;
+
+ try {
+ if (file.canRead()) {
+ checksum.reset();
+ FileInputStream fis = new FileInputStream(file);
+ CheckedInputStream check = new CheckedInputStream(fis, checksum);
+ BufferedInputStream in = new BufferedInputStream(check);
+ while (in.read() != -1) {
+ // Read the file
+ }
+ rval = Long.toString(check.getChecksum().getValue());
+ in.close();
+ }
+ } catch (Exception e) {
+ rval = null;
+ }
+ return rval;
+ }
+
+
+ /**
+ * Override Object.toString().
+ * @return some information about this algorithm.
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<ChecksumAlgorithm:");
+ buf.append("algorithm=").append(algorithm);
+ buf.append(">");
+ return buf.toString();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java
new file mode 100644
index 00000000..085b4fe7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.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.tools.ant.types.selectors.modifiedselector;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.util.Locale;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Computes a 'hashvalue' for the content of file using
+ * java.security.MessageDigest.
+ * Use of this algorithm doesn't require any additional nested <param>s.
+ * Supported <param>s are:
+ * <table>
+ * <tr>
+ * <th>name</th><th>values</th><th>description</th><th>required</th>
+ * </tr>
+ * <tr>
+ * <td> algorithm.algorithm </td>
+ * <td> MD5 | SHA (default provider) </td>
+ * <td> name of the algorithm the provider should use </td>
+ * <td> no, defaults to MD5 </td>
+ * </tr>
+ * <tr>
+ * <td> algorithm.provider </td>
+ * <td> </td>
+ * <td> name of the provider to use </td>
+ * <td> no, defaults to <i>null</i> </td>
+ * </tr>
+ * </table>
+ *
+ * @version 2004-07-08
+ * @since Ant 1.6
+ */
+public class DigestAlgorithm implements Algorithm {
+
+ private static final int BYTE_MASK = 0xFF;
+ private static final int BUFFER_SIZE = 8192;
+
+ // ----- member variables -----
+
+
+ /**
+ * MessageDigest algorithm to be used.
+ */
+ private String algorithm = "MD5";
+
+ /**
+ * MessageDigest Algorithm provider
+ */
+ private String provider = null;
+
+ /**
+ * Message Digest instance
+ */
+ private MessageDigest messageDigest = null;
+
+ /**
+ * Size of the read buffer to use.
+ */
+ private int readBufferSize = BUFFER_SIZE;
+
+
+ // ----- Algorithm-Configuration -----
+
+
+ /**
+ * Specifies the algorithm to be used to compute the checksum.
+ * Defaults to "MD5". Other popular algorithms like "SHA" may be used as well.
+ * @param algorithm the digest algorithm to use
+ */
+ public void setAlgorithm(String algorithm) {
+ this.algorithm = algorithm != null
+ ? algorithm.toUpperCase(Locale.ENGLISH) : null;
+ }
+
+
+ /**
+ * Sets the MessageDigest algorithm provider to be used
+ * to calculate the checksum.
+ * @param provider provider to use
+ */
+ public void setProvider(String provider) {
+ this.provider = provider;
+ }
+
+
+ /** Initialize the security message digest. */
+ public void initMessageDigest() {
+ if (messageDigest != null) {
+ return;
+ }
+
+ if ((provider != null) && !"".equals(provider) && !"null".equals(provider)) {
+ try {
+ messageDigest = MessageDigest.getInstance(algorithm, provider);
+ } catch (NoSuchAlgorithmException noalgo) {
+ throw new BuildException(noalgo);
+ } catch (NoSuchProviderException noprovider) {
+ throw new BuildException(noprovider);
+ }
+ } else {
+ try {
+ messageDigest = MessageDigest.getInstance(algorithm);
+ } catch (NoSuchAlgorithmException noalgo) {
+ throw new BuildException(noalgo);
+ }
+ }
+ }
+
+
+ // ----- Logic -----
+
+
+ /**
+ * This algorithm supports only MD5 and SHA.
+ * @return <i>true</i> if all is ok, otherwise <i>false</i>.
+ */
+ public boolean isValid() {
+ return "SHA".equals(algorithm) || "MD5".equals(algorithm);
+ }
+
+
+ /**
+ * Computes a value for a file content with the specified digest algorithm.
+ * @param file File object for which the value should be evaluated.
+ * @return The value for that file
+ */
+ // implementation adapted from ...taskdefs.Checksum, thanks to Magesh for hint
+ public String getValue(File file) {
+ initMessageDigest();
+ String checksum = null;
+ try {
+ if (!file.canRead()) {
+ return null;
+ }
+ FileInputStream fis = null;
+
+ byte[] buf = new byte[readBufferSize];
+ try {
+ messageDigest.reset();
+ fis = new FileInputStream(file);
+ DigestInputStream dis = new DigestInputStream(fis,
+ messageDigest);
+ while (dis.read(buf, 0, readBufferSize) != -1) {
+ // do nothing
+ }
+ dis.close();
+ fis.close();
+ fis = null;
+ byte[] fileDigest = messageDigest.digest();
+ StringBuffer checksumSb = new StringBuffer();
+ for (int i = 0; i < fileDigest.length; i++) {
+ String hexStr
+ = Integer.toHexString(BYTE_MASK & fileDigest[i]);
+ if (hexStr.length() < 2) {
+ checksumSb.append("0");
+ }
+ checksumSb.append(hexStr);
+ }
+ checksum = checksumSb.toString();
+ } catch (Exception e) {
+ return null;
+ }
+ } catch (Exception e) {
+ return null;
+ }
+ return checksum;
+ }
+
+
+ /**
+ * Override Object.toString().
+ * @return some information about this algorithm.
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<DigestAlgorithm:");
+ buf.append("algorithm=").append(algorithm);
+ buf.append(";provider=").append(provider);
+ buf.append(">");
+ return buf.toString();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/EqualComparator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/EqualComparator.java
new file mode 100644
index 00000000..94fb9351
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/EqualComparator.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.tools.ant.types.selectors.modifiedselector;
+
+
+import java.util.Comparator;
+
+
+/**
+ * Simple implementation of Comparator for use in CacheSelector.
+ * compare() returns '0' (should not be selected) if both parameter
+ * are equal otherwise '1' (should be selected).
+ *
+ * @version 2003-09-13
+ * @since Ant 1.6
+ */
+public class EqualComparator implements Comparator<Object> {
+
+ /**
+ * Implements Comparator.compare().
+ * @param o1 the first object
+ * @param o2 the second object
+ * @return 0, if both are equal, otherwise 1
+ */
+ public int compare(Object o1, Object o2) {
+ if (o1 == null) {
+ if (o2 == null) {
+ return 1;
+ }
+ return 0;
+ }
+ return (o1.equals(o2)) ? 0 : 1;
+ }
+
+ /**
+ * Override Object.toString().
+ * @return information about this comparator
+ */
+ public String toString() {
+ return "EqualComparator";
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/HashvalueAlgorithm.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/HashvalueAlgorithm.java
new file mode 100644
index 00000000..8af9d12e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/HashvalueAlgorithm.java
@@ -0,0 +1,80 @@
+/*
+ * 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.tools.ant.types.selectors.modifiedselector;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.Reader;
+
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Computes a 'hashvalue' for the content of file using String.hashValue().
+ * Use of this algorithm doesn't require any additional nested <param>s and
+ * doesn't support any.
+ *
+ * @version 2003-09-13
+ * @since Ant 1.6
+ */
+public class HashvalueAlgorithm implements Algorithm {
+
+ /**
+ * This algorithm doesn't need any configuration.
+ * Therefore it's always valid.
+ * @return always true
+ */
+ public boolean isValid() {
+ return true;
+ }
+
+ /**
+ * Computes a 'hashvalue' for a file content.
+ * It reads the content of a file, convert that to String and use the
+ * String.hashCode() method.
+ * @param file The file for which the value should be computed
+ * @return the hashvalue or <i>null</i> if the file couldn't be read
+ */
+ // Because the content is only read the file will not be damaged. I tested
+ // with JPG, ZIP and PDF as binary files.
+ public String getValue(File file) {
+ Reader r = null;
+ try {
+ if (!file.canRead()) {
+ return null;
+ }
+ r = new FileReader(file);
+ int hash = FileUtils.readFully(r).hashCode();
+ return Integer.toString(hash);
+ } catch (Exception e) {
+ return null;
+ } finally {
+ FileUtils.close(r);
+ }
+ }
+
+
+ /**
+ * Override Object.toString().
+ * @return information about this comparator
+ */
+ public String toString() {
+ return "HashvalueAlgorithm";
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java
new file mode 100644
index 00000000..9f538098
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java
@@ -0,0 +1,971 @@
+/*
+ * 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.tools.ant.types.selectors.modifiedselector;
+
+
+// Java
+import java.io.File;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.IntrospectionHelper;
+// Ant
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+import org.apache.tools.ant.types.selectors.BaseExtendSelector;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.ResourceUtils;
+
+
+/**
+ * <p>Selector class that uses <i>Algorithm</i>, <i>Cache</i> and <i>Comparator</i>
+ * for its work.
+ * The <i>Algorithm</i> is used for computing a hashvalue for a file.
+ * The <i>Comparator</i> decides whether to select or not.
+ * The <i>Cache</i> stores the other value for comparison by the <i>Comparator</i>
+ * in a persistent manner.</p>
+ *
+ * <p>The ModifiedSelector is implemented as a <b>CoreSelector</b> and uses default
+ * values for all its attributes therefore the simplest example is <pre>
+ * &lt;copy todir="dest"&gt;
+ * &lt;filelist dir="src"&gt;
+ * &lt;modified/&gt;
+ * &lt;/filelist&gt;
+ * &lt;/copy&gt;
+ * </pre></p>
+ *
+ * <p>The same example rewritten as CoreSelector with setting the all values
+ * (same as defaults are) would be <pre>
+ * &lt;copy todir="dest"&gt;
+ * &lt;filelist dir="src"&gt;
+ * &lt;modified update="true"
+ * cache="propertyfile"
+ * algorithm="digest"
+ * comparator="equal"&gt;
+ * &lt;param name="cache.cachefile" value="cache.properties"/&gt;
+ * &lt;param name="algorithm.algorithm" value="MD5"/&gt;
+ * &lt;/modified&gt;
+ * &lt;/filelist&gt;
+ * &lt;/copy&gt;
+ * </pre></p>
+ *
+ * <p>And the same rewritten as CustomSelector would be<pre>
+ * &lt;copy todir="dest"&gt;
+ * &lt;filelist dir="src"&gt;
+ * &lt;custom class="org.apache.tools.ant.type.selectors.ModifiedSelector"&gt;
+ * &lt;param name="update" value="true"/&gt;
+ * &lt;param name="cache" value="propertyfile"/&gt;
+ * &lt;param name="algorithm" value="digest"/&gt;
+ * &lt;param name="comparator" value="equal"/&gt;
+ * &lt;param name="cache.cachefile" value="cache.properties"/&gt;
+ * &lt;param name="algorithm.algorithm" value="MD5"/&gt;
+ * &lt;/custom&gt;
+ * &lt;/filelist&gt;
+ * &lt;/copy&gt;
+ * </pre></p>
+ *
+ * <p>If you want to provide your own interface implementation you can do
+ * that via the *classname attributes. If the classes are not on Ant's core
+ * classpath, you will have to provide the path via nested &lt;classpath&gt;
+ * element, so that the selector can find the classes. <pre>
+ * &lt;modified cacheclassname="com.mycompany.MyCache"&gt;
+ * &lt;classpath&gt;
+ * &lt;pathelement location="lib/mycompany-antutil.jar"/&gt;
+ * &lt;/classpath&gt;
+ * &lt;/modified&gt;
+ * </pre></p>
+ *
+ * <p>All these three examples copy the files from <i>src</i> to <i>dest</i>
+ * using the ModifiedSelector. The ModifiedSelector uses the <i>PropertyfileCache
+ * </i>, the <i>DigestAlgorithm</i> and the <i>EqualComparator</i> for its
+ * work. The PropertyfileCache stores key-value-pairs in a simple java
+ * properties file. The filename is <i>cache.properties</i>. The <i>update</i>
+ * flag lets the selector update the values in the cache (and on first call
+ * creates the cache). The <i>DigestAlgorithm</i> computes a hashvalue using the
+ * java.security.MessageDigest class with its MD5-Algorithm and its standard
+ * provider. The new computed hashvalue and the stored one are compared by
+ * the <i>EqualComparator</i> which returns 'true' (more correct a value not
+ * equals zero (1)) if the values are not the same using simple String
+ * comparison.</p>
+ *
+ * <p>A useful scenario for this selector is inside a build environment
+ * for homepage generation (e.g. with <a href="http://forrest.apache.org/">
+ * Apache Forrest</a>). <pre>
+ * &lt;target name="generate-and-upload-site"&gt;
+ * &lt;echo&gt; generate the site using forrest &lt;/echo&gt;
+ * &lt;antcall target="site"/&gt;
+ *
+ * &lt;echo&gt; upload the changed files &lt;/echo&gt;
+ * &lt;ftp server="${ftp.server}" userid="${ftp.user}" password="${ftp.pwd}"&gt;
+ * &lt;fileset dir="htdocs/manual"&gt;
+ * &lt;modified/&gt;
+ * &lt;/fileset&gt;
+ * &lt;/ftp&gt;
+ * &lt;/target&gt;
+ * </pre> Here all <b>changed</b> files are uploaded to the server. The
+ * ModifiedSelector saves therefore much upload time.</p>
+ *
+ *
+ * <p>This selector uses reflection for setting the values of its three interfaces
+ * (using org.apache.tools.ant.IntrospectionHelper) therefore no special
+ * 'configuration interfaces' has to be implemented by new caches, algorithms or
+ * comparators. All present <i>set</i>XX methods can be used. E.g. the DigestAlgorithm
+ * can use a specified provider for computing its value. For selecting this
+ * there is a <i>setProvider(String providername)</i> method. So you can use
+ * a nested <i>&lt;param name="algorithm.provider" value="MyProvider"/&gt;</i>.
+ *
+ *
+ * @since Ant 1.6
+ */
+public class ModifiedSelector extends BaseExtendSelector
+ implements BuildListener, ResourceSelector {
+
+ private static final String CACHE_PREFIX = "cache.";
+ private static final String ALGORITHM_PREFIX = "algorithm.";
+ private static final String COMPARATOR_PREFIX = "comparator.";
+
+
+ // ----- attributes -----
+
+
+ /** Cache name for later instantiation. */
+ private CacheName cacheName = null;
+
+ /** User specified classname for Cache. */
+ private String cacheClass;
+
+ /** Algorithm name for later instantiation. */
+ private AlgorithmName algoName = null;
+
+ /** User specified classname for Algorithm. */
+ private String algorithmClass;
+
+ /** Comparator name for later instantiation. */
+ private ComparatorName compName = null;
+
+ /** User specified classname for Comparator. */
+ private String comparatorClass;
+
+ /** Should the cache be updated? */
+ private boolean update = true;
+
+ /** Are directories selected? */
+ private boolean selectDirectories = true;
+
+ /**
+ * Should Resources whithout an InputStream, and
+ * therefore without checking, be selected?
+ */
+ private boolean selectResourcesWithoutInputStream = true;
+
+ /** Delay the writing of the cache file */
+ private boolean delayUpdate = true;
+
+
+ // ----- internal member variables -----
+
+
+ /** How should the cached value and the new one compared? */
+ private Comparator<? super String> comparator = null;
+
+ /** Algorithm for computing new values and updating the cache. */
+ private Algorithm algorithm = null;
+
+ /** The Cache containing the old values. */
+ private Cache cache = null;
+
+ /** Count of modified properties */
+ private int modified = 0;
+
+ /** Flag whether this object is configured. Configuration is only done once. */
+ private boolean isConfigured = false;
+
+ /**
+ * Parameter vector with parameters for later initialization.
+ * @see #configure
+ */
+ private Vector<Parameter> configParameter = new Vector<Parameter>();
+
+ /**
+ * Parameter vector with special parameters for later initialization.
+ * The names have the pattern '*.*', e.g. 'cache.cachefile'.
+ * These parameters are used <b>after</b> the parameters with the pattern '*'.
+ * @see #configure
+ */
+ private Vector<Parameter> specialParameter = new Vector<Parameter>();
+
+ /** The classloader of this class. */
+ private ClassLoader myClassLoader = null;
+
+ /** provided classpath for the classloader */
+ private Path classpath = null;
+
+
+ // ----- constructors -----
+
+
+ /** Bean-Constructor. */
+ public ModifiedSelector() {
+ }
+
+
+ // ----- configuration -----
+
+
+ /** Overrides BaseSelector.verifySettings(). */
+ public void verifySettings() {
+ configure();
+ if (cache == null) {
+ setError("Cache must be set.");
+ } else if (algorithm == null) {
+ setError("Algorithm must be set.");
+ } else if (!cache.isValid()) {
+ setError("Cache must be proper configured.");
+ } else if (!algorithm.isValid()) {
+ setError("Algorithm must be proper configured.");
+ }
+ }
+
+
+ /**
+ * Configures this Selector.
+ * Does this work only once per Selector object.
+ * <p>Because some problems while configuring from <custom>Selector
+ * the configuration is done in the following order:<ol>
+ * <li> collect the configuration data </li>
+ * <li> wait for the first isSelected() call </li>
+ * <li> set the default values </li>
+ * <li> set values for name pattern '*': update, cache, algorithm, comparator </li>
+ * <li> set values for name pattern '*.*: cache.cachefile, ... </li>
+ * </ol></p>
+ * <p>This configuration algorithm is needed because you don't know
+ * the order of arriving config-data. E.g. if you first set the
+ * <i>cache.cachefilename</i> and after that the <i>cache</i> itself,
+ * the default value for cachefilename is used, because setting the
+ * cache implies creating a new Cache instance - with its defaults.</p>
+ */
+ public void configure() {
+ //
+ // ----- The "Singleton" -----
+ //
+ if (isConfigured) {
+ return;
+ }
+ isConfigured = true;
+
+ //
+ // ----- Set default values -----
+ //
+ Project p = getProject();
+ String filename = "cache.properties";
+ File cachefile = null;
+ if (p != null) {
+ // normal use inside Ant
+ cachefile = new File(p.getBaseDir(), filename);
+
+ // set self as a BuildListener to delay cachefile saves
+ getProject().addBuildListener(this);
+ } else {
+ // no reference to project - e.g. during normal JUnit tests
+ cachefile = new File(filename);
+ setDelayUpdate(false);
+ }
+ Cache defaultCache = new PropertiesfileCache(cachefile);
+ Algorithm defaultAlgorithm = new DigestAlgorithm();
+ Comparator<? super String> defaultComparator = new EqualComparator();
+
+ //
+ // ----- Set the main attributes, pattern '*' -----
+ //
+ for (Parameter parameter : configParameter) {
+ if (parameter.getName().indexOf(".") > 0) {
+ // this is a *.* parameter for later use
+ specialParameter.add(parameter);
+ } else {
+ useParameter(parameter);
+ }
+ }
+ configParameter = new Vector<Parameter>();
+
+ // specify the algorithm classname
+ if (algoName != null) {
+ // use Algorithm defined via name
+ if ("hashvalue".equals(algoName.getValue())) {
+ algorithm = new HashvalueAlgorithm();
+ } else if ("digest".equals(algoName.getValue())) {
+ algorithm = new DigestAlgorithm();
+ } else if ("checksum".equals(algoName.getValue())) {
+ algorithm = new ChecksumAlgorithm();
+ }
+ } else {
+ if (algorithmClass != null) {
+ // use Algorithm specified by classname
+ algorithm = loadClass(
+ algorithmClass,
+ "is not an Algorithm.",
+ Algorithm.class);
+ } else {
+ // nothing specified - use default
+ algorithm = defaultAlgorithm;
+ }
+ }
+
+ // specify the cache classname
+ if (cacheName != null) {
+ // use Cache defined via name
+ if ("propertyfile".equals(cacheName.getValue())) {
+ cache = new PropertiesfileCache();
+ }
+ } else {
+ if (cacheClass != null) {
+ // use Cache specified by classname
+ cache = loadClass(cacheClass, "is not a Cache.", Cache.class);
+ } else {
+ // nothing specified - use default
+ cache = defaultCache;
+ }
+ }
+
+ // specify the comparator classname
+ if (compName != null) {
+ // use Algorithm defined via name
+ if ("equal".equals(compName.getValue())) {
+ comparator = new EqualComparator();
+ } else if ("rule".equals(compName.getValue())) {
+ // TODO there is a problem with the constructor for the RBC.
+ // you have to provide the rules in the constructors - no setters
+ // available.
+ throw new BuildException("RuleBasedCollator not yet supported.");
+ // Have to think about lazy initialization here... JHM
+ // comparator = new java.text.RuleBasedCollator();
+ }
+ } else {
+ if (comparatorClass != null) {
+ // use Algorithm specified by classname
+ @SuppressWarnings("unchecked")
+ Comparator<? super String> localComparator = loadClass(comparatorClass, "is not a Comparator.", Comparator.class);
+ comparator = localComparator;
+ } else {
+ // nothing specified - use default
+ comparator = defaultComparator;
+ }
+ }
+
+ //
+ // ----- Set the special attributes, pattern '*.*' -----
+ //
+ for (Iterator<Parameter> itSpecial = specialParameter.iterator(); itSpecial.hasNext();) {
+ Parameter par = itSpecial.next();
+ useParameter(par);
+ }
+ specialParameter = new Vector<Parameter>();
+ }
+
+
+ /**
+ * Loads the specified class and initializes an object of that class.
+ * Throws a BuildException using the given message if an error occurs during
+ * loading/instantiation or if the object is not from the given type.
+ * @param classname the classname
+ * @param msg the message-part for the BuildException
+ * @param type the type to check against
+ * @return a castable object
+ */
+ protected <T> T loadClass(String classname, String msg, Class<? extends T> type) {
+ try {
+ // load the specified class
+ ClassLoader cl = getClassLoader();
+ Class<?> clazz = null;
+ if (cl != null) {
+ clazz = cl.loadClass(classname);
+ } else {
+ clazz = Class.forName(classname);
+ }
+
+ Object rv = clazz.newInstance();
+
+ if (!type.isInstance(rv)) {
+ throw new BuildException("Specified class (" + classname + ") " + msg);
+ }
+ return (T) rv;
+ } catch (ClassNotFoundException e) {
+ throw new BuildException("Specified class (" + classname + ") not found.");
+ } catch (Exception e) {
+ throw new BuildException(e);
+ }
+ }
+
+
+ // ----- the selection work -----
+
+
+ /**
+ * Implementation of ResourceSelector.isSelected().
+ *
+ * @param resource The resource to check
+ * @return whether the resource is selected
+ * @see ResourceSelector#isSelected(Resource)
+ */
+ public boolean isSelected(Resource resource) {
+ if (resource.isFilesystemOnly()) {
+ // We have a 'resourced' file, so reconvert it and use
+ // the 'old' implementation.
+ FileResource fileResource = (FileResource) resource;
+ File file = fileResource.getFile();
+ String filename = fileResource.getName();
+ File basedir = fileResource.getBaseDir();
+ return isSelected(basedir, filename, file);
+ } else {
+ try {
+ // How to handle non-file-Resources? I copy temporarily the
+ // resource to a file and use the file-implementation.
+ FileUtils fu = FileUtils.getFileUtils();
+ File tmpFile = fu.createTempFile("modified-", ".tmp", null, true, false);
+ Resource tmpResource = new FileResource(tmpFile);
+ ResourceUtils.copyResource(resource, tmpResource);
+ boolean isSelected = isSelected(tmpFile.getParentFile(),
+ tmpFile.getName(),
+ resource.toLongString());
+ tmpFile.delete();
+ return isSelected;
+ } catch (UnsupportedOperationException uoe) {
+ log("The resource '"
+ + resource.getName()
+ + "' does not provide an InputStream, so it is not checked. "
+ + "Akkording to 'selres' attribute value it is "
+ + ((selectResourcesWithoutInputStream) ? "" : " not")
+ + "selected.", Project.MSG_INFO);
+ return selectResourcesWithoutInputStream;
+ } catch (Exception e) {
+ throw new BuildException(e);
+ }
+ }
+ }
+
+
+ /**
+ * Implementation of BaseExtendSelector.isSelected().
+ *
+ * @param basedir as described in BaseExtendSelector
+ * @param filename as described in BaseExtendSelector
+ * @param file as described in BaseExtendSelector
+ * @return as described in BaseExtendSelector
+ */
+ public boolean isSelected(File basedir, String filename, File file) {
+ return isSelected(basedir, filename, file.getAbsolutePath());
+ }
+
+
+ /**
+ * The business logic of this selector for use as ResourceSelector of
+ * FileSelector.
+ *
+ * @param basedir as described in BaseExtendSelector
+ * @param filename as described in BaseExtendSelector
+ * @param cacheKey the name for the key for storing the hashvalue
+ * @return <tt>true</tt> if the file is selected otherwise <tt>false</tt>
+ */
+ private boolean isSelected(File basedir, String filename, String cacheKey) {
+ validate();
+ File f = new File(basedir, filename);
+
+ // You can not compute a value for a directory
+ if (f.isDirectory()) {
+ return selectDirectories;
+ }
+
+ // Get the values and do the comparison
+ String cachedValue = String.valueOf(cache.get(f.getAbsolutePath()));
+ String newValue = algorithm.getValue(f);
+
+ boolean rv = (comparator.compare(cachedValue, newValue) != 0);
+
+ // Maybe update the cache
+ if (update && rv) {
+ cache.put(f.getAbsolutePath(), newValue);
+ setModified(getModified() + 1);
+ if (!getDelayUpdate()) {
+ saveCache();
+ }
+ }
+
+ return rv;
+ }
+
+
+ /**
+ * save the cache file
+ */
+ protected void saveCache() {
+ if (getModified() > 0) {
+ cache.save();
+ setModified(0);
+ }
+ }
+
+
+ // ----- attribute and nested element support -----
+
+
+ /**
+ * Setter for algorithmClass.
+ * @param classname new value
+ */
+ public void setAlgorithmClass(String classname) {
+ algorithmClass = classname;
+ }
+
+
+ /**
+ * Setter for comparatorClass.
+ * @param classname new value
+ */
+ public void setComparatorClass(String classname) {
+ comparatorClass = classname;
+ }
+
+
+ /**
+ * Setter for cacheClass.
+ * @param classname new value
+ */
+ public void setCacheClass(String classname) {
+ cacheClass = classname;
+ }
+
+
+ /**
+ * Support for <i>update</i> attribute.
+ * @param update new value
+ */
+ public void setUpdate(boolean update) {
+ this.update = update;
+ }
+
+
+ /**
+ * Support for <i>seldirs</i> attribute.
+ * @param seldirs new value
+ */
+ public void setSeldirs(boolean seldirs) {
+ selectDirectories = seldirs;
+ }
+
+
+ /**
+ * Support for <i>selres</i> attribute.
+ * @param newValue the new value
+ */
+ public void setSelres(boolean newValue) {
+ this.selectResourcesWithoutInputStream = newValue;
+ }
+
+
+ /**
+ * Getter for the modified count
+ * @return modified count
+ */
+ public int getModified() {
+ return modified;
+ }
+
+
+ /**
+ * Setter for the modified count
+ * @param modified count
+ */
+ public void setModified(int modified) {
+ this.modified = modified;
+ }
+
+
+ /**
+ * Getter for the delay update
+ * @return true if we should delay for performance
+ */
+ public boolean getDelayUpdate() {
+ return delayUpdate;
+ }
+
+
+ /**
+ * Setter for the delay update
+ * @param delayUpdate true if we should delay for performance
+ */
+ public void setDelayUpdate(boolean delayUpdate) {
+ this.delayUpdate = delayUpdate;
+ }
+
+
+ /**
+ * Add the classpath.
+ * @param path the classpath
+ */
+ public void addClasspath(Path path) {
+ if (classpath != null) {
+ throw new BuildException("<classpath> can be set only once.");
+ }
+ classpath = path;
+ }
+
+
+ /**
+ * Returns and initializes the classloader for this class.
+ * @return the classloader
+ */
+ public ClassLoader getClassLoader() {
+ if (myClassLoader == null) {
+ myClassLoader = (classpath == null)
+ // the usual classloader
+ ? getClass().getClassLoader()
+ // additional use the provided classpath
+ // Memory leak in line below
+ : getProject().createClassLoader(classpath);
+ }
+ return myClassLoader;
+ }
+
+
+ /**
+ * Set the used ClassLoader.
+ * If you invoke this selector by API (e.g. inside some testcases) the selector
+ * will use a different classloader for loading the interface implementations than
+ * the caller. Therefore you will get a ClassCastException if you get the
+ * implementations from the selector and cast them.
+ * @param loader the ClassLoader to use
+ */
+ public void setClassLoader(ClassLoader loader) {
+ myClassLoader = loader;
+ }
+
+
+ /**
+ * Support for nested &lt;param&gt; tags.
+ * @param key the key of the parameter
+ * @param value the value of the parameter
+ */
+ public void addParam(String key, Object value) {
+ Parameter par = new Parameter();
+ par.setName(key);
+ par.setValue(String.valueOf(value));
+ configParameter.add(par);
+ }
+
+
+ /**
+ * Support for nested &lt;param&gt; tags.
+ * @param parameter the parameter object
+ */
+ public void addParam(Parameter parameter) {
+ configParameter.add(parameter);
+ }
+
+
+ /**
+ * Defined in org.apache.tools.ant.types.Parameterizable.
+ * Overwrite implementation in superclass because only special
+ * parameters are valid.
+ * @see #addParam(String,Object).
+ * @param parameters the parameters to set.
+ */
+ public void setParameters(Parameter[] parameters) {
+ if (parameters != null) {
+ for (int i = 0; i < parameters.length; i++) {
+ configParameter.add(parameters[i]);
+ }
+ }
+ }
+
+
+ /**
+ * Support for nested <param name="" value=""/> tags.
+ * Parameter named <i>cache</i>, <i>algorithm</i>,
+ * <i>comparator</i> or <i>update</i> are mapped to
+ * the respective set-Method.
+ * Parameter which names starts with <i>cache.</i> or
+ * <i>algorithm.</i> or <i>comparator.</i> are tried
+ * to set on the appropriate object via its set-methods.
+ * Other parameters are invalid and an BuildException will
+ * be thrown.
+ *
+ * @param parameter Key and value as parameter object
+ */
+ public void useParameter(Parameter parameter) {
+ String key = parameter.getName();
+ String value = parameter.getValue();
+ if ("cache".equals(key)) {
+ CacheName cn = new CacheName();
+ cn.setValue(value);
+ setCache(cn);
+ } else if ("algorithm".equals(key)) {
+ AlgorithmName an = new AlgorithmName();
+ an.setValue(value);
+ setAlgorithm(an);
+ } else if ("comparator".equals(key)) {
+ ComparatorName cn = new ComparatorName();
+ cn.setValue(value);
+ setComparator(cn);
+ } else if ("update".equals(key)) {
+ boolean updateValue =
+ ("true".equalsIgnoreCase(value))
+ ? true
+ : false;
+ setUpdate(updateValue);
+ } else if ("delayupdate".equals(key)) {
+ boolean updateValue =
+ ("true".equalsIgnoreCase(value))
+ ? true
+ : false;
+ setDelayUpdate(updateValue);
+ } else if ("seldirs".equals(key)) {
+ boolean sdValue =
+ ("true".equalsIgnoreCase(value))
+ ? true
+ : false;
+ setSeldirs(sdValue);
+ } else if (key.startsWith(CACHE_PREFIX)) {
+ String name = key.substring(CACHE_PREFIX.length());
+ tryToSetAParameter(cache, name, value);
+ } else if (key.startsWith(ALGORITHM_PREFIX)) {
+ String name = key.substring(ALGORITHM_PREFIX.length());
+ tryToSetAParameter(algorithm, name, value);
+ } else if (key.startsWith(COMPARATOR_PREFIX)) {
+ String name = key.substring(COMPARATOR_PREFIX.length());
+ tryToSetAParameter(comparator, name, value);
+ } else {
+ setError("Invalid parameter " + key);
+ }
+ }
+
+
+ /**
+ * Try to set a value on an object using reflection.
+ * Helper method for easier access to IntrospectionHelper.setAttribute().
+ * @param obj the object on which the attribute should be set
+ * @param name the attributename
+ * @param value the new value
+ */
+ protected void tryToSetAParameter(Object obj, String name, String value) {
+ Project prj = (getProject() != null) ? getProject() : new Project();
+ IntrospectionHelper iHelper
+ = IntrospectionHelper.getHelper(prj, obj.getClass());
+ try {
+ iHelper.setAttribute(prj, obj, name, value);
+ } catch (org.apache.tools.ant.BuildException e) {
+ // no-op
+ }
+ }
+
+
+ // ----- 'beautiful' output -----
+
+
+ /**
+ * Override Object.toString().
+ * @return information about this selector
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer("{modifiedselector");
+ buf.append(" update=").append(update);
+ buf.append(" seldirs=").append(selectDirectories);
+ buf.append(" cache=").append(cache);
+ buf.append(" algorithm=").append(algorithm);
+ buf.append(" comparator=").append(comparator);
+ buf.append("}");
+ return buf.toString();
+ }
+
+
+ // ----- BuildListener interface methods -----
+
+
+ /**
+ * Signals that the last target has finished.
+ * @param event received BuildEvent
+ */
+ public void buildFinished(BuildEvent event) {
+ if (getDelayUpdate()) {
+ saveCache();
+ }
+ }
+
+
+ /**
+ * Signals that a target has finished.
+ * @param event received BuildEvent
+ */
+ public void targetFinished(BuildEvent event) {
+ if (getDelayUpdate()) {
+ saveCache();
+ }
+ }
+
+
+ /**
+ * Signals that a task has finished.
+ * @param event received BuildEvent
+ */
+ public void taskFinished(BuildEvent event) {
+ if (getDelayUpdate()) {
+ saveCache();
+ }
+ }
+
+
+ /**
+ * Signals that a build has started.
+ * @param event received BuildEvent
+ */
+ public void buildStarted(BuildEvent event) {
+ // no-op
+ }
+
+
+ /**
+ * Signals that a target is starting.
+ * @param event received BuildEvent
+ */
+ public void targetStarted(BuildEvent event) {
+ // no-op
+ }
+
+
+
+ /**
+ * Signals that a task is starting.
+ * @param event received BuildEvent
+ */
+ public void taskStarted(BuildEvent event) {
+ // no-op
+ }
+
+
+ /**
+ * Signals a message logging event.
+ * @param event received BuildEvent
+ */
+ public void messageLogged(BuildEvent event) {
+ // no-op
+ }
+
+
+ // The EnumeratedAttributes for the three interface implementations.
+ // Name-Classname mapping is done in the configure() method.
+
+
+ /**
+ * Get the cache type to use.
+ * @return the enumerated cache type
+ */
+ public Cache getCache() {
+ return cache;
+ }
+
+ /**
+ * Set the cache type to use.
+ * @param name an enumerated cache type.
+ */
+ public void setCache(CacheName name) {
+ cacheName = name;
+ }
+
+ /**
+ * The enumerated type for cache.
+ * The values are "propertyfile".
+ */
+ public static class CacheName extends EnumeratedAttribute {
+ /**
+ * {@inheritDoc}
+ * @see EnumeratedAttribute#getValues()
+ */
+ public String[] getValues() {
+ return new String[] {"propertyfile" };
+ }
+ }
+
+ /**
+ * Get the algorithm type to use.
+ * @return the enumerated algorithm type
+ */
+ public Algorithm getAlgorithm() {
+ return algorithm;
+ }
+
+ /**
+ * Set the algorithm type to use.
+ * @param name an enumerated algorithm type.
+ */
+ public void setAlgorithm(AlgorithmName name) {
+ algoName = name;
+ }
+
+ /**
+ * The enumerated type for algorithm.
+ * The values are "hashValue", "digest" and "checksum".
+ */
+ public static class AlgorithmName extends EnumeratedAttribute {
+ /**
+ * {@inheritDoc}
+ * @see EnumeratedAttribute#getValues()
+ */
+ public String[] getValues() {
+ return new String[] {"hashvalue", "digest", "checksum" };
+ }
+ }
+
+ /**
+ * Get the comparator type to use.
+ * @return the enumerated comparator type
+ */
+ public Comparator<? super String> getComparator() {
+ return comparator;
+ }
+
+ /**
+ * Set the comparator type to use.
+ * @param name an enumerated comparator type.
+ */
+ public void setComparator(ComparatorName name) {
+ compName = name;
+ }
+
+ /**
+ * The enumerated type for algorithm.
+ * The values are "equal" and "rule".
+ */
+ public static class ComparatorName extends EnumeratedAttribute {
+ /**
+ * {@inheritDoc}
+ * @see EnumeratedAttribute#getValues()
+ */
+ public String[] getValues() {
+ return new String[] {"equal", "rule" };
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java
new file mode 100644
index 00000000..1446e890
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java
@@ -0,0 +1,236 @@
+/*
+ * 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.tools.ant.types.selectors.modifiedselector;
+
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Vector;
+
+
+/**
+ * Use java.util.Properties for storing the values.
+ * The use of this Cache-implementation requires the use of the parameter
+ * <param name="cache.cachefile" .../> for defining, where to store the
+ * properties file.
+ *
+ * The ModifiedSelector sets the <i>cachefile</i> to the default value
+ * <i>cache.properties</i>.
+ *
+ * Supported <param>s are:
+ * <table>
+ * <tr>
+ * <th>name</th><th>values</th><th>description</th><th>required</th>
+ * </tr>
+ * <tr>
+ * <td> cache.cachefile </td>
+ * <td> <i>path to file</i> </td>
+ * <td> the name of the properties file </td>
+ * <td> yes </td>
+ * </tr>
+ * </table>
+ *
+ * @version 2003-09-13
+ * @since Ant 1.6
+ */
+public class PropertiesfileCache implements Cache {
+
+
+ // ----- member variables - configuration -----
+
+
+ /** Where to store the properties? */
+ private File cachefile = null;
+
+ /** Object for storing the key-value-pairs. */
+ private Properties cache = new Properties();
+
+
+ // ----- member variables - internal use -----
+
+
+ /** Is the cache already loaded? Prevents from multiple load operations. */
+ private boolean cacheLoaded = false;
+
+ /** Must the cache be saved? Prevents from multiple save operations. */
+ private boolean cacheDirty = true;
+
+
+ // ----- Constructors -----
+
+
+ /** Bean-Constructor. */
+ public PropertiesfileCache() {
+ }
+
+ /**
+ * Constructor.
+ * @param cachefile set the cachefile
+ */
+ public PropertiesfileCache(File cachefile) {
+ this.cachefile = cachefile;
+ }
+
+
+ // ----- Cache-Configuration -----
+
+
+ /**
+ * Setter.
+ * @param file new value
+ */
+ public void setCachefile(File file) {
+ cachefile = file;
+ }
+
+
+ /**
+ * Getter.
+ * @return the cachefile
+ */
+ public File getCachefile() {
+ return cachefile;
+ }
+
+ /**
+ * This cache is valid if the cachefile is set.
+ * @return true if all is ok false otherwise
+ */
+ public boolean isValid() {
+ return (cachefile != null);
+ }
+
+
+ // ----- Data Access
+
+
+ /**
+ * Load the cache from underlying properties file.
+ */
+ public void load() {
+ if ((cachefile != null) && cachefile.isFile() && cachefile.canRead()) {
+ try {
+ BufferedInputStream bis = new BufferedInputStream(
+ new FileInputStream(cachefile));
+ cache.load(bis);
+ bis.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ // after loading the cache is up to date with the file
+ cacheLoaded = true;
+ cacheDirty = false;
+ }
+
+ /**
+ * Saves modification of the cache.
+ * Cache is only saved if there is one ore more entries.
+ * Because entries can not be deleted by this API, this Cache
+ * implementation checks the existence of entries before creating the file
+ * for performance optimisation.
+ */
+ public void save() {
+ if (!cacheDirty) {
+ return;
+ }
+ if ((cachefile != null) && cache.propertyNames().hasMoreElements()) {
+ try {
+ BufferedOutputStream bos = new BufferedOutputStream(
+ new FileOutputStream(cachefile));
+ cache.store(bos, null);
+ bos.flush();
+ bos.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ cacheDirty = false;
+ }
+
+ /** Deletes the cache and its underlying file. */
+ public void delete() {
+ cache = new Properties();
+ cachefile.delete();
+ cacheLoaded = true;
+ cacheDirty = false;
+ }
+
+ /**
+ * Returns a value for a given key from the cache.
+ * @param key the key
+ * @return the stored value
+ */
+ public Object get(Object key) {
+ if (!cacheLoaded) {
+ load();
+ }
+ try {
+ return cache.getProperty(String.valueOf(key));
+ } catch (ClassCastException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Saves a key-value-pair in the cache.
+ * @param key the key
+ * @param value the value
+ */
+ public void put(Object key, Object value) {
+ cache.put(String.valueOf(key), String.valueOf(value));
+ cacheDirty = true;
+ }
+
+ /**
+ * Returns an iterator over the keys in the cache.
+ * @return An iterator over the keys.
+ */
+ public Iterator<String> iterator() {
+ Vector<String> v = new Vector<String>();
+ Enumeration<?> en = cache.propertyNames();
+ while (en.hasMoreElements()) {
+ v.add(en.nextElement().toString());
+ }
+ return v.iterator();
+ }
+
+
+ // ----- additional -----
+
+
+ /**
+ * Override Object.toString().
+ * @return information about this cache
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append("<PropertiesfileCache:");
+ buf.append("cachefile=").append(cachefile);
+ buf.append(";noOfEntries=").append(cache.size());
+ buf.append(">");
+ return buf.toString();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/spi/Provider.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/spi/Provider.java
new file mode 100644
index 00000000..f73b0191
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/spi/Provider.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.tools.ant.types.spi;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * ANT Jar-Task SPI extension
+ * This class corresponds to the nested element
+ * &lt;provider type="type"&gt; in the &lt;service type=""&gt;
+ * nested element of the jar task.
+ * @see <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=31520">
+ * http://issues.apache.org/bugzilla/show_bug.cgi?id=31520</a>
+ */
+public class Provider extends ProjectComponent {
+ private String type;
+
+ /**
+ * @return the class name for
+ */
+ public String getClassName() {
+ return type;
+ }
+
+ /**
+ * Set the provider classname.
+ * @param type the value to set.
+ */
+ public void setClassName(String type) {
+ this.type = type;
+ }
+
+ /**
+ * Check if the component has been configured correctly.
+ */
+ public void check() {
+ if (type == null) {
+ throw new BuildException(
+ "classname attribute must be set for provider element",
+ getLocation());
+ }
+ if (type.length() == 0) {
+ throw new BuildException(
+ "Invalid empty classname", getLocation());
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/spi/Service.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/spi/Service.java
new file mode 100644
index 00000000..96c8e4e5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/types/spi/Service.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.tools.ant.types.spi;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * ANT Jar-Task SPI extension
+ *
+ * @see <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=31520">
+ * http://issues.apache.org/bugzilla/show_bug.cgi?id=31520</a>
+ */
+public class Service extends ProjectComponent {
+ private List<Provider> providerList = new ArrayList<Provider>();
+ private String type;
+
+ /**
+ * Set the provider classname.
+ * @param className the classname of a provider of this service.
+ */
+ public void setProvider(String className) {
+ Provider provider = new Provider();
+ provider.setClassName(className);
+ providerList.add(provider);
+ }
+
+ /**
+ * Add a nested provider element.
+ * @param provider a provider element.
+ */
+ public void addConfiguredProvider(Provider provider) {
+ provider.check();
+ providerList.add(provider);
+ }
+
+ /**
+ * @return the service type.
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Set the service type.
+ * @param type the service type, a classname of
+ * an interface or a class (normally
+ * abstract).
+ */
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ /**
+ * Return the implementations of this
+ * services as an inputstream.
+ * @return an inputstream of the classname names
+ * encoded as UTF-8.
+ * @throws IOException if there is an error.
+ */
+ public InputStream getAsStream() throws IOException {
+ ByteArrayOutputStream arrayOut = new ByteArrayOutputStream();
+ Writer writer = new OutputStreamWriter(arrayOut, "UTF-8");
+ for (Provider provider : providerList) {
+ writer.write(provider.getClassName());
+ writer.write("\n");
+ }
+ writer.close();
+ return new ByteArrayInputStream(arrayOut.toByteArray());
+ }
+
+ /**
+ * Check if this object is configured correctly as a nested
+ * element.
+ */
+ public void check() {
+ if (type == null) {
+ throw new BuildException(
+ "type attribute must be set for service element",
+ getLocation());
+ }
+ if (type.length() == 0) {
+ throw new BuildException(
+ "Invalid empty type classname", getLocation());
+ }
+ if (providerList.size() == 0) {
+ throw new BuildException(
+ "provider attribute or nested provider element must be set!",
+ getLocation());
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Base64Converter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Base64Converter.java
new file mode 100644
index 00000000..5d60a145
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Base64Converter.java
@@ -0,0 +1,124 @@
+/*
+ * 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.tools.ant.util;
+
+/**
+ * BASE 64 encoding of a String or an array of bytes.
+ *
+ * Based on RFC 1421.
+ *
+ **/
+public class Base64Converter {
+
+ private static final int BYTE = 8;
+ private static final int WORD = 16;
+ private static final int BYTE_MASK = 0xFF;
+ private static final int POS_0_MASK = 0x0000003F;
+ private static final int POS_1_MASK = 0x00000FC0;
+ private static final int POS_1_SHIFT = 6;
+ private static final int POS_2_MASK = 0x0003F000;
+ private static final int POS_2_SHIFT = 12;
+ private static final int POS_3_MASK = 0x00FC0000;
+ private static final int POS_3_SHIFT = 18;
+
+
+ private static final char[] ALPHABET = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 to 7
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8 to 15
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16 to 23
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24 to 31
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32 to 39
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40 to 47
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48 to 55
+ '4', '5', '6', '7', '8', '9', '+', '/'}; // 56 to 63
+
+ // CheckStyle:ConstantNameCheck OFF - bc
+ /** Provided for BC purposes */
+ public static final char[] alphabet = ALPHABET;
+ // CheckStyle:ConstantNameCheck ON
+
+
+ /**
+ * Encode a string into base64 encoding.
+ * @param s the string to encode.
+ * @return the encoded string.
+ */
+ public String encode(String s) {
+ return encode(s.getBytes());
+ }
+
+ /**
+ * Encode a byte array into base64 encoding.
+ * @param octetString the byte array to encode.
+ * @return the encoded string.
+ */
+ public String encode(byte[] octetString) {
+ int bits24;
+ int bits6;
+
+ // CheckStyle:MagicNumber OFF
+ char[] out = new char[((octetString.length - 1) / 3 + 1) * 4];
+ // CheckStyle:MagicNumber ON
+ int outIndex = 0;
+ int i = 0;
+
+ // CheckStyle:MagicNumber OFF
+ while ((i + 3) <= octetString.length) {
+ // CheckStyle:MagicNumber ON
+ // store the octets
+ bits24 = (octetString[i++] & BYTE_MASK) << WORD;
+ bits24 |= (octetString[i++] & BYTE_MASK) << BYTE;
+ bits24 |= octetString[i++] & BYTE_MASK;
+
+ bits6 = (bits24 & POS_3_MASK) >> POS_3_SHIFT;
+ out[outIndex++] = ALPHABET[bits6];
+ bits6 = (bits24 & POS_2_MASK) >> POS_2_SHIFT;
+ out[outIndex++] = ALPHABET[bits6];
+ bits6 = (bits24 & POS_1_MASK) >> POS_1_SHIFT;
+ out[outIndex++] = ALPHABET[bits6];
+ bits6 = (bits24 & POS_0_MASK);
+ out[outIndex++] = ALPHABET[bits6];
+ }
+ if (octetString.length - i == 2) {
+ // store the octets
+ bits24 = (octetString[i] & BYTE_MASK) << WORD;
+ bits24 |= (octetString[i + 1] & BYTE_MASK) << BYTE;
+ bits6 = (bits24 & POS_3_MASK) >> POS_3_SHIFT;
+ out[outIndex++] = ALPHABET[bits6];
+ bits6 = (bits24 & POS_2_MASK) >> POS_2_SHIFT;
+ out[outIndex++] = ALPHABET[bits6];
+ bits6 = (bits24 & POS_1_MASK) >> POS_1_SHIFT;
+ out[outIndex++] = ALPHABET[bits6];
+
+ // padding
+ out[outIndex++] = '=';
+ } else if (octetString.length - i == 1) {
+ // store the octets
+ bits24 = (octetString[i] & BYTE_MASK) << WORD;
+ bits6 = (bits24 & POS_3_MASK) >> POS_3_SHIFT;
+ out[outIndex++] = ALPHABET[bits6];
+ bits6 = (bits24 & POS_2_MASK) >> POS_2_SHIFT;
+ out[outIndex++] = ALPHABET[bits6];
+
+ // padding
+ out[outIndex++] = '=';
+ out[outIndex++] = '=';
+ }
+ return new String(out);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ChainedMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ChainedMapper.java
new file mode 100644
index 00000000..635a0538
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ChainedMapper.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.tools.ant.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A <code>ContainerMapper</code> that chains the results of the first
+ * nested <code>FileNameMapper</code>s into sourcefiles for the second,
+ * the second to the third, and so on, returning the resulting mapped
+ * filenames from the last nested <code>FileNameMapper</code>.
+ */
+public class ChainedMapper extends ContainerMapper {
+
+ /** {@inheritDoc}. */
+ public String[] mapFileName(String sourceFileName) {
+ List inputs = new ArrayList();
+ List results = new ArrayList();
+ results.add(sourceFileName);
+ FileNameMapper mapper = null;
+
+ for (Iterator mIter = getMappers().iterator(); mIter.hasNext();) {
+ mapper = (FileNameMapper) (mIter.next());
+ if (mapper != null) {
+ inputs.clear();
+ inputs.addAll(results);
+ results.clear();
+
+ for (Iterator it = inputs.iterator(); it.hasNext();) {
+ String[] mapped = mapper.mapFileName((String) (it.next()));
+ if (mapped != null) {
+ results.addAll(Arrays.asList(mapped));
+ }
+ }
+ }
+ }
+ return (results.size() == 0) ? null
+ : (String[]) results.toArray(new String[results.size()]);
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ClasspathUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ClasspathUtils.java
new file mode 100644
index 00000000..309860e9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ClasspathUtils.java
@@ -0,0 +1,463 @@
+/*
+ * 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.tools.ant.util;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+// CheckStyle:HideUtilityClassConstructorCheck OFF - bc
+
+/**
+ * Offers some helper methods on the Path structure in ant.
+ *
+ * <p>The basic idea behind this utility class is to use it from inside the
+ * different Ant objects (and user defined objects) that need classLoading
+ * for their operation.
+ * Normally those would have a setClasspathRef() {for the @classpathref}
+ * and/or a createClasspath() {for the nested &lt;classpath&gt;}
+ * Typically one would have in your Ant Task or DataType</p>
+ *
+ * <pre><code>
+ * ClasspathUtils.Delegate cpDelegate;
+ *
+ * public void init() {
+ * this.cpDelegate = ClasspathUtils.getDelegate(this);
+ * super.init();
+ * }
+ *
+ * public void setClasspathRef(Reference r) {
+ * this.cpDelegate.setClasspathRef(r);
+ * }
+ *
+ * public Path createClasspath() {
+ * return this.cpDelegate.createClasspath();
+ * }
+ *
+ * public void setClassname(String fqcn) {
+ * this.cpDelegate.setClassname(fqcn);
+ * }
+ * </code></pre>
+ *
+ * <p>At execution time, when you actually need the classloading
+ * you can just:</p>
+ *
+ * <pre><code>
+ * Object o = this.cpDelegate.newInstance();
+ * </code></pre>
+ *
+ * @since Ant 1.6
+ */
+public class ClasspathUtils {
+
+ /**
+ * Name of the magic property that controls classloader reuse in Ant 1.4.
+ */
+ public static final String REUSE_LOADER_REF = MagicNames.REFID_CLASSPATH_REUSE_LOADER;
+
+ /**
+ * Convenience overloaded version of {@link
+ * #getClassLoaderForPath(Project, Reference, boolean)}.
+ *
+ * <p>Assumes the logical 'false' for the reverseLoader.</p>
+ *
+ * @param p the project
+ * @param ref the reference
+ * @return The class loader
+ */
+ public static ClassLoader getClassLoaderForPath(Project p, Reference ref) {
+ return getClassLoaderForPath(p, ref, false);
+ }
+
+ /**
+ * Convenience overloaded version of {@link #getClassLoaderForPath(Project, Path,
+ * String, boolean)}.
+ *
+ * <p>Delegates to the other one after extracting the referenced
+ * Path from the Project. This checks also that the passed
+ * Reference is pointing to a Path all right.</p>
+ * @param p current Ant project
+ * @param ref Reference to Path structure
+ * @param reverseLoader if set to true this new loader will take
+ * precedence over its parent (which is contra the regular
+ * classloader behaviour)
+ * @return The class loader
+ */
+ public static ClassLoader getClassLoaderForPath(
+ Project p, Reference ref, boolean reverseLoader) {
+ String pathId = ref.getRefId();
+ Object path = p.getReference(pathId);
+ if (!(path instanceof Path)) {
+ throw new BuildException("The specified classpathref " + pathId
+ + " does not reference a Path.");
+ }
+ String loaderId = MagicNames.REFID_CLASSPATH_LOADER_PREFIX + pathId;
+ return getClassLoaderForPath(p, (Path) path, loaderId, reverseLoader);
+ }
+
+ /**
+ * Convenience overloaded version of {@link
+ * #getClassLoaderForPath(Project, Path, String, boolean)}.
+ *
+ * <p>Assumes the logical 'false' for the reverseLoader.</p>
+ *
+ * @param p current Ant project
+ * @param path the path
+ * @param loaderId the loader id string
+ * @return The class loader
+ */
+ public static ClassLoader getClassLoaderForPath(Project p, Path path, String loaderId) {
+ return getClassLoaderForPath(p, path, loaderId, false);
+ }
+
+ /**
+ * Convenience overloaded version of {@link
+ * #getClassLoaderForPath(Project, Path, String, boolean, boolean)}.
+ *
+ * <p>Sets value for 'reuseLoader' to true if the magic property
+ * has been set.</p>
+ *
+ * @param p the project
+ * @param path the path
+ * @param loaderId the loader id string
+ * @param reverseLoader if set to true this new loader will take
+ * precedence over its parent (which is contra the regular
+ * classloader behaviour)
+ * @return The class loader
+ */
+ public static ClassLoader getClassLoaderForPath(
+ Project p, Path path, String loaderId, boolean reverseLoader) {
+ return getClassLoaderForPath(p, path, loaderId, reverseLoader, isMagicPropertySet(p));
+ }
+
+ /**
+ * Gets a classloader that loads classes from the classpath
+ * defined in the path argument.
+ *
+ * <p>Based on the setting of the magic property
+ * 'ant.reuse.loader' this will try to reuse the previously
+ * created loader with that id, and of course store it there upon
+ * creation.</p>
+ * @param p Ant Project where the handled components are living in.
+ * @param path Path object to be used as classpath for this classloader
+ * @param loaderId identification for this Loader,
+ * @param reverseLoader if set to true this new loader will take
+ * precedence over its parent (which is contra the regular
+ * classloader behaviour)
+ * @param reuseLoader if true reuse the loader if it is found
+ * @return ClassLoader that uses the Path as its classpath.
+ */
+ public static ClassLoader getClassLoaderForPath(
+ Project p, Path path, String loaderId, boolean reverseLoader, boolean reuseLoader) {
+ ClassLoader cl = null;
+
+ // magic property
+ if (loaderId != null && reuseLoader) {
+ Object reusedLoader = p.getReference(loaderId);
+ if (reusedLoader != null && !(reusedLoader instanceof ClassLoader)) {
+ throw new BuildException("The specified loader id " + loaderId
+ + " does not reference a class loader");
+ }
+ cl = (ClassLoader) reusedLoader;
+ }
+ if (cl == null) {
+ cl = getUniqueClassLoaderForPath(p, path, reverseLoader);
+ if (loaderId != null && reuseLoader) {
+ p.addReference(loaderId, cl);
+ }
+ }
+ return cl;
+ }
+
+ /**
+ * Gets a fresh, different, previously unused classloader that uses the
+ * passed path as its classpath.
+ *
+ * <p>This method completely ignores the ant.reuse.loader magic
+ * property and should be used with caution.</p>
+ * @param p Ant Project where the handled components are living in.
+ * @param path the classpath for this loader
+ * @param reverseLoader if set to true this new loader will take
+ * precedence over its parent (which is contra the regular
+ * classloader behaviour)
+ * @return The fresh, different, previously unused class loader.
+ */
+ public static ClassLoader getUniqueClassLoaderForPath(Project p, Path path,
+ boolean reverseLoader) {
+ AntClassLoader acl = p.createClassLoader(path);
+ if (reverseLoader) {
+ acl.setParentFirst(false);
+ acl.addJavaLibraries();
+ }
+ return acl;
+ }
+
+ /**
+ * Creates a fresh object instance of the specified classname.
+ *
+ * <p> This uses the userDefinedLoader to load the specified class,
+ * and then makes an instance using the default no-argument constructor.
+ * </p>
+ *
+ * @param className the full qualified class name to load.
+ * @param userDefinedLoader the classloader to use.
+ * @return The fresh object instance
+ * @throws BuildException when loading or instantiation failed.
+ */
+ public static Object newInstance(String className, ClassLoader userDefinedLoader) {
+ return newInstance(className, userDefinedLoader, Object.class);
+ }
+
+ /**
+ * Creates a fresh object instance of the specified classname.
+ *
+ * <p> This uses the userDefinedLoader to load the specified class,
+ * and then makes an instance using the default no-argument constructor.
+ * </p>
+ *
+ * @param className the full qualified class name to load.
+ * @param userDefinedLoader the classloader to use.
+ * @param expectedType the Class that the result should be assignment
+ * compatible with. (No ClassCastException will be thrown in case
+ * the result of this method is casted to the expectedType)
+ * @return The fresh object instance
+ * @throws BuildException when loading or instantiation failed.
+ * @since Ant 1.7
+ */
+ public static Object newInstance(String className, ClassLoader userDefinedLoader,
+ Class expectedType) {
+ try {
+ Class clazz = Class.forName(className, true, userDefinedLoader);
+ Object o = clazz.newInstance();
+ if (!expectedType.isInstance(o)) {
+ throw new BuildException("Class of unexpected Type: " + className + " expected :"
+ + expectedType);
+ }
+ return o;
+ } catch (ClassNotFoundException e) {
+ throw new BuildException("Class not found: " + className, e);
+ } catch (InstantiationException e) {
+ throw new BuildException("Could not instantiate " + className
+ + ". Specified class should have a no " + "argument constructor.", e);
+ } catch (IllegalAccessException e) {
+ throw new BuildException("Could not instantiate " + className
+ + ". Specified class should have a " + "public constructor.", e);
+ } catch (LinkageError e) {
+ throw new BuildException("Class " + className
+ + " could not be loaded because of an invalid dependency.", e);
+ }
+ }
+
+ /**
+ * Obtains a delegate that helps out with classic classpath configuration.
+ *
+ * @param component your projectComponent that needs the assistence
+ * @return the helper, delegate.
+ * @see ClasspathUtils.Delegate
+ */
+ public static Delegate getDelegate(ProjectComponent component) {
+ return new Delegate(component);
+ }
+
+ /**
+ * Checks for the magic property that enables class loader reuse
+ * for <taskdef> and <typedef> in Ant 1.5 and earlier.
+ */
+ private static boolean isMagicPropertySet(Project p) {
+ return p.getProperty(REUSE_LOADER_REF) != null;
+ }
+
+ /**
+ * Delegate that helps out any specific ProjectComponent that needs
+ * dynamic classloading.
+ *
+ * <p>Ant ProjectComponents that need a to be able to dynamically load
+ * Classes and instantiate them often expose the following ant syntax
+ * sugar: </p>
+ *
+ * <ul><li> nested &lt;classpath&gt; </li>
+ * <li> attribute @classpathref </li>
+ * <li> attribute @classname </li></ul>
+ *
+ * <p> This class functions as a delegate handling the configuration
+ * issues for this recurring pattern. Its usage pattern, as the name
+ * suggests, is delegation rather than inheritance. </p>
+ *
+ * @since Ant 1.6
+ */
+ public static class Delegate {
+ private final ProjectComponent component;
+ private Path classpath;
+ private String classpathId;
+ private String className;
+ private String loaderId;
+ private boolean reverseLoader = false;
+
+ /**
+ * Construct a Delegate
+ * @param component the ProjectComponent this delegate is for.
+ */
+ Delegate(ProjectComponent component) {
+ this.component = component;
+ }
+
+ /**
+ * This method is a Delegate method handling the @classpath attribute.
+ *
+ * <p>This attribute can set a path to add to the classpath.</p>
+ *
+ * @param classpath the path to use for the classpath.
+ */
+ public void setClasspath(Path classpath) {
+ if (this.classpath == null) {
+ this.classpath = classpath;
+ } else {
+ this.classpath.append(classpath);
+ }
+ }
+
+ /**
+ * Delegate method handling the &lt;classpath&gt; tag.
+ *
+ * <p>This nested path-like structure can set a path to add to the
+ * classpath.</p>
+ *
+ * @return the created path.
+ */
+ public Path createClasspath() {
+ if (this.classpath == null) {
+ this.classpath = new Path(component.getProject());
+ }
+ return this.classpath.createPath();
+ }
+
+ /**
+ * Delegate method handling the @classname attribute.
+ *
+ * <p>This attribute sets the full qualified class name of the class
+ * to load and instantiate.</p>
+ *
+ * @param fcqn the name of the class to load.
+ */
+ public void setClassname(String fcqn) {
+ this.className = fcqn;
+ }
+
+ /**
+ * Delegate method handling the @classpathref attribute.
+ *
+ * <p>This attribute can add a referenced path-like structure to the
+ * classpath.</p>
+ *
+ * @param r the reference to the classpath.
+ */
+ public void setClasspathref(Reference r) {
+ this.classpathId = r.getRefId();
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * Delegate method handling the @reverseLoader attribute.
+ *
+ * <p>This attribute can set a boolean indicating that the used
+ * classloader should NOT follow the classical parent-first scheme.
+ * </p>
+ *
+ * <p>By default this is supposed to be false.</p>
+ *
+ * <p>Caution: this behaviour is contradictory to the normal way
+ * classloaders work. Do not let your ProjectComponent use it if
+ * you are not really sure.</p>
+ *
+ * @param reverseLoader if true reverse the order of looking up a class.
+ */
+ public void setReverseLoader(boolean reverseLoader) {
+ this.reverseLoader = reverseLoader;
+ }
+
+ /**
+ * Sets the loaderRef.
+ * @param r the reference to the loader.
+ */
+ public void setLoaderRef(Reference r) {
+ this.loaderId = r.getRefId();
+ }
+
+
+ /**
+ * Finds or creates the classloader for this object.
+ * @return The class loader.
+ */
+ public ClassLoader getClassLoader() {
+ return getClassLoaderForPath(getContextProject(), classpath, getClassLoadId(),
+ reverseLoader, loaderId != null || isMagicPropertySet(getContextProject()));
+ }
+
+ /**
+ * The project of the ProjectComponent we are working for.
+ */
+ private Project getContextProject() {
+ return component.getProject();
+ }
+
+ /**
+ * Computes the loaderId based on the configuration of the component.
+ * @return a loader identifier.
+ */
+ public String getClassLoadId() {
+ if (loaderId == null && classpathId != null) {
+ return MagicNames.REFID_CLASSPATH_LOADER_PREFIX + classpathId;
+ } else {
+ return loaderId;
+ }
+ }
+
+ /**
+ * Helper method obtaining a fresh instance of the class specified
+ * in the @classname and using the specified classpath.
+ *
+ * @return the fresh instantiated object.
+ */
+ public Object newInstance() {
+ return ClasspathUtils.newInstance(this.className, getClassLoader());
+ }
+
+ /**
+ * The classpath.
+ * @return the classpath.
+ */
+ public Path getClasspath() {
+ return classpath;
+ }
+
+ /**
+ * Get the reverseLoader setting.
+ * @return true if looking up in reverse order.
+ */
+ public boolean isReverseLoader() {
+ return reverseLoader;
+ }
+
+ //TODO no methods yet for getClassname
+ //TODO no method for newInstance using a reverse-classloader
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/CollectionUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/CollectionUtils.java
new file mode 100644
index 00000000..03c48d93
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/CollectionUtils.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.tools.ant.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Vector;
+
+// CheckStyle:HideUtilityClassConstructorCheck OFF - bc
+
+/**
+ * A set of helper methods related to collection manipulation.
+ *
+ * @since Ant 1.5
+ */
+public class CollectionUtils {
+
+ /**
+ * Collections.emptyList() is Java5+.
+ */
+ @SuppressWarnings("rawtypes")
+ @Deprecated
+ public static final List EMPTY_LIST = Collections.EMPTY_LIST;
+
+ /**
+ * Please use Vector.equals() or List.equals().
+ * @param v1 the first vector.
+ * @param v2 the second vector.
+ * @return true if the vectors are equal.
+ * @since Ant 1.5
+ * @deprecated since 1.6.x.
+ */
+ public static boolean equals(Vector<?> v1, Vector<?> v2) {
+ if (v1 == v2) {
+ return true;
+ }
+
+ if (v1 == null || v2 == null) {
+ return false;
+ }
+
+ return v1.equals(v2);
+ }
+
+ /**
+ * Dictionary does not have an equals.
+ * Please use Map.equals().
+ *
+ * <p>Follows the equals contract of Java 2's Map.</p>
+ * @param d1 the first directory.
+ * @param d2 the second directory.
+ * @return true if the directories are equal.
+ * @since Ant 1.5
+ * @deprecated since 1.6.x.
+ */
+ public static boolean equals(Dictionary<?, ?> d1, Dictionary<?, ?> d2) {
+ if (d1 == d2) {
+ return true;
+ }
+
+ if (d1 == null || d2 == null) {
+ return false;
+ }
+
+ if (d1.size() != d2.size()) {
+ return false;
+ }
+
+ Enumeration<?> e1 = d1.keys();
+ while (e1.hasMoreElements()) {
+ Object key = e1.nextElement();
+ Object value1 = d1.get(key);
+ Object value2 = d2.get(key);
+ if (value2 == null || !value1.equals(value2)) {
+ return false;
+ }
+ }
+
+ // don't need the opposite check as the Dictionaries have the
+ // same size, so we've also covered all keys of d2 already.
+
+ return true;
+ }
+
+ /**
+ * Creates a comma separated list of all values held in the given
+ * collection.
+ *
+ * @param c collection to transform
+ * @return string representation of the collection
+ * @since Ant 1.8.0
+ */
+ public static String flattenToString(Collection<?> c) {
+ final StringBuilder sb = new StringBuilder();
+ for (Object o : c) {
+ if (sb.length() != 0) {
+ sb.append(",");
+ }
+ sb.append(o);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Dictionary does not know the putAll method. Please use Map.putAll().
+ * @param m1 the to directory.
+ * @param m2 the from directory.
+ * @param <K> type of the key
+ * @param <V> type of the value
+ * @since Ant 1.6
+ * @deprecated since 1.6.x.
+ */
+ public static <K, V> void putAll(Dictionary<? super K, ? super V> m1, Dictionary<? extends K, ? extends V> m2) {
+ for (Enumeration<? extends K> it = m2.keys(); it.hasMoreElements();) {
+ K key = it.nextElement();
+ m1.put(key, m2.get(key));
+ }
+ }
+
+ /**
+ * An empty enumeration.
+ * @since Ant 1.6
+ */
+ public static final class EmptyEnumeration<E> implements Enumeration<E> {
+ /** Constructor for the EmptyEnumeration */
+ public EmptyEnumeration() {
+ }
+
+ /**
+ * @return false always.
+ */
+ public boolean hasMoreElements() {
+ return false;
+ }
+
+ /**
+ * @return nothing.
+ * @throws NoSuchElementException always.
+ */
+ public E nextElement() throws NoSuchElementException {
+ throw new NoSuchElementException();
+ }
+ }
+
+ /**
+ * Append one enumeration to another.
+ * Elements are evaluated lazily.
+ * @param e1 the first enumeration.
+ * @param e2 the subsequent enumeration.
+ * @param <E> element type
+ * @return an enumeration representing e1 followed by e2.
+ * @since Ant 1.6.3
+ */
+ public static <E> Enumeration<E> append(Enumeration<E> e1, Enumeration<E> e2) {
+ return new CompoundEnumeration<E>(e1, e2);
+ }
+
+ /**
+ * Adapt the specified Iterator to the Enumeration interface.
+ * @param iter the Iterator to adapt.
+ * @param <E> element type
+ * @return an Enumeration.
+ */
+ public static <E> Enumeration<E> asEnumeration(final Iterator<E> iter) {
+ return new Enumeration<E>() {
+ public boolean hasMoreElements() {
+ return iter.hasNext();
+ }
+ public E nextElement() {
+ return iter.next();
+ }
+ };
+ }
+
+ /**
+ * Adapt the specified Enumeration to the Iterator interface.
+ * @param e the Enumeration to adapt.
+ * @param <E> element type
+ * @return an Iterator.
+ */
+ public static <E> Iterator<E> asIterator(final Enumeration<E> e) {
+ return new Iterator<E>() {
+ public boolean hasNext() {
+ return e.hasMoreElements();
+ }
+ public E next() {
+ return e.nextElement();
+ }
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ /**
+ * Returns a collection containing all elements of the iterator.
+ *
+ * @param iter the Iterator to convert
+ * @param <T> element type
+ * @return the collection
+ * @since Ant 1.8.0
+ */
+ public static <T> Collection<T> asCollection(final Iterator<? extends T> iter) {
+ List<T> l = new ArrayList<T>();
+ while (iter.hasNext()) {
+ l.add(iter.next());
+ }
+ return l;
+ }
+
+ private static final class CompoundEnumeration<E> implements Enumeration<E> {
+
+ private final Enumeration<E> e1, e2;
+
+ public CompoundEnumeration(Enumeration<E> e1, Enumeration<E> e2) {
+ this.e1 = e1;
+ this.e2 = e2;
+ }
+
+ public boolean hasMoreElements() {
+ return e1.hasMoreElements() || e2.hasMoreElements();
+ }
+
+ public E nextElement() throws NoSuchElementException {
+ if (e1.hasMoreElements()) {
+ return e1.nextElement();
+ } else {
+ return e2.nextElement();
+ }
+ }
+
+ }
+
+ /**
+ * Counts how often the given Object occurs in the given
+ * collection using equals() for comparison.
+ *
+ * @param c collection in which to search
+ * @param o object to search
+ * @return frequency
+ * @since Ant 1.8.0
+ */
+ public static int frequency(Collection<?> c, Object o) {
+ // same as Collections.frequency introduced with JDK 1.5
+ int freq = 0;
+ if (c != null) {
+ for (Iterator<?> i = c.iterator(); i.hasNext();) {
+ Object test = i.next();
+ if (o == null ? test == null : o.equals(test)) {
+ freq++;
+ }
+ }
+ }
+ return freq;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/CompositeMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/CompositeMapper.java
new file mode 100644
index 00000000..d04a5fc9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/CompositeMapper.java
@@ -0,0 +1,50 @@
+/*
+ * 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.tools.ant.util;
+
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+
+/**
+ * A <code>ContainerMapper</code> that unites the results of its constituent
+ * <code>FileNameMapper</code>s into a single set of result filenames.
+ */
+public class CompositeMapper extends ContainerMapper {
+
+ /** {@inheritDoc}. */
+ public String[] mapFileName(String sourceFileName) {
+ LinkedHashSet results = new LinkedHashSet();
+
+ FileNameMapper mapper = null;
+ for (Iterator mIter = getMappers().iterator(); mIter.hasNext();) {
+ mapper = (FileNameMapper) (mIter.next());
+ if (mapper != null) {
+ String[] mapped = mapper.mapFileName(sourceFileName);
+ if (mapped != null) {
+ for (int i = 0; i < mapped.length; i++) {
+ results.add(mapped[i]);
+ }
+ }
+ }
+ }
+ return (results.size() == 0) ? null
+ : (String[]) results.toArray(new String[results.size()]);
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java
new file mode 100644
index 00000000..22dcb7fb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java
@@ -0,0 +1,136 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+
+/**
+ * Special <code>InputStream</code> that will
+ * concatenate the contents of an array of files.
+ */
+public class ConcatFileInputStream extends InputStream {
+
+ private static final int EOF = -1;
+ private int currentIndex = -1;
+ private boolean eof = false;
+ private File[] file;
+ private InputStream currentStream;
+ private ProjectComponent managingPc;
+
+ /**
+ * Construct a new <code>ConcatFileInputStream</code>
+ * with the specified <code>File[]</code>.
+ * @param file <code>File[]</code>.
+ * @throws IOException if I/O errors occur.
+ */
+ public ConcatFileInputStream(File[] file) throws IOException {
+ this.file = file;
+ }
+
+ /**
+ * Close the stream.
+ * @throws IOException if there is an error.
+ */
+ public void close() throws IOException {
+ closeCurrent();
+ eof = true;
+ }
+
+ /**
+ * Read a byte.
+ * @return the byte (0 - 255) or -1 if this is the end of the stream.
+ * @throws IOException if there is an error.
+ */
+ public int read() throws IOException {
+ int result = readCurrent();
+ if (result == EOF && !eof) {
+ openFile(++currentIndex);
+ result = readCurrent();
+ }
+ return result;
+ }
+
+ /**
+ * Set a managing <code>Task</code> for
+ * this <code>ConcatFileInputStream</code>.
+ * @param task the managing <code>Task</code>.
+ */
+ public void setManagingTask(Task task) {
+ setManagingComponent(task);
+ }
+
+ /**
+ * Set a managing <code>Task</code> for
+ * this <code>ConcatFileInputStream</code>.
+ * @param pc the managing <code>Task</code>.
+ */
+ public void setManagingComponent(ProjectComponent pc) {
+ this.managingPc = pc;
+ }
+
+ /**
+ * Log a message with the specified logging level.
+ * @param message the <code>String</code> message.
+ * @param loglevel the <code>int</code> logging level.
+ */
+ public void log(String message, int loglevel) {
+ if (managingPc != null) {
+ managingPc.log(message, loglevel);
+ } else {
+ if (loglevel > Project.MSG_WARN) {
+ System.out.println(message);
+ } else {
+ System.err.println(message);
+ }
+ }
+ }
+
+ private int readCurrent() throws IOException {
+ return (eof || currentStream == null) ? EOF : currentStream.read();
+ }
+
+ private void openFile(int index) throws IOException {
+ closeCurrent();
+ if (file != null && index < file.length) {
+ log("Opening " + file[index], Project.MSG_VERBOSE);
+ try {
+ currentStream = new BufferedInputStream(
+ new FileInputStream(file[index]));
+ } catch (IOException eyeOhEx) {
+ log("Failed to open " + file[index], Project.MSG_ERR);
+ throw eyeOhEx;
+ }
+ } else {
+ eof = true;
+ }
+ }
+
+ private void closeCurrent() {
+ FileUtils.close(currentStream);
+ currentStream = null;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ConcatResourceInputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ConcatResourceInputStream.java
new file mode 100644
index 00000000..7bae58e5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ConcatResourceInputStream.java
@@ -0,0 +1,147 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * Special <code>InputStream</code> that will
+ * concatenate the contents of Resources from a single ResourceCollection.
+ * @since Ant 1.7
+ */
+public class ConcatResourceInputStream extends InputStream {
+
+ private static final int EOF = -1;
+ private boolean eof = false;
+ private Iterator<Resource> iter;
+ private InputStream currentStream;
+ private ProjectComponent managingPc;
+ private boolean ignoreErrors = false;
+
+ /**
+ * Construct a new ConcatResourceInputStream
+ * for the specified ResourceCollection.
+ * @param rc the ResourceCollection to combine.
+ */
+ public ConcatResourceInputStream(ResourceCollection rc) {
+ iter = rc.iterator();
+ }
+
+ /**
+ * Set whether this ConcatResourceInputStream ignores errors.
+ * @param b whether to ignore errors.
+ */
+ public void setIgnoreErrors(boolean b) {
+ ignoreErrors = b;
+ }
+
+ /**
+ * Find out whether this ConcatResourceInputStream ignores errors.
+ * @return boolean ignore-errors flag.
+ */
+ public boolean isIgnoreErrors() {
+ return ignoreErrors;
+ }
+
+ /**
+ * Close the stream.
+ * @throws IOException if there is an error.
+ */
+ public void close() throws IOException {
+ closeCurrent();
+ eof = true;
+ }
+
+ /**
+ * Read a byte.
+ * @return the byte (0 - 255) or -1 if this is the end of the stream.
+ * @throws IOException if there is an error.
+ */
+ public int read() throws IOException {
+ if (eof) {
+ return EOF;
+ }
+ int result = readCurrent();
+ if (result == EOF) {
+ nextResource();
+ result = readCurrent();
+ }
+ return result;
+ }
+
+ /**
+ * Set a managing <code>ProjectComponent</code> for
+ * this <code>ConcatResourceInputStream</code>.
+ * @param pc the managing <code>ProjectComponent</code>.
+ */
+ public void setManagingComponent(ProjectComponent pc) {
+ this.managingPc = pc;
+ }
+
+ /**
+ * Log a message with the specified logging level.
+ * @param message the <code>String</code> message.
+ * @param loglevel the <code>int</code> logging level.
+ */
+ public void log(String message, int loglevel) {
+ if (managingPc != null) {
+ managingPc.log(message, loglevel);
+ } else {
+ (loglevel > Project.MSG_WARN ? System.out : System.err).println(message);
+ }
+ }
+
+ private int readCurrent() throws IOException {
+ return eof || currentStream == null ? EOF : currentStream.read();
+ }
+
+ private void nextResource() throws IOException {
+ closeCurrent();
+ while (iter.hasNext()) {
+ Resource r = (Resource) iter.next();
+ if (!r.isExists()) {
+ continue;
+ }
+ log("Concating " + r.toLongString(), Project.MSG_VERBOSE);
+ try {
+ currentStream = new BufferedInputStream(r.getInputStream());
+ return;
+ } catch (IOException eyeOhEx) {
+ if (!ignoreErrors) {
+ log("Failed to get input stream for " + r, Project.MSG_ERR);
+ throw eyeOhEx;
+ }
+ }
+ }
+ eof = true;
+ }
+
+ private void closeCurrent() {
+ FileUtils.close(currentStream);
+ currentStream = null;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ContainerMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ContainerMapper.java
new file mode 100644
index 00000000..990ee148
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ContainerMapper.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.tools.ant.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.types.Mapper;
+
+/**
+ * A <code>FileNameMapper</code> that contains
+ * other <code>FileNameMapper</code>s.
+ * @see FileNameMapper
+ */
+public abstract class ContainerMapper implements FileNameMapper {
+
+ private List mappers = new ArrayList();
+
+ /**
+ * Add a <code>Mapper</code>.
+ * @param mapper the <code>Mapper</code> to add.
+ */
+ public void addConfiguredMapper(Mapper mapper) {
+ add(mapper.getImplementation());
+ }
+
+ /**
+ * An add configured version of the add method.
+ * This class used to contain an add method and an
+ * addConfiguredMapper method. Dur to ordering,
+ * the add method was always called first. This
+ * addConfigured method has been added to allow
+ * chaining to work correctly.
+ * @param fileNameMapper a <code>FileNameMapper</code>.
+ */
+ public void addConfigured(FileNameMapper fileNameMapper) {
+ add(fileNameMapper);
+ }
+
+ /**
+ * Add a <code>FileNameMapper</code>.
+ * @param fileNameMapper a <code>FileNameMapper</code>.
+ * @throws IllegalArgumentException if attempting to add this
+ * <code>ContainerMapper</code> to itself, or if the specified
+ * <code>FileNameMapper</code> is itself a <code>ContainerMapper</code>
+ * that contains this <code>ContainerMapper</code>.
+ */
+ public synchronized void add(FileNameMapper fileNameMapper) {
+ if (this == fileNameMapper
+ || (fileNameMapper instanceof ContainerMapper
+ && ((ContainerMapper) fileNameMapper).contains(this))) {
+ throw new IllegalArgumentException(
+ "Circular mapper containment condition detected");
+ } else {
+ mappers.add(fileNameMapper);
+ }
+ }
+
+ /**
+ * Return <code>true</code> if this <code>ContainerMapper</code> or any of
+ * its sub-elements contains the specified <code>FileNameMapper</code>.
+ * @param fileNameMapper the <code>FileNameMapper</code> to search for.
+ * @return <code>boolean</code>.
+ */
+ protected synchronized boolean contains(FileNameMapper fileNameMapper) {
+ boolean foundit = false;
+ for (Iterator iter = mappers.iterator(); iter.hasNext() && !foundit;) {
+ FileNameMapper next = (FileNameMapper) (iter.next());
+ foundit = (next == fileNameMapper
+ || (next instanceof ContainerMapper
+ && ((ContainerMapper) next).contains(fileNameMapper)));
+ }
+ return foundit;
+ }
+
+ /**
+ * Get the <code>List</code> of <code>FileNameMapper</code>s.
+ * @return <code>List</code>.
+ */
+ public synchronized List getMappers() {
+ return Collections.unmodifiableList(mappers);
+ }
+
+ /**
+ * Empty implementation.
+ * @param ignore ignored.
+ */
+ public void setFrom(String ignore) {
+ //Empty
+ }
+
+ /**
+ * Empty implementation.
+ * @param ignore ignored.
+ */
+ public void setTo(String ignore) {
+ //Empty
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DOMElementWriter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DOMElementWriter.java
new file mode 100644
index 00000000..14cbaee2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DOMElementWriter.java
@@ -0,0 +1,640 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
+/**
+ * Writes a DOM tree to a given Writer.
+ * warning: this utility currently does not declare XML Namespaces.
+ * <p>Utility class used by {@link org.apache.tools.ant.XmlLogger
+ * XmlLogger} and
+ * org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter
+ * XMLJUnitResultFormatter}.</p>
+ *
+ */
+public class DOMElementWriter {
+
+ private static final int HEX = 16;
+
+ private static final String[] WS_ENTITIES = new String['\r' - '\t' + 1];
+ static {
+ for (int i = '\t'; i < '\r' + 1; i++) {
+ WS_ENTITIES[i - '\t'] = "&#x" + Integer.toHexString(i) + ";";
+ }
+ }
+
+ /** prefix for generated prefixes */
+ private static final String NS = "ns";
+
+ /** xml declaration is on by default */
+ private boolean xmlDeclaration = true;
+
+ /**
+ * XML Namespaces are ignored by default.
+ */
+ private XmlNamespacePolicy namespacePolicy = XmlNamespacePolicy.IGNORE;
+
+ /**
+ * Map (URI to prefix) of known namespaces.
+ */
+ private HashMap nsPrefixMap = new HashMap();
+
+ /**
+ * Number of generated prefix to use next.
+ */
+ private int nextPrefix = 0;
+
+ /**
+ * Map (Element to URI) of namespaces defined on a given element.
+ */
+ private HashMap nsURIByElement = new HashMap();
+
+ /**
+ * Whether namespaces should be ignored for elements and attributes.
+ *
+ * @since Ant 1.7
+ */
+ public static class XmlNamespacePolicy {
+ private boolean qualifyElements;
+ private boolean qualifyAttributes;
+
+ /**
+ * Ignores namespaces for elements and attributes, the default.
+ */
+ public static final XmlNamespacePolicy IGNORE =
+ new XmlNamespacePolicy(false, false);
+
+ /**
+ * Ignores namespaces for attributes.
+ */
+ public static final XmlNamespacePolicy ONLY_QUALIFY_ELEMENTS =
+ new XmlNamespacePolicy(true, false);
+
+ /**
+ * Qualifies namespaces for elements and attributes.
+ */
+ public static final XmlNamespacePolicy QUALIFY_ALL =
+ new XmlNamespacePolicy(true, true);
+
+ /**
+ * @param qualifyElements whether to qualify elements
+ * @param qualifyAttributes whether to qualify elements
+ */
+ public XmlNamespacePolicy(boolean qualifyElements,
+ boolean qualifyAttributes) {
+ this.qualifyElements = qualifyElements;
+ this.qualifyAttributes = qualifyAttributes;
+ }
+ }
+
+ /**
+ * Create an element writer.
+ * The ?xml? declaration will be included, namespaces ignored.
+ */
+ public DOMElementWriter() {
+ }
+
+ /**
+ * Create an element writer
+ * XML namespaces will be ignored.
+ * @param xmlDeclaration flag to indicate whether the ?xml? declaration
+ * should be included.
+ * @since Ant1.7
+ */
+ public DOMElementWriter(boolean xmlDeclaration) {
+ this(xmlDeclaration, XmlNamespacePolicy.IGNORE);
+ }
+
+ /**
+ * Create an element writer
+ * XML namespaces will be ignored.
+ * @param xmlDeclaration flag to indicate whether the ?xml? declaration
+ * should be included.
+ * @param namespacePolicy the policy to use.
+ * @since Ant1.7
+ */
+ public DOMElementWriter(boolean xmlDeclaration,
+ XmlNamespacePolicy namespacePolicy) {
+ this.xmlDeclaration = xmlDeclaration;
+ this.namespacePolicy = namespacePolicy;
+ }
+
+ private static String lSep = System.getProperty("line.separator");
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ /**
+ * Don't try to be too smart but at least recognize the predefined
+ * entities.
+ */
+ protected String[] knownEntities = {"gt", "amp", "lt", "apos", "quot"};
+ // CheckStyle:VisibilityModifier ON
+
+
+ /**
+ * Writes a DOM tree to a stream in UTF8 encoding. Note that
+ * it prepends the &lt;?xml version='1.0' encoding='UTF-8'?&gt; if
+ * the xmlDeclaration field is true.
+ * The indent number is set to 0 and a 2-space indent.
+ * @param root the root element of the DOM tree.
+ * @param out the outputstream to write to.
+ * @throws IOException if an error happens while writing to the stream.
+ */
+ public void write(Element root, OutputStream out) throws IOException {
+ Writer wri = new OutputStreamWriter(out, "UTF8");
+ writeXMLDeclaration(wri);
+ write(root, wri, 0, " ");
+ wri.flush();
+ }
+
+ /**
+ * Writes the XML declaration if xmlDeclaration is true.
+ * @param wri the writer to write to.
+ * @throws IOException if there is an error.
+ * @since Ant 1.7.0
+ */
+ public void writeXMLDeclaration(Writer wri) throws IOException {
+ if (xmlDeclaration) {
+ wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ }
+ }
+
+ /**
+ * Writes a DOM tree to a stream.
+ *
+ * @param element the Root DOM element of the tree
+ * @param out where to send the output
+ * @param indent number of
+ * @param indentWith string that should be used to indent the
+ * corresponding tag.
+ * @throws IOException if an error happens while writing to the stream.
+ */
+ public void write(Element element, Writer out, int indent,
+ String indentWith)
+ throws IOException {
+
+ // Write child elements and text
+ NodeList children = element.getChildNodes();
+ boolean hasChildren = (children.getLength() > 0);
+ boolean hasChildElements = false;
+ openElement(element, out, indent, indentWith, hasChildren);
+
+ if (hasChildren) {
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+
+ switch (child.getNodeType()) {
+
+ case Node.ELEMENT_NODE:
+ hasChildElements = true;
+ if (i == 0) {
+ out.write(lSep);
+ }
+ write((Element) child, out, indent + 1, indentWith);
+ break;
+
+ case Node.TEXT_NODE:
+ out.write(encode(child.getNodeValue()));
+ break;
+
+ case Node.COMMENT_NODE:
+ out.write("<!--");
+ out.write(encode(child.getNodeValue()));
+ out.write("-->");
+ break;
+
+ case Node.CDATA_SECTION_NODE:
+ out.write("<![CDATA[");
+ encodedata(out, ((Text) child).getData());
+ out.write("]]>");
+ break;
+
+ case Node.ENTITY_REFERENCE_NODE:
+ out.write('&');
+ out.write(child.getNodeName());
+ out.write(';');
+ break;
+
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ out.write("<?");
+ out.write(child.getNodeName());
+ String data = child.getNodeValue();
+ if (data != null && data.length() > 0) {
+ out.write(' ');
+ out.write(data);
+ }
+ out.write("?>");
+ break;
+ default:
+ // Do nothing
+ }
+ }
+ closeElement(element, out, indent, indentWith, hasChildElements);
+ }
+ }
+
+ /**
+ * Writes the opening tag - including all attributes -
+ * corresponding to a DOM element.
+ *
+ * @param element the DOM element to write
+ * @param out where to send the output
+ * @param indent number of
+ * @param indentWith string that should be used to indent the
+ * corresponding tag.
+ * @throws IOException if an error happens while writing to the stream.
+ */
+ public void openElement(Element element, Writer out, int indent,
+ String indentWith)
+ throws IOException {
+ openElement(element, out, indent, indentWith, true);
+ }
+
+ /**
+ * Writes the opening tag - including all attributes -
+ * corresponding to a DOM element.
+ *
+ * @param element the DOM element to write
+ * @param out where to send the output
+ * @param indent number of
+ * @param indentWith string that should be used to indent the
+ * corresponding tag.
+ * @param hasChildren whether this element has children.
+ * @throws IOException if an error happens while writing to the stream.
+ * @since Ant 1.7
+ */
+ public void openElement(Element element, Writer out, int indent,
+ String indentWith, boolean hasChildren)
+ throws IOException {
+ // Write indent characters
+ for (int i = 0; i < indent; i++) {
+ out.write(indentWith);
+ }
+
+ // Write element
+ out.write("<");
+ if (namespacePolicy.qualifyElements) {
+ String uri = getNamespaceURI(element);
+ String prefix = (String) nsPrefixMap.get(uri);
+ if (prefix == null) {
+ if (nsPrefixMap.isEmpty()) {
+ // steal default namespace
+ prefix = "";
+ } else {
+ prefix = NS + (nextPrefix++);
+ }
+ nsPrefixMap.put(uri, prefix);
+ addNSDefinition(element, uri);
+ }
+ if (!"".equals(prefix)) {
+ out.write(prefix);
+ out.write(":");
+ }
+ }
+ out.write(element.getTagName());
+
+ // Write attributes
+ NamedNodeMap attrs = element.getAttributes();
+ for (int i = 0; i < attrs.getLength(); i++) {
+ Attr attr = (Attr) attrs.item(i);
+ out.write(" ");
+ if (namespacePolicy.qualifyAttributes) {
+ String uri = getNamespaceURI(attr);
+ String prefix = (String) nsPrefixMap.get(uri);
+ if (prefix == null) {
+ prefix = NS + (nextPrefix++);
+ nsPrefixMap.put(uri, prefix);
+ addNSDefinition(element, uri);
+ }
+ out.write(prefix);
+ out.write(":");
+ }
+ out.write(attr.getName());
+ out.write("=\"");
+ out.write(encodeAttributeValue(attr.getValue()));
+ out.write("\"");
+ }
+
+ // write namespace declarations
+ ArrayList al = (ArrayList) nsURIByElement.get(element);
+ if (al != null) {
+ Iterator iter = al.iterator();
+ while (iter.hasNext()) {
+ String uri = (String) iter.next();
+ String prefix = (String) nsPrefixMap.get(uri);
+ out.write(" xmlns");
+ if (!"".equals(prefix)) {
+ out.write(":");
+ out.write(prefix);
+ }
+ out.write("=\"");
+ out.write(uri);
+ out.write("\"");
+ }
+ }
+
+ if (hasChildren) {
+ out.write(">");
+ } else {
+ removeNSDefinitions(element);
+ out.write(" />");
+ out.write(lSep);
+ out.flush();
+ }
+ }
+
+ /**
+ * Writes a DOM tree to a stream.
+ *
+ * @param element the Root DOM element of the tree
+ * @param out where to send the output
+ * @param indent number of
+ * @param indentWith string that should be used to indent the
+ * corresponding tag.
+ * @param hasChildren if true indent.
+ * @throws IOException if an error happens while writing to the stream.
+ */
+ public void closeElement(Element element, Writer out, int indent,
+ String indentWith, boolean hasChildren)
+ throws IOException {
+ // If we had child elements, we need to indent before we close
+ // the element, otherwise we're on the same line and don't need
+ // to indent
+ if (hasChildren) {
+ for (int i = 0; i < indent; i++) {
+ out.write(indentWith);
+ }
+ }
+
+ // Write element close
+ out.write("</");
+ if (namespacePolicy.qualifyElements) {
+ String uri = getNamespaceURI(element);
+ String prefix = (String) nsPrefixMap.get(uri);
+ if (prefix != null && !"".equals(prefix)) {
+ out.write(prefix);
+ out.write(":");
+ }
+ removeNSDefinitions(element);
+ }
+ out.write(element.getTagName());
+ out.write(">");
+ out.write(lSep);
+ out.flush();
+ }
+
+ /**
+ * Escape &lt;, &gt; &amp; &apos;, &quot; as their entities and
+ * drop characters that are illegal in XML documents.
+ * @param value the string to encode.
+ * @return the encoded string.
+ */
+ public String encode(String value) {
+ return encode(value, false);
+ }
+
+ /**
+ * Escape &lt;, &gt; &amp; &apos;, &quot; as their entities, \n,
+ * \r and \t as numeric entities and drop characters that are
+ * illegal in XML documents.
+ * @param value the string to encode.
+ * @return the encoded string.
+ */
+ public String encodeAttributeValue(String value) {
+ return encode(value, true);
+ }
+
+ private String encode(final String value, final boolean encodeWhitespace) {
+ final int len = value.length();
+ final StringBuffer sb = new StringBuffer(len);
+ for (int i = 0; i < len; i++) {
+ final char c = value.charAt(i);
+ switch (c) {
+ case '<':
+ sb.append("&lt;");
+ break;
+ case '>':
+ sb.append("&gt;");
+ break;
+ case '\'':
+ sb.append("&apos;");
+ break;
+ case '\"':
+ sb.append("&quot;");
+ break;
+ case '&':
+ sb.append("&amp;");
+ break;
+ case '\r':
+ case '\n':
+ case '\t':
+ if (encodeWhitespace) {
+ sb.append(WS_ENTITIES[c - '\t']);
+ } else {
+ sb.append(c);
+ }
+ break;
+ default:
+ if (isLegalCharacter(c)) {
+ sb.append(c);
+ }
+ break;
+ }
+ }
+ return sb.substring(0);
+ }
+
+ /**
+ * Drop characters that are illegal in XML documents.
+ *
+ * <p>Also ensure that we are not including an <code>]]&gt;</code>
+ * marker by replacing that sequence with
+ * <code>&amp;#x5d;&amp;#x5d;&amp;gt;</code>.</p>
+ *
+ * <p>See XML 1.0 2.2 <a
+ * href="http://www.w3.org/TR/1998/REC-xml-19980210#charsets">
+ * http://www.w3.org/TR/1998/REC-xml-19980210#charsets</a> and
+ * 2.7 <a
+ * href="http://www.w3.org/TR/1998/REC-xml-19980210#sec-cdata-sect">http://www.w3.org/TR/1998/REC-xml-19980210#sec-cdata-sect</a>.</p>
+ * @param value the value to be encoded.
+ * @return the encoded value.
+ */
+ public String encodedata(final String value) {
+ final StringWriter out = new StringWriter();
+ try {
+ encodedata(out, value);
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ return out.toString();
+ }
+
+ /**
+ * Drop characters that are illegal in XML documents and write the
+ * rest to the given writer.
+ *
+ * <p>Also ensure that we are not including an <code>]]&gt;</code>
+ * marker by replacing that sequence with
+ * <code>&amp;#x5d;&amp;#x5d;&amp;gt;</code>.</p>
+ *
+ * <p>See XML 1.0 2.2 <a
+ * href="http://www.w3.org/TR/1998/REC-xml-19980210#charsets">
+ * http://www.w3.org/TR/1998/REC-xml-19980210#charsets</a> and
+ * 2.7 <a
+ * href="http://www.w3.org/TR/1998/REC-xml-19980210#sec-cdata-sect">http://www.w3.org/TR/1998/REC-xml-19980210#sec-cdata-sect</a>.</p>
+ * @param value the value to be encoded.
+ * @param out where to write the encoded data to.
+ */
+ public void encodedata(final Writer out, final String value) throws IOException {
+ final int len = value.length();
+ int prevEnd = 0, cdataEndPos = value.indexOf("]]>");
+ while (prevEnd < len) {
+ final int end = (cdataEndPos < 0 ? len : cdataEndPos);
+ // Write out stretches of legal characters in the range [prevEnd, end).
+ for (int prevLegalCharPos = prevEnd; prevLegalCharPos < end;/*empty*/) {
+ int illegalCharPos;
+ for (illegalCharPos = prevLegalCharPos; true; ++illegalCharPos) {
+ if (illegalCharPos >= end
+ || !isLegalCharacter(value.charAt(illegalCharPos))) {
+ break;
+ }
+ }
+ out.write(value, prevLegalCharPos, illegalCharPos - prevLegalCharPos);
+ prevLegalCharPos = illegalCharPos + 1;
+ }
+
+ if (cdataEndPos >= 0) {
+ out.write("]]]]><![CDATA[>");
+ prevEnd = cdataEndPos + 3;
+ cdataEndPos = value.indexOf("]]>", prevEnd);
+ } else {
+ prevEnd = end;
+ }
+ }
+ }
+
+ /**
+ * Is the given argument a character or entity reference?
+ * @param ent the value to be checked.
+ * @return true if it is an entity.
+ */
+ public boolean isReference(String ent) {
+ if (!(ent.charAt(0) == '&') || !ent.endsWith(";")) {
+ return false;
+ }
+
+ if (ent.charAt(1) == '#') {
+ if (ent.charAt(2) == 'x') {
+ try {
+ // CheckStyle:MagicNumber OFF
+ Integer.parseInt(ent.substring(3, ent.length() - 1), HEX);
+ // CheckStyle:MagicNumber ON
+ return true;
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ } else {
+ try {
+ Integer.parseInt(ent.substring(2, ent.length() - 1));
+ return true;
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ }
+ }
+
+ String name = ent.substring(1, ent.length() - 1);
+ for (int i = 0; i < knownEntities.length; i++) {
+ if (name.equals(knownEntities[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Is the given character allowed inside an XML document?
+ *
+ * <p>See XML 1.0 2.2 <a
+ * href="http://www.w3.org/TR/1998/REC-xml-19980210#charsets">
+ * http://www.w3.org/TR/1998/REC-xml-19980210#charsets</a>.</p>
+ * @param c the character to test.
+ * @return true if the character is allowed.
+ * @since 1.10, Ant 1.5
+ */
+ public boolean isLegalCharacter(final char c) {
+ // CheckStyle:MagicNumber OFF
+ if (c == 0x9 || c == 0xA || c == 0xD) {
+ return true;
+ } else if (c < 0x20) {
+ return false;
+ } else if (c <= 0xD7FF) {
+ return true;
+ } else if (c < 0xE000) {
+ return false;
+ } else if (c <= 0xFFFD) {
+ return true;
+ }
+ // CheckStyle:MagicNumber ON
+ return false;
+ }
+
+ private void removeNSDefinitions(Element element) {
+ ArrayList al = (ArrayList) nsURIByElement.get(element);
+ if (al != null) {
+ Iterator iter = al.iterator();
+ while (iter.hasNext()) {
+ nsPrefixMap.remove(iter.next());
+ }
+ nsURIByElement.remove(element);
+ }
+ }
+
+ private void addNSDefinition(Element element, String uri) {
+ ArrayList al = (ArrayList) nsURIByElement.get(element);
+ if (al == null) {
+ al = new ArrayList();
+ nsURIByElement.put(element, al);
+ }
+ al.add(uri);
+ }
+
+ private static String getNamespaceURI(Node n) {
+ String uri = n.getNamespaceURI();
+ if (uri == null) {
+ // FIXME: Is "No Namespace is Empty Namespace" really OK?
+ uri = "";
+ }
+ return uri;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DOMUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DOMUtils.java
new file mode 100644
index 00000000..db00213d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DOMUtils.java
@@ -0,0 +1,163 @@
+/*
+ * 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.tools.ant.util;
+
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Text;
+
+// CheckStyle:HideUtilityClassConstructorCheck OFF - bc
+
+/**
+ * Some utility methods for common tasks when building DOM trees in memory.
+ *
+ * <p>In this documentation <code>&lt;a&gt;</code> means an {@link
+ * org.w3c.dom.Element Element} instance with name <code>a</code>.</p>
+ *
+ * @since Ant 1.6.3
+ */
+public class DOMUtils {
+
+ /**
+ * Get a new Document instance,
+ * @return the document.
+ * @since Ant 1.6.3
+ */
+ public static Document newDocument() {
+ return JAXPUtils.getDocumentBuilder().newDocument();
+ }
+
+ /**
+ * Creates a named Element and appends it to the given element,
+ * returns it.
+ *
+ * <p>This means
+ * <pre>createChildElement(&lt;a&gt;, "b")</pre>
+ * creates
+ * <pre>
+ * &lt;a&gt;
+ * &lt;b/&gt;
+ * &lt;/a&gt;
+ * </pre>
+ * and returns <code>&lt;b&gt;</code>.
+ *
+ * @param parent element that will receive the new element as child.
+ * @param name name of the new element.
+ * @return the new element.
+ *
+ * @since Ant 1.6.3
+ */
+ public static Element createChildElement(Element parent, String name) {
+ Document doc = parent.getOwnerDocument();
+ Element e = doc.createElement(name);
+ parent.appendChild(e);
+ return e;
+ }
+
+ /**
+ * Adds nested text.
+ *
+ * <p>This means
+ * <pre>appendText(&lt;a&gt;, "b")</pre>
+ * creates
+ * <pre>
+ * &lt;a&gt;b&lt;/a&gt;
+ * </pre>
+ *
+ * @param parent element that will receive the new element as child.
+ * @param content text content.
+ *
+ * @since Ant 1.6.3
+ */
+ public static void appendText(Element parent, String content) {
+ Document doc = parent.getOwnerDocument();
+ Text t = doc.createTextNode(content);
+ parent.appendChild(t);
+ }
+
+ /**
+ * Adds a nested CDATA section.
+ *
+ * <p>This means
+ * <pre>appendCDATA(&lt;a&gt;, "b")</pre>
+ * creates
+ * <pre>
+ * &lt;a&gt;&lt;[!CDATA[b]]&gt;&lt;/a&gt;
+ * </pre>
+ *
+ * @param parent element that will receive the new element as child.
+ * @param content text content.
+ *
+ * @since Ant 1.6.3
+ */
+ public static void appendCDATA(Element parent, String content) {
+ Document doc = parent.getOwnerDocument();
+ CDATASection c = doc.createCDATASection(content);
+ parent.appendChild(c);
+ }
+
+ /**
+ * Adds nested text in a new child element.
+ *
+ * <p>This means
+ * <pre>appendTextElement(&lt;a&gt;, "b", "c")</pre>
+ * creates
+ * <pre>
+ * &lt;a&gt;
+ * &lt;b&gt;c&lt;/b&gt;
+ * &lt;/a&gt;
+ * </pre>
+ *
+ * @param parent element that will receive the new element as child.
+ * @param name of the child element.
+ * @param content text content.
+ *
+ * @since Ant 1.6.3
+ */
+ public static void appendTextElement(Element parent, String name,
+ String content) {
+ Element e = createChildElement(parent, name);
+ appendText(e, content);
+ }
+
+ /**
+ * Adds a nested CDATA section in a new child element.
+ *
+ * <p>This means
+ * <pre>appendCDATAElement(&lt;a&gt;, "b", "c")</pre>
+ * creates
+ * <pre>
+ * &lt;a&gt;
+ * &lt;b&gt;&lt;![CDATA[c]]&gt;&lt;/b&gt;
+ * &lt;/a&gt;
+ * </pre>
+ *
+ * @param parent element that will receive the new element as child.
+ * @param name of the child element.
+ * @param content text content.
+ *
+ * @since Ant 1.6.3
+ */
+ public static void appendCDATAElement(Element parent, String name,
+ String content) {
+ Element e = createChildElement(parent, name);
+ appendCDATA(e, content);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DateUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DateUtils.java
new file mode 100644
index 00000000..9ce737b0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DateUtils.java
@@ -0,0 +1,301 @@
+/*
+ * 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.tools.ant.util;
+
+import java.text.ChoiceFormat;
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Helper methods to deal with date/time formatting with a specific
+ * defined format (<a href="http://www.w3.org/TR/NOTE-datetime">ISO8601</a>)
+ * or a plurialization correct elapsed time in minutes and seconds.
+ *
+ * @since Ant 1.5
+ *
+ */
+public final class DateUtils {
+
+ private static final int ONE_SECOND = 1000;
+ private static final int ONE_MINUTE = 60;
+ private static final int ONE_HOUR = 60;
+ private static final int TEN = 10;
+ /**
+ * ISO8601-like pattern for date-time. It does not support timezone.
+ * <tt>yyyy-MM-ddTHH:mm:ss</tt>
+ */
+ public static final String ISO8601_DATETIME_PATTERN
+ = "yyyy-MM-dd'T'HH:mm:ss";
+
+ /**
+ * ISO8601-like pattern for date. <tt>yyyy-MM-dd</tt>
+ */
+ public static final String ISO8601_DATE_PATTERN
+ = "yyyy-MM-dd";
+
+ /**
+ * ISO8601-like pattern for time. <tt>HH:mm:ss</tt>
+ */
+ public static final String ISO8601_TIME_PATTERN
+ = "HH:mm:ss";
+
+ /**
+ * Format used for SMTP (and probably other) Date headers.
+ * @deprecated DateFormat is not thread safe, and we cannot guarantee that
+ * some other code is using the format in parallel.
+ * Deprecated since ant 1.8
+ */
+ public static final DateFormat DATE_HEADER_FORMAT
+ = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ", Locale.US);
+
+ private static final DateFormat DATE_HEADER_FORMAT_INT
+ = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ", Locale.US);
+
+
+// code from Magesh moved from DefaultLogger and slightly modified
+ private static final MessageFormat MINUTE_SECONDS
+ = new MessageFormat("{0}{1}");
+
+ private static final double[] LIMITS = {0, 1, 2};
+
+ private static final String[] MINUTES_PART = {"", "1 minute ", "{0,number,###############} minutes "};
+
+ private static final String[] SECONDS_PART = {"0 seconds", "1 second", "{1,number} seconds"};
+
+ private static final ChoiceFormat MINUTES_FORMAT =
+ new ChoiceFormat(LIMITS, MINUTES_PART);
+
+ private static final ChoiceFormat SECONDS_FORMAT =
+ new ChoiceFormat(LIMITS, SECONDS_PART);
+
+ static {
+ MINUTE_SECONDS.setFormat(0, MINUTES_FORMAT);
+ MINUTE_SECONDS.setFormat(1, SECONDS_FORMAT);
+ }
+
+ /** private constructor */
+ private DateUtils() {
+ }
+
+
+ /**
+ * Format a date/time into a specific pattern.
+ * @param date the date to format expressed in milliseconds.
+ * @param pattern the pattern to use to format the date.
+ * @return the formatted date.
+ */
+ public static String format(long date, String pattern) {
+ return format(new Date(date), pattern);
+ }
+
+
+ /**
+ * Format a date/time into a specific pattern.
+ * @param date the date to format expressed in milliseconds.
+ * @param pattern the pattern to use to format the date.
+ * @return the formatted date.
+ */
+ public static String format(Date date, String pattern) {
+ DateFormat df = createDateFormat(pattern);
+ return df.format(date);
+ }
+
+
+ /**
+ * Format an elapsed time into a plurialization correct string.
+ * It is limited only to report elapsed time in minutes and
+ * seconds and has the following behavior.
+ * <ul>
+ * <li>minutes are not displayed when 0. (ie: "45 seconds")</li>
+ * <li>seconds are always displayed in plural form (ie "0 seconds" or
+ * "10 seconds") except for 1 (ie "1 second")</li>
+ * </ul>
+ * @param millis the elapsed time to report in milliseconds.
+ * @return the formatted text in minutes/seconds.
+ */
+ public static String formatElapsedTime(long millis) {
+ long seconds = millis / ONE_SECOND;
+ long minutes = seconds / ONE_MINUTE;
+ Object[] args = {new Long(minutes), new Long(seconds % ONE_MINUTE)};
+ return MINUTE_SECONDS.format(args);
+ }
+
+ /**
+ * return a lenient date format set to GMT time zone.
+ * @param pattern the pattern used for date/time formatting.
+ * @return the configured format for this pattern.
+ */
+ private static DateFormat createDateFormat(String pattern) {
+ SimpleDateFormat sdf = new SimpleDateFormat(pattern);
+ TimeZone gmt = TimeZone.getTimeZone("GMT");
+ sdf.setTimeZone(gmt);
+ sdf.setLenient(true);
+ return sdf;
+ }
+
+ /**
+ * Calculate the phase of the moon for a given date.
+ *
+ * <p>Code heavily influenced by hacklib.c in <a
+ * href="http://www.nethack.org/">Nethack</a></p>
+ *
+ * <p>The Algorithm:
+ *
+ * <pre>
+ * moon period = 29.53058 days ~= 30, year = 365.2422 days
+ *
+ * days moon phase advances on first day of year compared to preceding year
+ * = 365.2422 - 12*29.53058 ~= 11
+ *
+ * years in Metonic cycle (time until same phases fall on the same days of
+ * the month) = 18.6 ~= 19
+ *
+ * moon phase on first day of year (epact) ~= (11*(year%19) + 18) % 30
+ * (18 as initial condition for 1900)
+ *
+ * current phase in days = first day phase + days elapsed in year
+ *
+ * 6 moons ~= 177 days
+ * 177 ~= 8 reported phases * 22
+ * + 11/22 for rounding
+ * </pre>
+ *
+ * @param cal the calendar.
+ *
+ * @return The phase of the moon as a number between 0 and 7 with
+ * 0 meaning new moon and 4 meaning full moon.
+ *
+ * @since 1.2, Ant 1.5
+ */
+ public static int getPhaseOfMoon(Calendar cal) {
+ // CheckStyle:MagicNumber OFF
+ int dayOfTheYear = cal.get(Calendar.DAY_OF_YEAR);
+ int yearInMetonicCycle = ((cal.get(Calendar.YEAR) - 1900) % 19) + 1;
+ int epact = (11 * yearInMetonicCycle + 18) % 30;
+ if ((epact == 25 && yearInMetonicCycle > 11) || epact == 24) {
+ epact++;
+ }
+ return (((((dayOfTheYear + epact) * 6) + 11) % 177) / 22) & 7;
+ // CheckStyle:MagicNumber ON
+ }
+
+ /**
+ * Returns the current Date in a format suitable for a SMTP date
+ * header.
+ * @return the current date.
+ * @since Ant 1.5.2
+ */
+ public static String getDateForHeader() {
+ Calendar cal = Calendar.getInstance();
+ TimeZone tz = cal.getTimeZone();
+ int offset = tz.getOffset(cal.get(Calendar.ERA),
+ cal.get(Calendar.YEAR),
+ cal.get(Calendar.MONTH),
+ cal.get(Calendar.DAY_OF_MONTH),
+ cal.get(Calendar.DAY_OF_WEEK),
+ cal.get(Calendar.MILLISECOND));
+ StringBuffer tzMarker = new StringBuffer(offset < 0 ? "-" : "+");
+ offset = Math.abs(offset);
+ int hours = offset / (ONE_HOUR * ONE_MINUTE * ONE_SECOND);
+ int minutes = offset / (ONE_MINUTE * ONE_SECOND) - ONE_HOUR * hours;
+ if (hours < TEN) {
+ tzMarker.append("0");
+ }
+ tzMarker.append(hours);
+ if (minutes < TEN) {
+ tzMarker.append("0");
+ }
+ tzMarker.append(minutes);
+ synchronized (DATE_HEADER_FORMAT_INT) {
+ return DATE_HEADER_FORMAT_INT.format(cal.getTime()) + tzMarker.toString();
+ }
+ }
+
+ /**
+ * Parses the string in a format suitable for a SMTP date header.
+ *
+ * @param datestr string to be parsed
+ *
+ * @return a java.util.Date object as parsed by the format.
+ * @exception ParseException if the supplied string cannot be parsed by
+ * this pattern.
+ * @since Ant 1.8.0
+ */
+ public static Date parseDateFromHeader(String datestr) throws ParseException {
+ synchronized (DATE_HEADER_FORMAT_INT) {
+ return DATE_HEADER_FORMAT_INT.parse(datestr);
+ }
+ }
+
+ /**
+ * Parse a string as a datetime using the ISO8601_DATETIME format which is
+ * <code>yyyy-MM-dd'T'HH:mm:ss</code>
+ *
+ * @param datestr string to be parsed
+ *
+ * @return a java.util.Date object as parsed by the format.
+ * @exception ParseException if the supplied string cannot be parsed by
+ * this pattern.
+ * @since Ant 1.6
+ */
+ public static Date parseIso8601DateTime(String datestr)
+ throws ParseException {
+ return new SimpleDateFormat(ISO8601_DATETIME_PATTERN).parse(datestr);
+ }
+
+ /**
+ * Parse a string as a date using the ISO8601_DATE format which is
+ * <code>yyyy-MM-dd</code>
+ *
+ * @param datestr string to be parsed
+ *
+ * @return a java.util.Date object as parsed by the format.
+ * @exception ParseException if the supplied string cannot be parsed by
+ * this pattern.
+ * @since Ant 1.6
+ */
+ public static Date parseIso8601Date(String datestr) throws ParseException {
+ return new SimpleDateFormat(ISO8601_DATE_PATTERN).parse(datestr);
+ }
+
+ /**
+ * Parse a string as a date using the either the ISO8601_DATETIME
+ * or ISO8601_DATE formats.
+ *
+ * @param datestr string to be parsed
+ *
+ * @return a java.util.Date object as parsed by the formats.
+ * @exception ParseException if the supplied string cannot be parsed by
+ * either of these patterns.
+ * @since Ant 1.6
+ */
+ public static Date parseIso8601DateTimeOrDate(String datestr)
+ throws ParseException {
+ try {
+ return parseIso8601DateTime(datestr);
+ } catch (ParseException px) {
+ return parseIso8601Date(datestr);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DeweyDecimal.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DeweyDecimal.java
new file mode 100644
index 00000000..003f1406
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/DeweyDecimal.java
@@ -0,0 +1,237 @@
+/*
+ * 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.tools.ant.util;
+
+import java.util.StringTokenizer;
+
+/**
+ * Utility class to contain version numbers in "Dewey Decimal"
+ * syntax. Numbers in the "Dewey Decimal" syntax consist of positive
+ * decimal integers separated by periods ".". For example, "2.0" or
+ * "1.2.3.4.5.6.7". This allows an extensible number to be used to
+ * represent major, minor, micro, etc versions. The version number
+ * must begin with a number.
+ *
+ */
+public class DeweyDecimal implements Comparable<DeweyDecimal> {
+
+ /** Array of components that make up DeweyDecimal */
+ private final int[] components;
+
+ /**
+ * Construct a DeweyDecimal from an array of integer components.
+ *
+ * @param components an array of integer components.
+ */
+ public DeweyDecimal(final int[] components) {
+ this.components = new int[components.length];
+ System.arraycopy(components, 0, this.components, 0, components.length);
+ }
+
+ /**
+ * Construct a DeweyDecimal from string in DeweyDecimal format.
+ *
+ * @param string the string in dewey decimal format
+ * @exception NumberFormatException if string is malformed
+ */
+ public DeweyDecimal(final String string)
+ throws NumberFormatException {
+ final StringTokenizer tokenizer = new StringTokenizer(string, ".", true);
+ final int size = tokenizer.countTokens();
+
+ components = new int[ (size + 1) / 2 ];
+
+ for (int i = 0; i < components.length; i++) {
+ final String component = tokenizer.nextToken();
+ if (component.length() == 0) {
+ throw new NumberFormatException("Empty component in string");
+ }
+
+ components[ i ] = Integer.parseInt(component);
+
+ //Strip '.' token
+ if (tokenizer.hasMoreTokens()) {
+ tokenizer.nextToken();
+
+ //If it ended in a dot, throw an exception
+ if (!tokenizer.hasMoreTokens()) {
+ throw new NumberFormatException("DeweyDecimal ended in a '.'");
+ }
+ }
+ }
+ }
+
+ /**
+ * Return number of components in <code>DeweyDecimal</code>.
+ *
+ * @return the number of components in dewey decimal
+ */
+ public int getSize() {
+ return components.length;
+ }
+
+ /**
+ * Return the component at specified index.
+ *
+ * @param index the index of components
+ * @return the value of component at index
+ */
+ public int get(final int index) {
+ return components[ index ];
+ }
+
+ /**
+ * Return <code>true</code> if this <code>DeweyDecimal</code> is
+ * equal to the other <code>DeweyDecimal</code>.
+ *
+ * @param other the other DeweyDecimal
+ * @return true if equal to other DeweyDecimal, false otherwise
+ */
+ public boolean isEqual(final DeweyDecimal other) {
+ final int max = Math.max(other.components.length, components.length);
+
+ for (int i = 0; i < max; i++) {
+ final int component1 = (i < components.length) ? components[ i ] : 0;
+ final int component2 = (i < other.components.length) ? other.components[ i ] : 0;
+
+ if (component2 != component1) {
+ return false;
+ }
+ }
+
+ return true; // Exact match
+ }
+
+ /**
+ * Return <code>true</code> if this <code>DeweyDecimal</code> is
+ * less than the other <code>DeweyDecimal</code>.
+ *
+ * @param other the other DeweyDecimal
+ * @return true if less than other DeweyDecimal, false otherwise
+ */
+ public boolean isLessThan(final DeweyDecimal other) {
+ return !isGreaterThanOrEqual(other);
+ }
+
+ /**
+ * Return <code>true</code> if this <code>DeweyDecimal</code> is
+ * less than or equal to the other <code>DeweyDecimal</code>.
+ *
+ * @param other the other DeweyDecimal
+ * @return true if less than or equal to other DeweyDecimal, false otherwise
+ */
+ public boolean isLessThanOrEqual(final DeweyDecimal other) {
+ return !isGreaterThan(other);
+ }
+
+ /**
+ * Return <code>true</code> if this <code>DeweyDecimal</code> is
+ * greater than the other <code>DeweyDecimal</code>.
+ *
+ * @param other the other DeweyDecimal
+ * @return true if greater than other DeweyDecimal, false otherwise
+ */
+ public boolean isGreaterThan(final DeweyDecimal other) {
+ final int max = Math.max(other.components.length, components.length);
+
+ for (int i = 0; i < max; i++) {
+ final int component1 = (i < components.length) ? components[ i ] : 0;
+ final int component2 = (i < other.components.length) ? other.components[ i ] : 0;
+
+ if (component2 > component1) {
+ return false;
+ }
+ if (component2 < component1) {
+ return true;
+ }
+ }
+
+ return false; // Exact match
+ }
+
+ /**
+ * Return <code>true</code> if this <code>DeweyDecimal</code> is
+ * greater than or equal to the other <code>DeweyDecimal</code>.
+ *
+ * @param other the other DeweyDecimal
+ * @return true if greater than or equal to other DeweyDecimal, false otherwise
+ */
+ public boolean isGreaterThanOrEqual(final DeweyDecimal other) {
+ final int max = Math.max(other.components.length, components.length);
+
+ for (int i = 0; i < max; i++) {
+ final int component1 = (i < components.length) ? components[ i ] : 0;
+ final int component2 = (i < other.components.length) ? other.components[ i ] : 0;
+
+ if (component2 > component1) {
+ return false;
+ }
+ if (component2 < component1) {
+ return true;
+ }
+ }
+
+ return true; // Exact match
+ }
+
+ /**
+ * Return string representation of <code>DeweyDecimal</code>.
+ *
+ * @return the string representation of DeweyDecimal.
+ */
+ @Override public String toString() {
+ final StringBuffer sb = new StringBuffer();
+
+ for (int i = 0; i < components.length; i++) {
+ if (i != 0) {
+ sb.append('.');
+ }
+ sb.append(components[ i ]);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Compares this DeweyDecimal with another one.
+ *
+ * @param other another DeweyDecimal to compare with
+ * @return result
+ * @see java.lang.Comparable#compareTo(Object)
+ */
+ public int compareTo(DeweyDecimal other) {
+ final int max = Math.max(other.components.length, components.length);
+ for (int i = 0; i < max; i++) {
+ final int component1 = (i < components.length) ? components[ i ] : 0;
+ final int component2 = (i < other.components.length) ? other.components[ i ] : 0;
+ if (component1 != component2) {
+ return component1 - component2;
+ }
+ }
+ return 0;
+ }
+
+ @Override public int hashCode() {
+ return toString().hashCode();
+ }
+
+ @Override public boolean equals(Object o) {
+ return o instanceof DeweyDecimal && isEqual((DeweyDecimal) o);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FileNameMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FileNameMapper.java
new file mode 100644
index 00000000..bbd82614
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FileNameMapper.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.tools.ant.util;
+
+/**
+ * Interface to be used by SourceFileScanner.
+ *
+ * <p>Used to find the name of the target file(s) corresponding to a
+ * source file.</p>
+ *
+ * <p>The rule by which the file names are transformed is specified
+ * via the setFrom and setTo methods. The exact meaning of these is
+ * implementation dependent.</p>
+ *
+ */
+public interface FileNameMapper {
+
+ /**
+ * Sets the from part of the transformation rule.
+ * @param from a string.
+ */
+ void setFrom(String from);
+
+ /**
+ * Sets the to part of the transformation rule.
+ * @param to a string.
+ */
+ void setTo(String to);
+
+ /**
+ * Returns an array containing the target filename(s) for the
+ * given source file.
+ *
+ * <p>if the given rule doesn't apply to the source file,
+ * implementation must return null. SourceFileScanner will then
+ * omit the source file in question.</p>
+ *
+ * @param sourceFileName the name of the source file relative to
+ * some given basedirectory.
+ * @return an array of strings if the rule applies to the source file, or
+ * null if it does not.
+ */
+ String[] mapFileName(String sourceFileName);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FileTokenizer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FileTokenizer.java
new file mode 100644
index 00000000..2807aa42
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FileTokenizer.java
@@ -0,0 +1,48 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Class to read the complete input into a string.
+ * @since Ant 1.7
+ */
+public class FileTokenizer extends ProjectComponent implements Tokenizer {
+
+ /**
+ * Get the complete input as a string
+ * @param in the reader object
+ * @return the complete input
+ * @throws IOException if error reading
+ */
+ public String getToken(Reader in) throws IOException {
+ return FileUtils.readFully(in);
+ }
+
+ /**
+ * Return the intra-token string
+ * @return an empty string always
+ */
+ public String getPostToken() {
+ return "";
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FileUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FileUtils.java
new file mode 100644
index 00000000..bcef5ecf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FileUtils.java
@@ -0,0 +1,1722 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.HttpURLConnection;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.channels.Channel;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.jar.JarFile;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.PathTokenizer;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.launch.Locator;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.types.resources.FileResource;
+
+/**
+ * This class also encapsulates methods which allow Files to be
+ * referred to using abstract path names which are translated to native
+ * system file paths at runtime as well as copying files or setting
+ * their last modification time.
+ *
+ */
+public class FileUtils {
+ private static final int DELETE_RETRY_SLEEP_MILLIS = 10;
+ private static final int EXPAND_SPACE = 50;
+ private static final FileUtils PRIMARY_INSTANCE = new FileUtils();
+
+ //get some non-crypto-grade randomness from various places.
+ private static Random rand = new Random(System.currentTimeMillis()
+ + Runtime.getRuntime().freeMemory());
+
+ private static final boolean ON_NETWARE = Os.isFamily("netware");
+ private static final boolean ON_DOS = Os.isFamily("dos");
+ private static final boolean ON_WIN9X = Os.isFamily("win9x");
+ private static final boolean ON_WINDOWS = Os.isFamily("windows");
+
+ static final int BUF_SIZE = 8192;
+
+
+ /**
+ * The granularity of timestamps under FAT.
+ */
+ public static final long FAT_FILE_TIMESTAMP_GRANULARITY = 2000;
+
+ /**
+ * The granularity of timestamps under Unix.
+ */
+ public static final long UNIX_FILE_TIMESTAMP_GRANULARITY = 1000;
+
+ /**
+ * The granularity of timestamps under the NT File System.
+ * NTFS has a granularity of 100 nanoseconds, which is less
+ * than 1 millisecond, so we round this up to 1 millisecond.
+ */
+ public static final long NTFS_FILE_TIMESTAMP_GRANULARITY = 1;
+
+ /**
+ * A one item cache for fromUri.
+ * fromUri is called for each element when parseing ant build
+ * files. It is a costly operation. This just caches the result
+ * of the last call.
+ */
+ private Object cacheFromUriLock = new Object();
+ private String cacheFromUriRequest = null;
+ private String cacheFromUriResponse = null;
+
+ /**
+ * Factory method.
+ *
+ * @return a new instance of FileUtils.
+ * @deprecated since 1.7.
+ * Use getFileUtils instead,
+ * FileUtils do not have state.
+ */
+ public static FileUtils newFileUtils() {
+ return new FileUtils();
+ }
+
+ /**
+ * Method to retrieve The FileUtils, which is shared by all users of this
+ * method.
+ * @return an instance of FileUtils.
+ * @since Ant 1.6.3
+ */
+ public static FileUtils getFileUtils() {
+ return PRIMARY_INSTANCE;
+ }
+
+ /**
+ * Empty constructor.
+ */
+ protected FileUtils() {
+ }
+
+ /**
+ * Get the URL for a file taking into account # characters.
+ *
+ * @param file the file whose URL representation is required.
+ * @return The FileURL value.
+ * @throws MalformedURLException if the URL representation cannot be
+ * formed.
+ */
+ public URL getFileURL(File file) throws MalformedURLException {
+ return new URL(file.toURI().toASCIIString());
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination.
+ * No filtering is performed.
+ *
+ * @param sourceFile Name of file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile Name of file to copy to.
+ * Must not be <code>null</code>.
+ *
+ * @throws IOException if the copying fails.
+ */
+ public void copyFile(String sourceFile, String destFile) throws IOException {
+ copyFile(new File(sourceFile), new File(destFile), null, false, false);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination
+ * specifying if token filtering must be used.
+ *
+ * @param sourceFile Name of file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile Name of file to copy to.
+ * Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ *
+ * @throws IOException if the copying fails.
+ */
+ public void copyFile(String sourceFile, String destFile, FilterSetCollection filters)
+ throws IOException {
+ copyFile(new File(sourceFile), new File(destFile), filters, false, false);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination specifying if token
+ * filtering must be used and if source files may overwrite newer destination files.
+ *
+ * @param sourceFile Name of file to copy from. Must not be <code>null</code>.
+ * @param destFile Name of file to copy to. Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param overwrite Whether or not the destination file should be overwritten if it already
+ * exists.
+ *
+ * @throws IOException if the copying fails.
+ */
+ public void copyFile(String sourceFile, String destFile, FilterSetCollection filters,
+ boolean overwrite) throws IOException {
+ copyFile(new File(sourceFile), new File(destFile), filters, overwrite, false);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination
+ * specifying if token
+ * filtering must be used, if source files may overwrite newer destination
+ * files and the last
+ * modified time of <code>destFile</code> file should be made equal to
+ * the last modified time
+ * of <code>sourceFile</code>.
+ *
+ * @param sourceFile Name of file to copy from. Must not be <code>null</code>.
+ * @param destFile Name of file to copy to. Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the resulting file
+ * should be set to that of the source file.
+ *
+ * @throws IOException if the copying fails.
+ */
+ public void copyFile(String sourceFile, String destFile,
+ FilterSetCollection filters,
+ boolean overwrite, boolean preserveLastModified)
+ throws IOException {
+ copyFile(new File(sourceFile), new File(destFile), filters, overwrite,
+ preserveLastModified);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination specifying if token
+ * filtering must be used, if source files may overwrite newer destination files and the last
+ * modified time of <code>destFile</code> file should be made equal to the last modified time
+ * of <code>sourceFile</code>.
+ *
+ * @param sourceFile Name of file to copy from. Must not be <code>null</code>.
+ * @param destFile Name of file to copy to. Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param overwrite Whether or not the destination file should be overwritten if it already
+ * exists.
+ * @param preserveLastModified Whether or not the last modified time of the resulting file
+ * should be set to that of the source file.
+ * @param encoding the encoding used to read and write the files.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.5
+ */
+ public void copyFile(String sourceFile, String destFile,
+ FilterSetCollection filters, boolean overwrite,
+ boolean preserveLastModified, String encoding) throws IOException {
+ copyFile(new File(sourceFile), new File(destFile), filters,
+ overwrite, preserveLastModified, encoding);
+ }
+
+ // CheckStyle:ParameterNumberCheck OFF - bc
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering must be used, if
+ * filter chains must be used, if source files may overwrite
+ * newer destination files and the last modified time of
+ * <code>destFile</code> file should be made equal
+ * to the last modified time of <code>sourceFile</code>.
+ *
+ * @param sourceFile Name of file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile Name of file to copy to.
+ * Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param filterChains filterChains to apply during the copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the resulting file should be set to that
+ * of the source file.
+ * @param encoding the encoding used to read and write the files.
+ * @param project the project instance.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.5
+ */
+ public void copyFile(String sourceFile, String destFile,
+ FilterSetCollection filters, Vector filterChains,
+ boolean overwrite, boolean preserveLastModified,
+ String encoding, Project project) throws IOException {
+ copyFile(new File(sourceFile), new File(destFile), filters, filterChains, overwrite,
+ preserveLastModified, encoding, project);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination specifying if token
+ * filtering must be used, if filter chains must be used, if source files may overwrite newer
+ * destination files and the last modified time of <code>destFile</code> file should be made
+ * equal to the last modified time of <code>sourceFile</code>.
+ *
+ * @param sourceFile Name of file to copy from. Must not be <code>null</code>.
+ * @param destFile Name of file to copy to. Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param filterChains filterChains to apply during the copy.
+ * @param overwrite Whether or not the destination file should be overwritten if it already
+ * exists.
+ * @param preserveLastModified Whether or not the last modified time of the resulting file
+ * should be set to that of the source file.
+ * @param inputEncoding the encoding used to read the files.
+ * @param outputEncoding the encoding used to write the files.
+ * @param project the project instance.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.6
+ */
+ public void copyFile(String sourceFile, String destFile,
+ FilterSetCollection filters, Vector filterChains,
+ boolean overwrite, boolean preserveLastModified,
+ String inputEncoding, String outputEncoding,
+ Project project) throws IOException {
+ copyFile(new File(sourceFile), new File(destFile), filters, filterChains, overwrite,
+ preserveLastModified, inputEncoding, outputEncoding, project);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination. No filtering is performed.
+ *
+ * @param sourceFile the file to copy from. Must not be <code>null</code>.
+ * @param destFile the file to copy to. Must not be <code>null</code>.
+ *
+ * @throws IOException if the copying fails.
+ */
+ public void copyFile(File sourceFile, File destFile) throws IOException {
+ copyFile(sourceFile, destFile, null, false, false);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination
+ * specifying if token filtering must be used.
+ *
+ * @param sourceFile the file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile the file to copy to.
+ * Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ *
+ * @throws IOException if the copying fails.
+ */
+ public void copyFile(File sourceFile, File destFile, FilterSetCollection filters)
+ throws IOException {
+ copyFile(sourceFile, destFile, filters, false, false);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering must be used and if
+ * source files may overwrite newer destination files.
+ *
+ * @param sourceFile the file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile the file to copy to.
+ * Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ *
+ * @throws IOException if the copying fails.
+ */
+ public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
+ boolean overwrite) throws IOException {
+ copyFile(sourceFile, destFile, filters, overwrite, false);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering must be used, if
+ * source files may overwrite newer destination files and the
+ * last modified time of <code>destFile</code> file should be made equal
+ * to the last modified time of <code>sourceFile</code>.
+ *
+ * @param sourceFile the file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile the file to copy to.
+ * Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the resulting file should be set to that
+ * of the source file.
+ *
+ * @throws IOException if the copying fails.
+ */
+ public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
+ boolean overwrite, boolean preserveLastModified) throws IOException {
+ copyFile(sourceFile, destFile, filters, overwrite, preserveLastModified, null);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination specifying if token
+ * filtering must be used, if source files may overwrite newer destination files, the last
+ * modified time of <code>destFile</code> file should be made equal to the last modified time
+ * of <code>sourceFile</code> and which character encoding to assume.
+ *
+ * @param sourceFile the file to copy from. Must not be <code>null</code>.
+ * @param destFile the file to copy to. Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param overwrite Whether or not the destination file should be overwritten if it already
+ * exists.
+ * @param preserveLastModified Whether or not the last modified time of the resulting file
+ * should be set to that of the source file.
+ * @param encoding the encoding used to read and write the files.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.5
+ */
+ public void copyFile(File sourceFile, File destFile,
+ FilterSetCollection filters, boolean overwrite,
+ boolean preserveLastModified, String encoding) throws IOException {
+ copyFile(sourceFile, destFile, filters, null, overwrite,
+ preserveLastModified, encoding, null);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering must be used, if
+ * filter chains must be used, if source files may overwrite
+ * newer destination files and the last modified time of
+ * <code>destFile</code> file should be made equal
+ * to the last modified time of <code>sourceFile</code>.
+ *
+ * @param sourceFile the file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile the file to copy to.
+ * Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param filterChains filterChains to apply during the copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the resulting file should be set to that
+ * of the source file.
+ * @param encoding the encoding used to read and write the files.
+ * @param project the project instance.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.5
+ */
+ public void copyFile(File sourceFile, File destFile,
+ FilterSetCollection filters, Vector filterChains,
+ boolean overwrite, boolean preserveLastModified,
+ String encoding, Project project) throws IOException {
+ copyFile(sourceFile, destFile, filters, filterChains,
+ overwrite, preserveLastModified, encoding, encoding, project);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering must be used, if
+ * filter chains must be used, if source files may overwrite
+ * newer destination files and the last modified time of
+ * <code>destFile</code> file should be made equal
+ * to the last modified time of <code>sourceFile</code>.
+ *
+ * @param sourceFile the file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile the file to copy to.
+ * Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param filterChains filterChains to apply during the copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the resulting file should be set to that
+ * of the source file.
+ * @param inputEncoding the encoding used to read the files.
+ * @param outputEncoding the encoding used to write the files.
+ * @param project the project instance.
+ *
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.6
+ */
+ public void copyFile(File sourceFile, File destFile,
+ FilterSetCollection filters, Vector filterChains,
+ boolean overwrite, boolean preserveLastModified,
+ String inputEncoding, String outputEncoding,
+ Project project) throws IOException {
+ copyFile(sourceFile, destFile, filters, filterChains, overwrite, preserveLastModified,
+ false, inputEncoding, outputEncoding, project);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering must be used, if
+ * filter chains must be used, if source files may overwrite
+ * newer destination files and the last modified time of
+ * <code>destFile</code> file should be made equal
+ * to the last modified time of <code>sourceFile</code>.
+ *
+ * @param sourceFile the file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile the file to copy to.
+ * Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param filterChains filterChains to apply during the copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the resulting file should be set to that
+ * of the source file.
+ * @param append whether to append to the destination file.
+ * @param inputEncoding the encoding used to read the files.
+ * @param outputEncoding the encoding used to write the files.
+ * @param project the project instance.
+ *
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.8
+ */
+ public void copyFile(File sourceFile, File destFile,
+ FilterSetCollection filters, Vector filterChains,
+ boolean overwrite, boolean preserveLastModified,
+ boolean append,
+ String inputEncoding, String outputEncoding,
+ Project project) throws IOException {
+ copyFile(sourceFile, destFile, filters, filterChains, overwrite,
+ preserveLastModified, append, inputEncoding, outputEncoding,
+ project, /* force: */ false);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering must be used, if
+ * filter chains must be used, if source files may overwrite
+ * newer destination files and the last modified time of
+ * <code>destFile</code> file should be made equal
+ * to the last modified time of <code>sourceFile</code>.
+ *
+ * @param sourceFile the file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile the file to copy to.
+ * Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param filterChains filterChains to apply during the copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the resulting file should be set to that
+ * of the source file.
+ * @param append whether to append to the destination file.
+ * @param inputEncoding the encoding used to read the files.
+ * @param outputEncoding the encoding used to write the files.
+ * @param project the project instance.
+ * @param force whether to overwrite read-only destination files.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.8.2
+ */
+ public void copyFile(File sourceFile, File destFile,
+ FilterSetCollection filters, Vector filterChains,
+ boolean overwrite, boolean preserveLastModified,
+ boolean append,
+ String inputEncoding, String outputEncoding,
+ Project project, boolean force) throws IOException {
+ ResourceUtils.copyResource(new FileResource(sourceFile),
+ new FileResource(destFile),
+ filters, filterChains, overwrite,
+ preserveLastModified, append, inputEncoding,
+ outputEncoding, project, force);
+ }
+
+ // CheckStyle:ParameterNumberCheck ON
+
+ /**
+ * Calls File.setLastModified(long time). Originally written to
+ * to dynamically bind to that call on Java1.2+.
+ *
+ * @param file the file whose modified time is to be set
+ * @param time the time to which the last modified time is to be set.
+ * if this is -1, the current time is used.
+ */
+ public void setFileLastModified(File file, long time) {
+ ResourceUtils.setLastModified(new FileResource(file), time);
+ }
+
+ /**
+ * Interpret the filename as a file relative to the given file
+ * unless the filename already represents an absolute filename.
+ * Differs from <code>new File(file, filename)</code> in that
+ * the resulting File's path will always be a normalized,
+ * absolute pathname. Also, if it is determined that
+ * <code>filename</code> is context-relative, <code>file</code>
+ * will be discarded and the reference will be resolved using
+ * available context/state information about the filesystem.
+ *
+ * @param file the "reference" file for relative paths. This
+ * instance must be an absolute file and must not contain
+ * &quot;./&quot; or &quot;../&quot; sequences (same for \ instead
+ * of /). If it is null, this call is equivalent to
+ * <code>new java.io.File(filename).getAbsoluteFile()</code>.
+ *
+ * @param filename a file name.
+ *
+ * @return an absolute file.
+ * @throws java.lang.NullPointerException if filename is null.
+ */
+ public File resolveFile(File file, String filename) {
+ if (!isAbsolutePath(filename)) {
+ char sep = File.separatorChar;
+ filename = filename.replace('/', sep).replace('\\', sep);
+ if (isContextRelativePath(filename)) {
+ file = null;
+ // on cygwin, our current directory can be a UNC;
+ // assume user.dir is absolute or all hell breaks loose...
+ String udir = System.getProperty("user.dir");
+ if (filename.charAt(0) == sep && udir.charAt(0) == sep) {
+ filename = dissect(udir)[0] + filename.substring(1);
+ }
+ }
+ filename = new File(file, filename).getAbsolutePath();
+ }
+ return normalize(filename);
+ }
+
+ /**
+ * On DOS and NetWare, the evaluation of certain file
+ * specifications is context-dependent. These are filenames
+ * beginning with a single separator (relative to current root directory)
+ * and filenames with a drive specification and no intervening separator
+ * (relative to current directory of the specified root).
+ * @param filename the filename to evaluate.
+ * @return true if the filename is relative to system context.
+ * @throws java.lang.NullPointerException if filename is null.
+ * @since Ant 1.7
+ */
+ public static boolean isContextRelativePath(String filename) {
+ if (!(ON_DOS || ON_NETWARE) || filename.length() == 0) {
+ return false;
+ }
+ char sep = File.separatorChar;
+ filename = filename.replace('/', sep).replace('\\', sep);
+ char c = filename.charAt(0);
+ int len = filename.length();
+ return (c == sep && (len == 1 || filename.charAt(1) != sep))
+ || (Character.isLetter(c) && len > 1
+ && filename.charAt(1) == ':'
+ && (len == 2 || filename.charAt(2) != sep));
+ }
+
+ /**
+ * Verifies that the specified filename represents an absolute path.
+ * Differs from new java.io.File("filename").isAbsolute() in that a path
+ * beginning with a double file separator--signifying a Windows UNC--must
+ * at minimum match "\\a\b" to be considered an absolute path.
+ * @param filename the filename to be checked.
+ * @return true if the filename represents an absolute path.
+ * @throws java.lang.NullPointerException if filename is null.
+ * @since Ant 1.6.3
+ */
+ public static boolean isAbsolutePath(String filename) {
+ int len = filename.length();
+ if (len == 0) {
+ return false;
+ }
+ char sep = File.separatorChar;
+ filename = filename.replace('/', sep).replace('\\', sep);
+ char c = filename.charAt(0);
+ if (!(ON_DOS || ON_NETWARE)) {
+ return (c == sep);
+ }
+ if (c == sep) {
+ // CheckStyle:MagicNumber OFF
+ if (!(ON_DOS && len > 4 && filename.charAt(1) == sep)) {
+ return false;
+ }
+ // CheckStyle:MagicNumber ON
+ int nextsep = filename.indexOf(sep, 2);
+ return nextsep > 2 && nextsep + 1 < len;
+ }
+ int colon = filename.indexOf(':');
+ return (Character.isLetter(c) && colon == 1
+ && filename.length() > 2 && filename.charAt(2) == sep)
+ || (ON_NETWARE && colon > 0);
+ }
+
+ /**
+ * Translate a path into its native (platform specific) format.
+ * <p>
+ * This method uses PathTokenizer to separate the input path
+ * into its components. This handles DOS style paths in a relatively
+ * sensible way. The file separators are then converted to their platform
+ * specific versions.
+ *
+ * @param toProcess The path to be translated.
+ * May be <code>null</code>.
+ *
+ * @return the native version of the specified path or
+ * an empty string if the path is <code>null</code> or empty.
+ *
+ * @since ant 1.7
+ * @see PathTokenizer
+ */
+ public static String translatePath(String toProcess) {
+ if (toProcess == null || toProcess.length() == 0) {
+ return "";
+ }
+ StringBuffer path = new StringBuffer(toProcess.length() + EXPAND_SPACE);
+ PathTokenizer tokenizer = new PathTokenizer(toProcess);
+ while (tokenizer.hasMoreTokens()) {
+ String pathComponent = tokenizer.nextToken();
+ pathComponent = pathComponent.replace('/', File.separatorChar);
+ pathComponent = pathComponent.replace('\\', File.separatorChar);
+ if (path.length() != 0) {
+ path.append(File.pathSeparatorChar);
+ }
+ path.append(pathComponent);
+ }
+ return path.toString();
+ }
+
+ /**
+ * &quot;Normalize&quot; the given absolute path.
+ *
+ * <p>This includes:
+ * <ul>
+ * <li>Uppercase the drive letter if there is one.</li>
+ * <li>Remove redundant slashes after the drive spec.</li>
+ * <li>Resolve all ./, .\, ../ and ..\ sequences.</li>
+ * <li>DOS style paths that start with a drive letter will have
+ * \ as the separator.</li>
+ * </ul>
+ * Unlike {@link File#getCanonicalPath()} this method
+ * specifically does not resolve symbolic links.
+ *
+ * @param path the path to be normalized.
+ * @return the normalized version of the path.
+ *
+ * @throws java.lang.NullPointerException if path is null.
+ */
+ public File normalize(final String path) {
+ Stack s = new Stack();
+ String[] dissect = dissect(path);
+ s.push(dissect[0]);
+
+ StringTokenizer tok = new StringTokenizer(dissect[1], File.separator);
+ while (tok.hasMoreTokens()) {
+ String thisToken = tok.nextToken();
+ if (".".equals(thisToken)) {
+ continue;
+ }
+ if ("..".equals(thisToken)) {
+ if (s.size() < 2) {
+ // Cannot resolve it, so skip it.
+ return new File(path);
+ }
+ s.pop();
+ } else { // plain component
+ s.push(thisToken);
+ }
+ }
+ StringBuffer sb = new StringBuffer();
+ final int size = s.size();
+ for (int i = 0; i < size; i++) {
+ if (i > 1) {
+ // not before the filesystem root and not after it, since root
+ // already contains one
+ sb.append(File.separatorChar);
+ }
+ sb.append(s.elementAt(i));
+ }
+ return new File(sb.toString());
+ }
+
+ /**
+ * Dissect the specified absolute path.
+ * @param path the path to dissect.
+ * @return String[] {root, remaining path}.
+ * @throws java.lang.NullPointerException if path is null.
+ * @since Ant 1.7
+ */
+ public String[] dissect(String path) {
+ char sep = File.separatorChar;
+ path = path.replace('/', sep).replace('\\', sep);
+
+ // make sure we are dealing with an absolute path
+ if (!isAbsolutePath(path)) {
+ throw new BuildException(path + " is not an absolute path");
+ }
+ String root = null;
+ int colon = path.indexOf(':');
+ if (colon > 0 && (ON_DOS || ON_NETWARE)) {
+
+ int next = colon + 1;
+ root = path.substring(0, next);
+ char[] ca = path.toCharArray();
+ root += sep;
+ //remove the initial separator; the root has it.
+ next = (ca[next] == sep) ? next + 1 : next;
+
+ StringBuffer sbPath = new StringBuffer();
+ // Eliminate consecutive slashes after the drive spec:
+ for (int i = next; i < ca.length; i++) {
+ if (ca[i] != sep || ca[i - 1] != sep) {
+ sbPath.append(ca[i]);
+ }
+ }
+ path = sbPath.toString();
+ } else if (path.length() > 1 && path.charAt(1) == sep) {
+ // UNC drive
+ int nextsep = path.indexOf(sep, 2);
+ nextsep = path.indexOf(sep, nextsep + 1);
+ root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path;
+ path = path.substring(root.length());
+ } else {
+ root = File.separator;
+ path = path.substring(1);
+ }
+ return new String[] {root, path};
+ }
+
+ /**
+ * Returns a VMS String representation of a <code>File</code> object.
+ * This is useful since the JVM by default internally converts VMS paths
+ * to Unix style.
+ * The returned String is always an absolute path.
+ *
+ * @param f The <code>File</code> to get the VMS path for.
+ * @return The absolute VMS path to <code>f</code>.
+ */
+ public String toVMSPath(File f) {
+ // format: "DEVICE:[DIR.SUBDIR]FILE"
+ String osPath;
+ String path = normalize(f.getAbsolutePath()).getPath();
+ String name = f.getName();
+ boolean isAbsolute = path.charAt(0) == File.separatorChar;
+ // treat directories specified using .DIR syntax as files
+ // CheckStyle:MagicNumber OFF
+ boolean isDirectory = f.isDirectory()
+ && !name.regionMatches(true, name.length() - 4, ".DIR", 0, 4);
+ // CheckStyle:MagicNumber ON
+ String device = null;
+ StringBuffer directory = null;
+ String file = null;
+
+ int index = 0;
+
+ if (isAbsolute) {
+ index = path.indexOf(File.separatorChar, 1);
+ if (index == -1) {
+ return path.substring(1) + ":[000000]";
+ }
+ device = path.substring(1, index++);
+ }
+ if (isDirectory) {
+ directory = new StringBuffer(path.substring(index).replace(File.separatorChar, '.'));
+ } else {
+ int dirEnd = path.lastIndexOf(File.separatorChar, path.length());
+ if (dirEnd == -1 || dirEnd < index) {
+ file = path.substring(index);
+ } else {
+ directory = new StringBuffer(path.substring(index, dirEnd).
+ replace(File.separatorChar, '.'));
+ index = dirEnd + 1;
+ if (path.length() > index) {
+ file = path.substring(index);
+ }
+ }
+ }
+ if (!isAbsolute && directory != null) {
+ directory.insert(0, '.');
+ }
+ osPath = ((device != null) ? device + ":" : "")
+ + ((directory != null) ? "[" + directory + "]" : "")
+ + ((file != null) ? file : "");
+ return osPath;
+ }
+
+ /**
+ * Create a File object for a temporary file in a given directory. Without
+ * actually creating the file.
+ *
+ * <p>
+ * The file denoted by the returned abstract pathname did not exist before
+ * this method was invoked, any subsequent invocation of this method will
+ * yield a different file name.
+ * </p>
+ * <p>
+ * The filename is prefixNNNNNsuffix where NNNN is a random number.
+ * </p>
+ *
+ * @param prefix
+ * prefix before the random number.
+ * @param suffix
+ * file extension; include the '.'.
+ * @param parentDir
+ * Directory to create the temporary file in; java.io.tmpdir used
+ * if not specified.
+ *
+ * @deprecated since ant 1.7.1 use createTempFile(String, String, File,
+ * boolean, boolean) instead.
+ * @return a File reference to the new, nonexistent temporary file.
+ */
+ public File createTempFile(String prefix, String suffix, File parentDir) {
+ return createTempFile(prefix, suffix, parentDir, false, false);
+ }
+
+ private static final String NULL_PLACEHOLDER = "null";
+
+ /**
+ * Create a temporary file in a given directory.
+ *
+ * <p>The file denoted by the returned abstract pathname did not
+ * exist before this method was invoked, any subsequent invocation
+ * of this method will yield a different file name.</p>
+ *
+ * @param prefix prefix before the random number.
+ * @param suffix file extension; include the '.'.
+ * @param parentDir Directory to create the temporary file in;
+ * java.io.tmpdir used if not specified.
+ * @param deleteOnExit whether to set the tempfile for deletion on
+ * normal VM exit.
+ * @param createFile true if the file must actually be created. If false
+ * chances exist that a file with the same name is created in the time
+ * between invoking this method and the moment the file is actually created.
+ * If possible set to true.
+ *
+ * @return a File reference to the new temporary file.
+ * @since Ant 1.7.1
+ */
+ public File createTempFile(String prefix, String suffix, File parentDir,
+ boolean deleteOnExit, boolean createFile) {
+ File result = null;
+ String parent = (parentDir == null)
+ ? System.getProperty("java.io.tmpdir")
+ : parentDir.getPath();
+ if (prefix == null) {
+ prefix = NULL_PLACEHOLDER;
+ }
+ if (suffix == null) {
+ suffix = NULL_PLACEHOLDER;
+ }
+
+ if (createFile) {
+ try {
+ result = File.createTempFile(prefix, suffix, new File(parent));
+ } catch (IOException e) {
+ throw new BuildException("Could not create tempfile in "
+ + parent, e);
+ }
+ } else {
+ DecimalFormat fmt = new DecimalFormat("#####");
+ synchronized (rand) {
+ do {
+ result = new File(parent, prefix
+ + fmt.format(rand.nextInt(Integer.MAX_VALUE)) + suffix);
+ } while (result.exists());
+ }
+ }
+
+ if (deleteOnExit) {
+ result.deleteOnExit();
+ }
+ return result;
+ }
+
+ /**
+ * Create a File object for a temporary file in a given directory. Without
+ * actually creating the file.
+ *
+ * <p>
+ * The file denoted by the returned abstract pathname did not exist before
+ * this method was invoked, any subsequent invocation of this method will
+ * yield a different file name.
+ * </p>
+ * <p>
+ * The filename is prefixNNNNNsuffix where NNNN is a random number.
+ * </p>
+ *
+ * @param prefix
+ * prefix before the random number.
+ * @param suffix
+ * file extension; include the '.'.
+ * @param parentDir
+ * Directory to create the temporary file in; java.io.tmpdir used
+ * if not specified.
+ * @param deleteOnExit
+ * whether to set the tempfile for deletion on normal VM exit.
+ *
+ * @deprecated since ant 1.7.1 use createTempFile(String, String, File,
+ * boolean, boolean) instead.
+ * @return a File reference to the new, nonexistent temporary file.
+ */
+ public File createTempFile(String prefix, String suffix,
+ File parentDir, boolean deleteOnExit) {
+ return createTempFile(prefix, suffix, parentDir, deleteOnExit, false);
+ }
+
+ /**
+ * Compares the contents of two files.
+ *
+ * @param f1 the file whose content is to be compared.
+ * @param f2 the other file whose content is to be compared.
+ *
+ * @return true if the content of the files is the same.
+ *
+ * @throws IOException if the files cannot be read.
+ */
+ public boolean contentEquals(File f1, File f2) throws IOException {
+ return contentEquals(f1, f2, false);
+ }
+
+ /**
+ * Compares the contents of two files.
+ *
+ * @param f1 the file whose content is to be compared.
+ * @param f2 the other file whose content is to be compared.
+ * @param textfile true if the file is to be treated as a text file and
+ * differences in kind of line break are to be ignored.
+ *
+ * @return true if the content of the files is the same.
+ *
+ * @throws IOException if the files cannot be read.
+ * @since Ant 1.6.3
+ */
+ public boolean contentEquals(File f1, File f2, boolean textfile) throws IOException {
+ return ResourceUtils.contentEquals(new FileResource(f1), new FileResource(f2), textfile);
+ }
+
+ /**
+ * This was originally an emulation of {@link File#getParentFile} for JDK 1.1, but it is now
+ * implemented using that method (Ant 1.6.3 onwards).
+ *
+ * @param f the file whose parent is required.
+ * @return the given file's parent, or null if the file does not have a parent.
+ * @since 1.10
+ * @deprecated since 1.7. Just use {@link File#getParentFile} directly.
+ */
+ public File getParentFile(File f) {
+ return (f == null) ? null : f.getParentFile();
+ }
+
+ /**
+ * Read from reader till EOF.
+ * @param rdr the reader from which to read.
+ * @return the contents read out of the given reader.
+ *
+ * @throws IOException if the contents could not be read out from the
+ * reader.
+ */
+ public static String readFully(Reader rdr) throws IOException {
+ return readFully(rdr, BUF_SIZE);
+ }
+
+ /**
+ * Read from reader till EOF.
+ *
+ * @param rdr the reader from which to read.
+ * @param bufferSize the buffer size to use when reading.
+ *
+ * @return the contents read out of the given reader.
+ *
+ * @throws IOException if the contents could not be read out from the
+ * reader.
+ */
+ public static String readFully(Reader rdr, int bufferSize)
+ throws IOException {
+ if (bufferSize <= 0) {
+ throw new IllegalArgumentException("Buffer size must be greater "
+ + "than 0");
+ }
+ final char[] buffer = new char[bufferSize];
+ int bufferLength = 0;
+ StringBuffer textBuffer = null;
+ while (bufferLength != -1) {
+ bufferLength = rdr.read(buffer);
+ if (bufferLength > 0) {
+ textBuffer = (textBuffer == null) ? new StringBuffer() : textBuffer;
+ textBuffer.append(new String(buffer, 0, bufferLength));
+ }
+ }
+ return (textBuffer == null) ? null : textBuffer.toString();
+ }
+
+ /**
+ * Safe read fully - do not return a null for an empty reader.
+ * @param reader the input to read from.
+ * @return the string.
+ * @throws IOException if unable to read from reader.
+ * @since Ant 1.7.1
+ */
+ public static String safeReadFully(Reader reader) throws IOException {
+ String ret = readFully(reader);
+ return ret == null ? "" : ret;
+ }
+
+ /**
+ * This was originally an emulation of File.createNewFile for JDK 1.1,
+ * but it is now implemented using that method (Ant 1.6.3 onwards).
+ *
+ * <p>This method has historically <strong>not</strong> guaranteed that the
+ * operation was atomic. In its current implementation it is.
+ *
+ * @param f the file to be created.
+ * @return true if the file did not exist already.
+ * @throws IOException on error.
+ * @since Ant 1.5
+ */
+ public boolean createNewFile(File f) throws IOException {
+ return f.createNewFile();
+ }
+
+ /**
+ * Create a new file, optionally creating parent directories.
+ *
+ * @param f the file to be created.
+ * @param mkdirs <code>boolean</code> whether to create parent directories.
+ * @return true if the file did not exist already.
+ * @throws IOException on error.
+ * @since Ant 1.6.3
+ */
+ public boolean createNewFile(File f, boolean mkdirs) throws IOException {
+ File parent = f.getParentFile();
+ if (mkdirs && !(parent.exists())) {
+ parent.mkdirs();
+ }
+ return f.createNewFile();
+ }
+
+ /**
+ * Checks whether a given file is a symbolic link.
+ *
+ * <p>It doesn't really test for symbolic links but whether the
+ * canonical and absolute paths of the file are identical--this
+ * may lead to false positives on some platforms.</p>
+ *
+ * @param parent the parent directory of the file to test
+ * @param name the name of the file to test.
+ *
+ * @return true if the file is a symbolic link.
+ * @throws IOException on error.
+ * @since Ant 1.5
+ * @deprecated use SymbolicLinkUtils instead
+ */
+ public boolean isSymbolicLink(File parent, String name)
+ throws IOException {
+ SymbolicLinkUtils u = SymbolicLinkUtils.getSymbolicLinkUtils();
+ if (parent == null) {
+ return u.isSymbolicLink(name);
+ }
+ return u.isSymbolicLink(parent, name);
+ }
+
+ /**
+ * Removes a leading path from a second path.
+ *
+ * @param leading The leading path, must not be null, must be absolute.
+ * @param path The path to remove from, must not be null, must be absolute.
+ *
+ * @return path's normalized absolute if it doesn't start with
+ * leading; path's path with leading's path removed otherwise.
+ *
+ * @since Ant 1.5
+ */
+ public String removeLeadingPath(File leading, File path) {
+ String l = normalize(leading.getAbsolutePath()).getAbsolutePath();
+ String p = normalize(path.getAbsolutePath()).getAbsolutePath();
+ if (l.equals(p)) {
+ return "";
+ }
+ // ensure that l ends with a /
+ // so we never think /foo was a parent directory of /foobar
+ if (!l.endsWith(File.separator)) {
+ l += File.separator;
+ }
+ return (p.startsWith(l)) ? p.substring(l.length()) : p;
+ }
+
+ /**
+ * Learn whether one path "leads" another.
+ * @param leading The leading path, must not be null, must be absolute.
+ * @param path The path to remove from, must not be null, must be absolute.
+ * @return true if path starts with leading; false otherwise.
+ * @since Ant 1.7
+ */
+ public boolean isLeadingPath(File leading, File path) {
+ String l = normalize(leading.getAbsolutePath()).getAbsolutePath();
+ String p = normalize(path.getAbsolutePath()).getAbsolutePath();
+ if (l.equals(p)) {
+ return true;
+ }
+ // ensure that l ends with a /
+ // so we never think /foo was a parent directory of /foobar
+ if (!l.endsWith(File.separator)) {
+ l += File.separator;
+ }
+ return p.startsWith(l);
+ }
+
+ /**
+ * Constructs a <code>file:</code> URI that represents the
+ * external form of the given pathname.
+ *
+ * <p>Will be an absolute URI if the given path is absolute.</p>
+ *
+ * <p>This code encodes non ASCII characters too.</p>
+ *
+ * <p>The coding of the output is the same as what File.toURI().toASCIIString() produces</p>
+ *
+ * See <a href="http://www.w3.org/TR/xml11/#dt-sysid">dt-sysid</a>
+ * which makes some mention of how
+ * characters not supported by URI Reference syntax should be escaped.
+ *
+ * @param path the path in the local file system.
+ * @return the URI version of the local path.
+ * @since Ant 1.6
+ */
+ public String toURI(String path) {
+ return new File(path).toURI().toASCIIString();
+ }
+
+ /**
+ * Constructs a file path from a <code>file:</code> URI.
+ *
+ * <p>Will be an absolute path if the given URI is absolute.</p>
+ *
+ * <p>Swallows '%' that are not followed by two characters,
+ * doesn't deal with non-ASCII characters.</p>
+ *
+ * @param uri the URI designating a file in the local filesystem.
+ * @return the local file system path for the file.
+ * @since Ant 1.6
+ */
+ public String fromURI(String uri) {
+ synchronized (cacheFromUriLock) {
+ if (uri.equals(cacheFromUriRequest)) {
+ return cacheFromUriResponse;
+ }
+ String path = Locator.fromURI(uri);
+ String ret = isAbsolutePath(path) ? normalize(path).getAbsolutePath() : path;
+ cacheFromUriRequest = uri;
+ cacheFromUriResponse = ret;
+ return ret;
+ }
+ }
+
+ /**
+ * Compares two filenames.
+ *
+ * <p>Unlike java.io.File#equals this method will try to compare
+ * the absolute paths and &quot;normalize&quot; the filenames
+ * before comparing them.</p>
+ *
+ * @param f1 the file whose name is to be compared.
+ * @param f2 the other file whose name is to be compared.
+ *
+ * @return true if the file are for the same file.
+ *
+ * @since Ant 1.5.3
+ */
+ public boolean fileNameEquals(File f1, File f2) {
+ return normalize(f1.getAbsolutePath()).getAbsolutePath().equals(
+ normalize(f2.getAbsolutePath()).getAbsolutePath());
+ }
+
+ /**
+ * Are the two File instances pointing to the same object on the
+ * file system?
+ * @since Ant 1.8.2
+ */
+ public boolean areSame(File f1, File f2) throws IOException {
+ if (f1 == null && f2 == null) {
+ return true;
+ }
+ if (f1 == null || f2 == null) {
+ return false;
+ }
+ File f1Normalized = normalize(f1.getAbsolutePath());
+ File f2Normalized = normalize(f2.getAbsolutePath());
+ return f1Normalized.equals(f2Normalized)
+ || f1Normalized.getCanonicalFile().equals(f2Normalized
+ .getCanonicalFile());
+ }
+
+ /**
+ * Renames a file, even if that involves crossing file system boundaries.
+ *
+ * <p>This will remove <code>to</code> (if it exists), ensure that
+ * <code>to</code>'s parent directory exists and move
+ * <code>from</code>, which involves deleting <code>from</code> as
+ * well.</p>
+ *
+ * @param from the file to move.
+ * @param to the new file name.
+ *
+ * @throws IOException if anything bad happens during this
+ * process. Note that <code>to</code> may have been deleted
+ * already when this happens.
+ *
+ * @since Ant 1.6
+ */
+ public void rename(File from, File to) throws IOException {
+ // identical logic lives in Move.renameFile():
+ from = normalize(from.getAbsolutePath()).getCanonicalFile();
+ to = normalize(to.getAbsolutePath());
+ if (!from.exists()) {
+ System.err.println("Cannot rename nonexistent file " + from);
+ return;
+ }
+ if (from.getAbsolutePath().equals(to.getAbsolutePath())) {
+ System.err.println("Rename of " + from + " to " + to + " is a no-op.");
+ return;
+ }
+ if (to.exists() && !(areSame(from, to) || tryHardToDelete(to))) {
+ throw new IOException("Failed to delete " + to + " while trying to rename " + from);
+ }
+ File parent = to.getParentFile();
+ if (parent != null && !parent.isDirectory()
+ && !(parent.mkdirs() || parent.isDirectory())) {
+ throw new IOException("Failed to create directory " + parent
+ + " while trying to rename " + from);
+ }
+ if (!from.renameTo(to)) {
+ copyFile(from, to);
+ if (!tryHardToDelete(from)) {
+ throw new IOException("Failed to delete " + from + " while trying to rename it.");
+ }
+ }
+ }
+
+ /**
+ * Get the granularity of file timestamps. The choice is made based on OS, which is
+ * incorrect--it should really be by filesystem. We do not have an easy way to probe for file
+ * systems, however, so this heuristic gives us a decent default.
+ *
+ * @return the difference, in milliseconds, which two file timestamps must have in order for the
+ * two files to be considered to have different timestamps.
+ */
+ public long getFileTimestampGranularity() {
+ if (ON_WIN9X) {
+ return FAT_FILE_TIMESTAMP_GRANULARITY;
+ }
+ if (ON_WINDOWS) {
+ return NTFS_FILE_TIMESTAMP_GRANULARITY;
+ }
+ if (ON_DOS) {
+ return FAT_FILE_TIMESTAMP_GRANULARITY;
+ }
+ return UNIX_FILE_TIMESTAMP_GRANULARITY;
+ }
+
+ /**
+ * test whether a file or directory exists, with an error in the
+ * upper/lower case spelling of the name.
+ * Using this method is only interesting on case insensitive file systems
+ * (Windows).<br>
+ * It will return true only if 3 conditions are met :
+ * <br>
+ * <ul>
+ * <li>operating system is case insensitive</li>
+ * <li>file exists</li>
+ * <li>actual name from directory reading is different from the
+ * supplied argument</li>
+ * </ul>
+ * <br>
+ * the purpose is to identify files or directories on case-insensitive
+ * filesystems whose case is not what is expected.<br>
+ * Possibly to rename them afterwards to the desired upper/lowercase
+ * combination.
+ *
+ * @param localFile file to test
+ * @return true if the file exists and the case of the actual file
+ * is not the case of the parameter
+ * @since Ant 1.7.1
+ */
+ public boolean hasErrorInCase(File localFile) {
+ localFile = normalize(localFile.getAbsolutePath());
+ if (!localFile.exists()) {
+ return false;
+ }
+ final String localFileName = localFile.getName();
+ FilenameFilter ff = new FilenameFilter () {
+ public boolean accept(File dir, String name) {
+ return name.equalsIgnoreCase(localFileName) && (!name.equals(localFileName));
+ }
+ };
+ String[] names = localFile.getParentFile().list(ff);
+ return names != null && names.length == 1;
+ }
+
+ /**
+ * Returns true if the source is older than the dest.
+ * If the dest file does not exist, then the test returns false; it is
+ * implicitly not up do date.
+ * @param source source file (should be the older).
+ * @param dest dest file (should be the newer).
+ * @param granularity an offset added to the source time.
+ * @return true if the source is older than the dest after accounting
+ * for granularity.
+ * @since Ant 1.6.3
+ */
+ public boolean isUpToDate(File source, File dest, long granularity) {
+ //do a check for the destination file existing
+ if (!dest.exists()) {
+ //if it does not, then the file is not up to date.
+ return false;
+ }
+ long sourceTime = source.lastModified();
+ long destTime = dest.lastModified();
+ return isUpToDate(sourceTime, destTime, granularity);
+ }
+
+ /**
+ * Returns true if the source is older than the dest.
+ * @param source source file (should be the older).
+ * @param dest dest file (should be the newer).
+ * @return true if the source is older than the dest, taking the granularity into account.
+ * @since Ant 1.6.3
+ */
+ public boolean isUpToDate(File source, File dest) {
+ return isUpToDate(source, dest, getFileTimestampGranularity());
+ }
+
+ /**
+ * Compare two timestamps for being up to date using
+ * the specified granularity.
+ *
+ * @param sourceTime timestamp of source file.
+ * @param destTime timestamp of dest file.
+ * @param granularity os/filesys granularity.
+ * @return true if the dest file is considered up to date.
+ */
+ public boolean isUpToDate(long sourceTime, long destTime, long granularity) {
+ return destTime != -1 && destTime >= sourceTime + granularity;
+ }
+
+ /**
+ * Compare two timestamps for being up to date using the
+ * current granularity.
+ *
+ * @param sourceTime timestamp of source file.
+ * @param destTime timestamp of dest file.
+ * @return true if the dest file is considered up to date.
+ */
+ public boolean isUpToDate(long sourceTime, long destTime) {
+ return isUpToDate(sourceTime, destTime, getFileTimestampGranularity());
+ }
+
+ /**
+ * Close a Writer without throwing any exception if something went wrong.
+ * Do not attempt to close it if the argument is null.
+ * @param device output writer, can be null.
+ */
+ public static void close(Writer device) {
+ if (null != device) {
+ try {
+ device.close();
+ } catch (IOException e) {
+ //ignore
+ }
+ }
+ }
+
+ /**
+ * Close a Reader without throwing any exception if something went wrong.
+ * Do not attempt to close it if the argument is null.
+ *
+ * @param device Reader, can be null.
+ */
+ public static void close(Reader device) {
+ if (null != device) {
+ try {
+ device.close();
+ } catch (IOException e) {
+ //ignore
+ }
+ }
+ }
+
+ /**
+ * Close a stream without throwing any exception if something went wrong.
+ * Do not attempt to close it if the argument is null.
+ *
+ * @param device stream, can be null.
+ */
+ public static void close(OutputStream device) {
+ if (null != device) {
+ try {
+ device.close();
+ } catch (IOException e) {
+ //ignore
+ }
+ }
+ }
+
+ /**
+ * Close a stream without throwing any exception if something went wrong.
+ * Do not attempt to close it if the argument is null.
+ *
+ * @param device stream, can be null.
+ */
+ public static void close(InputStream device) {
+ if (null != device) {
+ try {
+ device.close();
+ } catch (IOException e) {
+ //ignore
+ }
+ }
+ }
+
+ /**
+ * Close a Channel without throwing any exception if something went wrong.
+ * Do not attempt to close it if the argument is null.
+ *
+ * @param device channel, can be null.
+ * @since Ant 1.8.0
+ */
+ public static void close(Channel device) {
+ if (null != device) {
+ try {
+ device.close();
+ } catch (IOException e) {
+ //ignore
+ }
+ }
+ }
+
+ /**
+ * Closes an URLConnection if its concrete implementation provides
+ * a way to close it that Ant knows of.
+ *
+ * @param conn connection, can be null
+ * @since Ant 1.8.0
+ */
+ public static void close(URLConnection conn) {
+ if (conn != null) {
+ try {
+ if (conn instanceof JarURLConnection) {
+ JarURLConnection juc = (JarURLConnection) conn;
+ JarFile jf = juc.getJarFile();
+ jf.close();
+ jf = null;
+ } else if (conn instanceof HttpURLConnection) {
+ ((HttpURLConnection) conn).disconnect();
+ }
+ } catch (IOException exc) {
+ //ignore
+ }
+ }
+ }
+
+ /**
+ * Delete the file with {@link File#delete()} if the argument is not null.
+ * Do nothing on a null argument.
+ * @param file file to delete.
+ */
+ public static void delete(File file) {
+ if (file != null) {
+ file.delete();
+ }
+ }
+
+ /**
+ * Accommodate Windows bug encountered in both Sun and IBM JDKs.
+ * Others possible. If the delete does not work, call System.gc(),
+ * wait a little and try again.
+ *
+ * @return whether deletion was successful
+ * @since Ant 1.8.0
+ */
+ public boolean tryHardToDelete(File f) {
+ return tryHardToDelete(f, ON_WINDOWS);
+ }
+
+ /**
+ * If delete does not work, call System.gc() if asked to, wait a
+ * little and try again.
+ *
+ * @return whether deletion was successful
+ * @since Ant 1.8.3
+ */
+ public boolean tryHardToDelete(File f, boolean runGC) {
+ if (!f.delete()) {
+ if (runGC) {
+ System.gc();
+ }
+ try {
+ Thread.sleep(DELETE_RETRY_SLEEP_MILLIS);
+ } catch (InterruptedException ex) {
+ // Ignore Exception
+ }
+ return f.delete();
+ }
+ return true;
+ }
+
+ /**
+ * Calculates the relative path between two files.
+ * <p>
+ * Implementation note:<br>This function may throw an IOException if an I/O error occurs
+ * because its use of the canonical pathname may require filesystem queries.
+ * </p>
+ *
+ * @param fromFile the <code>File</code> to calculate the path from
+ * @param toFile the <code>File</code> to calculate the path to
+ * @return the relative path between the files
+ * @throws Exception for undocumented reasons
+ * @see File#getCanonicalPath()
+ *
+ * @since Ant 1.7
+ */
+ public static String getRelativePath(File fromFile, File toFile) throws Exception {
+ String fromPath = fromFile.getCanonicalPath();
+ String toPath = toFile.getCanonicalPath();
+
+ // build the path stack info to compare
+ String[] fromPathStack = getPathStack(fromPath);
+ String[] toPathStack = getPathStack(toPath);
+
+ if (0 < toPathStack.length && 0 < fromPathStack.length) {
+ if (!fromPathStack[0].equals(toPathStack[0])) {
+ // not the same device (would be "" on Linux/Unix)
+
+ return getPath(Arrays.asList(toPathStack));
+ }
+ } else {
+ // no comparison possible
+ return getPath(Arrays.asList(toPathStack));
+ }
+
+ int minLength = Math.min(fromPathStack.length, toPathStack.length);
+ int same = 1; // Used outside the for loop
+
+ // get index of parts which are equal
+ for (;
+ same < minLength && fromPathStack[same].equals(toPathStack[same]);
+ same++) {
+ // Do nothing
+ }
+
+ List relativePathStack = new ArrayList();
+
+ // if "from" part is longer, fill it up with ".."
+ // to reach path which is equal to both paths
+ for (int i = same; i < fromPathStack.length; i++) {
+ relativePathStack.add("..");
+ }
+
+ // fill it up path with parts which were not equal
+ for (int i = same; i < toPathStack.length; i++) {
+ relativePathStack.add(toPathStack[i]);
+ }
+
+ return getPath(relativePathStack);
+ }
+
+ /**
+ * Gets all names of the path as an array of <code>String</code>s.
+ *
+ * @param path to get names from
+ * @return <code>String</code>s, never <code>null</code>
+ *
+ * @since Ant 1.7
+ */
+ public static String[] getPathStack(String path) {
+ String normalizedPath = path.replace(File.separatorChar, '/');
+
+ return normalizedPath.split("/");
+ }
+
+ /**
+ * Gets path from a <code>List</code> of <code>String</code>s.
+ *
+ * @param pathStack <code>List</code> of <code>String</code>s to be concatenated as a path.
+ * @return <code>String</code>, never <code>null</code>
+ *
+ * @since Ant 1.7
+ */
+ public static String getPath(List pathStack) {
+ // can safely use '/' because Windows understands '/' as separator
+ return getPath(pathStack, '/');
+ }
+
+ /**
+ * Gets path from a <code>List</code> of <code>String</code>s.
+ *
+ * @param pathStack <code>List</code> of <code>String</code>s to be concated as a path.
+ * @param separatorChar <code>char</code> to be used as separator between names in path
+ * @return <code>String</code>, never <code>null</code>
+ *
+ * @since Ant 1.7
+ */
+ public static String getPath(final List pathStack, final char separatorChar) {
+ final StringBuffer buffer = new StringBuffer();
+
+ final Iterator iter = pathStack.iterator();
+ if (iter.hasNext()) {
+ buffer.append(iter.next());
+ }
+ while (iter.hasNext()) {
+ buffer.append(separatorChar);
+ buffer.append(iter.next());
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Get the default encoding.
+ * This is done by opening an InputStreamReader on
+ * a dummy InputStream and getting the encoding.
+ * Could use System.getProperty("file.encoding"), but cannot
+ * see where this is documented.
+ * @return the default file encoding.
+ */
+ public String getDefaultEncoding() {
+ InputStreamReader is = new InputStreamReader(
+ new InputStream() {
+ public int read() {
+ return -1;
+ }
+ });
+ try {
+ return is.getEncoding();
+ } finally {
+ close(is);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FirstMatchMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FirstMatchMapper.java
new file mode 100644
index 00000000..b0e47f29
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FirstMatchMapper.java
@@ -0,0 +1,45 @@
+/*
+ * 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.tools.ant.util;
+
+import java.util.Iterator;
+
+/**
+ * A <code>ContainerMapper</code> that returns the results of its
+ * first constituent <code>FileNameMapper</code>s that matches.
+ *
+ * @since Ant 1.8.0
+ */
+public class FirstMatchMapper extends ContainerMapper {
+
+ /** {@inheritDoc}. */
+ public String[] mapFileName(String sourceFileName) {
+ for (Iterator iter = getMappers().iterator(); iter.hasNext();) {
+ FileNameMapper mapper = (FileNameMapper) iter.next();
+ if (mapper != null) {
+ String[] mapped = mapper.mapFileName(sourceFileName);
+ if (mapped != null) {
+ return mapped;
+ }
+ }
+ }
+ return null;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FlatFileNameMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FlatFileNameMapper.java
new file mode 100644
index 00000000..420ccc6c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/FlatFileNameMapper.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.tools.ant.util;
+
+/**
+ * Implementation of FileNameMapper that always returns the source
+ * file name without any leading directory information.
+ *
+ * <p>This is the default FileNameMapper for the copy and move
+ * tasks if the flatten attribute has been set.</p>
+ *
+ */
+public class FlatFileNameMapper implements FileNameMapper {
+
+ /**
+ * Ignored.
+ * @param from ignored.
+ */
+ public void setFrom(String from) {
+ }
+
+ /**
+ * Ignored.
+ * @param to ignored.
+ */
+ public void setTo(String to) {
+ }
+
+ /**
+ * Returns an one-element array containing the source file name
+ * without any leading directory information.
+ * @param sourceFileName the name to map.
+ * @return the file name in a one-element array.
+ */
+ public String[] mapFileName(String sourceFileName) {
+ return new String[] {new java.io.File(sourceFileName).getName()};
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/GlobPatternMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/GlobPatternMapper.java
new file mode 100644
index 00000000..da2a0f16
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/GlobPatternMapper.java
@@ -0,0 +1,203 @@
+/*
+ * 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.tools.ant.util;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Implementation of FileNameMapper that does simple wildcard pattern
+ * replacements.
+ *
+ * <p>This does simple translations like *.foo -&gt; *.bar where the
+ * prefix to .foo will be left unchanged. It only handles a single *
+ * character, use regular expressions for more complicated
+ * situations.</p>
+ *
+ * <p>This is one of the more useful Mappers, it is used by javac for
+ * example.</p>
+ *
+ */
+public class GlobPatternMapper implements FileNameMapper {
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ /**
+ * Part of &quot;from&quot; pattern before the *.
+ */
+ protected String fromPrefix = null;
+
+ /**
+ * Part of &quot;from&quot; pattern after the *.
+ */
+ protected String fromPostfix = null;
+
+ /**
+ * Length of the prefix (&quot;from&quot; pattern).
+ */
+ protected int prefixLength;
+
+ /**
+ * Length of the postfix (&quot;from&quot; pattern).
+ */
+ protected int postfixLength;
+
+ /**
+ * Part of &quot;to&quot; pattern before the *.
+ */
+ protected String toPrefix = null;
+
+ /**
+ * Part of &quot;to&quot; pattern after the *.
+ */
+ protected String toPostfix = null;
+
+ // CheckStyle:VisibilityModifier ON
+
+ private boolean fromContainsStar = false;
+ private boolean toContainsStar = false;
+ private boolean handleDirSep = false;
+ private boolean caseSensitive = true;
+
+ /**
+ * Attribute specifying whether to ignore the difference
+ * between / and \ (the two common directory characters).
+ * @param handleDirSep a boolean, default is false.
+ * @since Ant 1.6.3
+ */
+ public void setHandleDirSep(boolean handleDirSep) {
+ this.handleDirSep = handleDirSep;
+ }
+
+ /**
+ * Attribute specifying whether to ignore the difference
+ * between / and \ (the two common directory characters).
+ * @since Ant 1.8.3
+ */
+ public boolean getHandleDirSep() {
+ return handleDirSep;
+ }
+
+ /**
+ * Attribute specifying whether to ignore the case difference
+ * in the names.
+ *
+ * @param caseSensitive a boolean, default is false.
+ * @since Ant 1.6.3
+ */
+ public void setCaseSensitive(boolean caseSensitive) {
+ this.caseSensitive = caseSensitive;
+ }
+
+ /**
+ * Sets the &quot;from&quot; pattern. Required.
+ * @param from a string
+ */
+ public void setFrom(String from) {
+ if (from != null) {
+ int index = from.lastIndexOf("*");
+ if (index == -1) {
+ fromPrefix = from;
+ fromPostfix = "";
+ } else {
+ fromPrefix = from.substring(0, index);
+ fromPostfix = from.substring(index + 1);
+ fromContainsStar = true;
+ }
+ prefixLength = fromPrefix.length();
+ postfixLength = fromPostfix.length();
+ } else {
+ throw new BuildException("this mapper requires a 'from' attribute");
+ }
+ }
+
+ /**
+ * Sets the &quot;to&quot; pattern. Required.
+ * @param to a string
+ */
+ public void setTo(String to) {
+ if (to != null) {
+ int index = to.lastIndexOf("*");
+ if (index == -1) {
+ toPrefix = to;
+ toPostfix = "";
+ } else {
+ toPrefix = to.substring(0, index);
+ toPostfix = to.substring(index + 1);
+ toContainsStar = true;
+ }
+ } else {
+ throw new BuildException("this mapper requires a 'to' attribute");
+ }
+ }
+
+ /**
+ * Returns null if the source file name doesn't match the
+ * &quot;from&quot; pattern, an one-element array containing the
+ * translated file otherwise.
+ * @param sourceFileName the filename to map
+ * @return a list of converted filenames
+ */
+ public String[] mapFileName(String sourceFileName) {
+ String modName = modifyName(sourceFileName);
+ if (fromPrefix == null
+ || (sourceFileName.length() < (prefixLength + postfixLength))
+ || (!fromContainsStar
+ && !modName.equals(modifyName(fromPrefix))
+ )
+ || (fromContainsStar
+ && (!modName.startsWith(modifyName(fromPrefix))
+ || !modName.endsWith(modifyName(fromPostfix)))
+ )
+ ) {
+ return null;
+ }
+ return new String[] {toPrefix
+ + (toContainsStar
+ ? extractVariablePart(sourceFileName)
+ + toPostfix
+ : "")};
+ }
+
+ /**
+ * Returns the part of the given string that matches the * in the
+ * &quot;from&quot; pattern.
+ * @param name the source file name
+ * @return the variable part of the name
+ */
+ protected String extractVariablePart(String name) {
+ return name.substring(prefixLength,
+ name.length() - postfixLength);
+ }
+
+ /**
+ * modify string based on dir char mapping and case sensitivity
+ * @param name the name to convert
+ * @return the converted name
+ */
+ private String modifyName(String name) {
+ if (!caseSensitive) {
+ name = name.toLowerCase();
+ }
+ if (handleDirSep) {
+ if (name.indexOf('\\') != -1) {
+ name = name.replace('\\', '/');
+ }
+ }
+ return name;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/IdentityMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/IdentityMapper.java
new file mode 100644
index 00000000..22c6c7ea
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/IdentityMapper.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.tools.ant.util;
+
+/**
+ * Implementation of FileNameMapper that always returns the source file name.
+ *
+ * <p>This is the default FileNameMapper for the copy and move
+ * tasks.</p>
+ *
+ */
+public class IdentityMapper implements FileNameMapper {
+
+ /**
+ * Ignored.
+ * @param from ignored.
+ */
+ public void setFrom(String from) {
+ }
+
+ /**
+ * Ignored.
+ * @param to ignored.
+ */
+ public void setTo(String to) {
+ }
+
+ /**
+ * Returns an one-element array containing the source file name.
+ * @param sourceFileName the name to map.
+ * @return the source filename in a one-element array.
+ */
+ public String[] mapFileName(String sourceFileName) {
+ return new String[] {sourceFileName};
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/IdentityStack.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/IdentityStack.java
new file mode 100644
index 00000000..ac806d78
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/IdentityStack.java
@@ -0,0 +1,130 @@
+/*
+ * 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.tools.ant.util;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * Identity Stack.
+ * @since Ant 1.7
+ */
+public class IdentityStack<E> extends Stack<E> {
+
+ private static final long serialVersionUID = -5555522620060077046L;
+
+ /**
+ * Get an IdentityStack containing the contents of the specified Stack.
+ * @param s the Stack to copy; ignored if null.
+ * @return an IdentityStack instance.
+ */
+ public static <E> IdentityStack<E> getInstance(Stack<E> s) {
+ if (s instanceof IdentityStack) {
+ return (IdentityStack<E>) s;
+ }
+ IdentityStack<E> result = new IdentityStack<E>();
+ if (s != null) {
+ result.addAll(s);
+ }
+ return result;
+ }
+
+ /**
+ * Default constructor.
+ */
+ public IdentityStack() {
+ }
+
+ /**
+ * Construct a new IdentityStack with the specified Object
+ * as the bottom element.
+ * @param o the bottom element.
+ */
+ public IdentityStack(E o) {
+ super();
+ push(o);
+ }
+
+ /**
+ * Override methods that use <code>.equals()</code> comparisons on elements.
+ * @param o the Object to search for.
+ * @return true if the stack contains the object.
+ * @see java.util.Vector#contains(Object)
+ */
+ public synchronized boolean contains(Object o) {
+ return indexOf(o) >= 0;
+ }
+
+ /**
+ * Override methods that use <code>.equals()</code> comparisons on elements.
+ * @param o the Object to search for.
+ * @param pos the position from which to search.
+ * @return the position of the object, -1 if not found.
+ * @see java.util.Vector#indexOf(Object, int)
+ */
+ public synchronized int indexOf(Object o, int pos) {
+ final int size = size();
+ for (int i = pos; i < size; i++) {
+ if (get(i) == o) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Override methods that use <code>.equals()</code> comparisons on elements.
+ * @param o the Object to search for.
+ * @param pos the position from which to search (backward).
+ * @return the position of the object, -1 if not found.
+ * @see java.util.Vector#indexOf(Object, int)
+ */
+ public synchronized int lastIndexOf(Object o, int pos) {
+ for (int i = pos; i >= 0; i--) {
+ if (get(i) == o) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public synchronized boolean removeAll(Collection<?> c) {
+ if (!(c instanceof Set)) {
+ c = new HashSet(c);
+ }
+ return super.removeAll(c);
+ }
+
+ public synchronized boolean retainAll(Collection c) {
+ if (!(c instanceof Set)) {
+ c = new HashSet(c);
+ }
+ return super.retainAll(c);
+ }
+
+ public synchronized boolean containsAll(Collection<?> c) {
+ IdentityHashMap map = new IdentityHashMap();
+ for (Object e : this) {
+ map.put(e, Boolean.TRUE);
+ }
+ return map.keySet().containsAll(c);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/JAXPUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/JAXPUtils.java
new file mode 100644
index 00000000..76460ae2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/JAXPUtils.java
@@ -0,0 +1,260 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.File;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.tools.ant.BuildException;
+import org.xml.sax.Parser;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+// CheckStyle:HideUtilityClassConstructorCheck OFF - bc
+
+/**
+ * Collection of helper methods that retrieve a ParserFactory or
+ * Parsers and Readers.
+ *
+ * <p>This class will create only a single factory instance.</p>
+ *
+ * @since Ant 1.5
+ */
+public class JAXPUtils {
+
+ /**
+ * Helper for systemId.
+ *
+ * @since Ant 1.6
+ */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Parser factory to use to create parsers.
+ * @see #getParserFactory
+ *
+ * @since Ant 1.5
+ */
+ private static SAXParserFactory parserFactory = null;
+
+ /**
+ * Parser Factory to create Namespace aware parsers.
+ *
+ * @since Ant 1.6
+ */
+ private static SAXParserFactory nsParserFactory = null;
+
+ /**
+ * Parser factory to use to create document builders.
+ *
+ * @since Ant 1.6
+ */
+ private static DocumentBuilderFactory builderFactory = null;
+
+ /**
+ * Returns the parser factory to use. Only one parser factory is
+ * ever created by this method and is then cached for future use.
+ *
+ * @return a SAXParserFactory to use.
+ * @throws BuildException on error.
+ *
+ * @since Ant 1.5
+ */
+ public static synchronized SAXParserFactory getParserFactory()
+ throws BuildException {
+
+ if (parserFactory == null) {
+ parserFactory = newParserFactory();
+ }
+ return parserFactory;
+ }
+
+ /**
+ * Returns the parser factory to use to create namespace aware parsers.
+ *
+ * @return a SAXParserFactory to use which supports manufacture of
+ * namespace aware parsers.
+ * @throws BuildException on error.
+ *
+ * @since Ant 1.6
+ */
+ public static synchronized SAXParserFactory getNSParserFactory()
+ throws BuildException {
+
+ if (nsParserFactory == null) {
+ nsParserFactory = newParserFactory();
+ nsParserFactory.setNamespaceAware(true);
+ }
+ return nsParserFactory;
+ }
+
+ /**
+ * Returns a new parser factory instance.
+ *
+ * @return the parser factory.
+ * @throws BuildException on error.
+ * @since Ant 1.5
+ */
+ public static SAXParserFactory newParserFactory() throws BuildException {
+
+ try {
+ return SAXParserFactory.newInstance();
+ } catch (FactoryConfigurationError e) {
+ throw new BuildException("XML parser factory has not been "
+ + "configured correctly: "
+ + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Returns a newly created SAX 1 Parser, using the default parser
+ * factory.
+ *
+ * @return a SAX 1 Parser.
+ * @throws BuildException on error.
+ * @see #getParserFactory
+ * @since Ant 1.5
+ */
+ public static Parser getParser() throws BuildException {
+ try {
+ return newSAXParser(getParserFactory()).getParser();
+ } catch (SAXException e) {
+ throw convertToBuildException(e);
+ }
+ }
+
+ /**
+ * Returns a newly created SAX 2 XMLReader, using the default parser
+ * factory.
+ *
+ * @return a SAX 2 XMLReader.
+ * @throws BuildException on error.
+ * @see #getParserFactory
+ * @since Ant 1.5
+ */
+ public static XMLReader getXMLReader() throws BuildException {
+ try {
+ return newSAXParser(getParserFactory()).getXMLReader();
+ } catch (SAXException e) {
+ throw convertToBuildException(e);
+ }
+ }
+
+ /**
+ * Returns a newly created SAX 2 XMLReader, which is namespace aware
+ *
+ * @return a SAX 2 XMLReader.
+ * @throws BuildException on error.
+ * @see #getParserFactory
+ * @since Ant 1.6
+ */
+ public static XMLReader getNamespaceXMLReader() throws BuildException {
+ try {
+ return newSAXParser(getNSParserFactory()).getXMLReader();
+ } catch (SAXException e) {
+ throw convertToBuildException(e);
+ }
+ }
+
+ /**
+ * This is a best attempt to provide a URL.toExternalForm() from
+ * a file URL. Some parsers like Crimson choke on uri that are made of
+ * backslashed paths (ie windows) as it is does not conform
+ * URI specifications.
+ * @param file the file to create the system id from.
+ * @return the systemid corresponding to the given file.
+ * @since Ant 1.5.2
+ */
+ public static String getSystemId(File file) {
+ return FILE_UTILS.toURI(file.getAbsolutePath());
+ }
+
+ /**
+ * Returns a newly created DocumentBuilder.
+ *
+ * @return a DocumentBuilder.
+ * @throws BuildException on error.
+ * @since Ant 1.6
+ */
+ public static DocumentBuilder getDocumentBuilder() throws BuildException {
+ try {
+ return getDocumentBuilderFactory().newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * @return a new SAXParser instance as helper for getParser and
+ * getXMLReader.
+ *
+ * @since Ant 1.5
+ */
+ private static SAXParser newSAXParser(SAXParserFactory factory)
+ throws BuildException {
+ try {
+ return factory.newSAXParser();
+ } catch (ParserConfigurationException e) {
+ throw new BuildException("Cannot create parser for the given "
+ + "configuration: " + e.getMessage(), e);
+ } catch (SAXException e) {
+ throw convertToBuildException(e);
+ }
+ }
+
+ /**
+ * Translate a SAXException into a BuildException
+ *
+ * @since Ant 1.5
+ */
+ private static BuildException convertToBuildException(SAXException e) {
+ Exception nested = e.getException();
+ if (nested != null) {
+ return new BuildException(nested);
+ } else {
+ return new BuildException(e);
+ }
+ }
+
+ /**
+ * Obtains the default builder factory if not already.
+ *
+ * @since Ant 1.6
+ */
+ private static synchronized
+ DocumentBuilderFactory getDocumentBuilderFactory()
+ throws BuildException {
+ if (builderFactory == null) {
+ try {
+ builderFactory = DocumentBuilderFactory.newInstance();
+ } catch (FactoryConfigurationError e) {
+ throw new BuildException("Document builder factory has not "
+ + "been configured correctly: "
+ + e.getMessage(), e);
+ }
+ }
+ return builderFactory;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/JavaEnvUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/JavaEnvUtils.java
new file mode 100644
index 00000000..df778208
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/JavaEnvUtils.java
@@ -0,0 +1,573 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Vector;
+
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+/**
+ * A set of helper methods related to locating executables or checking
+ * conditions of a given Java installation.
+ *
+ * @since Ant 1.5
+ */
+public final class JavaEnvUtils {
+
+ private JavaEnvUtils() {
+ }
+
+ /** Are we on a DOS-based system */
+ private static final boolean IS_DOS = Os.isFamily("dos");
+ /** Are we on Novell NetWare */
+ private static final boolean IS_NETWARE = Os.isName("netware");
+ /** Are we on AIX */
+ private static final boolean IS_AIX = Os.isName("aix");
+
+ /** shortcut for System.getProperty("java.home") */
+ private static final String JAVA_HOME = System.getProperty("java.home");
+
+ /** FileUtils instance for path normalization */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** Version of currently running VM. */
+ private static String javaVersion;
+
+ /** floating version of the JVM */
+ private static int javaVersionNumber;
+
+ /** Version constant for Java 1.0 */
+ public static final String JAVA_1_0 = "1.0";
+ /** Number Version constant for Java 1.0 */
+ public static final int VERSION_1_0 = 10;
+
+ /** Version constant for Java 1.1 */
+ public static final String JAVA_1_1 = "1.1";
+ /** Number Version constant for Java 1.1 */
+ public static final int VERSION_1_1 = 11;
+
+ /** Version constant for Java 1.2 */
+ public static final String JAVA_1_2 = "1.2";
+ /** Number Version constant for Java 1.2 */
+ public static final int VERSION_1_2 = 12;
+
+ /** Version constant for Java 1.3 */
+ public static final String JAVA_1_3 = "1.3";
+ /** Number Version constant for Java 1.3 */
+ public static final int VERSION_1_3 = 13;
+
+ /** Version constant for Java 1.4 */
+ public static final String JAVA_1_4 = "1.4";
+ /** Number Version constant for Java 1.4 */
+ public static final int VERSION_1_4 = 14;
+
+ /** Version constant for Java 1.5 */
+ public static final String JAVA_1_5 = "1.5";
+ /** Number Version constant for Java 1.5 */
+ public static final int VERSION_1_5 = 15;
+
+ /** Version constant for Java 1.6 */
+ public static final String JAVA_1_6 = "1.6";
+ /** Number Version constant for Java 1.6 */
+ public static final int VERSION_1_6 = 16;
+
+ /** Version constant for Java 1.7 */
+ public static final String JAVA_1_7 = "1.7";
+ /** Number Version constant for Java 1.7 */
+ public static final int VERSION_1_7 = 17;
+
+ /** Version constant for Java 1.8 */
+ public static final String JAVA_1_8 = "1.8";
+ /** Number Version constant for Java 1.8 */
+ public static final int VERSION_1_8 = 18;
+
+ /** Version constant for Java 1.9 */
+ public static final String JAVA_1_9 = "1.9";
+ /** Number Version constant for Java 1.9 */
+ public static final int VERSION_1_9 = 19;
+
+ /** Whether this is the Kaffe VM */
+ private static boolean kaffeDetected;
+
+ /** Wheter this is a GNU Classpath based VM */
+ private static boolean classpathDetected;
+
+ /** Whether this is the GNU VM (gcj/gij) */
+ private static boolean gijDetected;
+
+ /** Whether this is Apache Harmony */
+ private static boolean harmonyDetected;
+
+ /** array of packages in the runtime */
+ private static Vector<String> jrePackages;
+
+
+ static {
+
+ // Determine the Java version by looking at available classes
+ // java.net.Proxy was introduced in JDK 1.5
+ // java.lang.CharSequence was introduced in JDK 1.4
+ // java.lang.StrictMath was introduced in JDK 1.3
+ // java.lang.ThreadLocal was introduced in JDK 1.2
+ // java.lang.Void was introduced in JDK 1.1
+ // Count up version until a NoClassDefFoundError ends the try
+
+ try {
+ javaVersion = JAVA_1_0;
+ javaVersionNumber = VERSION_1_0;
+ Class.forName("java.lang.Void");
+ javaVersion = JAVA_1_1;
+ javaVersionNumber++;
+ Class.forName("java.lang.ThreadLocal");
+ javaVersion = JAVA_1_2;
+ javaVersionNumber++;
+ Class.forName("java.lang.StrictMath");
+ javaVersion = JAVA_1_3;
+ javaVersionNumber++;
+ Class.forName("java.lang.CharSequence");
+ javaVersion = JAVA_1_4;
+ javaVersionNumber++;
+ Class.forName("java.net.Proxy");
+ javaVersion = JAVA_1_5;
+ javaVersionNumber++;
+ Class.forName("java.net.CookieStore");
+ javaVersion = JAVA_1_6;
+ javaVersionNumber++;
+ Class.forName("java.nio.file.FileSystem");
+ javaVersion = JAVA_1_7;
+ javaVersionNumber++;
+ Class.forName("java.lang.reflect.Executable");
+ javaVersion = JAVA_1_8;
+ javaVersionNumber++;
+ checkForJava9();
+ javaVersion = JAVA_1_9;
+ javaVersionNumber++;
+ } catch (Throwable t) {
+ // swallow as we've hit the max class version that
+ // we have
+ }
+ kaffeDetected = false;
+ try {
+ Class.forName("kaffe.util.NotImplemented");
+ kaffeDetected = true;
+ } catch (Throwable t) {
+ // swallow as this simply doesn't seem to be Kaffe
+ }
+ classpathDetected = false;
+ try {
+ Class.forName("gnu.classpath.Configuration");
+ classpathDetected = true;
+ } catch (Throwable t) {
+ // swallow as this simply doesn't seem to be GNU classpath based.
+ }
+ gijDetected = false;
+ try {
+ Class.forName("gnu.gcj.Core");
+ gijDetected = true;
+ } catch (Throwable t) {
+ // swallow as this simply doesn't seem to be gcj/gij
+ }
+ harmonyDetected = false;
+ try {
+ Class.forName("org.apache.harmony.luni.util.Base64");
+ harmonyDetected = true;
+ } catch (Throwable t) {
+ // swallow as this simply doesn't seem to be Apache Harmony
+ }
+ }
+
+ /**
+ * Returns the version of Java this class is running under.
+ * @return the version of Java as a String, e.g. "1.6"
+ */
+ public static String getJavaVersion() {
+ return javaVersion;
+ }
+
+
+ /**
+ * Checks for a give Java 9 runtime.
+ * At the time of writing the actual version of the JDK was 1.9.0_b06.
+ * Searching for new classes gave no hits, so we need another aproach.
+ * Searching for changes (grep -r -i -n "@since 1.9" .) in the sources gave
+ * only one hit: a new constant in the class SourceVersion.
+ * So we have to check that ...
+ *
+ * @throws Exception if we can't load the class or don't find the new constant.
+ * This is the behavior when searching for new features on older versions.
+ * @since Ant 1.9.4
+ */
+ private static void checkForJava9() throws Exception {
+ Class<?> clazz = Class.forName("javax.lang.model.SourceVersion");
+ clazz.getDeclaredField("RELEASE_9");
+ }
+
+
+ /**
+ * Returns the version of Java this class is running under.
+ * This number can be used for comparisons; it will always be
+ * @return the version of Java as a number 10x the major/minor,
+ * e.g Java1.5 has a value of 15
+ */
+ public static int getJavaVersionNumber() {
+ return javaVersionNumber;
+ }
+
+ /**
+ * Compares the current Java version to the passed in String -
+ * assumes the argument is one of the constants defined in this
+ * class.
+ * Note that Ant now requires JDK 1.5+ so {@link #JAVA_1_0} through
+ * {@link #JAVA_1_4} need no longer be tested for.
+ * @param version the version to check against the current version.
+ * @return true if the version of Java is the same as the given version.
+ * @since Ant 1.5
+ */
+ public static boolean isJavaVersion(String version) {
+ return javaVersion.equals(version);
+ }
+
+ /**
+ * Compares the current Java version to the passed in String -
+ * assumes the argument is one of the constants defined in this
+ * class.
+ * Note that Ant now requires JDK 1.5+ so {@link #JAVA_1_0} through
+ * {@link #JAVA_1_4} need no longer be tested for.
+ * @param version the version to check against the current version.
+ * @return true if the version of Java is the same or higher than the
+ * given version.
+ * @since Ant 1.7
+ */
+ public static boolean isAtLeastJavaVersion(String version) {
+ return javaVersion.compareTo(version) >= 0;
+ }
+
+ /**
+ * Checks whether the current Java VM is Kaffe.
+ * @return true if the current Java VM is Kaffe.
+ * @since Ant 1.6.3
+ * @see <a href="http://www.kaffe.org/">http://www.kaffe.org/</a>
+ */
+ public static boolean isKaffe() {
+ return kaffeDetected;
+ }
+
+ /**
+ * Checks whether the current Java VM is GNU Classpath
+ * @since Ant 1.9.1
+ * @return true if the version of Java is GNU Classpath
+ */
+ public static boolean isClasspathBased() {
+ return classpathDetected;
+ }
+
+ /**
+ * Checks whether the current Java VM is the GNU interpreter gij
+ * or we are running in a gcj precompiled binary.
+ * @since Ant 1.8.2
+ * @return true if the current Java VM is gcj/gij.
+ */
+ public static boolean isGij() {
+ return gijDetected;
+ }
+
+ /**
+ * Checks whether the current VM is Apache Harmony.
+ * @since Ant 1.8.2
+ * @return true if the current VM is Apache Harmony.
+ */
+ public static boolean isApacheHarmony() {
+ return harmonyDetected;
+ }
+
+ /**
+ * Finds an executable that is part of a JRE installation based on
+ * the java.home system property.
+ *
+ * <p><code>java</code>, <code>keytool</code>,
+ * <code>policytool</code>, <code>orbd</code>, <code>rmid</code>,
+ * <code>rmiregistry</code>, <code>servertool</code> and
+ * <code>tnameserv</code> are JRE executables on Sun based
+ * JRE's.</p>
+ *
+ * <p>You typically find them in <code>JAVA_HOME/jre/bin</code> if
+ * <code>JAVA_HOME</code> points to your JDK installation. JDK
+ * &lt; 1.2 has them in the same directory as the JDK
+ * executables.</p>
+ * @param command the java executable to find.
+ * @return the path to the command.
+ * @since Ant 1.5
+ */
+ public static String getJreExecutable(String command) {
+ if (IS_NETWARE) {
+ // Extrapolating from:
+ // "NetWare may have a "java" in that directory, but 99% of
+ // the time, you don't want to execute it" -- Jeff Tulley
+ // <JTULLEY@novell.com>
+ return command;
+ }
+
+ File jExecutable = null;
+
+ if (IS_AIX) {
+ // On IBM's JDK 1.2 the directory layout is different, 1.3 follows
+ // Sun's layout.
+ jExecutable = findInDir(JAVA_HOME + "/sh", command);
+ }
+
+ if (jExecutable == null) {
+ jExecutable = findInDir(JAVA_HOME + "/bin", command);
+ }
+
+ if (jExecutable != null) {
+ return jExecutable.getAbsolutePath();
+ } else {
+ // Unfortunately on Windows java.home doesn't always refer
+ // to the correct location, so we need to fall back to
+ // assuming java is somewhere on the PATH.
+ return addExtension(command);
+ }
+ }
+
+ /**
+ * Finds an executable that is part of a JDK installation based on
+ * the java.home system property.
+ *
+ * <p>You typically find them in <code>JAVA_HOME/bin</code> if
+ * <code>JAVA_HOME</code> points to your JDK installation.</p>
+ * @param command the java executable to find.
+ * @return the path to the command.
+ * @since Ant 1.5
+ */
+ public static String getJdkExecutable(String command) {
+ if (IS_NETWARE) {
+ // Extrapolating from:
+ // "NetWare may have a "java" in that directory, but 99% of
+ // the time, you don't want to execute it" -- Jeff Tulley
+ // <JTULLEY@novell.com>
+ return command;
+ }
+
+ File jExecutable = null;
+
+ if (IS_AIX) {
+ // On IBM's JDK 1.2 the directory layout is different, 1.3 follows
+ // Sun's layout.
+ jExecutable = findInDir(JAVA_HOME + "/../sh", command);
+ }
+
+ if (jExecutable == null) {
+ jExecutable = findInDir(JAVA_HOME + "/../bin", command);
+ }
+
+ if (jExecutable != null) {
+ return jExecutable.getAbsolutePath();
+ } else {
+ // fall back to JRE bin directory, also catches JDK 1.0 and 1.1
+ // where java.home points to the root of the JDK and Mac OS X where
+ // the whole directory layout is different from Sun's
+ // and also catches JDK 1.9 (and probably later) which
+ // merged JDK and JRE dirs
+ return getJreExecutable(command);
+ }
+ }
+
+ /**
+ * Adds a system specific extension to the name of an executable.
+ *
+ * @since Ant 1.5
+ */
+ private static String addExtension(String command) {
+ // This is the most common extension case - exe for windows and OS/2,
+ // nothing for *nix.
+ return command + (IS_DOS ? ".exe" : "");
+ }
+
+ /**
+ * Look for an executable in a given directory.
+ *
+ * @return null if the executable cannot be found.
+ */
+ private static File findInDir(String dirName, String commandName) {
+ File dir = FILE_UTILS.normalize(dirName);
+ File executable = null;
+ if (dir.exists()) {
+ executable = new File(dir, addExtension(commandName));
+ if (!executable.exists()) {
+ executable = null;
+ }
+ }
+ return executable;
+ }
+
+ /**
+ * demand creation of the package list.
+ * When you add a new package, add a new test below.
+ */
+
+ private static void buildJrePackages() {
+ jrePackages = new Vector<String>();
+ switch(javaVersionNumber) {
+ case VERSION_1_9:
+ case VERSION_1_8:
+ case VERSION_1_7:
+ case VERSION_1_6:
+ case VERSION_1_5:
+ //In Java1.5, the apache stuff moved.
+ jrePackages.addElement("com.sun.org.apache");
+ //fall through.
+ case VERSION_1_4:
+ if (javaVersionNumber == VERSION_1_4) {
+ jrePackages.addElement("org.apache.crimson");
+ jrePackages.addElement("org.apache.xalan");
+ jrePackages.addElement("org.apache.xml");
+ jrePackages.addElement("org.apache.xpath");
+ }
+ jrePackages.addElement("org.ietf.jgss");
+ jrePackages.addElement("org.w3c.dom");
+ jrePackages.addElement("org.xml.sax");
+ // fall through
+ case VERSION_1_3:
+ jrePackages.addElement("org.omg");
+ jrePackages.addElement("com.sun.corba");
+ jrePackages.addElement("com.sun.jndi");
+ jrePackages.addElement("com.sun.media");
+ jrePackages.addElement("com.sun.naming");
+ jrePackages.addElement("com.sun.org.omg");
+ jrePackages.addElement("com.sun.rmi");
+ jrePackages.addElement("sunw.io");
+ jrePackages.addElement("sunw.util");
+ // fall through
+ case VERSION_1_2:
+ jrePackages.addElement("com.sun.java");
+ jrePackages.addElement("com.sun.image");
+ // are there any here that we forgot?
+ // fall through
+ case VERSION_1_1:
+ default:
+ //things like sun.reflection, sun.misc, sun.net
+ jrePackages.addElement("sun");
+ jrePackages.addElement("java");
+ jrePackages.addElement("javax");
+ break;
+ }
+ }
+
+ /**
+ * Testing helper method; kept here for unification of changes.
+ * @return a list of test classes depending on the java version.
+ */
+ public static Vector<String> getJrePackageTestCases() {
+ Vector<String> tests = new Vector<String>();
+ tests.addElement("java.lang.Object");
+ switch(javaVersionNumber) {
+ case VERSION_1_9:
+ case VERSION_1_8:
+ case VERSION_1_7:
+ case VERSION_1_6:
+ case VERSION_1_5:
+ tests.addElement(
+ "com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl ");
+ // Fall through
+ case VERSION_1_4:
+ tests.addElement("sun.audio.AudioPlayer");
+ if (javaVersionNumber == VERSION_1_4) {
+ // only for 1.4, not for higher versions which fall through
+ tests.addElement("org.apache.crimson.parser.ContentModel");
+ tests.addElement("org.apache.xalan.processor.ProcessorImport");
+ tests.addElement("org.apache.xml.utils.URI");
+ tests.addElement("org.apache.xpath.XPathFactory");
+ }
+ tests.addElement("org.ietf.jgss.Oid");
+ tests.addElement("org.w3c.dom.Attr");
+ tests.addElement("org.xml.sax.XMLReader");
+ // fall through
+ case VERSION_1_3:
+ tests.addElement("org.omg.CORBA.Any");
+ tests.addElement("com.sun.corba.se.internal.corba.AnyImpl");
+ tests.addElement("com.sun.jndi.ldap.LdapURL");
+ tests.addElement("com.sun.media.sound.Printer");
+ tests.addElement("com.sun.naming.internal.VersionHelper");
+ tests.addElement("com.sun.org.omg.CORBA.Initializer");
+ tests.addElement("sunw.io.Serializable");
+ tests.addElement("sunw.util.EventListener");
+ // fall through
+ case VERSION_1_2:
+ tests.addElement("javax.accessibility.Accessible");
+ tests.addElement("sun.misc.BASE64Encoder");
+ tests.addElement("com.sun.image.codec.jpeg.JPEGCodec");
+ // fall through
+ case VERSION_1_1:
+ default:
+ //things like sun.reflection, sun.misc, sun.net
+ tests.addElement("sun.reflect.SerializationConstructorAccessorImpl");
+ tests.addElement("sun.net.www.http.HttpClient");
+ tests.addElement("sun.audio.AudioPlayer");
+ break;
+ }
+ return tests;
+ }
+ /**
+ * get a vector of strings of packages built into
+ * that platforms runtime jar(s)
+ * @return list of packages.
+ */
+ public static Vector<String> getJrePackages() {
+ if (jrePackages == null) {
+ buildJrePackages();
+ }
+ return jrePackages;
+ }
+
+ /**
+ *
+ * Writes the command into a temporary DCL script and returns the
+ * corresponding File object.
+ * It is the job of the caller to delete the file on exit.
+ * @param cmd the command.
+ * @return the file containing the command.
+ * @throws IOException if there is an error writing to the file.
+ */
+ public static File createVmsJavaOptionFile(String[] cmd)
+ throws IOException {
+ File script = FILE_UTILS.createTempFile("ANT", ".JAVA_OPTS", null, false, true);
+ BufferedWriter out = null;
+ try {
+ out = new BufferedWriter(new FileWriter(script));
+ for (int i = 0; i < cmd.length; i++) {
+ out.write(cmd[i]);
+ out.newLine();
+ }
+ } finally {
+ FileUtils.close(out);
+ }
+ return script;
+ }
+
+ /**
+ * Return the value of ${java.home}
+ * @return the java home value.
+ */
+ public static String getJavaHome() {
+ return JAVA_HOME;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/KeepAliveInputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/KeepAliveInputStream.java
new file mode 100644
index 00000000..debde59a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/KeepAliveInputStream.java
@@ -0,0 +1,66 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Class that can be used to wrap <tt>System.in</tt>
+ * without getting anxious about any client closing the stream.
+ *
+ * <p>
+ * In code-language it means that it is not necessary to do:
+ * <pre>
+ * if (out != System.in) {
+ * in.close();
+ * }
+ * </pre>
+ *
+ * @since Ant 1.6
+ */
+public class KeepAliveInputStream extends FilterInputStream {
+
+ /**
+ * Constructor of KeepAliveInputStream.
+ *
+ * @param in an InputStream value, it should be standard input.
+ */
+ public KeepAliveInputStream(InputStream in) {
+ super(in);
+ }
+
+ /**
+ * This method does nothing.
+ * @throws IOException as we are overriding FilterInputStream.
+ */
+ public void close() throws IOException {
+ // do not close the stream
+ }
+
+ /**
+ * Convenience factory method that returns a non-closing
+ * InputStream around System.in.
+ *
+ * @since Ant 1.8.0
+ */
+ public static InputStream wrapSystemIn() {
+ return new KeepAliveInputStream(System.in);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/KeepAliveOutputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/KeepAliveOutputStream.java
new file mode 100644
index 00000000..27f3d7e4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/KeepAliveOutputStream.java
@@ -0,0 +1,83 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+/**
+ * Class that can be used to wrap <tt>System.out</tt> and <tt>System.err</tt>
+ * without getting anxious about any client closing the stream.
+ *
+ * <p>
+ * In code-language it means that it is not necessary to do:
+ * <pre>
+ * if (out != System.out &amp;&amp; out != System.err) {
+ * out.close();
+ * }
+ * </pre>
+ *
+ */
+public class KeepAliveOutputStream extends FilterOutputStream {
+
+ /**
+ * Constructor of KeepAliveOutputStream.
+ *
+ * @param out an OutputStream value, it should be standard output.
+ */
+ public KeepAliveOutputStream(OutputStream out) {
+ super(out);
+ }
+
+ /**
+ * This method does nothing.
+ * @throws IOException as we are overriding FilterOutputStream.
+ */
+ public void close() throws IOException {
+ // do not close the stream
+ }
+
+ /**
+ * Convenience factory method that returns a non-closing
+ * PrintStream around System.out.
+ *
+ * @since Ant 1.8.0
+ */
+ public static PrintStream wrapSystemOut() {
+ return wrap(System.out);
+ }
+
+ /**
+ * Convenience factory method that returns a non-closing
+ * PrintStream around System.err.
+ *
+ * @since Ant 1.8.0
+ */
+ public static PrintStream wrapSystemErr() {
+ return wrap(System.err);
+ }
+
+ /**
+ * @since Ant 1.8.0
+ */
+ private static PrintStream wrap(PrintStream ps) {
+ return new PrintStream(new KeepAliveOutputStream(ps));
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LayoutPreservingProperties.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LayoutPreservingProperties.java
new file mode 100644
index 00000000..aed6f371
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LayoutPreservingProperties.java
@@ -0,0 +1,775 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.PushbackReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Properties;
+
+/**
+ * <p>A Properties collection which preserves comments and whitespace
+ * present in the input stream from which it was loaded.</p>
+ * <p>The class defers the usual work of the <a href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a>
+ * class to there, but it also keeps track of the contents of the
+ * input stream from which it was loaded (if applicable), so that it can
+ * write out the properties in as close a form as possible to the input.</p>
+ * <p>If no changes occur to property values, the output should be the same
+ * as the input, except for the leading date stamp, as normal for a
+ * properties file. Properties added are appended to the file. Properties
+ * whose values are changed are changed in place. Properties that are
+ * removed are excised. If the <code>removeComments</code> flag is set,
+ * then the comments immediately preceding the property are also removed.</p>
+ * <p>If a second set of properties is loaded into an existing set, the
+ * lines of the second set are added to the end. Note however, that if a
+ * property already stored is present in a stream subsequently loaded, then
+ * that property is removed before the new value is set. For example,
+ * consider the file</p>
+ * <pre> # the first line
+ * alpha=one
+ *
+ * # the second line
+ * beta=two</pre>
+ * <p>This file is loaded, and then the following is also loaded into the
+ * same <code>LayoutPreservingProperties</code> object</p>
+ * <pre> # association
+ * beta=band
+ *
+ * # and finally
+ * gamma=rays</pre>
+ * <p>The resulting collection sequence of logical lines depends on whether
+ * or not <code>removeComments</code> was set at the time the second stream
+ * is loaded. If it is set, then the resulting list of lines is</p>
+ * <pre> # the first line
+ * alpha=one
+ *
+ * # association
+ * beta=band
+ *
+ * # and finally
+ * gamma=rays</pre>
+ * <p>If the flag is not set, then the comment "the second line" is retained,
+ * although the key-value pair <code>beta=two</code> is removed.</p>
+ */
+public class LayoutPreservingProperties extends Properties {
+ private String LS = StringUtils.LINE_SEP;
+
+ /**
+ * Logical lines have escaping and line continuation taken care
+ * of. Comments and blank lines are logical lines; they are not
+ * removed.
+ */
+ private ArrayList logicalLines = new ArrayList();
+
+ /**
+ * Position in the <code>logicalLines</code> list, keyed by property name.
+ */
+ private HashMap keyedPairLines = new HashMap();
+
+ /**
+ * Flag to indicate that, when we remove a property from the file, we
+ * also want to remove the comments that precede it.
+ */
+ private boolean removeComments;
+
+ /**
+ * Create a new, empty, Properties collection, with no defaults.
+ */
+ public LayoutPreservingProperties() {
+ super();
+ }
+
+ /**
+ * Create a new, empty, Properties collection, with the specified defaults.
+ * @param defaults the default property values
+ */
+ public LayoutPreservingProperties(final Properties defaults) {
+ super(defaults);
+ }
+
+ /**
+ * Returns <code>true</code> if comments are removed along with
+ * properties, or <code>false</code> otherwise. If
+ * <code>true</code>, then when a property is removed, the comment
+ * preceding it in the original file is removed also.
+ * @return <code>true</code> if leading comments are removed when
+ * a property is removed; <code>false</code> otherwise
+ */
+ public boolean isRemoveComments() {
+ return removeComments;
+ }
+
+ /**
+ * Sets the behaviour for comments accompanying properties that
+ * are being removed. If <code>true</code>, then when a property
+ * is removed, the comment preceding it in the original file is
+ * removed also.
+ * @param val <code>true</code> if leading comments are to be
+ * removed when a property is removed; <code>false</code>
+ * otherwise
+ */
+ public void setRemoveComments(final boolean val) {
+ removeComments = val;
+ }
+
+ @Override
+ public void load(final InputStream inStream) throws IOException {
+ final String s = readLines(inStream);
+ final byte[] ba = s.getBytes(ResourceUtils.ISO_8859_1);
+ final ByteArrayInputStream bais = new ByteArrayInputStream(ba);
+ super.load(bais);
+ }
+
+ @Override
+ public Object put(final Object key, final Object value) throws NullPointerException {
+ final Object obj = super.put(key, value);
+ // the above call will have failed if key or value are null
+ innerSetProperty(key.toString(), value.toString());
+ return obj;
+ }
+
+ @Override
+ public Object setProperty(final String key, final String value)
+ throws NullPointerException {
+ final Object obj = super.setProperty(key, value);
+ // the above call will have failed if key or value are null
+ innerSetProperty(key, value);
+ return obj;
+ }
+
+ /**
+ * Store a new key-value pair, or add a new one. The normal
+ * functionality is taken care of by the superclass in the call to
+ * {@link #setProperty}; this method takes care of this classes
+ * extensions.
+ * @param key the key of the property to be stored
+ * @param value the value to be stored
+ */
+ private void innerSetProperty(String key, String value) {
+ value = escapeValue(value);
+
+ if (keyedPairLines.containsKey(key)) {
+ final Integer i = (Integer) keyedPairLines.get(key);
+ final Pair p = (Pair) logicalLines.get(i.intValue());
+ p.setValue(value);
+ } else {
+ key = escapeName(key);
+ final Pair p = new Pair(key, value);
+ p.setNew(true);
+ keyedPairLines.put(key, new Integer(logicalLines.size()));
+ logicalLines.add(p);
+ }
+ }
+
+ @Override
+ public void clear() {
+ super.clear();
+ keyedPairLines.clear();
+ logicalLines.clear();
+ }
+
+ @Override
+ public Object remove(final Object key) {
+ final Object obj = super.remove(key);
+ final Integer i = (Integer) keyedPairLines.remove(key);
+ if (null != i) {
+ if (removeComments) {
+ removeCommentsEndingAt(i.intValue());
+ }
+ logicalLines.set(i.intValue(), null);
+ }
+ return obj;
+ }
+
+ @Override
+ public Object clone() {
+ final LayoutPreservingProperties dolly =
+ (LayoutPreservingProperties) super.clone();
+ dolly.keyedPairLines = (HashMap) this.keyedPairLines.clone();
+ dolly.logicalLines = (ArrayList) this.logicalLines.clone();
+ final int size = dolly.logicalLines.size();
+ for (int j = 0; j < size; j++) {
+ final LogicalLine line = (LogicalLine) dolly.logicalLines.get(j);
+ if (line instanceof Pair) {
+ final Pair p = (Pair) line;
+ dolly.logicalLines.set(j, p.clone());
+ }
+ // no reason to clone other lines are they are immutable
+ }
+ return dolly;
+ }
+
+ /**
+ * Echo the lines of the properties (including blanks and comments) to the
+ * stream.
+ * @param out the stream to write to
+ */
+ public void listLines(final PrintStream out) {
+ out.println("-- logical lines --");
+ final Iterator i = logicalLines.iterator();
+ while (i.hasNext()) {
+ final LogicalLine line = (LogicalLine) i.next();
+ if (line instanceof Blank) {
+ out.println("blank: \"" + line + "\"");
+ } else if (line instanceof Comment) {
+ out.println("comment: \"" + line + "\"");
+ } else if (line instanceof Pair) {
+ out.println("pair: \"" + line + "\"");
+ }
+ }
+ }
+
+ /**
+ * Save the properties to a file.
+ * @param dest the file to write to
+ */
+ public void saveAs(final File dest) throws IOException {
+ final FileOutputStream fos = new FileOutputStream(dest);
+ store(fos, null);
+ fos.close();
+ }
+
+ @Override
+ public void store(final OutputStream out, final String header) throws IOException {
+ final OutputStreamWriter osw = new OutputStreamWriter(out, ResourceUtils.ISO_8859_1);
+
+ int skipLines = 0;
+ final int totalLines = logicalLines.size();
+
+ if (header != null) {
+ osw.write("#" + header + LS);
+ if (totalLines > 0
+ && logicalLines.get(0) instanceof Comment
+ && header.equals(logicalLines.get(0).toString().substring(1))) {
+ skipLines = 1;
+ }
+ }
+
+ // we may be updatiung a file written by this class, replace
+ // the date comment instead of adding a new one and preserving
+ // the one written last time
+ if (totalLines > skipLines
+ && logicalLines.get(skipLines) instanceof Comment) {
+ try {
+ DateUtils.parseDateFromHeader(logicalLines
+ .get(skipLines)
+ .toString().substring(1));
+ skipLines++;
+ } catch (final java.text.ParseException pe) {
+ // not an existing date comment
+ }
+ }
+ osw.write("#" + DateUtils.getDateForHeader() + LS);
+
+ boolean writtenSep = false;
+ for (final Iterator i = logicalLines.subList(skipLines, totalLines).iterator();
+ i.hasNext();) {
+ final LogicalLine line = (LogicalLine) i.next();
+ if (line instanceof Pair) {
+ if (((Pair)line).isNew()) {
+ if (!writtenSep) {
+ osw.write(LS);
+ writtenSep = true;
+ }
+ }
+ osw.write(line.toString() + LS);
+ } else if (line != null) {
+ osw.write(line.toString() + LS);
+ }
+ }
+ osw.close();
+ }
+
+ /**
+ * Reads a properties file into an internally maintained
+ * collection of logical lines (possibly spanning physcial lines),
+ * which make up the comments, blank lines and properties of the
+ * file.
+ * @param is the stream from which to read the data
+ */
+ private String readLines(final InputStream is) throws IOException {
+ final InputStreamReader isr = new InputStreamReader(is, ResourceUtils.ISO_8859_1);
+ final PushbackReader pbr = new PushbackReader(isr, 1);
+
+ if (logicalLines.size() > 0) {
+ // we add a blank line for spacing
+ logicalLines.add(new Blank());
+ }
+
+ String s = readFirstLine(pbr);
+ final BufferedReader br = new BufferedReader(pbr);
+
+ boolean continuation = false;
+ boolean comment = false;
+ final StringBuffer fileBuffer = new StringBuffer();
+ final StringBuffer logicalLineBuffer = new StringBuffer();
+ while (s != null) {
+ fileBuffer.append(s).append(LS);
+
+ if (continuation) {
+ // put in the line feed that was removed
+ s = "\n" + s;
+ } else {
+ // could be a comment, if first non-whitespace is a # or !
+ comment = s.matches("^( |\t|\f)*(#|!).*");
+ }
+
+ // continuation if not a comment and the line ends is an
+ // odd number of backslashes
+ if (!comment) {
+ continuation = requiresContinuation(s);
+ }
+
+ logicalLineBuffer.append(s);
+
+ if (!continuation) {
+ LogicalLine line = null;
+ if (comment) {
+ line = new Comment(logicalLineBuffer.toString());
+ } else if (logicalLineBuffer.toString().trim().length() == 0) {
+ line = new Blank();
+ } else {
+ line = new Pair(logicalLineBuffer.toString());
+ final String key = unescape(((Pair)line).getName());
+ if (keyedPairLines.containsKey(key)) {
+ // this key is already present, so we remove it and add
+ // the new one
+ remove(key);
+ }
+ keyedPairLines.put(key, new Integer(logicalLines.size()));
+ }
+ logicalLines.add(line);
+ logicalLineBuffer.setLength(0);
+ }
+ s = br.readLine();
+ }
+ return fileBuffer.toString();
+ }
+
+ /**
+ * Reads the first line and determines the EOL-style of the file
+ * (relies on the style to be consistent, of course).
+ *
+ * <p>Sets LS as a side-effect.</p>
+ *
+ * @return the first line without any line separator, leaves the
+ * reader positioned after the first line separator
+ *
+ * @since Ant 1.8.2
+ */
+ private String readFirstLine(final PushbackReader r) throws IOException {
+ final StringBuffer sb = new StringBuffer(80);
+ int ch = r.read();
+ boolean hasCR = false;
+ // when reaching EOF before the first EOL, assume native line
+ // feeds
+ LS = StringUtils.LINE_SEP;
+
+ while (ch >= 0) {
+ if (hasCR && ch != '\n') {
+ // line feed is sole CR
+ r.unread(ch);
+ break;
+ }
+
+ if (ch == '\r') {
+ LS = "\r";
+ hasCR = true;
+ } else if (ch == '\n') {
+ LS = hasCR ? "\r\n" : "\n";
+ break;
+ } else {
+ sb.append((char) ch);
+ }
+ ch = r.read();
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns <code>true</code> if the line represented by
+ * <code>s</code> is to be continued on the next line of the file,
+ * or <code>false</code> otherwise.
+ * @param s the contents of the line to examine
+ * @return <code>true</code> if the line is to be continued,
+ * <code>false</code> otherwise
+ */
+ private boolean requiresContinuation(final String s) {
+ final char[] ca = s.toCharArray();
+ int i = ca.length - 1;
+ while (i > 0 && ca[i] == '\\') {
+ i--;
+ }
+ // trailing backslashes
+ final int tb = ca.length - i - 1;
+ return tb % 2 == 1;
+ }
+
+ /**
+ * Unescape the string according to the rules for a Properites
+ * file, as laid out in the docs for <a
+ * href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a>.
+ * @param s the string to unescape (coming from the source file)
+ * @return the unescaped string
+ */
+ private String unescape(final String s) {
+ /*
+ * The following combinations are converted:
+ * \n newline
+ * \r carraige return
+ * \f form feed
+ * \t tab
+ * \\ backslash
+ * \u0000 unicode character
+ * Any other slash is ignored, so
+ * \b becomes 'b'.
+ */
+
+ final char[] ch = new char[s.length() + 1];
+ s.getChars(0, s.length(), ch, 0);
+ ch[s.length()] = '\n';
+ final StringBuffer buffy = new StringBuffer(s.length());
+ for (int i = 0; i < ch.length; i++) {
+ char c = ch[i];
+ if (c == '\n') {
+ // we have hit out end-of-string marker
+ break;
+ } else if (c == '\\') {
+ // possibly an escape sequence
+ c = ch[++i];
+ if (c == 'n') {
+ buffy.append('\n');
+ } else if (c == 'r') {
+ buffy.append('\r');
+ } else if (c == 'f') {
+ buffy.append('\f');
+ } else if (c == 't') {
+ buffy.append('\t');
+ } else if (c == 'u') {
+ // handle unicode escapes
+ c = unescapeUnicode(ch, i+1);
+ i += 4;
+ buffy.append(c);
+ } else {
+ buffy.append(c);
+ }
+ } else {
+ buffy.append(c);
+ }
+ }
+ return buffy.toString();
+ }
+
+ /**
+ * Retrieve the unicode character whose code is listed at position
+ * <code>i</code> in the character array <code>ch</code>.
+ * @param ch the character array containing the unicode character code
+ * @return the character extracted
+ */
+ private char unescapeUnicode(final char[] ch, final int i) {
+ final String s = new String(ch, i, 4);
+ return (char) Integer.parseInt(s, 16);
+ }
+
+ /**
+ * Escape the string <code>s</code> according to the rules in the
+ * docs for <a
+ * href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a>.
+ * @param s the string to escape
+ * @return the escaped string
+ */
+ private String escapeValue(final String s) {
+ return escape(s, false);
+ }
+
+ /**
+ * Escape the string <code>s</code> according to the rules in the
+ * docs for <a
+ * href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a>.
+ * This method escapes all the whitespace, not just the stuff at
+ * the beginning.
+ * @param s the string to escape
+ * @return the escaped string
+ */
+ private String escapeName(final String s) {
+ return escape(s, true);
+ }
+
+ /**
+ * Escape the string <code>s</code> according to the rules in the
+ * docs for <a
+ * href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a>.
+ * @param s the string to escape
+ * @param escapeAllSpaces if <code>true</code> the method escapes
+ * all the spaces, if <code>false</code>, it escapes only the
+ * leading whitespace
+ * @return the escaped string
+ */
+ private String escape(final String s, final boolean escapeAllSpaces) {
+ if (s == null) {
+ return null;
+ }
+
+ final char[] ch = new char[s.length()];
+ s.getChars(0, s.length(), ch, 0);
+ final String forEscaping = "\t\f\r\n\\:=#!";
+ final String escaped = "tfrn\\:=#!";
+ final StringBuffer buffy = new StringBuffer(s.length());
+ boolean leadingSpace = true;
+ for (int i = 0; i < ch.length; i++) {
+ final char c = ch[i];
+ if (c == ' ') {
+ if (escapeAllSpaces || leadingSpace) {
+ buffy.append("\\");
+ }
+ } else {
+ leadingSpace = false;
+ }
+ final int p = forEscaping.indexOf(c);
+ if (p != -1) {
+ buffy.append("\\").append(escaped.substring(p,p+1));
+ } else if (c < 0x0020 || c > 0x007e) {
+ buffy.append(escapeUnicode(c));
+ } else {
+ buffy.append(c);
+ }
+ }
+ return buffy.toString();
+ }
+
+ /**
+ * Return the unicode escape sequence for a character, in the form
+ * \u005CuNNNN.
+ * @param ch the character to encode
+ * @return the unicode escape sequence
+ */
+ private String escapeUnicode(final char ch) {
+ return "\\" + UnicodeUtil.EscapeUnicode(ch);
+ }
+
+ /**
+ * Remove the comments in the leading up the {@link logicalLines}
+ * list leading up to line <code>pos</code>.
+ * @param pos the line number to which the comments lead
+ */
+ private void removeCommentsEndingAt(int pos) {
+ /* We want to remove comments preceding this position. Step
+ * back counting blank lines (call this range B1) until we hit
+ * something non-blank. If what we hit is not a comment, then
+ * exit. If what we hit is a comment, then step back counting
+ * comment lines (call this range C1). Nullify lines in C1 and
+ * B1.
+ */
+
+ final int end = pos - 1;
+
+ // step pos back until it hits something non-blank
+ for (pos = end; pos > 0; pos--) {
+ if (!(logicalLines.get(pos) instanceof Blank)) {
+ break;
+ }
+ }
+
+ // if the thing it hits is not a comment, then we have nothing
+ // to remove
+ if (!(logicalLines.get(pos) instanceof Comment)) {
+ return;
+ }
+
+ // step back until we hit the start of the comment
+ for (; pos >= 0; pos--) {
+ if (!(logicalLines.get(pos) instanceof Comment)) {
+ break;
+ }
+ }
+
+ // now we want to delete from pos+1 to end
+ for (pos++; pos <= end; pos++) {
+ logicalLines.set(pos, null);
+ }
+ }
+
+ /**
+ * A logical line of the properties input stream.
+ */
+ private abstract static class LogicalLine {
+ private String text;
+
+ public LogicalLine(final String text) {
+ this.text = text;
+ }
+
+ public void setText(final String text) {
+ this.text = text;
+ }
+
+ @Override
+ public String toString() {
+ return text;
+ }
+ }
+
+ /**
+ * A blank line of the input stream.
+ */
+ private static class Blank extends LogicalLine {
+ public Blank() {
+ super("");
+ }
+ }
+
+ /**
+ * A comment line of the input stream.
+ */
+ private class Comment extends LogicalLine {
+ public Comment(final String text) {
+ super(text);
+ }
+ }
+
+ /**
+ * A key-value pair from the input stream. This may span more than
+ * one physical line, but it is constitutes as a single logical
+ * line.
+ */
+ private static class Pair extends LogicalLine implements Cloneable {
+ private String name;
+ private String value;
+ private boolean added;
+
+ public Pair(final String text) {
+ super(text);
+ parsePair(text);
+ }
+
+ public Pair(final String name, final String value) {
+ this(name + "=" + value);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(final String value) {
+ this.value = value;
+ setText(name + "=" + value);
+ }
+
+ public boolean isNew() {
+ return added;
+ }
+
+ public void setNew(final boolean val) {
+ added = val;
+ }
+
+ @Override
+ public Object clone() {
+ Object dolly = null;
+ try {
+ dolly = super.clone();
+ } catch (final CloneNotSupportedException e) {
+ // should be fine
+ e.printStackTrace();
+ }
+ return dolly;
+ }
+
+ private void parsePair(final String text) {
+ // need to find first non-escaped '=', ':', '\t' or ' '.
+ final int pos = findFirstSeparator(text);
+ if (pos == -1) {
+ // trim leading whitespace only
+ name = text;
+ value = null;
+ } else {
+ name = text.substring(0, pos);
+ value = text.substring(pos+1, text.length());
+ }
+ // trim leading whitespace only
+ name = stripStart(name, " \t\f");
+ }
+
+ private String stripStart(final String s, final String chars) {
+ if (s == null) {
+ return null;
+ }
+
+ int i = 0;
+ for (;i < s.length(); i++) {
+ if (chars.indexOf(s.charAt(i)) == -1) {
+ break;
+ }
+ }
+ if (i == s.length()) {
+ return "";
+ }
+ return s.substring(i);
+ }
+
+ private int findFirstSeparator(String s) {
+ // Replace double backslashes with underscores so that they don't
+ // confuse us looking for '\t' or '\=', for example, but they also
+ // don't change the position of other characters
+ s = s.replaceAll("\\\\\\\\", "__");
+
+ // Replace single backslashes followed by separators, so we don't
+ // pick them up
+ s = s.replaceAll("\\\\=", "__");
+ s = s.replaceAll("\\\\:", "__");
+ s = s.replaceAll("\\\\ ", "__");
+ s = s.replaceAll("\\\\t", "__");
+
+ // Now only the unescaped separators are left
+ return indexOfAny(s, " :=\t");
+ }
+
+ private int indexOfAny(final String s, final String chars) {
+ if (s == null || chars == null) {
+ return -1;
+ }
+
+ int p = s.length() + 1;
+ for (int i = 0; i < chars.length(); i++) {
+ final int x = s.indexOf(chars.charAt(i));
+ if (x != -1 && x < p) {
+ p = x;
+ }
+ }
+ if (p == s.length() + 1) {
+ return -1;
+ }
+ return p;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LazyFileOutputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LazyFileOutputStream.java
new file mode 100644
index 00000000..7e5bf786
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LazyFileOutputStream.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.tools.ant.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Class that delays opening the output file until the first bytes
+ * shall be written or the method {@link #open open} has been invoked
+ * explicitly.
+ *
+ * @since Ant 1.6
+ */
+public class LazyFileOutputStream extends OutputStream {
+
+ private FileOutputStream fos;
+ private File file;
+ private boolean append;
+ private boolean alwaysCreate;
+ private boolean opened = false;
+ private boolean closed = false;
+
+ /**
+ * Creates a stream that will eventually write to the file with
+ * the given name and replace it.
+ * @param name the filename.
+ */
+ public LazyFileOutputStream(String name) {
+ this(name, false);
+ }
+
+ /**
+ * Creates a stream that will eventually write to the file with
+ * the given name and optionally append to instead of replacing
+ * it.
+ * @param name the filename.
+ * @param append if true append rather than replace.
+ */
+ public LazyFileOutputStream(String name, boolean append) {
+ this(new File(name), append);
+ }
+
+ /**
+ * Creates a stream that will eventually write to the file with
+ * the given name and replace it.
+ * @param f the file to create.
+ */
+ public LazyFileOutputStream(File f) {
+ this(f, false);
+ }
+
+ /**
+ * Creates a stream that will eventually write to the file with
+ * the given name and optionally append to instead of replacing
+ * it.
+ * @param file the file to create.
+ * @param append if true append rather than replace.
+ */
+ public LazyFileOutputStream(File file, boolean append) {
+ this(file, append, false);
+ }
+
+ /**
+ * Creates a stream that will eventually write to the file with
+ * the given name, optionally append to instead of replacing
+ * it, and optionally always create a file (even if zero length).
+ * @param file the file to create.
+ * @param append if true append rather than replace.
+ * @param alwaysCreate if true create the file even if nothing to write.
+ */
+ public LazyFileOutputStream(File file, boolean append,
+ boolean alwaysCreate) {
+ this.file = file;
+ this.append = append;
+ this.alwaysCreate = alwaysCreate;
+ }
+
+ /**
+ * Explicitly open the file for writing.
+ *
+ * <p>Returns silently if the file has already been opened.</p>
+ * @throws IOException if there is an error.
+ */
+ public void open() throws IOException {
+ ensureOpened();
+ }
+
+ /**
+ * Close the file.
+ * @throws IOException if there is an error.
+ */
+ public synchronized void close() throws IOException {
+ if (alwaysCreate && !closed) {
+ ensureOpened();
+ }
+ if (opened) {
+ fos.close();
+ }
+ closed = true;
+ }
+
+ /**
+ * Delegates to the three-arg version.
+ * @param b the bytearray to write.
+ * @throws IOException if there is a problem.
+ */
+ public void write(byte[] b) throws IOException {
+ write(b, 0, b.length);
+ }
+
+ /**
+ * Write part of a byte array.
+ * @param b the byte array.
+ * @param offset write from this index.
+ * @param len the number of bytes to write.
+ * @throws IOException if there is a problem.
+ */
+ public synchronized void write(byte[] b, int offset, int len)
+ throws IOException {
+ ensureOpened();
+ fos.write(b, offset, len);
+ }
+
+ /**
+ * Write a byte.
+ * @param b the byte to write.
+ * @throws IOException if there is a problem.
+ */
+ public synchronized void write(int b) throws IOException {
+ ensureOpened();
+ fos.write(b);
+ }
+
+ private synchronized void ensureOpened() throws IOException {
+ if (closed) {
+ throw new IOException(file + " has already been closed.");
+ }
+
+ if (!opened) {
+ fos = new FileOutputStream(file.getAbsolutePath(), append);
+ opened = true;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LazyHashtable.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LazyHashtable.java
new file mode 100644
index 00000000..1df953cf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LazyHashtable.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.tools.ant.util;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+/** Hashtable implementation that allows delayed construction
+ * of expensive objects
+ *
+ * All operations that need access to the full list of objects
+ * will call initAll() first. Get and put are cheap.
+ *
+ * @since Ant 1.6
+ */
+public class LazyHashtable extends Hashtable {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected boolean initAllDone = false;
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /** No arg constructor. */
+ public LazyHashtable() {
+ super();
+ }
+
+ /** Used to be part of init. It must be done once - but
+ * we delay it until we do need _all_ tasks. Otherwise we
+ * just get the tasks that we need, and avoid costly init.
+ */
+ protected void initAll() {
+ if (initAllDone) {
+ return;
+ }
+ initAllDone = true;
+ }
+
+
+ /**
+ * Get a enumeration over the elements.
+ * @return an enumeration.
+ */
+ public Enumeration elements() {
+ initAll();
+ return super.elements();
+ }
+
+ /**
+ * Check if the table is empty.
+ * @return true if it is.
+ */
+ public boolean isEmpty() {
+ initAll();
+ return super.isEmpty();
+ }
+
+ /**
+ * Get the size of the table.
+ * @return the size.
+ */
+ public int size() {
+ initAll();
+ return super.size();
+ }
+
+ /**
+ * Check if the table contains a particular value.
+ * @param value the value to look for.
+ * @return true if the table contains the value.
+ */
+ public boolean contains(Object value) {
+ initAll();
+ return super.contains(value);
+ }
+
+ /**
+ * Check if the table contains a particular key.
+ * @param value the key to look for.
+ * @return true if the table contains key.
+ */
+ public boolean containsKey(Object value) {
+ initAll();
+ return super.containsKey(value);
+ }
+
+ /**
+ * Delegates to {@link #contains contains}.
+ * @param value the value to look for.
+ * @return true if the table contains the value.
+ */
+ public boolean containsValue(Object value) {
+ return contains(value);
+ }
+
+ /**
+ * Get an enumeration over the keys.
+ * @return an enumeration.
+ */
+ public Enumeration keys() {
+ initAll();
+ return super.keys();
+ }
+
+ // TODO Unfortunately JDK1.2 adds entrySet(), keySet(), values() -
+ // implementing this requires a small hack, we can add it later.
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java
new file mode 100644
index 00000000..00819128
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java
@@ -0,0 +1,161 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+
+/**
+ * Special <code>PipedInputStream</code> that will not die
+ * when the writing <code>Thread</code> is no longer alive.
+ * @since Ant 1.6.2
+ */
+public class LeadPipeInputStream extends PipedInputStream {
+ private static final int BYTE_MASK = 0xFF;
+ private ProjectComponent managingPc;
+
+ /**
+ * Construct a new <code>LeadPipeInputStream</code>.
+ */
+ public LeadPipeInputStream() {
+ super();
+ }
+
+ /**
+ * Construct a new <code>LeadPipeInputStream</code>
+ * with the specified buffer size.
+ * @param size the size of the circular buffer.
+ */
+ public LeadPipeInputStream(int size) {
+ super();
+ setBufferSize(size);
+ }
+
+ /**
+ * Construct a new <code>LeadPipeInputStream</code> to pull
+ * from the specified <code>PipedOutputStream</code>.
+ * @param src the <code>PipedOutputStream</code> source.
+ * @throws IOException if unable to construct the stream.
+ */
+ public LeadPipeInputStream(PipedOutputStream src) throws IOException {
+ super(src);
+ }
+
+ /**
+ * Construct a new <code>LeadPipeInputStream</code> to pull
+ * from the specified <code>PipedOutputStream</code>, using a
+ * circular buffer of the specified size.
+ * @param src the <code>PipedOutputStream</code> source.
+ * @param size the size of the circular buffer.
+ * @throws IOException if there is an error.
+ */
+ public LeadPipeInputStream(PipedOutputStream src, int size) throws IOException {
+ super(src);
+ setBufferSize(size);
+ }
+
+ //inherit doc
+ /**
+ * Read a byte from the stream.
+ * @return the byte (0 to 255) or -1 if there are no more.
+ * @throws IOException if there is an error.
+ */
+ public synchronized int read() throws IOException {
+ int result = -1;
+ try {
+ result = super.read();
+ } catch (IOException eyeOhEx) {
+ String msg = eyeOhEx.getMessage();
+ if ("write end dead".equalsIgnoreCase(msg)
+ || "pipe broken".equalsIgnoreCase(msg)) {
+ if (super.in > 0 && super.out < super.buffer.length
+ && super.out > super.in) {
+ result = super.buffer[super.out++] & BYTE_MASK;
+ }
+ } else {
+ log("error at LeadPipeInputStream.read(): " + msg,
+ Project.MSG_INFO);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Set the size of the buffer.
+ * @param size the new buffer size. Ignored if &lt;= current size.
+ */
+ public synchronized void setBufferSize(int size) {
+ if (size > buffer.length) {
+ byte[] newBuffer = new byte[size];
+ if (in >= 0) {
+ if (in > out) {
+ System.arraycopy(buffer, out, newBuffer, out, in - out);
+ } else {
+ int outlen = buffer.length - out;
+ System.arraycopy(buffer, out, newBuffer, 0, outlen);
+ System.arraycopy(buffer, 0, newBuffer, outlen, in);
+ in += outlen;
+ out = 0;
+ }
+ }
+ buffer = newBuffer;
+ }
+ }
+
+ /**
+ * Set a managing <code>Task</code> for
+ * this <code>LeadPipeInputStream</code>.
+ * @param task the managing <code>Task</code>.
+ */
+ public void setManagingTask(Task task) {
+ setManagingComponent(task);
+ }
+
+ /**
+ * Set a managing <code>ProjectComponent</code> for
+ * this <code>LeadPipeInputStream</code>.
+ * @param pc the managing <code>ProjectComponent</code>.
+ */
+ public void setManagingComponent(ProjectComponent pc) {
+ this.managingPc = pc;
+ }
+
+ /**
+ * Log a message with the specified logging level.
+ * @param message the <code>String</code> message.
+ * @param loglevel the <code>int</code> logging level.
+ */
+ public void log(String message, int loglevel) {
+ if (managingPc != null) {
+ managingPc.log(message, loglevel);
+ } else {
+ if (loglevel > Project.MSG_WARN) {
+ System.out.println(message);
+ } else {
+ System.err.println(message);
+ }
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LineOrientedOutputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LineOrientedOutputStream.java
new file mode 100644
index 00000000..073a89f1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LineOrientedOutputStream.java
@@ -0,0 +1,158 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Invokes {@link #processLine processLine} whenever a full line has
+ * been written to this stream.
+ *
+ * <p>Tries to be smart about line separators.</p>
+ */
+public abstract class LineOrientedOutputStream extends OutputStream {
+
+ /** Initial buffer size. */
+ private static final int INITIAL_SIZE = 132;
+
+ /** Carriage return */
+ private static final int CR = 0x0d;
+
+ /** Linefeed */
+ private static final int LF = 0x0a;
+
+ private ByteArrayOutputStream buffer
+ = new ByteArrayOutputStream(INITIAL_SIZE);
+ private boolean skip = false;
+
+ /**
+ * Write the data to the buffer and flush the buffer, if a line
+ * separator is detected.
+ *
+ * @param cc data to log (byte).
+ * @throws IOException if there is an error.
+ */
+ @Override
+ public final void write(int cc) throws IOException {
+ final byte c = (byte) cc;
+ if ((c == LF) || (c == CR)) {
+ if (!skip) {
+ processBuffer();
+ }
+ } else {
+ buffer.write(cc);
+ }
+ skip = (c == CR);
+ }
+
+ /**
+ * Flush this log stream
+ * @throws IOException if there is an error.
+ */
+ @Override
+ public void flush() throws IOException {
+ }
+
+ /**
+ * Converts the buffer to a byte[] and sends it to
+ * <code>processLine</code>
+ * @throws IOException if there is an error.
+ */
+ protected void processBuffer() throws IOException {
+ try {
+ processLine(buffer.toByteArray());
+ } finally {
+ buffer.reset();
+ }
+ }
+
+ /**
+ * Processes a line.
+ *
+ * @param line the line to log.
+ * @throws IOException if there is an error.
+ */
+ protected abstract void processLine(String line) throws IOException;
+
+ /**
+ * Processes a line.
+ *
+ * <p>This implementations invokes the string-arg version
+ * converting the byte array using the default encoding.
+ * Subclasses are encouraged to override this method (and provide
+ * a dummy implementation of the String-arg version) so they don't
+ * interfere with the encoding of the underlying stream.</p>
+ *
+ * @param line the line to log.
+ * @throws IOException if there is an error.
+ * @since Ant 1.8.3
+ */
+ protected void processLine(byte[] line) throws IOException {
+ processLine(new String(line));
+ }
+
+ /**
+ * Writes all remaining
+ * @throws IOException if there is an error.
+ */
+ @Override
+ public void close() throws IOException {
+ if (buffer.size() > 0) {
+ processBuffer();
+ }
+ super.close();
+ }
+
+ /**
+ * Write a block of characters to the output stream
+ *
+ * @param b the array containing the data
+ * @param off the offset into the array where data starts
+ * @param len the length of block
+ *
+ * @throws IOException if the data cannot be written into the stream.
+ */
+ @Override
+ public final void write(byte[] b, int off, int len) throws IOException {
+ // find the line breaks and pass other chars through in blocks
+ int offset = off;
+ int blockStartOffset = offset;
+ int remaining = len;
+ while (remaining > 0) {
+ while (remaining > 0 && b[offset] != LF && b[offset] != CR) {
+ offset++;
+ remaining--;
+ }
+ // either end of buffer or a line separator char
+ int blockLength = offset - blockStartOffset;
+ if (blockLength > 0) {
+ buffer.write(b, blockStartOffset, blockLength);
+ }
+ while (remaining > 0 && (b[offset] == LF || b[offset] == CR)) {
+ write(b[offset]);
+ offset++;
+ remaining--;
+ }
+ blockStartOffset = offset;
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LineOrientedOutputStreamRedirector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LineOrientedOutputStreamRedirector.java
new file mode 100644
index 00000000..48bad5cc
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LineOrientedOutputStreamRedirector.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.tools.ant.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Output stream which buffer and redirect a stream line by line.
+ * <p>
+ * If the source stream doesn't end with a end of line, one will be added. This
+ * is particularly useful in combination with the OutputStreamFunneler so each
+ * funneled stream get its line.
+ *
+ * @since Ant 1.8.3
+ */
+public class LineOrientedOutputStreamRedirector
+ extends LineOrientedOutputStream {
+
+ private OutputStream stream;
+
+ // these should be in the ASCII range and hopefully are single bytes
+ // (for LF and CR respectively) for any encoding thrown at this class
+ private static final byte[] EOL =
+ System.getProperty("line.separator").getBytes();
+
+ public LineOrientedOutputStreamRedirector(OutputStream stream) {
+ this.stream = stream;
+ }
+
+ @Override
+ protected void processLine(byte[] b) throws IOException {
+ stream.write(b);
+ stream.write(EOL);
+ }
+
+ @Override
+ protected void processLine(String line) throws IOException {
+ stream.write((line + System.getProperty("line.separator")).getBytes());
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ stream.close();
+ }
+
+ @Override
+ public void flush() throws IOException {
+ super.flush();
+ stream.flush();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LineTokenizer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LineTokenizer.java
new file mode 100644
index 00000000..778606dd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LineTokenizer.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.tools.ant.util;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * class to tokenize the input as lines separated
+ * by \r (mac style), \r\n (dos/windows style) or \n (unix style)
+ * @since Ant 1.6
+ */
+public class LineTokenizer extends ProjectComponent
+ implements Tokenizer {
+ private static final int NOT_A_CHAR = -2;
+ private String lineEnd = "";
+ private int pushed = NOT_A_CHAR;
+ private boolean includeDelims = false;
+
+ /**
+ * attribute includedelims - whether to include
+ * the line ending with the line, or to return
+ * it in the posttoken
+ * default false
+ * @param includeDelims if true include /r and /n in the line
+ */
+
+ public void setIncludeDelims(boolean includeDelims) {
+ this.includeDelims = includeDelims;
+ }
+
+ /**
+ * get the next line from the input
+ *
+ * @param in the input reader
+ * @return the line excluding /r or /n, unless includedelims is set
+ * @exception IOException if an error occurs reading
+ */
+ public String getToken(Reader in) throws IOException {
+ int ch = -1;
+ if (pushed != NOT_A_CHAR) {
+ ch = pushed;
+ pushed = NOT_A_CHAR;
+ } else {
+ ch = in.read();
+ }
+ if (ch == -1) {
+ return null;
+ }
+
+ lineEnd = "";
+ StringBuffer line = new StringBuffer();
+
+ int state = 0;
+ while (ch != -1) {
+ if (state == 0) {
+ if (ch == '\r') {
+ state = 1;
+ } else if (ch == '\n') {
+ lineEnd = "\n";
+ break;
+ } else {
+ line.append((char) ch);
+ }
+ } else {
+ state = 0;
+ if (ch == '\n') {
+ lineEnd = "\r\n";
+ } else {
+ pushed = ch;
+ lineEnd = "\r";
+ }
+ break;
+ }
+ ch = in.read();
+ }
+ if (ch == -1 && state == 1) {
+ lineEnd = "\r";
+ }
+
+ if (includeDelims) {
+ line.append(lineEnd);
+ }
+ return line.toString();
+ }
+
+ /**
+ * @return the line ending character(s) for the current line
+ */
+ public String getPostToken() {
+ if (includeDelims) {
+ return "";
+ }
+ return lineEnd;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LinkedHashtable.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LinkedHashtable.java
new file mode 100644
index 00000000..73fc83c6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LinkedHashtable.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.tools.ant.util;
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Subclass of Hashtable that wraps a LinkedHashMap to provide
+ * predictable iteration order.
+ *
+ * <p>This is not a general purpose class but has been written because
+ * the protected members of {@link org.apache.tools.ant.taskdefs.Copy
+ * Copy} prohibited later revisions from using a more predictable
+ * collection.</p>
+ *
+ * <p>Methods are synchronized to keep Hashtable's contract.</p>
+ *
+ * @since Ant 1.8.2
+ */
+public class LinkedHashtable<K, V> extends Hashtable<K, V> {
+ private static final long serialVersionUID = 1L;
+
+ private final LinkedHashMap<K, V> map;
+
+ public LinkedHashtable() {
+ map = new LinkedHashMap<K, V>();
+ }
+
+ public LinkedHashtable(int initialCapacity) {
+ map = new LinkedHashMap<K, V>(initialCapacity);
+ }
+
+ public LinkedHashtable(int initialCapacity, float loadFactor) {
+ map = new LinkedHashMap<K, V>(initialCapacity, loadFactor);
+ }
+
+ public LinkedHashtable(Map<K, V> m) {
+ map = new LinkedHashMap<K, V>(m);
+ }
+
+ public synchronized void clear() {
+ map.clear();
+ }
+
+ public boolean contains(Object value) {
+ return containsKey(value);
+ }
+
+ public synchronized boolean containsKey(Object value) {
+ return map.containsKey(value);
+ }
+
+ public synchronized boolean containsValue(Object value) {
+ return map.containsValue(value);
+ }
+
+ public Enumeration<V> elements() {
+ return CollectionUtils.asEnumeration(values().iterator());
+ }
+
+ public synchronized Set<Map.Entry<K, V>> entrySet() {
+ return map.entrySet();
+ }
+
+ public synchronized boolean equals(Object o) {
+ return map.equals(o);
+ }
+
+ public synchronized V get(Object k) {
+ return map.get(k);
+ }
+
+ public synchronized int hashCode() {
+ return map.hashCode();
+ }
+
+ public synchronized boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ public Enumeration<K> keys() {
+ return CollectionUtils.asEnumeration(keySet().iterator());
+ }
+
+ public synchronized Set<K> keySet() {
+ return map.keySet();
+ }
+
+ public synchronized V put(K k, V v) {
+ return map.put(k, v);
+ }
+
+ public synchronized void putAll(Map<? extends K, ? extends V> m) {
+ map.putAll(m);
+ }
+
+ public synchronized V remove(Object k) {
+ return map.remove(k);
+ }
+
+ public synchronized int size() {
+ return map.size();
+ }
+
+ public synchronized String toString() {
+ return map.toString();
+ }
+
+ public synchronized Collection<V> values() {
+ return map.values();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LoaderUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LoaderUtils.java
new file mode 100644
index 00000000..e0514f65
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LoaderUtils.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.tools.ant.util;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.launch.Locator;
+
+// CheckStyle:HideUtilityClassConstructorCheck OFF - bc
+
+/**
+ * ClassLoader utility methods
+ *
+ */
+public class LoaderUtils {
+
+ /** Utilities used for file operations */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Set the context classloader
+ *
+ * @param loader the ClassLoader to be used as the context class loader
+ * on the current thread.
+ */
+ public static void setContextClassLoader(ClassLoader loader) {
+ Thread currentThread = Thread.currentThread();
+ currentThread.setContextClassLoader(loader);
+ }
+
+
+ /**
+ * JDK1.1 compatible access to set the context class loader.
+ *
+ * @return the ClassLoader instance being used as the context
+ * classloader on the current thread. Returns null on JDK 1.1
+ */
+ public static ClassLoader getContextClassLoader() {
+ Thread currentThread = Thread.currentThread();
+ return currentThread.getContextClassLoader();
+ }
+
+ /**
+ * Indicates if the context class loader methods are available
+ *
+ * @return true if the get and set methods dealing with the context
+ * classloader are available.
+ */
+ public static boolean isContextLoaderAvailable() {
+ return true;
+ }
+
+ /**
+ * Normalize a source location
+ *
+ * @param source the source location to be normalized.
+ *
+ * @return the normalized source location.
+ */
+ private static File normalizeSource(File source) {
+ if (source != null) {
+ try {
+ source = FILE_UTILS.normalize(source.getAbsolutePath());
+ } catch (BuildException e) {
+ // relative path
+ }
+ }
+
+ return source;
+ }
+
+ /**
+ * Find the directory or jar file the class has been loaded from.
+ *
+ * @param c the class whose location is required.
+ * @return the file or jar with the class or null if we cannot
+ * determine the location.
+ *
+ * @since Ant 1.6
+ */
+ public static File getClassSource(Class c) {
+ return normalizeSource(Locator.getClassSource(c));
+ }
+
+ /**
+ * Find the directory or a give resource has been loaded from.
+ *
+ * @param c the classloader to be consulted for the source
+ * @param resource the resource whose location is required.
+ *
+ * @return the file with the resource source or null if
+ * we cannot determine the location.
+ *
+ * @since Ant 1.6
+ */
+ public static File getResourceSource(ClassLoader c, String resource) {
+ if (c == null) {
+ c = LoaderUtils.class.getClassLoader();
+ }
+ return normalizeSource(Locator.getResourceSource(c, resource));
+ }
+
+ /**
+ * Return the resource name of a class name.
+ * @param className the name of the class to convert.
+ * @return the corresponding resource name.
+ * @since Ant 1.7.0.
+ */
+ public static String classNameToResource(String className) {
+ return className.replace('.', '/') + ".class";
+ }
+
+ /**
+ * Check if a classloader has a classname resource.
+ * @param loader the classloader to look it.
+ * @param className the name of the class to look for.
+ * @return true if the classexists, false otherwise
+ * @since Ant 1.7.0.
+ */
+ public static boolean classExists(ClassLoader loader, String className) {
+ return loader.getResource(classNameToResource(className)) != null;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/MergingMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/MergingMapper.java
new file mode 100644
index 00000000..7f158dbf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/MergingMapper.java
@@ -0,0 +1,67 @@
+/*
+ * 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.tools.ant.util;
+
+/**
+ * Implementation of FileNameMapper that always returns the same
+ * target file name.
+ *
+ * <p>This is the default FileNameMapper for the archiving tasks and
+ * uptodate.</p>
+ *
+ */
+public class MergingMapper implements FileNameMapper {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected String[] mergedFile = null;
+ // CheckStyle:VisibilityModifier ON
+
+ public MergingMapper() {}
+
+ /**
+ * @since Ant 1.8.0
+ */
+ public MergingMapper(String to) {
+ setTo(to);
+ }
+
+ /**
+ * Ignored.
+ * @param from ignored.
+ */
+ public void setFrom(String from) {
+ }
+
+ /**
+ * Sets the name of the merged file.
+ * @param to the name of the merged file.
+ */
+ public void setTo(String to) {
+ mergedFile = new String[] {to};
+ }
+
+ /**
+ * Returns an one-element array containing the file name set via setTo.
+ * @param sourceFileName ignored.
+ * @return a one-element array containing the merged filename.
+ */
+ public String[] mapFileName(String sourceFileName) {
+ return mergedFile;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/OutputStreamFunneler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/OutputStreamFunneler.java
new file mode 100644
index 00000000..6694c3f0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/OutputStreamFunneler.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.tools.ant.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Manages a set of <code>OutputStream</code>s to
+ * write to a single underlying stream, which is
+ * closed only when the last &quot;funnel&quot;
+ * has been closed.
+ */
+public class OutputStreamFunneler {
+
+ /**
+ * Default timeout.
+ * @see #setTimeout(long)
+ */
+ public static final long DEFAULT_TIMEOUT_MILLIS = 1000;
+
+ private final class Funnel extends OutputStream {
+ private boolean closed = false;
+
+ private Funnel() {
+ synchronized (OutputStreamFunneler.this) {
+ ++count;
+ }
+ }
+
+ public void flush() throws IOException {
+ synchronized (OutputStreamFunneler.this) {
+ dieIfClosed();
+ out.flush();
+ }
+ }
+
+ public void write(int b) throws IOException {
+ synchronized (OutputStreamFunneler.this) {
+ dieIfClosed();
+ out.write(b);
+ }
+ }
+
+ public void write(byte[] b) throws IOException {
+ synchronized (OutputStreamFunneler.this) {
+ dieIfClosed();
+ out.write(b);
+ }
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ synchronized (OutputStreamFunneler.this) {
+ dieIfClosed();
+ out.write(b, off, len);
+ }
+ }
+
+ public void close() throws IOException {
+ release(this);
+ }
+ }
+
+ private OutputStream out;
+ private int count = 0;
+ private boolean closed;
+ private long timeoutMillis;
+
+ /**
+ * Create a new <code>OutputStreamFunneler</code> for
+ * the specified <code>OutputStream</code>.
+ * @param out <code>OutputStream</code>.
+ */
+ public OutputStreamFunneler(OutputStream out) {
+ this(out, DEFAULT_TIMEOUT_MILLIS);
+ }
+
+ /**
+ * Create a new <code>OutputStreamFunneler</code> for
+ * the specified <code>OutputStream</code>, with the
+ * specified timeout value.
+ * @param out <code>OutputStream</code>.
+ * @param timeoutMillis <code>long</code>.
+ * @see #setTimeout(long)
+ */
+ public OutputStreamFunneler(OutputStream out, long timeoutMillis) {
+ if (out == null) {
+ throw new IllegalArgumentException(
+ "OutputStreamFunneler.<init>: out == null");
+ }
+ this.out = out;
+ this.closed = false; //as far as we know
+ setTimeout(timeoutMillis);
+ }
+
+ /**
+ * Set the timeout for this <code>OutputStreamFunneler</code>.
+ * This is the maximum time that may elapse between the closure
+ * of the last &quot;funnel&quot; and the next call to
+ * <code>getOutputStream()</code> without closing the
+ * underlying stream.
+ * @param timeoutMillis <code>long</code> timeout value.
+ */
+ public synchronized void setTimeout(long timeoutMillis) {
+ this.timeoutMillis = timeoutMillis;
+ }
+
+ /**
+ * Get a &quot;funnel&quot; <code>OutputStream</code> instance to
+ * write to this <code>OutputStreamFunneler</code>'s underlying
+ * <code>OutputStream</code>.
+ * @return <code>OutputStream</code>.
+ * @throws IOException if unable to create the funnel.
+ */
+ public synchronized OutputStream getFunnelInstance()
+ throws IOException {
+ dieIfClosed();
+ try {
+ return new Funnel();
+ } finally {
+ notifyAll();
+ }
+ }
+
+ private synchronized void release(Funnel funnel) throws IOException {
+ //ignore release of an already-closed funnel
+ if (!funnel.closed) {
+ try {
+ if (timeoutMillis > 0) {
+ try {
+ wait(timeoutMillis);
+ } catch (InterruptedException eyeEx) {
+ //ignore
+ }
+ }
+ if (--count == 0) {
+ close();
+ }
+ } finally {
+ funnel.closed = true;
+ }
+ }
+ }
+
+ private synchronized void close() throws IOException {
+ try {
+ dieIfClosed();
+ out.close();
+ } finally {
+ closed = true;
+ }
+ }
+
+ private synchronized void dieIfClosed() throws IOException {
+ if (closed) {
+ throw new IOException("The funneled OutputStream has been closed.");
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/PackageNameMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/PackageNameMapper.java
new file mode 100644
index 00000000..30256670
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/PackageNameMapper.java
@@ -0,0 +1,49 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.File;
+
+/**
+ * Maps directory name matches into a dotted package name. This is
+ * useful for matching JUnit test cases against their XML formatter
+ * results.
+ * <pre>
+ * &lt;mapper classname="org.apache.tools.ant.util.PackageNameMapper"
+ * from="*Test.java" to="${test.data.dir}/TEST-*Test.xml"/&gt;
+ * </pre>
+ *
+ */
+public class PackageNameMapper extends GlobPatternMapper {
+ /**
+ * Returns the part of the given string that matches the * in the
+ * &quot;from&quot; pattern replacing file separators with dots
+ *
+ *@param name Source filename
+ *@return Replaced variable part
+ */
+ protected String extractVariablePart(String name) {
+ String var = name.substring(prefixLength,
+ name.length() - postfixLength);
+ if (getHandleDirSep()) {
+ var = var.replace('/', '.').replace('\\', '.');
+ }
+ return var.replace(File.separatorChar, '.');
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ProcessUtil.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ProcessUtil.java
new file mode 100644
index 00000000..f6e71b12
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ProcessUtil.java
@@ -0,0 +1,65 @@
+/*
+ * 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.tools.ant.util;
+
+import java.lang.management.ManagementFactory;
+
+/**
+ * Process Utilities
+ * @since Ant 1.9.4
+ */
+public class ProcessUtil {
+
+ private ProcessUtil() {
+ }
+
+ /**
+ * provide id of the current process
+ * @param fallback
+ * @return current process id
+ */
+ public static String getProcessId(final String fallback) {
+ // Note: may fail in some JVM implementations
+ // therefore fallback has to be provided
+
+ // something like '<pid>@<hostname>', at least in SUN / Oracle JVMs
+ final String jvmName = ManagementFactory.getRuntimeMXBean().getName();
+ final int index = jvmName.indexOf('@');
+
+ if (index < 1) {
+ // part before '@' empty (index = 0) / '@' not found (index = -1)
+ return fallback;
+ }
+
+ try {
+ return Long.toString(Long.parseLong(jvmName.substring(0, index)));
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+ return fallback;
+ }
+
+ public static void main(String [] args) {
+ System.out.println(getProcessId("<PID>"));
+ try {
+ Thread.sleep(120000);
+ } catch (Exception exc) {
+ // ignore
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/PropertyOutputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/PropertyOutputStream.java
new file mode 100644
index 00000000..59a1b7e8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/PropertyOutputStream.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.tools.ant.util;
+
+import java.io.ByteArrayOutputStream;
+
+import org.apache.tools.ant.Project;
+
+/**
+ * Exception thrown when an attempt is made to get an OutputStream
+ * from an immutable Resource.
+ * @since Ant 1.7
+ */
+public class PropertyOutputStream extends ByteArrayOutputStream {
+ private Project project;
+ private String property;
+ private boolean trim;
+
+ /**
+ * Construct a new PropertyOutputStream for the specified Project
+ * and property name, trimming the property value.
+ * @param p the associated Ant Project.
+ * @param s the String property name.
+ */
+ public PropertyOutputStream(Project p, String s) {
+ this(p, s, true);
+ }
+
+ /**
+ * Construct a new PropertyOutputStream for
+ * the specified Project, property name, and trim mode.
+ * @param p the associated Ant Project.
+ * @param s the String property name.
+ * @param b the boolean trim mode.
+ */
+ public PropertyOutputStream(Project p, String s, boolean b) {
+ project = p;
+ property = s;
+ trim = b;
+ }
+
+ /**
+ * Close the PropertyOutputStream, storing the property.
+ */
+ public void close() {
+ if (project != null && property != null) {
+ String s = new String(toByteArray());
+ project.setNewProperty(property, trim ? s.trim() : s);
+ }
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ProxySetup.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ProxySetup.java
new file mode 100644
index 00000000..f077f87c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ProxySetup.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.tools.ant.util;
+
+import org.apache.tools.ant.Project;
+
+/**
+ * Code to do proxy setup. This is just factored out of the main system just to
+ * keep everything else less convoluted.
+ * @since Ant1.7
+ */
+
+public class ProxySetup {
+
+ /**
+ * owner project; used for logging and extracting properties
+ */
+ private Project owner;
+
+ /**
+ * Java1.5 property that enables use of system proxies.
+ */
+ public static final String USE_SYSTEM_PROXIES = "java.net.useSystemProxies";
+ /** the http proxyhost property */
+ public static final String HTTP_PROXY_HOST = "http.proxyHost";
+ /** the http proxyport property */
+ public static final String HTTP_PROXY_PORT = "http.proxyPort";
+ /** the https proxyhost property */
+ public static final String HTTPS_PROXY_HOST = "https.proxyHost";
+ /** the https proxyport property */
+ public static final String HTTPS_PROXY_PORT = "https.proxyPort";
+ /** the ftp proxyhost property */
+ public static final String FTP_PROXY_HOST = "ftp.proxyHost";
+ /** the ftp proxyport property */
+ public static final String FTP_PROXY_PORT = "ftp.proxyPort";
+ /** the ftp proxyport property */
+ public static final String HTTP_NON_PROXY_HOSTS = "http.nonProxyHosts";
+ /** the http hosts not to be proxied property */
+ public static final String HTTPS_NON_PROXY_HOSTS = "https.nonProxyHosts";
+ /** the ftp hosts not to be proxied property */
+ public static final String FTP_NON_PROXY_HOSTS = "ftp.nonProxyHosts";
+ /** the http proxy username property */
+ public static final String HTTP_PROXY_USERNAME = "http.proxyUser";
+ /** the http proxy password property */
+ public static final String HTTP_PROXY_PASSWORD = "http.proxyPassword";
+ /** the socks proxy host property */
+ public static final String SOCKS_PROXY_HOST = "socksProxyHost";
+ /** the socks proxy port property */
+ public static final String SOCKS_PROXY_PORT = "socksProxyPort";
+ /** the socks proxy username property */
+ public static final String SOCKS_PROXY_USERNAME = "java.net.socks.username";
+ /** the socks proxy password property */
+ public static final String SOCKS_PROXY_PASSWORD = "java.net.socks.password";
+
+ /**
+ * create a proxy setup class bound to this project
+ * @param owner the project that owns this setup.
+ */
+ public ProxySetup(Project owner) {
+ this.owner = owner;
+ }
+
+ /**
+ * Get the current system property settings
+ * @return current value; null for none or no access
+ */
+ public static String getSystemProxySetting() {
+ try {
+ return System.getProperty(USE_SYSTEM_PROXIES);
+ } catch (SecurityException e) {
+ //if you cannot read it, you won't be able to write it either
+ return null;
+ }
+ }
+
+ /**
+ * turn proxies on;
+ * if the proxy key is already set to some value: leave alone.
+ * if an ant property of the value {@link #USE_SYSTEM_PROXIES}
+ * is set, use that instead. Else set to "true".
+ */
+ public void enableProxies() {
+ if (!(getSystemProxySetting() != null)) {
+ String proxies = owner.getProperty(USE_SYSTEM_PROXIES);
+ if (proxies == null || Project.toBoolean(proxies)) {
+ proxies = "true";
+ }
+ String message = "setting " + USE_SYSTEM_PROXIES + " to " + proxies;
+ try {
+ owner.log(message, Project.MSG_DEBUG);
+ System.setProperty(USE_SYSTEM_PROXIES, proxies);
+ } catch (SecurityException e) {
+ //log security exceptions and continue; it aint that
+ //important and may be quite common running Ant embedded.
+ owner.log("Security Exception when " + message);
+ }
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ReaderInputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ReaderInputStream.java
new file mode 100644
index 00000000..620af8d5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ReaderInputStream.java
@@ -0,0 +1,205 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * Adapts a <code>Reader</code> as an <code>InputStream</code>.
+ * Adapted from <code>StringInputStream</code>.
+ *
+ */
+public class ReaderInputStream extends InputStream {
+ private static final int BYTE_MASK = 0xFF;
+
+ /** Source Reader */
+ private Reader in;
+
+ private String encoding = System.getProperty("file.encoding");
+
+ private byte[] slack;
+
+ private int begin;
+
+ /**
+ * Construct a <code>ReaderInputStream</code>
+ * for the specified <code>Reader</code>.
+ *
+ * @param reader <code>Reader</code>. Must not be <code>null</code>.
+ */
+ public ReaderInputStream(Reader reader) {
+ in = reader;
+ }
+
+ /**
+ * Construct a <code>ReaderInputStream</code>
+ * for the specified <code>Reader</code>,
+ * with the specified encoding.
+ *
+ * @param reader non-null <code>Reader</code>.
+ * @param encoding non-null <code>String</code> encoding.
+ */
+ public ReaderInputStream(Reader reader, String encoding) {
+ this(reader);
+ if (encoding == null) {
+ throw new IllegalArgumentException("encoding must not be null");
+ } else {
+ this.encoding = encoding;
+ }
+ }
+
+ /**
+ * Reads from the <code>Reader</code>, returning the same value.
+ *
+ * @return the value of the next character in the <code>Reader</code>.
+ *
+ * @exception IOException if the original <code>Reader</code> fails to be read
+ */
+ public synchronized int read() throws IOException {
+ if (in == null) {
+ throw new IOException("Stream Closed");
+ }
+
+ byte result;
+ if (slack != null && begin < slack.length) {
+ result = slack[begin];
+ if (++begin == slack.length) {
+ slack = null;
+ }
+ } else {
+ byte[] buf = new byte[1];
+ if (read(buf, 0, 1) <= 0) {
+ return -1;
+ } else {
+ result = buf[0];
+ }
+ }
+ return result & BYTE_MASK;
+ }
+
+ /**
+ * Reads from the <code>Reader</code> into a byte array
+ *
+ * @param b the byte array to read into
+ * @param off the offset in the byte array
+ * @param len the length in the byte array to fill
+ * @return the actual number read into the byte array, -1 at
+ * the end of the stream
+ * @exception IOException if an error occurs
+ */
+ public synchronized int read(byte[] b, int off, int len)
+ throws IOException {
+ if (in == null) {
+ throw new IOException("Stream Closed");
+ }
+ if (len == 0) {
+ return 0;
+ }
+ while (slack == null) {
+ char[] buf = new char[len]; // might read too much
+ int n = in.read(buf);
+ if (n == -1) {
+ return -1;
+ }
+ if (n > 0) {
+ slack = new String(buf, 0, n).getBytes(encoding);
+ begin = 0;
+ }
+ }
+
+ if (len > slack.length - begin) {
+ len = slack.length - begin;
+ }
+
+ System.arraycopy(slack, begin, b, off, len);
+
+ begin += len;
+ if (begin >= slack.length) {
+ slack = null;
+ }
+
+ return len;
+ }
+
+ /**
+ * Marks the read limit of the Reader.
+ *
+ * @param limit the maximum limit of bytes that can be read before the
+ * mark position becomes invalid
+ */
+ public synchronized void mark(final int limit) {
+ try {
+ in.mark(limit);
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe.getMessage());
+ }
+ }
+
+
+ /**
+ * @return the current number of bytes ready for reading
+ * @exception IOException if an error occurs
+ */
+ public synchronized int available() throws IOException {
+ if (in == null) {
+ throw new IOException("Stream Closed");
+ }
+ if (slack != null) {
+ return slack.length - begin;
+ }
+ if (in.ready()) {
+ return 1;
+ }
+ return 0;
+ }
+
+ /**
+ * @return false - mark is not supported
+ */
+ public boolean markSupported () {
+ return false; // would be imprecise
+ }
+
+ /**
+ * Resets the Reader.
+ *
+ * @exception IOException if the Reader fails to be reset
+ */
+ public synchronized void reset() throws IOException {
+ if (in == null) {
+ throw new IOException("Stream Closed");
+ }
+ slack = null;
+ in.reset();
+ }
+
+ /**
+ * Closes the Reader.
+ *
+ * @exception IOException if the original Reader fails to be closed
+ */
+ public synchronized void close() throws IOException {
+ if (in != null) {
+ in.close();
+ slack = null;
+ in = null;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ReflectUtil.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ReflectUtil.java
new file mode 100644
index 00000000..88b7911e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ReflectUtil.java
@@ -0,0 +1,212 @@
+/*
+ * 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.tools.ant.util;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Utility class to handle reflection on java objects.
+ * The class contains static methods to call reflection
+ * methods, catch any exceptions, converting them
+ * to BuildExceptions.
+ */
+// CheckStyle:FinalClassCheck OFF - backward compatible
+public class ReflectUtil {
+
+ /** private constructor */
+ private ReflectUtil() {
+ }
+
+ /**
+ * Create an instance of a class using the constructor matching
+ * the given arguments.
+ * @since Ant 1.8.0
+ */
+ public static <T> T newInstance(Class<T> ofClass,
+ Class<?>[] argTypes,
+ Object[] args) {
+ try {
+ Constructor<T> con = ofClass.getConstructor(argTypes);
+ return con.newInstance(args);
+ } catch (Exception t) {
+ throwBuildException(t);
+ return null; // NotReached
+ }
+ }
+
+ /**
+ * Call a method on the object with no parameters.
+ * @param obj the object to invoke the method on.
+ * @param methodName the name of the method to call
+ * @return the object returned by the method
+ */
+ public static Object invoke(Object obj, String methodName) {
+ try {
+ Method method;
+ method = obj.getClass().getMethod(
+ methodName, (Class[]) null);
+ return method.invoke(obj, (Object[]) null);
+ } catch (Exception t) {
+ throwBuildException(t);
+ return null; // NotReached
+ }
+ }
+
+ /**
+ * Call a method on the object with no parameters.
+ * Note: Unlike the invoke method above, this
+ * calls class or static methods, not instance methods.
+ * @param obj the object to invoke the method on.
+ * @param methodName the name of the method to call
+ * @return the object returned by the method
+ */
+ public static Object invokeStatic(Object obj, String methodName) {
+ try {
+ Method method;
+ method = ((Class<?>) obj).getMethod(
+ methodName, (Class[]) null);
+ return method.invoke(obj, (Object[]) null);
+ } catch (Exception t) {
+ throwBuildException(t);
+ return null; // NotReached
+ }
+ }
+
+ /**
+ * Call a method on the object with one argument.
+ * @param obj the object to invoke the method on.
+ * @param methodName the name of the method to call
+ * @param argType the type of argument.
+ * @param arg the value of the argument.
+ * @return the object returned by the method
+ */
+ public static Object invoke(
+ Object obj, String methodName, Class<?> argType, Object arg) {
+ try {
+ Method method;
+ method = obj.getClass().getMethod(
+ methodName, new Class[] {argType});
+ return method.invoke(obj, new Object[] {arg});
+ } catch (Exception t) {
+ throwBuildException(t);
+ return null; // NotReached
+ }
+ }
+
+ /**
+ * Call a method on the object with two argument.
+ * @param obj the object to invoke the method on.
+ * @param methodName the name of the method to call
+ * @param argType1 the type of the first argument.
+ * @param arg1 the value of the first argument.
+ * @param argType2 the type of the second argument.
+ * @param arg2 the value of the second argument.
+ * @return the object returned by the method
+ */
+ public static Object invoke(
+ Object obj, String methodName, Class<?> argType1, Object arg1,
+ Class<?> argType2, Object arg2) {
+ try {
+ Method method;
+ method = obj.getClass().getMethod(
+ methodName, new Class[] {argType1, argType2});
+ return method.invoke(obj, new Object[] {arg1, arg2});
+ } catch (Exception t) {
+ throwBuildException(t);
+ return null; // NotReached
+ }
+ }
+
+ /**
+ * Get the value of a field in an object.
+ * @param obj the object to look at.
+ * @param fieldName the name of the field in the object.
+ * @return the value of the field.
+ * @throws BuildException if there is an error.
+ */
+ public static Object getField(Object obj, String fieldName)
+ throws BuildException {
+ try {
+ Field field = obj.getClass().getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return field.get(obj);
+ } catch (Exception t) {
+ throwBuildException(t);
+ return null; // NotReached
+ }
+ }
+
+ /**
+ * A method to convert an invocationTargetException to
+ * a buildexception and throw it.
+ * @param t the invocation target exception.
+ * @throws BuildException the converted exception.
+ */
+ public static void throwBuildException(Exception t)
+ throws BuildException {
+ throw toBuildException(t);
+ }
+
+ /**
+ * A method to convert an invocationTargetException to
+ * a buildexception.
+ * @param t the invocation target exception.
+ * @return the converted exception.
+ * @since ant 1.7.1
+ */
+ public static BuildException toBuildException(Exception t) {
+ if (t instanceof InvocationTargetException) {
+ Throwable t2 = ((InvocationTargetException) t)
+ .getTargetException();
+ if (t2 instanceof BuildException) {
+ return (BuildException) t2;
+ }
+ return new BuildException(t2);
+ } else {
+ return new BuildException(t);
+ }
+ }
+
+ /**
+ * A method to test if an object responds to a given
+ * message (method call)
+ * @param o the object
+ * @param methodName the method to check for
+ * @return true if the object has the method.
+ * @throws BuildException if there is a problem.
+ */
+ public static boolean respondsTo(Object o, String methodName)
+ throws BuildException {
+ try {
+ Method[] methods = o.getClass().getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ if (methods[i].getName().equals(methodName)) {
+ return true;
+ }
+ }
+ return false;
+ } catch (Exception t) {
+ throw toBuildException(t);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ReflectWrapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ReflectWrapper.java
new file mode 100644
index 00000000..e34363ea
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ReflectWrapper.java
@@ -0,0 +1,99 @@
+/*
+ * 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.tools.ant.util;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * Utility class to handle reflection on java objects.
+ * The class is a holder class for an object and
+ * uses java reflection to call methods on the objects.
+ * If things go wrong, BuildExceptions are thrown.
+ */
+
+public class ReflectWrapper {
+ private Object obj;
+ /**
+ * Construct a wrapped object using the no arg constructor.
+ * @param loader the classloader to use to construct the class.
+ * @param name the classname of the object to construct.
+ */
+ public ReflectWrapper(ClassLoader loader, String name) {
+ try {
+ Class clazz;
+ clazz = Class.forName(name, true, loader);
+ Constructor constructor;
+ constructor = clazz.getConstructor((Class[]) null);
+ obj = constructor.newInstance((Object[]) null);
+ } catch (Exception t) {
+ ReflectUtil.throwBuildException(t);
+ }
+ }
+
+ /**
+ * Constructor using a passed in object.
+ * @param obj the object to wrap.
+ */
+ public ReflectWrapper(Object obj) {
+ this.obj = obj;
+ }
+
+ /**
+ * @return the wrapped object.
+ */
+ public Object getObject() {
+ return obj;
+ }
+
+ /**
+ * Call a method on the object with no parameters.
+ * @param methodName the name of the method to call
+ * @return the object returned by the method
+ */
+ public Object invoke(String methodName) {
+ return ReflectUtil.invoke(obj, methodName);
+ }
+
+ /**
+ * Call a method on the object with one argument.
+ * @param methodName the name of the method to call
+ * @param argType the type of argument.
+ * @param arg the value of the argument.
+ * @return the object returned by the method
+ */
+ public Object invoke(
+ String methodName, Class argType, Object arg) {
+ return ReflectUtil.invoke(obj, methodName, argType, arg);
+ }
+
+ /**
+ * Call a method on the object with one argument.
+ * @param methodName the name of the method to call
+ * @param argType1 the type of the first argument.
+ * @param arg1 the value of the first argument.
+ * @param argType2 the type of the second argument.
+ * @param arg2 the value of the second argument.
+ * @return the object returned by the method
+ */
+ public Object invoke(
+ String methodName, Class argType1, Object arg1,
+ Class argType2, Object arg2) {
+ return ReflectUtil.invoke(
+ obj, methodName, argType1, arg1, argType2, arg2);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/RegexpPatternMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/RegexpPatternMapper.java
new file mode 100644
index 00000000..fa620d9a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/RegexpPatternMapper.java
@@ -0,0 +1,159 @@
+/*
+ * 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.tools.ant.util;
+
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.regexp.RegexpMatcher;
+import org.apache.tools.ant.util.regexp.RegexpMatcherFactory;
+import org.apache.tools.ant.util.regexp.RegexpUtil;
+
+/**
+ * Implementation of FileNameMapper that does regular expression
+ * replacements.
+ *
+ */
+public class RegexpPatternMapper implements FileNameMapper {
+
+ private static final int DECIMAL = 10;
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected RegexpMatcher reg = null;
+ protected char[] to = null;
+ protected StringBuffer result = new StringBuffer();
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Constructor for RegexpPatternMapper.
+ * @throws BuildException on error.
+ */
+ public RegexpPatternMapper() throws BuildException {
+ reg = (new RegexpMatcherFactory()).newRegexpMatcher();
+ }
+
+ private boolean handleDirSep = false;
+ private int regexpOptions = 0;
+
+ /**
+ * Attribute specifying whether to ignore the difference
+ * between / and \ (the two common directory characters).
+ * @param handleDirSep a boolean, default is false.
+ * @since Ant 1.6.3
+ */
+ public void setHandleDirSep(boolean handleDirSep) {
+ this.handleDirSep = handleDirSep;
+ }
+
+ /**
+ * Attribute specifying whether to ignore the case difference
+ * in the names.
+ *
+ * @param caseSensitive a boolean, default is false.
+ * @since Ant 1.6.3
+ */
+ public void setCaseSensitive(boolean caseSensitive) {
+ regexpOptions = RegexpUtil.asOptions(caseSensitive);
+ }
+
+ /**
+ * Sets the &quot;from&quot; pattern. Required.
+ * @param from the from pattern.
+ * @throws BuildException on error.
+ */
+ public void setFrom(String from) throws BuildException {
+ if (from != null) {
+ try {
+ reg.setPattern(from);
+ } catch (NoClassDefFoundError e) {
+ // depending on the implementation the actual RE won't
+ // get instantiated in the constructor.
+ throw new BuildException("Cannot load regular expression matcher",
+ e);
+ }
+ } else {
+ throw new BuildException("this mapper requires a 'from' attribute");
+ }
+ }
+
+ /**
+ * Sets the &quot;to&quot; pattern. Required.
+ * @param to the to pattern.
+ * @throws BuildException on error.
+ */
+ public void setTo(String to) {
+ if (to != null) {
+ this.to = to.toCharArray();
+ } else {
+ throw new BuildException("this mapper requires a 'to' attribute");
+ }
+ }
+
+ /**
+ * Returns null if the source file name doesn't match the
+ * &quot;from&quot; pattern, an one-element array containing the
+ * translated file otherwise.
+ * @param sourceFileName the source file name
+ * @return a one-element array containing the translated file or
+ * null if the to pattern did not match
+ */
+ public String[] mapFileName(String sourceFileName) {
+ if (handleDirSep) {
+ if (sourceFileName.indexOf("\\") != -1) {
+ sourceFileName = sourceFileName.replace('\\', '/');
+ }
+ }
+ if (reg == null || to == null
+ || !reg.matches(sourceFileName, regexpOptions)) {
+ return null;
+ }
+ return new String[] {replaceReferences(sourceFileName)};
+ }
+
+ /**
+ * Replace all backreferences in the to pattern with the matched
+ * groups of the source.
+ * @param source the source file name.
+ * @return the translated file name.
+ */
+ protected String replaceReferences(String source) {
+ Vector v = reg.getGroups(source, regexpOptions);
+
+ result.setLength(0);
+ for (int i = 0; i < to.length; i++) {
+ if (to[i] == '\\') {
+ if (++i < to.length) {
+ int value = Character.digit(to[i], DECIMAL);
+ if (value > -1) {
+ result.append((String) v.elementAt(value));
+ } else {
+ result.append(to[i]);
+ }
+ } else {
+ // TODO - should throw an exception instead?
+ result.append('\\');
+ }
+ } else {
+ result.append(to[i]);
+ }
+ }
+ return result.substring(0);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ResourceUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ResourceUtils.java
new file mode 100644
index 00000000..6397f714
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ResourceUtils.java
@@ -0,0 +1,860 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.nio.channels.FileChannel;
+import java.util.Arrays;
+import java.util.Vector;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.types.TimeComparison;
+import org.apache.tools.ant.types.resources.Appendable;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.Resources;
+import org.apache.tools.ant.types.resources.Restrict;
+import org.apache.tools.ant.types.resources.StringResource;
+import org.apache.tools.ant.types.resources.Touchable;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.types.resources.selectors.Date;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+
+// CheckStyle:HideUtilityClassConstructorCheck OFF - bc
+
+/**
+ * This class provides utility methods to process Resources.
+ *
+ * @since Ant 1.5.2
+ */
+public class ResourceUtils {
+
+ /** Utilities used for file operations */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Name of charset "ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1".
+ *
+ * @since Ant 1.8.1
+ */
+ public static final String ISO_8859_1 = "ISO-8859-1";
+
+ private static final long MAX_IO_CHUNK_SIZE = 16*1024*1024; // 16 MB
+
+ /**
+ * Tells which source files should be reprocessed based on the
+ * last modification date of target files.
+ * @param logTo where to send (more or less) interesting output.
+ * @param source array of resources bearing relative path and last
+ * modification date.
+ * @param mapper filename mapper indicating how to find the target
+ * files.
+ * @param targets object able to map as a resource a relative path
+ * at <b>destination</b>.
+ * @return array containing the source files which need to be
+ * copied or processed, because the targets are out of date or do
+ * not exist.
+ */
+ public static Resource[] selectOutOfDateSources(final ProjectComponent logTo,
+ final Resource[] source,
+ final FileNameMapper mapper,
+ final ResourceFactory targets) {
+ return selectOutOfDateSources(logTo, source, mapper, targets,
+ FILE_UTILS.getFileTimestampGranularity());
+ }
+
+ /**
+ * Tells which source files should be reprocessed based on the
+ * last modification date of target files.
+ * @param logTo where to send (more or less) interesting output.
+ * @param source array of resources bearing relative path and last
+ * modification date.
+ * @param mapper filename mapper indicating how to find the target
+ * files.
+ * @param targets object able to map as a resource a relative path
+ * at <b>destination</b>.
+ * @param granularity The number of milliseconds leeway to give
+ * before deciding a target is out of date.
+ * @return array containing the source files which need to be
+ * copied or processed, because the targets are out of date or do
+ * not exist.
+ * @since Ant 1.6.2
+ */
+ public static Resource[] selectOutOfDateSources(final ProjectComponent logTo,
+ final Resource[] source,
+ final FileNameMapper mapper,
+ final ResourceFactory targets,
+ final long granularity) {
+ final Union u = new Union();
+ u.addAll(Arrays.asList(source));
+ final ResourceCollection rc
+ = selectOutOfDateSources(logTo, u, mapper, targets, granularity);
+ return rc.size() == 0 ? new Resource[0] : ((Union) rc).listResources();
+ }
+
+ /**
+ * Tells which sources should be reprocessed based on the
+ * last modification date of targets.
+ * @param logTo where to send (more or less) interesting output.
+ * @param source ResourceCollection.
+ * @param mapper filename mapper indicating how to find the target Resources.
+ * @param targets object able to map a relative path as a Resource.
+ * @param granularity The number of milliseconds leeway to give
+ * before deciding a target is out of date.
+ * @return ResourceCollection.
+ * @since Ant 1.7
+ */
+ public static ResourceCollection selectOutOfDateSources(final ProjectComponent logTo,
+ final ResourceCollection source,
+ final FileNameMapper mapper,
+ final ResourceFactory targets,
+ final long granularity) {
+ logFuture(logTo, source, granularity);
+ final ResourceSelectorProvider p =
+ new ResourceSelectorProvider() {
+ public ResourceSelector
+ getTargetSelectorForSource(final Resource sr) {
+ return new ResourceSelector() {
+ public boolean isSelected(final Resource target) {
+ /* Extra I/O, probably wasted:
+ if (target.isDirectory()) {
+ return false;
+ }
+ */
+ return SelectorUtils.isOutOfDate(sr, target,
+ granularity);
+ }
+ };
+ }
+ };
+ return selectSources(logTo, source, mapper, targets, p);
+ }
+
+ /**
+ * Tells which sources should be reprocessed because the given
+ * selector selects at least one target.
+ *
+ * @param logTo where to send (more or less) interesting output.
+ * @param source ResourceCollection.
+ * @param mapper filename mapper indicating how to find the target Resources.
+ * @param targets object able to map a relative path as a Resource.
+ * @param selector returns a selector that is applied to target
+ * files. If it selects at least one target the source will be
+ * added to the returned collection.
+ * @return ResourceCollection.
+ * @since Ant 1.8.0
+ */
+ public static ResourceCollection selectSources(final ProjectComponent logTo,
+ ResourceCollection source,
+ final FileNameMapper mapper,
+ final ResourceFactory targets,
+ final ResourceSelectorProvider selector) {
+ if (source.size() == 0) {
+ logTo.log("No sources found.", Project.MSG_VERBOSE);
+ return Resources.NONE;
+ }
+ source = Union.getInstance(source);
+
+ final Union result = new Union();
+ for (final Resource sr : source) {
+ String srName = sr.getName();
+ srName = srName == null
+ ? srName : srName.replace('/', File.separatorChar);
+
+ String[] targetnames = null;
+ try {
+ targetnames = mapper.mapFileName(srName);
+ } catch (final Exception e) {
+ logTo.log("Caught " + e + " mapping resource " + sr,
+ Project.MSG_VERBOSE);
+ }
+ if (targetnames == null || targetnames.length == 0) {
+ logTo.log(sr + " skipped - don\'t know how to handle it",
+ Project.MSG_VERBOSE);
+ continue;
+ }
+ for (int i = 0; i < targetnames.length; i++) {
+ if (targetnames[i] == null) {
+ targetnames[i] = "(no name)";
+ }
+ }
+ final Union targetColl = new Union();
+ for (int i = 0; i < targetnames.length; i++) {
+ targetColl.add(targets.getResource(
+ targetnames[i].replace(File.separatorChar, '/')));
+ }
+ //find the out-of-date targets:
+ final Restrict r = new Restrict();
+ r.add(selector.getTargetSelectorForSource(sr));
+ r.add(targetColl);
+ if (r.size() > 0) {
+ result.add(sr);
+ final Resource t = r.iterator().next();
+ logTo.log(sr.getName() + " added as " + t.getName()
+ + (t.isExists() ? " is outdated." : " doesn\'t exist."),
+ Project.MSG_VERBOSE);
+ continue;
+ }
+ //log uptodateness of all targets:
+ logTo.log(sr.getName()
+ + " omitted as " + targetColl.toString()
+ + (targetColl.size() == 1 ? " is" : " are ")
+ + " up to date.", Project.MSG_VERBOSE);
+ }
+ return result;
+ }
+
+ /**
+ * Convenience method to copy content from one Resource to another.
+ * No filtering is performed.
+ *
+ * @param source the Resource to copy from.
+ * Must not be <code>null</code>.
+ * @param dest the Resource to copy to.
+ * Must not be <code>null</code>.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.7
+ */
+ public static void copyResource(final Resource source, final Resource dest) throws IOException {
+ copyResource(source, dest, null);
+ }
+
+ /**
+ * Convenience method to copy content from one Resource to another.
+ * No filtering is performed.
+ *
+ * @param source the Resource to copy from.
+ * Must not be <code>null</code>.
+ * @param dest the Resource to copy to.
+ * Must not be <code>null</code>.
+ * @param project the project instance.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.7
+ */
+ public static void copyResource(final Resource source, final Resource dest, final Project project)
+ throws IOException {
+ copyResource(source, dest, null, null, false,
+ false, null, null, project);
+ }
+
+ // CheckStyle:ParameterNumberCheck OFF - bc
+ /**
+ * Convenience method to copy content from one Resource to another
+ * specifying whether token filtering must be used, whether filter chains
+ * must be used, whether newer destination files may be overwritten and
+ * whether the last modified time of <code>dest</code> file should be made
+ * equal to the last modified time of <code>source</code>.
+ *
+ * @param source the Resource to copy from.
+ * Must not be <code>null</code>.
+ * @param dest the Resource to copy to.
+ * Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param filterChains filterChains to apply during the copy.
+ * @param overwrite Whether or not the destination Resource should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the destination Resource should be set to that
+ * of the source.
+ * @param inputEncoding the encoding used to read the files.
+ * @param outputEncoding the encoding used to write the files.
+ * @param project the project instance.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.7
+ */
+ public static void copyResource(final Resource source, final Resource dest,
+ final FilterSetCollection filters, final Vector filterChains,
+ final boolean overwrite, final boolean preserveLastModified,
+ final String inputEncoding, final String outputEncoding,
+ final Project project)
+ throws IOException {
+ copyResource(source, dest, filters, filterChains, overwrite, preserveLastModified, false, inputEncoding, outputEncoding, project);
+ }
+
+ // CheckStyle:ParameterNumberCheck OFF - bc
+ /**
+ * Convenience method to copy content from one Resource to another
+ * specifying whether token filtering must be used, whether filter chains
+ * must be used, whether newer destination files may be overwritten and
+ * whether the last modified time of <code>dest</code> file should be made
+ * equal to the last modified time of <code>source</code>.
+ *
+ * @param source the Resource to copy from.
+ * Must not be <code>null</code>.
+ * @param dest the Resource to copy to.
+ * Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param filterChains filterChains to apply during the copy.
+ * @param overwrite Whether or not the destination Resource should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the destination Resource should be set to that
+ * of the source.
+ * @param append Whether to append to an Appendable Resource.
+ * @param inputEncoding the encoding used to read the files.
+ * @param outputEncoding the encoding used to write the files.
+ * @param project the project instance.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.8
+ */
+ public static void copyResource(final Resource source, final Resource dest,
+ final FilterSetCollection filters, final Vector filterChains,
+ final boolean overwrite, final boolean preserveLastModified,
+ final boolean append,
+ final String inputEncoding, final String outputEncoding,
+ final Project project)
+ throws IOException {
+ copyResource(source, dest, filters, filterChains, overwrite,
+ preserveLastModified, append, inputEncoding,
+ outputEncoding, project, /* force: */ false);
+ }
+
+ /**
+ * Convenience method to copy content from one Resource to another
+ * specifying whether token filtering must be used, whether filter chains
+ * must be used, whether newer destination files may be overwritten and
+ * whether the last modified time of <code>dest</code> file should be made
+ * equal to the last modified time of <code>source</code>.
+ *
+ * @param source the Resource to copy from.
+ * Must not be <code>null</code>.
+ * @param dest the Resource to copy to.
+ * Must not be <code>null</code>.
+ * @param filters the collection of filters to apply to this copy.
+ * @param filterChains filterChains to apply during the copy.
+ * @param overwrite Whether or not the destination Resource should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the destination Resource should be set to that
+ * of the source.
+ * @param append Whether to append to an Appendable Resource.
+ * @param inputEncoding the encoding used to read the files.
+ * @param outputEncoding the encoding used to write the files.
+ * @param project the project instance.
+ * @param force whether read-only target files will be overwritten
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.8.2
+ */
+ public static void copyResource(final Resource source, final Resource dest,
+ final FilterSetCollection filters, final Vector filterChains,
+ final boolean overwrite, final boolean preserveLastModified,
+ final boolean append,
+ final String inputEncoding, final String outputEncoding,
+ final Project project, final boolean force)
+ throws IOException {
+ if (!(overwrite || SelectorUtils.isOutOfDate(source, dest, FileUtils.getFileUtils()
+ .getFileTimestampGranularity()))) {
+ return;
+ }
+ final boolean filterSetsAvailable = (filters != null
+ && filters.hasFilters());
+ final boolean filterChainsAvailable = (filterChains != null
+ && filterChains.size() > 0);
+ String effectiveInputEncoding = null;
+ if (source instanceof StringResource) {
+ effectiveInputEncoding = ((StringResource) source).getEncoding();
+ } else {
+ effectiveInputEncoding = inputEncoding;
+ }
+ File destFile = null;
+ if (dest.as(FileProvider.class) != null) {
+ destFile = dest.as(FileProvider.class).getFile();
+ }
+ if (destFile != null && destFile.isFile() && !destFile.canWrite()) {
+ if (!force) {
+ throw new ReadOnlyTargetFileException(destFile);
+ } else if (!FILE_UTILS.tryHardToDelete(destFile)) {
+ throw new IOException("failed to delete read-only "
+ + "destination file " + destFile);
+ }
+ }
+
+ if (filterSetsAvailable) {
+ copyWithFilterSets(source, dest, filters, filterChains,
+ filterChainsAvailable, append,
+ effectiveInputEncoding, outputEncoding,
+ project);
+ } else if (filterChainsAvailable
+ || (effectiveInputEncoding != null
+ && !effectiveInputEncoding.equals(outputEncoding))
+ || (effectiveInputEncoding == null && outputEncoding != null)) {
+ copyWithFilterChainsOrTranscoding(source, dest, filterChains,
+ filterChainsAvailable, append,
+ effectiveInputEncoding,
+ outputEncoding, project);
+ } else {
+ boolean copied = false;
+ if (source.as(FileProvider.class) != null
+ && destFile != null && !append) {
+ final File sourceFile =
+ source.as(FileProvider.class).getFile();
+ try {
+ copyUsingFileChannels(sourceFile, destFile);
+ copied = true;
+ } catch (final IOException ex) {
+ String msg = "Attempt to copy " + sourceFile
+ + " to " + destFile + " using NIO Channels"
+ + " failed due to '" + ex.getMessage()
+ + "'. Falling back to streams.";
+ if (project != null) {
+ project.log(msg, Project.MSG_WARN);
+ } else {
+ System.err.println(msg);
+ }
+ }
+ }
+ if (!copied) {
+ copyUsingStreams(source, dest, append, project);
+ }
+ }
+ if (preserveLastModified) {
+ final Touchable t = dest.as(Touchable.class);
+ if (t != null) {
+ setLastModified(t, source.getLastModified());
+ }
+ }
+ }
+ // CheckStyle:ParameterNumberCheck ON
+
+ /**
+ * Set the last modified time of an object implementing
+ * org.apache.tools.ant.types.resources.Touchable .
+ *
+ * @param t the Touchable whose modified time is to be set.
+ * @param time the time to which the last modified time is to be set.
+ * if this is -1, the current time is used.
+ * @since Ant 1.7
+ */
+ public static void setLastModified(final Touchable t, final long time) {
+ t.touch((time < 0) ? System.currentTimeMillis() : time);
+ }
+
+ /**
+ * Compares the contents of two Resources.
+ *
+ * @param r1 the Resource whose content is to be compared.
+ * @param r2 the other Resource whose content is to be compared.
+ * @param text true if the content is to be treated as text and
+ * differences in kind of line break are to be ignored.
+ *
+ * @return true if the content of the Resources is the same.
+ *
+ * @throws IOException if the Resources cannot be read.
+ * @since Ant 1.7
+ */
+ public static boolean contentEquals(final Resource r1, final Resource r2, final boolean text) throws IOException {
+ if (r1.isExists() != r2.isExists()) {
+ return false;
+ }
+ if (!r1.isExists()) {
+ // two not existing files are equal
+ return true;
+ }
+ // should the following two be switched? If r1 and r2 refer to the same file,
+ // isn't their content equal regardless of whether that file is a directory?
+ if (r1.isDirectory() || r2.isDirectory()) {
+ // don't want to compare directory contents for now
+ return false;
+ }
+ if (r1.equals(r2)) {
+ return true;
+ }
+ if (!text) {
+ final long s1 = r1.getSize();
+ final long s2 = r2.getSize();
+ if (s1 != Resource.UNKNOWN_SIZE && s2 != Resource.UNKNOWN_SIZE
+ && s1 != s2) {
+ return false;
+ }
+ }
+ return compareContent(r1, r2, text) == 0;
+ }
+
+ /**
+ * Compare the content of two Resources. A nonexistent Resource's
+ * content is "less than" that of an existing Resource; a directory-type
+ * Resource's content is "less than" that of a file-type Resource.
+ * @param r1 the Resource whose content is to be compared.
+ * @param r2 the other Resource whose content is to be compared.
+ * @param text true if the content is to be treated as text and
+ * differences in kind of line break are to be ignored.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ * @throws IOException if the Resources cannot be read.
+ * @since Ant 1.7
+ */
+ public static int compareContent(final Resource r1, final Resource r2, final boolean text) throws IOException {
+ if (r1.equals(r2)) {
+ return 0;
+ }
+ final boolean e1 = r1.isExists();
+ final boolean e2 = r2.isExists();
+ if (!(e1 || e2)) {
+ return 0;
+ }
+ if (e1 != e2) {
+ return e1 ? 1 : -1;
+ }
+ final boolean d1 = r1.isDirectory();
+ final boolean d2 = r2.isDirectory();
+ if (d1 && d2) {
+ return 0;
+ }
+ if (d1 || d2) {
+ return d1 ? -1 : 1;
+ }
+ return text ? textCompare(r1, r2) : binaryCompare(r1, r2);
+ }
+
+ /**
+ * Convenience method to turn any fileProvider into a basic
+ * FileResource with the file's immediate parent as the basedir,
+ * for tasks that need one.
+ * @param fileProvider input
+ * @return fileProvider if it is a FileResource instance, or a new
+ * FileResource with fileProvider's file.
+ * @since Ant 1.8
+ */
+ public static FileResource asFileResource(final FileProvider fileProvider) {
+ if (fileProvider instanceof FileResource || fileProvider == null) {
+ return (FileResource) fileProvider;
+ }
+ final FileResource result = new FileResource(fileProvider.getFile());
+ result.setProject(Project.getProject(fileProvider));
+ return result;
+ }
+
+ /**
+ * Binary compares the contents of two Resources.
+ * <p>
+ * simple but sub-optimal comparison algorithm. written for working
+ * rather than fast. Better would be a block read into buffers followed
+ * by long comparisons apart from the final 1-7 bytes.
+ * </p>
+ *
+ * @param r1 the Resource whose content is to be compared.
+ * @param r2 the other Resource whose content is to be compared.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ * @throws IOException if the Resources cannot be read.
+ * @since Ant 1.7
+ */
+ private static int binaryCompare(final Resource r1, final Resource r2) throws IOException {
+ InputStream in1 = null;
+ InputStream in2 = null;
+ try {
+ in1 = new BufferedInputStream(r1.getInputStream());
+ in2 = new BufferedInputStream(r2.getInputStream());
+
+ for (int b1 = in1.read(); b1 != -1; b1 = in1.read()) {
+ final int b2 = in2.read();
+ if (b1 != b2) {
+ return b1 > b2 ? 1 : -1;
+ }
+ }
+ return in2.read() == -1 ? 0 : -1;
+ } finally {
+ FileUtils.close(in1);
+ FileUtils.close(in2);
+ }
+ }
+
+ /**
+ * Text compares the contents of two Resources.
+ * Ignores different kinds of line endings.
+ * @param r1 the Resource whose content is to be compared.
+ * @param r2 the other Resource whose content is to be compared.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ * @throws IOException if the Resources cannot be read.
+ * @since Ant 1.7
+ */
+ private static int textCompare(final Resource r1, final Resource r2) throws IOException {
+ BufferedReader in1 = null;
+ BufferedReader in2 = null;
+ try {
+ in1 = new BufferedReader(new InputStreamReader(r1.getInputStream()));
+ in2 = new BufferedReader(new InputStreamReader(r2.getInputStream()));
+
+ String expected = in1.readLine();
+ while (expected != null) {
+ final String actual = in2.readLine();
+ if (!expected.equals(actual)) {
+ if (actual == null) {
+ return 1;
+ }
+ return expected.compareTo(actual);
+ }
+ expected = in1.readLine();
+ }
+ return in2.readLine() == null ? 0 : -1;
+ } finally {
+ FileUtils.close(in1);
+ FileUtils.close(in2);
+ }
+ }
+
+ /**
+ * Log which Resources (if any) have been modified in the future.
+ * @param logTo the ProjectComponent to do the logging.
+ * @param rc the collection of Resources to check.
+ * @param granularity the timestamp granularity to use.
+ * @since Ant 1.7
+ */
+ private static void logFuture(final ProjectComponent logTo,
+ final ResourceCollection rc, final long granularity) {
+ final long now = System.currentTimeMillis() + granularity;
+ final Date sel = new Date();
+ sel.setMillis(now);
+ sel.setWhen(TimeComparison.AFTER);
+ final Restrict future = new Restrict();
+ future.add(sel);
+ future.add(rc);
+ for (final Resource r : future) {
+ logTo.log("Warning: " + r.getName() + " modified in the future.", Project.MSG_WARN);
+ }
+ }
+
+ private static void copyWithFilterSets(final Resource source, final Resource dest,
+ final FilterSetCollection filters,
+ final Vector filterChains,
+ final boolean filterChainsAvailable,
+ final boolean append, final String inputEncoding,
+ final String outputEncoding,
+ final Project project)
+ throws IOException {
+ BufferedReader in = null;
+ BufferedWriter out = null;
+ try {
+ InputStreamReader isr = null;
+ if (inputEncoding == null) {
+ isr = new InputStreamReader(source.getInputStream());
+ } else {
+ isr = new InputStreamReader(source.getInputStream(),
+ inputEncoding);
+ }
+ in = new BufferedReader(isr);
+ final OutputStream os = getOutputStream(dest, append, project);
+ OutputStreamWriter osw;
+ if (outputEncoding == null) {
+ osw = new OutputStreamWriter(os);
+ } else {
+ osw = new OutputStreamWriter(os, outputEncoding);
+ }
+ out = new BufferedWriter(osw);
+ if (filterChainsAvailable) {
+ final ChainReaderHelper crh = new ChainReaderHelper();
+ crh.setBufferSize(FileUtils.BUF_SIZE);
+ crh.setPrimaryReader(in);
+ crh.setFilterChains(filterChains);
+ crh.setProject(project);
+ final Reader rdr = crh.getAssembledReader();
+ in = new BufferedReader(rdr);
+ }
+ final LineTokenizer lineTokenizer = new LineTokenizer();
+ lineTokenizer.setIncludeDelims(true);
+ String newline = null;
+ String line = lineTokenizer.getToken(in);
+ while (line != null) {
+ if (line.length() == 0) {
+ // this should not happen, because the lines are
+ // returned with the end of line delimiter
+ out.newLine();
+ } else {
+ newline = filters.replaceTokens(line);
+ out.write(newline);
+ }
+ line = lineTokenizer.getToken(in);
+ }
+ } finally {
+ FileUtils.close(out);
+ FileUtils.close(in);
+ }
+ }
+
+ private static void copyWithFilterChainsOrTranscoding(final Resource source,
+ final Resource dest,
+ final Vector filterChains,
+ final boolean filterChainsAvailable,
+ final boolean append,
+ final String inputEncoding,
+ final String outputEncoding,
+ final Project project)
+ throws IOException {
+ BufferedReader in = null;
+ BufferedWriter out = null;
+ try {
+ InputStreamReader isr = null;
+ if (inputEncoding == null) {
+ isr = new InputStreamReader(source.getInputStream());
+ } else {
+ isr = new InputStreamReader(source.getInputStream(),
+ inputEncoding);
+ }
+ in = new BufferedReader(isr);
+ final OutputStream os = getOutputStream(dest, append, project);
+ OutputStreamWriter osw;
+ if (outputEncoding == null) {
+ osw = new OutputStreamWriter(os);
+ } else {
+ osw = new OutputStreamWriter(os, outputEncoding);
+ }
+ out = new BufferedWriter(osw);
+ if (filterChainsAvailable) {
+ final ChainReaderHelper crh = new ChainReaderHelper();
+ crh.setBufferSize(FileUtils.BUF_SIZE);
+ crh.setPrimaryReader(in);
+ crh.setFilterChains(filterChains);
+ crh.setProject(project);
+ final Reader rdr = crh.getAssembledReader();
+ in = new BufferedReader(rdr);
+ }
+ final char[] buffer = new char[FileUtils.BUF_SIZE];
+ while (true) {
+ final int nRead = in.read(buffer, 0, buffer.length);
+ if (nRead == -1) {
+ break;
+ }
+ out.write(buffer, 0, nRead);
+ }
+ } finally {
+ FileUtils.close(out);
+ FileUtils.close(in);
+ }
+ }
+
+ private static void copyUsingFileChannels(final File sourceFile,
+ final File destFile)
+ throws IOException {
+
+ final File parent = destFile.getParentFile();
+ if (parent != null && !parent.isDirectory()
+ && !(parent.mkdirs() || parent.isDirectory())) {
+ throw new IOException("failed to create the parent directory"
+ + " for " + destFile);
+ }
+
+ FileInputStream in = null;
+ FileOutputStream out = null;
+ FileChannel srcChannel = null;
+ FileChannel destChannel = null;
+
+ try {
+ in = new FileInputStream(sourceFile);
+ out = new FileOutputStream(destFile);
+
+ srcChannel = in.getChannel();
+ destChannel = out.getChannel();
+
+ long position = 0;
+ final long count = srcChannel.size();
+ while (position < count) {
+ final long chunk = Math.min(MAX_IO_CHUNK_SIZE, count - position);
+ position +=
+ destChannel.transferFrom(srcChannel, position, chunk);
+ }
+ } finally {
+ FileUtils.close(srcChannel);
+ FileUtils.close(destChannel);
+ FileUtils.close(out);
+ FileUtils.close(in);
+ }
+ }
+
+ private static void copyUsingStreams(final Resource source, final Resource dest,
+ final boolean append, final Project project)
+ throws IOException {
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ in = source.getInputStream();
+ out = getOutputStream(dest, append, project);
+
+ final byte[] buffer = new byte[FileUtils.BUF_SIZE];
+ int count = 0;
+ do {
+ out.write(buffer, 0, count);
+ count = in.read(buffer, 0, buffer.length);
+ } while (count != -1);
+ } finally {
+ FileUtils.close(out);
+ FileUtils.close(in);
+ }
+ }
+
+ private static OutputStream getOutputStream(final Resource resource, final boolean append, final Project project)
+ throws IOException {
+ if (append) {
+ final Appendable a = resource.as(Appendable.class);
+ if (a != null) {
+ return a.getAppendOutputStream();
+ }
+ String msg = "Appendable OutputStream not available for non-appendable resource "
+ + resource + "; using plain OutputStream";
+ if (project != null) {
+ project.log(msg, Project.MSG_VERBOSE);
+ } else {
+ System.out.println(msg);
+ }
+ }
+ return resource.getOutputStream();
+ }
+
+ public interface ResourceSelectorProvider {
+ ResourceSelector getTargetSelectorForSource(Resource source);
+ }
+
+ /**
+ * @since Ant 1.9.4
+ */
+ public static class ReadOnlyTargetFileException extends IOException {
+ private static final long serialVersionUID = 1L;
+
+ public ReadOnlyTargetFileException(final File destFile) {
+ super("can't write to read-only destination file " + destFile);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/RetryHandler.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/RetryHandler.java
new file mode 100644
index 00000000..dd62bc27
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/RetryHandler.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.tools.ant.util;
+
+import java.io.IOException;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * A simple utility class to take a piece of code (that implements
+ * <code>Retryable</code> interface) and executes that with possibility to
+ * retry the execution in case of IOException.
+ */
+public class RetryHandler {
+
+ private int retriesAllowed = 0;
+ private Task task;
+
+ /**
+ * Create a new RetryingHandler.
+ *
+ * @param retriesAllowed how many times to retry
+ * @param task the Ant task that is is executed from, used for logging only
+ */
+ public RetryHandler(int retriesAllowed, Task task) {
+ this.retriesAllowed = retriesAllowed;
+ this.task = task;
+ }
+
+ /**
+ * Execute the <code>Retryable</code> code with specified number of retries.
+ *
+ * @param exe the code to execute
+ * @param desc some descriptive text for this piece of code, used for logging
+ * @throws IOException if the number of retries has exceeded the allowed limit
+ */
+ public void execute(Retryable exe, String desc) throws IOException {
+ int retries = 0;
+ while (true) {
+ try {
+ exe.execute();
+ break;
+ } catch (IOException e) {
+ retries++;
+ if (retries > this.retriesAllowed && this.retriesAllowed > -1) {
+ task.log("try #" + retries + ": IO error ("
+ + desc + "), number of maximum retries reached ("
+ + this.retriesAllowed + "), giving up", Project.MSG_WARN);
+ throw e;
+ } else {
+ task.log("try #" + retries + ": IO error (" + desc
+ + "), retrying", Project.MSG_WARN);
+ }
+ }
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Retryable.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Retryable.java
new file mode 100644
index 00000000..537244a1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Retryable.java
@@ -0,0 +1,38 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.IOException;
+
+
+/**
+ * Simple interface for executing a piece of code. Used for writing anonymous inner
+ * classes in FTP task for retry-on-IOException behaviour.
+ *
+ * @see RetryHandler
+ */
+public interface Retryable {
+ /** The value to use to never give up. */
+ int RETRY_FOREVER = -1;
+ /**
+ * Called to execute the code.
+ * @throws IOException if there is a problem.
+ */
+ void execute() throws IOException;
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptFixBSFPath.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptFixBSFPath.java
new file mode 100644
index 00000000..ca76e56b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptFixBSFPath.java
@@ -0,0 +1,147 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * A class to modify a classloader to
+ * support BSF language support.
+ */
+public class ScriptFixBSFPath {
+ private static final String UTIL_OPTIONAL_PACKAGE
+ = "org.apache.tools.ant.util.optional";
+
+ private static final String BSF_PACKAGE = "org.apache.bsf";
+ private static final String BSF_MANAGER = BSF_PACKAGE + ".BSFManager";
+ private static final String BSF_SCRIPT_RUNNER
+ = UTIL_OPTIONAL_PACKAGE + ".ScriptRunner";
+
+ /**
+ * The following are languages that have
+ * scripting engines embedded in bsf.jar.
+ * The array is converted to a map of
+ * languagename->classname.
+ */
+ private static final String[] BSF_LANGUAGES =
+ new String[] {
+ "js", "org.mozilla.javascript.Scriptable",
+ "javascript", "org.mozilla.javascript.Scriptable",
+ "jacl", "tcl.lang.Interp",
+ "netrexx", "netrexx.lang.Rexx",
+ "nrx", "netrexx.lang.Rexx",
+ "jython", "org.python.core.Py",
+ "py", "org.python.core.Py",
+ "xslt", "org.apache.xpath.objects.XObject"};
+
+ /** A map of languages for which the engine in located in bsf */
+ private static final Map BSF_LANGUAGE_MAP = new HashMap();
+ static {
+ for (int i = 0; i < BSF_LANGUAGES.length; i = i + 2) {
+ BSF_LANGUAGE_MAP.put(BSF_LANGUAGES[i], BSF_LANGUAGES[i + 1]);
+ }
+ }
+
+ private File getClassSource(ClassLoader loader, String className) {
+ return LoaderUtils.getResourceSource(
+ loader,
+ LoaderUtils.classNameToResource(className));
+ }
+
+ private File getClassSource(String className) {
+ return getClassSource(getClass().getClassLoader(), className);
+ }
+
+ /**
+ * Check if need to mess about with the classloader.
+ * The class loader will need to be modified for two
+ * reasons:
+ * <ol>
+ * <li>language is at a higher level than bsf for engines in bsf,
+ * move bsf.
+ * </li>
+ * <li>bsf is at a higher level than oata.util.optional.ScriptRunner
+ * </li>
+ * </ol>
+ *
+ * Assume a simple model for the loader:
+ * thisloader&lt;-customloader
+ * or
+ * thisloader
+ *
+ * @param loader the classloader to fix.
+ * @param language the language to use.
+ */
+ public void fixClassLoader(ClassLoader loader, String language) {
+ if (loader == getClass().getClassLoader()
+ || !(loader instanceof AntClassLoader)) {
+ return;
+ }
+ ClassLoader myLoader = getClass().getClassLoader();
+ AntClassLoader fixLoader = (AntClassLoader) loader;
+
+ // Check for location of bsf in this classloader
+ File bsfSource = getClassSource(BSF_MANAGER);
+
+ // If bsf is not in the classloader for this, need to move
+ // runner.
+ boolean needMoveRunner = (bsfSource == null);
+
+ // Check for location of language
+ String languageClassName = (String) BSF_LANGUAGE_MAP.get(language);
+
+ // Check if need to need to move bsf
+ boolean needMoveBsf =
+ bsfSource != null
+ && languageClassName != null
+ && !LoaderUtils.classExists(myLoader, languageClassName)
+ && LoaderUtils.classExists(loader, languageClassName);
+
+ // Update need to move runner
+ needMoveRunner = needMoveRunner || needMoveBsf;
+
+ // Check if bsf in place
+ if (bsfSource == null) {
+ bsfSource = getClassSource(loader, BSF_MANAGER);
+ }
+
+ if (bsfSource == null) {
+ throw new BuildException(
+ "Unable to find BSF classes for scripting");
+ }
+
+ if (needMoveBsf) {
+ fixLoader.addPathComponent(bsfSource);
+ fixLoader.addLoaderPackageRoot(BSF_PACKAGE);
+ }
+
+ if (needMoveRunner) {
+ fixLoader.addPathComponent(
+ LoaderUtils.getResourceSource(
+ fixLoader,
+ LoaderUtils.classNameToResource(BSF_SCRIPT_RUNNER)));
+ fixLoader.addLoaderPackageRoot(UTIL_OPTIONAL_PACKAGE);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunner.java
new file mode 100644
index 00000000..735e5555
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunner.java
@@ -0,0 +1,27 @@
+/*
+ * 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.tools.ant.util;
+
+/**
+ * This class is here for backwards compatibility.
+ * @deprecated Implementation moved to another location. Use
+ * that org.apache.tools.ant.types.optional.ScriptRunner instead.
+ */
+public class ScriptRunner
+ extends org.apache.tools.ant.util.optional.ScriptRunner {
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java
new file mode 100644
index 00000000..b8aa01a8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunnerBase.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.tools.ant.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * This is a common abstract base case for script runners.
+ * These classes need to implement executeScript, evaluateScript
+ * and supportsLanguage.
+ * @since Ant 1.7.0
+ */
+public abstract class ScriptRunnerBase {
+ /** Whether to keep the engine between calls to execute/eval */
+ private boolean keepEngine = false;
+
+ /** Script language */
+ private String language;
+
+ /** Script content */
+ private String script = "";
+
+ /** Project this runner is used in */
+ private Project project;
+
+ /** Classloader to be used when running the script. */
+ private ClassLoader scriptLoader;
+
+ /** Beans to be provided to the script */
+ private Map beans = new HashMap();
+
+ /**
+ * Add a list of named objects to the list to be exported to the script
+ *
+ * @param dictionary a map of objects to be placed into the script context
+ * indexed by String names.
+ */
+ public void addBeans(Map dictionary) {
+ for (Iterator i = dictionary.keySet().iterator(); i.hasNext();) {
+ String key = (String) i.next();
+ try {
+ Object val = dictionary.get(key);
+ addBean(key, val);
+ } catch (BuildException ex) {
+ // The key is in the dictionary but cannot be retrieved
+ // This is usually due references that refer to tasks
+ // that have not been taskdefed in the current run.
+ // Ignore
+ }
+ }
+ }
+
+ /**
+ * Add a single object into the script context.
+ *
+ * @param key the name in the context this object is to stored under.
+ * @param bean the object to be stored in the script context.
+ */
+ public void addBean(String key, Object bean) {
+ boolean isValid = key.length() > 0
+ && Character.isJavaIdentifierStart(key.charAt(0));
+
+ for (int i = 1; isValid && i < key.length(); i++) {
+ isValid = Character.isJavaIdentifierPart(key.charAt(i));
+ }
+
+ if (isValid) {
+ beans.put(key, bean);
+ }
+ }
+
+ /**
+ * Get the beans used for the script.
+ * @return the map of beans.
+ */
+ protected Map getBeans() {
+ return beans;
+ }
+
+ /**
+ * Do the work.
+ * @param execName the name that will be passed to BSF for this script
+ * execution.
+ */
+ public abstract void executeScript(String execName);
+
+ /**
+ * Evaluate the script.
+ * @param execName the name that will be passed to the
+ * scripting engine for this script execution.
+ * @return the result of evaluating the script.
+ */
+ public abstract Object evaluateScript(String execName);
+
+ /**
+ * Check if a script engine can be created for
+ * this language.
+ * @return true if a script engine can be created, false
+ * otherwise.
+ */
+ public abstract boolean supportsLanguage();
+
+ /**
+ * Get the name of the manager prefix used for this
+ * scriptrunner.
+ * @return the prefix string.
+ */
+ public abstract String getManagerName();
+
+ /**
+ * Defines the language (required).
+ * @param language the scripting language name for the script.
+ */
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ /**
+ * Get the script language
+ * @return the script language
+ */
+ public String getLanguage() {
+ return language;
+ }
+
+ /**
+ * Set the script classloader.
+ * @param classLoader the classloader to use.
+ */
+ public void setScriptClassLoader(ClassLoader classLoader) {
+ this.scriptLoader = classLoader;
+ }
+
+ /**
+ * Get the classloader used to load the script engine.
+ * @return the classloader.
+ */
+ protected ClassLoader getScriptClassLoader() {
+ return scriptLoader;
+ }
+
+ /**
+ * Whether to keep the script engine between calls.
+ * @param keepEngine if true, keep the engine.
+ */
+ public void setKeepEngine(boolean keepEngine) {
+ this.keepEngine = keepEngine;
+ }
+
+ /**
+ * Get the keep engine attribute.
+ * @return the attribute.
+ */
+ public boolean getKeepEngine() {
+ return keepEngine;
+ }
+
+ /**
+ * Load the script from an external file; optional.
+ * @param file the file containing the script source.
+ */
+ public void setSrc(File file) {
+ String filename = file.getPath();
+ if (!file.exists()) {
+ throw new BuildException("file " + filename + " not found.");
+ }
+ try {
+ readSource(new FileReader(file), filename);
+ } catch (FileNotFoundException e) {
+ //this can only happen if the file got deleted a short moment ago
+ throw new BuildException("file " + filename + " not found.");
+ }
+ }
+
+ /**
+ * Read some source in from the given reader
+ * @param reader the reader; this is closed afterwards.
+ * @param name the name to use in error messages
+ */
+ private void readSource(Reader reader, String name) {
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(reader);
+ script += FileUtils.safeReadFully(in);
+ } catch (IOException ex) {
+ throw new BuildException("Failed to read " + name, ex);
+ } finally {
+ FileUtils.close(in);
+ }
+ }
+
+
+ /**
+ * Add a resource to the source list.
+ * @since Ant 1.7.1
+ * @param sourceResource the resource to load
+ * @throws BuildException if the resource cannot be read
+ */
+ public void loadResource(Resource sourceResource) {
+ String name = sourceResource.toLongString();
+ InputStream in = null;
+ try {
+ in = sourceResource.getInputStream();
+ } catch (IOException e) {
+ throw new BuildException("Failed to open " + name, e);
+ } catch (UnsupportedOperationException e) {
+ throw new BuildException(
+ "Failed to open " + name + " -it is not readable", e);
+ }
+ readSource(new InputStreamReader(in), name);
+ }
+
+ /**
+ * Add all resources in a resource collection to the source list.
+ * @since Ant 1.7.1
+ * @param collection the resource to load
+ * @throws BuildException if a resource cannot be read
+ */
+ public void loadResources(ResourceCollection collection) {
+ for (Resource resource : collection) {
+ loadResource(resource);
+ }
+ }
+
+ /**
+ * Set the script text. Properties in the text are not expanded!
+ *
+ * @param text a component of the script text to be added.
+ */
+ public void addText(String text) {
+ script += text;
+ }
+
+ /**
+ * Get the current script text content.
+ * @return the script text.
+ */
+ public String getScript() {
+ return script;
+ }
+
+ /**
+ * Clear the current script text content.
+ */
+ public void clearScript() {
+ this.script = "";
+ }
+
+ /**
+ * Set the project for this runner.
+ * @param project the project.
+ */
+ public void setProject(Project project) {
+ this.project = project;
+ }
+
+ /**
+ * Get the project for this runner.
+ * @return the project.
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * Bind the runner to a project component.
+ * Properties, targets and references are all added as beans;
+ * project is bound to project, and self to the component.
+ * @param component to become <code>self</code>
+ */
+ public void bindToComponent(ProjectComponent component) {
+ project = component.getProject();
+ addBeans(project.getProperties());
+ addBeans(project.getUserProperties());
+ addBeans(project.getCopyOfTargets());
+ addBeans(project.getCopyOfReferences());
+ addBean("project", project);
+ addBean("self", component);
+ }
+
+ /**
+ * Bind the runner to a project component.
+ * The project and self are the only beans set.
+ * @param component to become <code>self</code>
+ */
+ public void bindToComponentMinimum(ProjectComponent component) {
+ project = component.getProject();
+ addBean("project", project);
+ addBean("self", component);
+ }
+
+ /**
+ * Check if the language attribute is set.
+ * @throws BuildException if it is not.
+ */
+ protected void checkLanguage() {
+ if (language == null) {
+ throw new BuildException(
+ "script language must be specified");
+ }
+ }
+
+ /**
+ * Replace the current context classloader with the
+ * script context classloader.
+ * @return the current context classloader.
+ */
+ protected ClassLoader replaceContextLoader() {
+ ClassLoader origContextClassLoader =
+ Thread.currentThread().getContextClassLoader();
+ if (getScriptClassLoader() == null) {
+ setScriptClassLoader(getClass().getClassLoader());
+ }
+ Thread.currentThread().setContextClassLoader(getScriptClassLoader());
+ return origContextClassLoader;
+ }
+
+ /**
+ * Restore the context loader with the original context classloader.
+ *
+ * script context loader.
+ * @param origLoader the original context classloader.
+ */
+ protected void restoreContextLoader(ClassLoader origLoader) {
+ Thread.currentThread().setContextClassLoader(
+ origLoader);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunnerCreator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunnerCreator.java
new file mode 100644
index 00000000..77e9ee73
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunnerCreator.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.tools.ant.util;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * This is a helper class used by ScriptRunnerHelper to
+ * create a ScriptRunner based on a classloader and on a language.
+ */
+public class ScriptRunnerCreator {
+ private static final String AUTO = "auto";
+ private static final String OATAU = "org.apache.tools.ant.util";
+ private static final String UTIL_OPT = OATAU + ".optional";
+
+ private static final String BSF = "bsf";
+ private static final String BSF_PACK = "org.apache.bsf";
+ private static final String BSF_MANAGER = BSF_PACK + ".BSFManager";
+ private static final String BSF_RUNNER = UTIL_OPT + ".ScriptRunner";
+
+ private static final String JAVAX = "javax";
+ private static final String JAVAX_MANAGER = "javax.script.ScriptEngineManager";
+ private static final String JAVAX_RUNNER = UTIL_OPT + ".JavaxScriptRunner";
+
+ private Project project;
+ private String manager;
+ private String language;
+ private ClassLoader scriptLoader = null;
+
+ /**
+ * Constructor for creator.
+ * @param project the current project.
+ */
+ public ScriptRunnerCreator(Project project) {
+ this.project = project;
+ }
+
+ /**
+ * Create a ScriptRunner.
+ * @param manager the script manager ("auto" | "bsf" | "javax")
+ * @param language the language.
+ * @param classLoader the classloader to use
+ * @return the created script runner.
+ * @throws BuildException if unable to create the ScriptRunner.
+ */
+ public synchronized ScriptRunnerBase createRunner(
+ String manager, String language, ClassLoader classLoader) {
+ this.manager = manager;
+ this.language = language;
+ this.scriptLoader = classLoader;
+
+ if (language == null) {
+ throw new BuildException("script language must be specified");
+ }
+ if (!manager.equals(AUTO) && !manager.equals(JAVAX) && !manager.equals(BSF)) {
+ throw new BuildException("Unsupported language prefix " + manager);
+ }
+
+ // Check for bsf first then javax
+ // This version does not check if the scriptManager
+ // supports the language.
+
+ ScriptRunnerBase ret = null;
+ ret = createRunner(BSF, BSF_MANAGER, BSF_RUNNER);
+ if (ret == null) {
+ ret = createRunner(JAVAX, JAVAX_MANAGER, JAVAX_RUNNER);
+ }
+ if (ret != null) {
+ return ret;
+ }
+ if (JAVAX.equals(manager)) {
+ throw new BuildException(
+ "Unable to load the script engine manager " + "(" + JAVAX_MANAGER + ")");
+ }
+ if (BSF.equals(manager)) {
+ throw new BuildException(
+ "Unable to load the BSF script engine manager " + "(" + BSF_MANAGER + ")");
+ }
+ throw new BuildException("Unable to load a script engine manager "
+ + "(" + BSF_MANAGER + " or " + JAVAX_MANAGER + ")");
+ }
+
+ /**
+ * Create a script runner if the scriptManager matches the passed
+ * in manager.
+ * This checks if the script manager exists in the scriptLoader
+ * classloader and if so it creates and returns the script runner.
+ * @param checkManager check if the manager matchs this value.
+ * @param managerClass the name of the script manager class.
+ * @param runnerClass the name of ant's script runner for this manager.
+ * @return the script runner class.
+ * @throws BuildException if there is a problem creating the runner class.
+ */
+ private ScriptRunnerBase createRunner(
+ String checkManager, String managerClass, String runnerClass) {
+ ScriptRunnerBase runner = null;
+ if (!manager.equals(AUTO) && !manager.equals(checkManager)) {
+ return null;
+ }
+ if (scriptLoader.getResource(LoaderUtils.classNameToResource(managerClass)) == null) {
+ return null;
+ }
+ if (managerClass.equals(BSF_MANAGER)) {
+ new ScriptFixBSFPath().fixClassLoader(scriptLoader, language);
+ }
+ try {
+ runner = (ScriptRunnerBase) Class.forName(
+ runnerClass, true, scriptLoader).newInstance();
+ runner.setProject(project);
+ } catch (Exception ex) {
+ throw ReflectUtil.toBuildException(ex);
+ }
+ runner.setLanguage(language);
+ runner.setScriptClassLoader(scriptLoader);
+ return runner;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunnerHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunnerHelper.java
new file mode 100644
index 00000000..9e814d96
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ScriptRunnerHelper.java
@@ -0,0 +1,206 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.File;
+
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.Union;
+
+/**
+ * A class to help in creating, setting and getting script runners.
+ */
+public class ScriptRunnerHelper {
+ private ClasspathUtils.Delegate cpDelegate = null;
+ private File srcFile;
+ private String manager = "auto";
+ private String language;
+ private String text;
+ private boolean setBeans = true;
+ private ProjectComponent projectComponent;
+ private ClassLoader scriptLoader = null;
+ private Union resources = new Union();
+
+ /**
+ * Set the project component associated with this helper.
+ * @param component the project component that owns this helper.
+ */
+ public void setProjectComponent(ProjectComponent component) {
+ this.projectComponent = component;
+ }
+
+ /**
+ * Create and set text on a script.
+ * @return the created or reused script runner.
+ */
+ public ScriptRunnerBase getScriptRunner() {
+ ScriptRunnerBase runner = getRunner();
+ if (srcFile != null) {
+ runner.setSrc(srcFile);
+ }
+ if (text != null) {
+ runner.addText(text);
+ }
+ if (resources != null) {
+ runner.loadResources(resources);
+ }
+ if (setBeans) {
+ runner.bindToComponent(projectComponent);
+ } else {
+ runner.bindToComponentMinimum(projectComponent);
+ }
+ return runner;
+ }
+
+ /**
+ * Classpath to be used when searching for classes and resources.
+ *
+ * @return an empty Path instance to be configured by Ant.
+ */
+ public Path createClasspath() {
+ return getClassPathDelegate().createClasspath();
+ }
+
+ /**
+ * Set the classpath to be used when searching for classes and resources.
+ *
+ * @param classpath an Ant Path object containing the search path.
+ */
+ public void setClasspath(Path classpath) {
+ getClassPathDelegate().setClasspath(classpath);
+ }
+
+ /**
+ * Set the classpath by reference.
+ *
+ * @param r a Reference to a Path instance to be used as the classpath
+ * value.
+ */
+ public void setClasspathRef(Reference r) {
+ getClassPathDelegate().setClasspathref(r);
+ }
+
+ /**
+ * Load the script from an external file ; optional.
+ *
+ * @param file the file containing the script source.
+ */
+ public void setSrc(File file) {
+ this.srcFile = file;
+ }
+
+ /**
+ * Add script text.
+ *
+ * @param text a component of the script text to be added.
+ */
+ public void addText(String text) {
+ this.text = text;
+ }
+
+ /**
+ * Defines the script manager - defaults to "auto".
+ *
+ * @param manager the scripting manager - "bsf" or "javax" or "auto"
+ */
+ public void setManager(String manager) {
+ this.manager = manager;
+ }
+
+ /**
+ * Defines the language (required).
+ *
+ * @param language the scripting language name for the script.
+ */
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ /**
+ * Get the language.
+ * @return the scripting language.
+ */
+ public String getLanguage() {
+ return language;
+ }
+
+ /**
+ * Set the setbeans attribute.
+ * If this is true, &lt;script&gt; will create variables in the
+ * script instance for all
+ * properties, targets and references of the current project.
+ * It this is false, only the project and self variables will
+ * be set.
+ * The default is true.
+ * @param setBeans the value to set.
+ */
+ public void setSetBeans(boolean setBeans) {
+ this.setBeans = setBeans;
+ }
+
+ /**
+ * Used when called by scriptdef.
+ * @param loader the loader used by scriptdef.
+ */
+ public void setClassLoader(ClassLoader loader) {
+ scriptLoader = loader;
+ }
+
+ private synchronized ClassLoader generateClassLoader() {
+ if (scriptLoader != null) {
+ return scriptLoader;
+ }
+ if (cpDelegate == null) {
+ scriptLoader = getClass().getClassLoader();
+ return scriptLoader;
+ }
+ scriptLoader = cpDelegate.getClassLoader();
+ return scriptLoader;
+ }
+
+ private ClasspathUtils.Delegate getClassPathDelegate() {
+ if (cpDelegate == null) {
+ if (projectComponent == null) {
+ throw new IllegalStateException("Can't access classpath without a project component");
+ }
+ cpDelegate = ClasspathUtils.getDelegate(projectComponent);
+ }
+ return cpDelegate;
+ }
+
+ /**
+ * Get a script runner.
+ */
+ private ScriptRunnerBase getRunner() {
+ return new ScriptRunnerCreator(projectComponent.getProject()).createRunner(
+ manager, language, generateClassLoader());
+ }
+
+ /**
+ * Add any source resource.
+ *
+ * @param resource source of script
+ * @since Ant 1.7.1
+ */
+ public void add(ResourceCollection resource) {
+ resources.add(resource);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/SourceFileScanner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/SourceFileScanner.java
new file mode 100644
index 00000000..c79f0347
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/SourceFileScanner.java
@@ -0,0 +1,173 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.File;
+import java.util.Vector;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.types.resources.FileResource;
+
+/**
+ * Utility class that collects the functionality of the various
+ * scanDir methods that have been scattered in several tasks before.
+ *
+ * <p>The only method returns an array of source files. The array is a
+ * subset of the files given as a parameter and holds only those that
+ * are newer than their corresponding target files.</p>
+ *
+ */
+public class SourceFileScanner implements ResourceFactory {
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected Task task;
+ // CheckStyle:VisibilityModifier ON
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+ private File destDir; // base directory of the fileset
+
+ /**
+ * Construct a new SourceFileScanner.
+ * @param task The task we should log messages through.
+ */
+ public SourceFileScanner(Task task) {
+ this.task = task;
+ }
+
+ /**
+ * Restrict the given set of files to those that are newer than
+ * their corresponding target files.
+ *
+ * @param files the original set of files.
+ * @param srcDir all files are relative to this directory.
+ * @param destDir target files live here. if null file names
+ * returned by the mapper are assumed to be absolute.
+ * @param mapper knows how to construct a target file names from
+ * source file names.
+ * @return an array of filenames.
+ */
+ public String[] restrict(String[] files, File srcDir, File destDir,
+ FileNameMapper mapper) {
+ return restrict(files, srcDir, destDir, mapper,
+ FILE_UTILS.getFileTimestampGranularity());
+ }
+
+ /**
+ * Restrict the given set of files to those that are newer than
+ * their corresponding target files.
+ *
+ * @param files the original set of files.
+ * @param srcDir all files are relative to this directory.
+ * @param destDir target files live here. If null file names
+ * returned by the mapper are assumed to be absolute.
+ * @param mapper knows how to construct a target file names from
+ * source file names.
+ * @param granularity The number of milliseconds leeway to give
+ * before deciding a target is out of date.
+ * @return an array of filenames.
+ *
+ * @since Ant 1.6.2
+ */
+ public String[] restrict(String[] files, File srcDir, File destDir,
+ FileNameMapper mapper, long granularity) {
+ // record destdir for later use in getResource
+ this.destDir = destDir;
+ Vector v = new Vector();
+ for (int i = 0; i < files.length; i++) {
+ final String name = files[i];
+ v.addElement(new FileResource(srcDir, name) {
+ public String getName() {
+ return name;
+ }
+ });
+ }
+ Resource[] sourceresources = new Resource[v.size()];
+ v.copyInto(sourceresources);
+
+ // build the list of sources which are out of date with
+ // respect to the target
+ Resource[] outofdate =
+ ResourceUtils.selectOutOfDateSources(task, sourceresources,
+ mapper, this, granularity);
+ String[] result = new String[outofdate.length];
+ for (int counter = 0; counter < outofdate.length; counter++) {
+ result[counter] = outofdate[counter].getName();
+ }
+ return result;
+ }
+
+ /**
+ * Convenience layer on top of restrict that returns the source
+ * files as File objects (containing absolute paths if srcDir is
+ * absolute).
+ * @param files the original set of files.
+ * @param srcDir all files are relative to this directory.
+ * @param destDir target files live here. If null file names
+ * returned by the mapper are assumed to be absolute.
+ * @param mapper knows how to construct a target file names from
+ * source file names.
+ * @return an array of files.
+ */
+ public File[] restrictAsFiles(String[] files, File srcDir, File destDir,
+ FileNameMapper mapper) {
+ return restrictAsFiles(files, srcDir, destDir, mapper,
+ FILE_UTILS.getFileTimestampGranularity());
+ }
+
+ /**
+ * Convenience layer on top of restrict that returns the source
+ * files as File objects (containing absolute paths if srcDir is
+ * absolute).
+ *
+ * @param files the original set of files.
+ * @param srcDir all files are relative to this directory.
+ * @param destDir target files live here. If null file names
+ * returned by the mapper are assumed to be absolute.
+ * @param mapper knows how to construct a target file names from
+ * source file names.
+ * @param granularity The number of milliseconds leeway to give
+ * before deciding a target is out of date.
+ * @return an array of files.
+ * @since Ant 1.6.2
+ */
+ public File[] restrictAsFiles(String[] files, File srcDir, File destDir,
+ FileNameMapper mapper, long granularity) {
+ String[] res = restrict(files, srcDir, destDir, mapper, granularity);
+ File[] result = new File[res.length];
+ for (int i = 0; i < res.length; i++) {
+ result[i] = new File(srcDir, res[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Returns resource information for a file at destination.
+ * @param name relative path of file at destination.
+ * @return data concerning a file whose relative path to destDir is name.
+ *
+ * @since Ant 1.5.2
+ */
+ public Resource getResource(String name) {
+ return new FileResource(destDir, name);
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/SplitClassLoader.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/SplitClassLoader.java
new file mode 100644
index 00000000..f48d3d3e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/SplitClassLoader.java
@@ -0,0 +1,73 @@
+/*
+ * 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.tools.ant.util;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Specialized classloader for tasks that need finer grained control
+ * over which classes are to be loaded via Ant's classloader and which
+ * should not even if they are available.
+ */
+public final class SplitClassLoader extends AntClassLoader {
+
+ private final String[] splitClasses;
+
+ /**
+ * @param splitClasses classes contained herin will not be loaded
+ * via Ant's classloader
+ */
+ public SplitClassLoader(ClassLoader parent, Path path, Project project,
+ String[] splitClasses) {
+ super(parent, project, path, true);
+ this.splitClasses = splitClasses;
+ }
+
+ // forceLoadClass is not convenient here since it would not
+ // properly deal with inner classes of these classes.
+ protected synchronized Class loadClass(String classname, boolean resolve)
+ throws ClassNotFoundException {
+ Class theClass = findLoadedClass(classname);
+ if (theClass != null) {
+ return theClass;
+ }
+ if (isSplit(classname)) {
+ theClass = findClass(classname);
+ if (resolve) {
+ resolveClass(theClass);
+ }
+ return theClass;
+ } else {
+ return super.loadClass(classname, resolve);
+ }
+ }
+
+ private boolean isSplit(String classname) {
+ String simplename = classname.substring(classname.lastIndexOf('.') + 1);
+ for (int i = 0; i < splitClasses.length; i++) {
+ if (simplename.equals(splitClasses[i])
+ || simplename.startsWith(splitClasses[i] + '$')) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/StringTokenizer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/StringTokenizer.java
new file mode 100644
index 00000000..7addf310
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/StringTokenizer.java
@@ -0,0 +1,154 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Class to tokenize the input as areas separated
+ * by white space, or by a specified list of
+ * delim characters. Behaves like java.util.StringTokenizer.
+ * If the stream starts with delim characters, the first
+ * token will be an empty string (unless the treat delims
+ * as tokens flag is set).
+ * @since Ant 1.7
+ */
+public class StringTokenizer extends ProjectComponent implements Tokenizer {
+ private static final int NOT_A_CHAR = -2;
+ private String intraString = "";
+ private int pushed = NOT_A_CHAR;
+ private char[] delims = null;
+ private boolean delimsAreTokens = false;
+ private boolean suppressDelims = false;
+ private boolean includeDelims = false;
+
+ /**
+ * attribute delims - the delimiter characters
+ * @param delims a string containing the delimiter characters
+ */
+ public void setDelims(String delims) {
+ this.delims = StringUtils.resolveBackSlash(delims).toCharArray();
+ }
+
+ /**
+ * attribute delimsaretokens - treat delimiters as
+ * separate tokens.
+ * @param delimsAreTokens true if delimiters are to be separate
+ */
+
+ public void setDelimsAreTokens(boolean delimsAreTokens) {
+ this.delimsAreTokens = delimsAreTokens;
+ }
+ /**
+ * attribute suppressdelims - suppress delimiters.
+ * default - false
+ * @param suppressDelims if true do not report delimiters
+ */
+ public void setSuppressDelims(boolean suppressDelims) {
+ this.suppressDelims = suppressDelims;
+ }
+
+ /**
+ * attribute includedelims - treat delimiters as part
+ * of the token.
+ * default - false
+ * @param includeDelims if true add delimiters to the token
+ */
+ public void setIncludeDelims(boolean includeDelims) {
+ this.includeDelims = includeDelims;
+ }
+
+ /**
+ * find and return the next token
+ *
+ * @param in the input stream
+ * @return the token
+ * @exception IOException if an error occurs reading
+ */
+ public String getToken(Reader in) throws IOException {
+ int ch = -1;
+ if (pushed != NOT_A_CHAR) {
+ ch = pushed;
+ pushed = NOT_A_CHAR;
+ } else {
+ ch = in.read();
+ }
+ if (ch == -1) {
+ return null;
+ }
+ boolean inToken = true;
+ intraString = "";
+ StringBuffer word = new StringBuffer();
+ StringBuffer padding = new StringBuffer();
+ while (ch != -1) {
+ char c = (char) ch;
+ boolean isDelim = isDelim(c);
+ if (inToken) {
+ if (isDelim) {
+ if (delimsAreTokens) {
+ if (word.length() == 0) {
+ word.append(c);
+ } else {
+ pushed = ch;
+ }
+ break;
+ }
+ padding.append(c);
+ inToken = false;
+ } else {
+ word.append(c);
+ }
+ } else {
+ if (isDelim) {
+ padding.append(c);
+ } else {
+ pushed = ch;
+ break;
+ }
+ }
+ ch = in.read();
+ }
+ intraString = padding.toString();
+ if (includeDelims) {
+ word.append(intraString);
+ }
+ return word.toString();
+ }
+
+ /**
+ * @return the intratoken string
+ */
+ public String getPostToken() {
+ return suppressDelims || includeDelims ? "" : intraString;
+ }
+
+ private boolean isDelim(char ch) {
+ if (delims == null) {
+ return Character.isWhitespace(ch);
+ }
+ for (int i = 0; i < delims.length; ++i) {
+ if (delims[i] == ch) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/StringUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/StringUtils.java
new file mode 100644
index 00000000..626fb224
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/StringUtils.java
@@ -0,0 +1,273 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * A set of helper methods related to string manipulation.
+ *
+ */
+public final class StringUtils {
+ private static final long KILOBYTE = 1024;
+ private static final long MEGABYTE = KILOBYTE * 1024;
+ private static final long GIGABYTE = MEGABYTE * 1024;
+ private static final long TERABYTE = GIGABYTE * 1024;
+ private static final long PETABYTE = TERABYTE * 1024;
+
+ /**
+ * constructor to stop anyone instantiating the class
+ */
+ private StringUtils() {
+ }
+
+ /** the line separator for this OS */
+ public static final String LINE_SEP = System.getProperty("line.separator");
+
+ /**
+ * Splits up a string into a list of lines. It is equivalent
+ * to <tt>split(data, '\n')</tt>.
+ * @param data the string to split up into lines.
+ * @return the list of lines available in the string.
+ */
+ public static Vector<String> lineSplit(String data) {
+ return split(data, '\n');
+ }
+
+ /**
+ * Splits up a string where elements are separated by a specific
+ * character and return all elements.
+ * @param data the string to split up.
+ * @param ch the separator character.
+ * @return the list of elements.
+ */
+ public static Vector<String> split(String data, int ch) {
+ Vector<String> elems = new Vector<String>();
+ int pos = -1;
+ int i = 0;
+ while ((pos = data.indexOf(ch, i)) != -1) {
+ String elem = data.substring(i, pos);
+ elems.addElement(elem);
+ i = pos + 1;
+ }
+ elems.addElement(data.substring(i));
+ return elems;
+ }
+
+ /**
+ * Replace occurrences into a string.
+ * @param data the string to replace occurrences into
+ * @param from the occurrence to replace.
+ * @param to the occurrence to be used as a replacement.
+ * @return the new string with replaced occurrences.
+ * @deprecated Use {@link String#replace(CharSequence, CharSequence)} now.
+ */
+ public static String replace(String data, String from, String to) {
+ return data.replace(from, to);
+ }
+
+ /**
+ * Convenient method to retrieve the full stacktrace from a given exception.
+ * @param t the exception to get the stacktrace from.
+ * @return the stacktrace from the given exception.
+ */
+ public static String getStackTrace(Throwable t) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw, true);
+ t.printStackTrace(pw);
+ pw.flush();
+ pw.close();
+ return sw.toString();
+ }
+
+ /**
+ * Checks that a string buffer ends up with a given string. It may sound
+ * trivial with the existing
+ * JDK API but the various implementation among JDKs can make those
+ * methods extremely resource intensive
+ * and perform poorly due to massive memory allocation and copying. See
+ * @param buffer the buffer to perform the check on
+ * @param suffix the suffix
+ * @return <code>true</code> if the character sequence represented by the
+ * argument is a suffix of the character sequence represented by
+ * the StringBuffer object; <code>false</code> otherwise. Note that the
+ * result will be <code>true</code> if the argument is the
+ * empty string.
+ */
+ public static boolean endsWith(StringBuffer buffer, String suffix) {
+ if (suffix.length() > buffer.length()) {
+ return false;
+ }
+ // this loop is done on purpose to avoid memory allocation performance
+ // problems on various JDKs
+ // StringBuffer.lastIndexOf() was introduced in jdk 1.4 and
+ // implementation is ok though does allocation/copying
+ // StringBuffer.toString().endsWith() does massive memory
+ // allocation/copying on JDK 1.5
+ // See http://issues.apache.org/bugzilla/show_bug.cgi?id=37169
+ int endIndex = suffix.length() - 1;
+ int bufferIndex = buffer.length() - 1;
+ while (endIndex >= 0) {
+ if (buffer.charAt(bufferIndex) != suffix.charAt(endIndex)) {
+ return false;
+ }
+ bufferIndex--;
+ endIndex--;
+ }
+ return true;
+ }
+
+ /**
+ * xml does not do "c" like interpretation of strings.
+ * i.e. \n\r\t etc.
+ * this method processes \n, \r, \t, \f, \\
+ * also subs \s -&gt; " \n\r\t\f"
+ * a trailing '\' will be ignored
+ *
+ * @param input raw string with possible embedded '\'s
+ * @return converted string
+ * @since Ant 1.7
+ */
+ public static String resolveBackSlash(String input) {
+ StringBuffer b = new StringBuffer();
+ boolean backSlashSeen = false;
+ for (int i = 0; i < input.length(); ++i) {
+ char c = input.charAt(i);
+ if (!backSlashSeen) {
+ if (c == '\\') {
+ backSlashSeen = true;
+ } else {
+ b.append(c);
+ }
+ } else {
+ switch (c) {
+ case '\\':
+ b.append((char) '\\');
+ break;
+ case 'n':
+ b.append((char) '\n');
+ break;
+ case 'r':
+ b.append((char) '\r');
+ break;
+ case 't':
+ b.append((char) '\t');
+ break;
+ case 'f':
+ b.append((char) '\f');
+ break;
+ case 's':
+ b.append(" \t\n\r\f");
+ break;
+ default:
+ b.append(c);
+ }
+ backSlashSeen = false;
+ }
+ }
+ return b.toString();
+ }
+
+ /**
+ * Takes a human readable size representation eg 10K
+ * a long value. Doesn't support 1.1K or other rational values.
+ * @param humanSize the amount as a human readable string.
+ * @return a long value representation
+ * @throws Exception if there is a problem.
+ * @since Ant 1.7
+ */
+ public static long parseHumanSizes(String humanSize) throws Exception {
+ long factor = 1L;
+ char s = humanSize.charAt(0);
+ switch (s) {
+ case '+':
+ humanSize = humanSize.substring(1);
+ break;
+ case '-':
+ factor = -1L;
+ humanSize = humanSize.substring(1);
+ break;
+ default:
+ break;
+ }
+ //last character isn't a digit
+ char c = humanSize.charAt(humanSize.length() - 1);
+ if (!Character.isDigit(c)) {
+ int trim = 1;
+ switch (c) {
+ case 'K':
+ factor *= KILOBYTE;
+ break;
+ case 'M':
+ factor *= MEGABYTE;
+ break;
+ case 'G':
+ factor *= GIGABYTE;
+ break;
+ case 'T':
+ factor *= TERABYTE;
+ break;
+ case 'P':
+ factor *= PETABYTE;
+ break;
+ default:
+ trim = 0;
+ }
+ humanSize = humanSize.substring(0, humanSize.length() - trim);
+ }
+ try {
+ return factor * Long.parseLong(humanSize);
+ } catch (NumberFormatException e) {
+ throw new BuildException("Failed to parse \"" + humanSize + "\"", e);
+ }
+ }
+
+ /**
+ * Removes the suffix from a given string, if the string contains
+ * that suffix.
+ * @param string String for check
+ * @param suffix Suffix to remove
+ * @return the <i>string</i> with the <i>suffix</i>
+ */
+ public static String removeSuffix(String string, String suffix) {
+ if (string.endsWith(suffix)) {
+ return string.substring(0, string.length() - suffix.length());
+ } else {
+ return string;
+ }
+ }
+
+ /**
+ * Removes the prefix from a given string, if the string contains
+ * that prefix.
+ * @param string String for check
+ * @param prefix Prefix to remove
+ * @return the <i>string</i> with the <i>prefix</i>
+ */
+ public static String removePrefix(String string, String prefix) {
+ if (string.startsWith(prefix)) {
+ return string.substring(prefix.length());
+ } else {
+ return string;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/SymbolicLinkUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/SymbolicLinkUtils.java
new file mode 100644
index 00000000..3bc9918e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/SymbolicLinkUtils.java
@@ -0,0 +1,296 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+
+/**
+ * Contains methods related to symbolic links - or what Ant thinks is
+ * a symbolic link based on the absent support for them in Java.
+ *
+ * @since Ant 1.8.0
+ */
+public class SymbolicLinkUtils {
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Shared instance.
+ */
+ private static final SymbolicLinkUtils PRIMARY_INSTANCE =
+ new SymbolicLinkUtils();
+
+ /**
+ * Method to retrieve The SymbolicLinkUtils, which is shared by
+ * all users of this method.
+ * @return an instance of SymbolicLinkUtils.
+ */
+ public static SymbolicLinkUtils getSymbolicLinkUtils() {
+ // keep the door open for Java X.Y specific subclass if symbolic
+ // links ever become supported in the classlib
+ return PRIMARY_INSTANCE;
+ }
+
+ /**
+ * Empty constructor.
+ */
+ protected SymbolicLinkUtils() {
+ }
+
+ /**
+ * Checks whether a given file is a symbolic link.
+ *
+ * <p>It doesn't really test for symbolic links but whether the
+ * canonical and absolute paths of the file are identical--this
+ * may lead to false positives on some platforms.</p>
+ *
+ * @param file the file to test. Must not be null.
+ *
+ * @return true if the file is a symbolic link.
+ * @throws IOException on error.
+ */
+ public boolean isSymbolicLink(final File file) throws IOException {
+ return isSymbolicLink(file.getParentFile(), file.getName());
+ }
+
+ /**
+ * Checks whether a given file is a symbolic link.
+ *
+ * <p>It doesn't really test for symbolic links but whether the
+ * canonical and absolute paths of the file are identical--this
+ * may lead to false positives on some platforms.</p>
+ *
+ * @param name the name of the file to test.
+ *
+ * @return true if the file is a symbolic link.
+ * @throws IOException on error.
+ */
+ public boolean isSymbolicLink(final String name) throws IOException {
+ return isSymbolicLink(new File(name));
+ }
+
+ /**
+ * Checks whether a given file is a symbolic link.
+ *
+ * <p>It doesn't really test for symbolic links but whether the
+ * canonical and absolute paths of the file are identical--this
+ * may lead to false positives on some platforms.</p>
+ *
+ * @param parent the parent directory of the file to test
+ * @param name the name of the file to test.
+ *
+ * @return true if the file is a symbolic link.
+ * @throws IOException on error.
+ */
+ public boolean isSymbolicLink(final File parent, final String name)
+ throws IOException {
+ final File toTest = parent != null
+ ? new File(parent.getCanonicalPath(), name)
+ : new File(name);
+ return !toTest.getAbsolutePath().equals(toTest.getCanonicalPath());
+ }
+
+ /**
+ * Checks whether a given file is a broken symbolic link.
+ *
+ * <p>It doesn't really test for symbolic links but whether Java
+ * reports that the File doesn't exist but its parent's child list
+ * contains it--this may lead to false positives on some
+ * platforms.</p>
+ *
+ * <p>Note that #isSymbolicLink returns false if this method
+ * returns true since Java won't produce a canonical name
+ * different from the abolute one if the link is broken.</p>
+ *
+ * @param name the name of the file to test.
+ *
+ * @return true if the file is a broken symbolic link.
+ * @throws IOException on error.
+ */
+ public boolean isDanglingSymbolicLink(final String name) throws IOException {
+ return isDanglingSymbolicLink(new File(name));
+ }
+
+ /**
+ * Checks whether a given file is a broken symbolic link.
+ *
+ * <p>It doesn't really test for symbolic links but whether Java
+ * reports that the File doesn't exist but its parent's child list
+ * contains it--this may lead to false positives on some
+ * platforms.</p>
+ *
+ * <p>Note that #isSymbolicLink returns false if this method
+ * returns true since Java won't produce a canonical name
+ * different from the abolute one if the link is broken.</p>
+ *
+ * @param file the file to test.
+ *
+ * @return true if the file is a broken symbolic link.
+ * @throws IOException on error.
+ */
+ public boolean isDanglingSymbolicLink(final File file) throws IOException {
+ return isDanglingSymbolicLink(file.getParentFile(), file.getName());
+ }
+
+ /**
+ * Checks whether a given file is a broken symbolic link.
+ *
+ * <p>It doesn't really test for symbolic links but whether Java
+ * reports that the File doesn't exist but its parent's child list
+ * contains it--this may lead to false positives on some
+ * platforms.</p>
+ *
+ * <p>Note that #isSymbolicLink returns false if this method
+ * returns true since Java won't produce a canonical name
+ * different from the abolute one if the link is broken.</p>
+ *
+ * @param parent the parent directory of the file to test
+ * @param name the name of the file to test.
+ *
+ * @return true if the file is a broken symbolic link.
+ * @throws IOException on error.
+ */
+ public boolean isDanglingSymbolicLink(final File parent, final String name)
+ throws IOException {
+ final File f = new File(parent, name);
+ if (!f.exists()) {
+ final String localName = f.getName();
+ final String[] c = parent.list(new FilenameFilter() {
+ public boolean accept(final File d, final String n) {
+ return localName.equals(n);
+ }
+ });
+ return c != null && c.length > 0;
+ }
+ return false;
+ }
+
+ /**
+ * Delete a symlink (without deleting the associated resource).
+ *
+ * <p>This is a utility method that removes a unix symlink without
+ * removing the resource that the symlink points to. If it is
+ * accidentally invoked on a real file, the real file will not be
+ * harmed, but silently ignored.</p>
+ *
+ * <p>Normally this method works by
+ * getting the canonical path of the link, using the canonical path to
+ * rename the resource (breaking the link) and then deleting the link.
+ * The resource is then returned to its original name inside a finally
+ * block to ensure that the resource is unharmed even in the event of
+ * an exception.</p>
+ *
+ * <p>There may be cases where the algorithm described above doesn't work,
+ * in that case the method tries to use the native "rm" command on
+ * the symlink instead.</p>
+ *
+ * @param link A <code>File</code> object of the symlink to delete.
+ * @param task An Ant Task required if "rm" needs to be invoked.
+ *
+ * @throws IOException If calls to <code>File.rename</code>,
+ * <code>File.delete</code> or <code>File.getCanonicalPath</code>
+ * fail.
+ * @throws BuildException if the execution of "rm" failed.
+ */
+ public void deleteSymbolicLink(File link, final Task task)
+ throws IOException {
+ if (isDanglingSymbolicLink(link)) {
+ if (!link.delete()) {
+ throw new IOException("failed to remove dangling symbolic link "
+ + link);
+ }
+ return;
+ }
+
+ if (!isSymbolicLink(link)) {
+ // plain file, not a link
+ return;
+ }
+
+ if (!link.exists()) {
+ throw new FileNotFoundException("No such symbolic link: " + link);
+ }
+
+ // find the resource of the existing link:
+ final File target = link.getCanonicalFile();
+
+ // no reason to try the renaming algorithm if we aren't allowed to
+ // write to the target's parent directory. Let's hope that
+ // File.canWrite works on all platforms.
+
+ if (task == null || target.getParentFile().canWrite()) {
+
+ // rename the resource, thus breaking the link:
+ final File temp = FILE_UTILS.createTempFile("symlink", ".tmp",
+ target.getParentFile(), false,
+ false);
+
+ if (FILE_UTILS.isLeadingPath(target, link)) {
+ // link points to a parent directory, renaming the parent
+ // will rename the file
+ link = new File(temp,
+ FILE_UTILS.removeLeadingPath(target, link));
+ }
+
+ boolean renamedTarget = false;
+ try {
+ try {
+ FILE_UTILS.rename(target, temp);
+ renamedTarget = true;
+ } catch (final IOException e) {
+ throw new IOException("Couldn't rename resource when "
+ + "attempting to delete '" + link
+ + "'. Reason: " + e.getMessage());
+ }
+ // delete the (now) broken link:
+ if (!link.delete()) {
+ throw new IOException("Couldn't delete symlink: "
+ + link
+ + " (was it a real file? is this "
+ + "not a UNIX system?)");
+ }
+ } finally {
+ if (renamedTarget) {
+ // return the resource to its original name:
+ try {
+ FILE_UTILS.rename(temp, target);
+ } catch (final IOException e) {
+ throw new IOException("Couldn't return resource "
+ + temp
+ + " to its original name: "
+ + target.getAbsolutePath()
+ + ". Reason: " + e.getMessage()
+ + "\n THE RESOURCE'S NAME ON DISK"
+ + " HAS BEEN CHANGED BY THIS"
+ + " ERROR!\n");
+ }
+ }
+ }
+ } else {
+ Execute.runCommand(task,
+ new String[] {"rm", link.getAbsolutePath()});
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/TaskLogger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/TaskLogger.java
new file mode 100644
index 00000000..9ce5c512
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/TaskLogger.java
@@ -0,0 +1,79 @@
+/*
+ * 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.tools.ant.util;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * A facade that makes logging nicer to use.
+ */
+public final class TaskLogger {
+ /**
+ * Task to use to do logging.
+ */
+ private Task task;
+
+ /**
+ * Constructor for the TaskLogger
+ * @param task the task
+ */
+ public TaskLogger(final Task task) {
+ this.task = task;
+ }
+
+ /**
+ * Log a message with <code>MSG_INFO</code> priority
+ * @param message the message to log
+ */
+ public void info(final String message) {
+ task.log(message, Project.MSG_INFO);
+ }
+
+ /**
+ * Log a message with <code>MSG_ERR</code> priority
+ * @param message the message to log
+ */
+ public void error(final String message) {
+ task.log(message, Project.MSG_ERR);
+ }
+
+ /**
+ * Log a message with <code>MSG_WARN</code> priority
+ * @param message the message to log
+ */
+ public void warning(final String message) {
+ task.log(message, Project.MSG_WARN);
+ }
+
+ /**
+ * Log a message with <code>MSG_VERBOSE</code> priority
+ * @param message the message to log
+ */
+ public void verbose(final String message) {
+ task.log(message, Project.MSG_VERBOSE);
+ }
+
+ /**
+ * Log a message with <code>MSG_DEBUG</code> priority
+ * @param message the message to log
+ */
+ public void debug(final String message) {
+ task.log(message, Project.MSG_DEBUG);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/TeeOutputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/TeeOutputStream.java
new file mode 100644
index 00000000..eb8da3fa
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/TeeOutputStream.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.tools.ant.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A simple T-piece to replicate an output stream into two separate streams
+ *
+ */
+public class TeeOutputStream extends OutputStream {
+ private OutputStream left;
+ private OutputStream right;
+
+ /**
+ * Constructor for TeeOutputStream.
+ * @param left one of the output streams.
+ * @param right the other output stream.
+ */
+ public TeeOutputStream(OutputStream left, OutputStream right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ /**
+ * Close both output streams.
+ * @throws IOException on error.
+ */
+ public void close() throws IOException {
+ try {
+ left.close();
+ } finally {
+ right.close();
+ }
+ }
+
+ /**
+ * Flush both output streams.
+ * @throws IOException on error
+ */
+ public void flush() throws IOException {
+ left.flush();
+ right.flush();
+ }
+
+ /**
+ * Write a byte array to both output streams.
+ * @param b an array of bytes.
+ * @throws IOException on error.
+ */
+ public void write(byte[] b) throws IOException {
+ left.write(b);
+ right.write(b);
+ }
+
+ /**
+ * Write a byte array to both output streams.
+ * @param b the data.
+ * @param off the start offset in the data.
+ * @param len the number of bytes to write.
+ * @throws IOException on error.
+ */
+ public void write(byte[] b, int off, int len) throws IOException {
+ left.write(b, off, len);
+ right.write(b, off, len);
+ }
+
+ /**
+ * Write a byte to both output streams.
+ * @param b the byte to write.
+ * @throws IOException on error.
+ */
+ public void write(int b) throws IOException {
+ left.write(b);
+ right.write(b);
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/TimeoutObserver.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/TimeoutObserver.java
new file mode 100644
index 00000000..ba2e0c76
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/TimeoutObserver.java
@@ -0,0 +1,37 @@
+/*
+ * 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.tools.ant.util;
+
+/**
+ * Interface for classes that want to be notified by Watchdog.
+ *
+ * @since Ant 1.5
+ *
+ * @see org.apache.tools.ant.util.Watchdog
+ *
+ */
+public interface TimeoutObserver {
+
+ /**
+ * Called when the watchdow times out.
+ *
+ * @param w the watchdog that timed out.
+ */
+ void timeoutOccured(Watchdog w);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Tokenizer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Tokenizer.java
new file mode 100644
index 00000000..25f89650
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Tokenizer.java
@@ -0,0 +1,44 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * input stream tokenizers implement this interface
+ *
+ * @version Ant 1.6
+ */
+public interface Tokenizer {
+ /**
+ * get the next token from the input stream
+ * @param in the input stream
+ * @return the next token, or null for the end
+ * of the stream
+ * @throws IOException if an error occurs
+ */
+ String getToken(Reader in) throws IOException;
+
+ /**
+ * return the string between tokens, after the
+ * previous token.
+ * @return the intra-token string
+ */
+ String getPostToken();
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/UUEncoder.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/UUEncoder.java
new file mode 100644
index 00000000..77e1bee1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/UUEncoder.java
@@ -0,0 +1,148 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+/**
+ * UUEncoding of an input stream placed into an outputstream.
+ * This class is meant to be a drop in replacement for
+ * sun.misc.UUEncoder, which was previously used by Ant.
+ * The uuencode algorithm code has been copied from the
+ * geronimo project.
+ **/
+
+public class UUEncoder {
+ protected static final int DEFAULT_MODE = 644;
+ private static final int MAX_CHARS_PER_LINE = 45;
+ private static final int INPUT_BUFFER_SIZE = MAX_CHARS_PER_LINE * 100;
+ private OutputStream out;
+ private String name;
+
+ /**
+ * Constructor specifying a name for the encoded buffer, begin
+ * line will be:
+ * <pre>
+ * begin 644 [NAME]
+ * </pre>
+ * @param name the name of the encoded buffer.
+ */
+ public UUEncoder(String name) {
+ this.name = name;
+ }
+
+ /**
+ * UUEncode bytes from the input stream, and write them as text characters
+ * to the output stream. This method will run until it exhausts the
+ * input stream.
+ * @param is the input stream.
+ * @param out the output stream.
+ * @throws IOException if there is an error.
+ */
+ public void encode(InputStream is, OutputStream out)
+ throws IOException {
+ this.out = out;
+ encodeBegin();
+ byte[] buffer = new byte[INPUT_BUFFER_SIZE];
+ int count;
+ while ((count = is.read(buffer, 0, buffer.length)) != -1) {
+ int pos = 0;
+ while (count > 0) {
+ int num = count > MAX_CHARS_PER_LINE
+ ? MAX_CHARS_PER_LINE
+ : count;
+ encodeLine(buffer, pos, num, out);
+ pos += num;
+ count -= num;
+ }
+ }
+ out.flush();
+ encodeEnd();
+ }
+
+ /**
+ * Encode a string to the output.
+ */
+ private void encodeString(String n) throws IOException {
+ PrintStream writer = new PrintStream(out);
+ writer.print(n);
+ writer.flush();
+ }
+
+ private void encodeBegin() throws IOException {
+ encodeString("begin " + DEFAULT_MODE + " " + name + "\n");
+ }
+
+ private void encodeEnd() throws IOException {
+ encodeString(" \nend\n");
+ }
+
+ /**
+ * Encode a single line of data (less than or equal to 45 characters).
+ *
+ * @param data The array of byte data.
+ * @param off The starting offset within the data.
+ * @param length Length of the data to encode.
+ * @param out The output stream the encoded data is written to.
+ *
+ * @exception IOException
+ */
+ private void encodeLine(
+ byte[] data, int offset, int length, OutputStream out)
+ throws IOException {
+ // write out the number of characters encoded in this line.
+ // CheckStyle:MagicNumber OFF
+ out.write((byte) ((length & 0x3F) + ' '));
+ // CheckStyle:MagicNumber ON
+ byte a;
+ byte b;
+ byte c;
+
+ for (int i = 0; i < length;) {
+ // set the padding defaults
+ b = 1;
+ c = 1;
+ // get the next 3 bytes (if we have them)
+ a = data[offset + i++];
+ if (i < length) {
+ b = data[offset + i++];
+ if (i < length) {
+ c = data[offset + i++];
+ }
+ }
+
+ // CheckStyle:MagicNumber OFF
+ byte d1 = (byte) (((a >>> 2) & 0x3F) + ' ');
+ byte d2 = (byte) ((((a << 4) & 0x30) | ((b >>> 4) & 0x0F)) + ' ');
+ byte d3 = (byte) ((((b << 2) & 0x3C) | ((c >>> 6) & 0x3)) + ' ');
+ byte d4 = (byte) ((c & 0x3F) + ' ');
+ // CheckStyle:MagicNumber ON
+
+ out.write(d1);
+ out.write(d2);
+ out.write(d3);
+ out.write(d4);
+ }
+
+ // terminate with a linefeed alone
+ out.write('\n');
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/UnPackageNameMapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/UnPackageNameMapper.java
new file mode 100644
index 00000000..1777279d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/UnPackageNameMapper.java
@@ -0,0 +1,48 @@
+/*
+ * 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.tools.ant.util;
+
+import java.io.File;
+
+/**
+ * Maps dotted package name matches to a directory name.
+ * This is the inverse of the package mapper.
+ * This is useful for matching XML formatter results against their JUnit test
+ * cases.
+ * <pre>
+ * &lt;mapper classname="org.apache.tools.ant.util.UnPackageNameMapper"
+ * from="${test.data.dir}/TEST-*Test.xml" to="*Test.java"&gt;
+ * </pre>
+ *
+ *
+ */
+public class UnPackageNameMapper extends GlobPatternMapper {
+ /**
+ * Returns the part of the given string that matches the * in the
+ * &quot;from&quot; pattern replacing dots with file separators
+ *
+ *@param name Source filename
+ *@return Replaced variable part
+ */
+ protected String extractVariablePart(String name) {
+ String var = name.substring(prefixLength,
+ name.length() - postfixLength);
+ return var.replace('.', File.separatorChar);
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/UnicodeUtil.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/UnicodeUtil.java
new file mode 100644
index 00000000..d3e5eec3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/UnicodeUtil.java
@@ -0,0 +1,46 @@
+/*
+ * 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.tools.ant.util;
+
+/**
+ * Contains one helper method to create a backslash u escape
+ *
+ * @since Ant 1.8.3
+ */
+public class UnicodeUtil {
+
+ private UnicodeUtil() {
+ }
+
+ /**
+ * returns the unicode representation of a char without the leading backslash
+ * @param ch
+ * @return unicode representation of a char for property files
+ */
+ public static StringBuffer EscapeUnicode(char ch) {
+ StringBuffer unicodeBuf = new StringBuffer("u0000");
+ String s = Integer.toHexString(ch);
+ //replace the last 0s by the chars contained in s
+ for (int i = 0; i < s.length(); i++) {
+ unicodeBuf.setCharAt(unicodeBuf.length()
+ - s.length() + i,
+ s.charAt(i));
+ }
+ return unicodeBuf;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/VectorSet.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/VectorSet.java
new file mode 100644
index 00000000..db13129d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/VectorSet.java
@@ -0,0 +1,242 @@
+/*
+ * 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.tools.ant.util;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Set;
+import java.util.Vector;
+
+/**
+ * Subclass of Vector that won't store duplicate entries and shows
+ * HashSet's constant time performance characteristics for the
+ * contains method.
+ *
+ * <p>This is not a general purpose class but has been written because
+ * the protected members of {@link
+ * org.apache.tools.ant.DirectoryScanner DirectoryScanner} prohibited
+ * later revisions from using a more efficient collection.</p>
+ *
+ * <p>Methods are synchronized to keep Vector's contract.</p>
+ *
+ * @since Ant 1.8.0
+ */
+public final class VectorSet<E> extends Vector<E> {
+ private static final long serialVersionUID = 1L;
+
+ private final HashSet<E> set = new HashSet<E>();
+
+ public VectorSet() { super(); }
+
+ public VectorSet(int initialCapacity) { super(initialCapacity); }
+
+ public VectorSet(int initialCapacity, int capacityIncrement) {
+ super(initialCapacity, capacityIncrement);
+ }
+
+ public VectorSet(Collection<? extends E> c) {
+ if (c != null) {
+ for (E e : c) {
+ add(e);
+ }
+ }
+ }
+
+ public synchronized boolean add(E o) {
+ if (!set.contains(o)) {
+ doAdd(size(), o);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This implementation may not add the element at the given index
+ * if it is already contained in the collection.
+ */
+ public void add(int index, E o) {
+ doAdd(index, o);
+ }
+
+ private synchronized void doAdd(int index, E o) {
+ // Vector.add seems to delegate to insertElementAt, but this
+ // is not documented so we may better implement it ourselves
+ if (set.add(o)) {
+ int count = size();
+ ensureCapacity(count + 1);
+ if (index != count) {
+ System.arraycopy(elementData, index, elementData, index + 1,
+ count - index);
+ }
+ elementData[index] = o;
+ elementCount++;
+ }
+ }
+
+ public synchronized void addElement(E o) {
+ doAdd(size(), o);
+ }
+
+ public synchronized boolean addAll(Collection<? extends E> c) {
+ boolean changed = false;
+ for (E e : c) {
+ changed |= add(e);
+ }
+ return changed;
+ }
+
+ /**
+ * This implementation may not add all elements at the given index
+ * if any of them are already contained in the collection.
+ */
+ public synchronized boolean addAll(int index, Collection<? extends E> c) {
+ LinkedList toAdd = new LinkedList();
+ for (E e : c) {
+ if (set.add(e)) {
+ toAdd.add(e);
+ }
+ }
+ if (toAdd.isEmpty()) {
+ return false;
+ }
+ int count = size();
+ ensureCapacity(count + toAdd.size());
+ if (index != count) {
+ System.arraycopy(elementData, index, elementData, index + toAdd.size(),
+ count - index);
+ }
+ for (Object o : toAdd) {
+ elementData[index++] = o;
+ }
+ elementCount += toAdd.size();
+ return true;
+ }
+
+ public synchronized void clear() {
+ super.clear();
+ set.clear();
+ }
+
+ public Object clone() {
+ @SuppressWarnings("unchecked")
+ final VectorSet<E> vs = (VectorSet<E>) super.clone();
+ vs.set.addAll(set);
+ return vs;
+ }
+
+ public synchronized boolean contains(Object o) {
+ return set.contains(o);
+ }
+
+ public synchronized boolean containsAll(Collection<?> c) {
+ return set.containsAll(c);
+ }
+
+ public void insertElementAt(E o, int index) {
+ doAdd(index, o);
+ }
+
+ public synchronized E remove(int index) {
+ E o = get(index);
+ remove(o);
+ return o;
+ }
+
+ public boolean remove(Object o) {
+ return doRemove(o);
+ }
+
+ private synchronized boolean doRemove(Object o) {
+ // again, remove seems to delegate to removeElement, but we
+ // shouldn't trust it
+ if (set.remove(o)) {
+ int index = indexOf(o);
+ if (index < elementData.length - 1) {
+ System.arraycopy(elementData, index + 1, elementData, index,
+ elementData.length - index - 1);
+ }
+ elementCount--;
+ return true;
+ }
+ return false;
+ }
+
+ public synchronized boolean removeAll(Collection<?> c) {
+ boolean changed = false;
+ for (Object o : c) {
+ changed |= remove(o);
+ }
+ return changed;
+ }
+
+ public synchronized void removeAllElements() {
+ set.clear();
+ super.removeAllElements();
+ }
+
+ public boolean removeElement(Object o) {
+ return doRemove(o);
+ }
+
+ public synchronized void removeElementAt(int index) {
+ remove(get(index));
+ }
+
+ public synchronized void removeRange(final int fromIndex, int toIndex) {
+ while (toIndex > fromIndex) {
+ remove(--toIndex);
+ }
+ }
+
+ public synchronized boolean retainAll(Collection<?> c) {
+ if (!(c instanceof Set)) {
+ c = new HashSet<Object>(c);
+ }
+ LinkedList<E> l = new LinkedList<E>();
+ for (E o : this) {
+ if (!c.contains(o)) {
+ l.addLast(o);
+ }
+ }
+ if (!l.isEmpty()) {
+ removeAll(l);
+ return true;
+ }
+ return false;
+ }
+
+ public synchronized E set(int index, E o) {
+ E orig = get(index);
+ if (set.add(o)) {
+ elementData[index] = o;
+ set.remove(orig);
+ } else {
+ int oldIndexOfO = indexOf(o);
+ remove(o);
+ remove(orig);
+ add(oldIndexOfO > index ? index : index - 1, o);
+ }
+ return orig;
+ }
+
+ public void setElementAt(E o, int index) {
+ set(index, o);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Watchdog.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Watchdog.java
new file mode 100644
index 00000000..318b5264
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/Watchdog.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.tools.ant.util;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * Generalization of <code>ExecuteWatchdog</code>
+ *
+ * @since Ant 1.5
+ *
+ * @see org.apache.tools.ant.taskdefs.ExecuteWatchdog
+ *
+ */
+public class Watchdog implements Runnable {
+
+ private Vector observers = new Vector(1);
+ private long timeout = -1;
+ /**
+ * marked as volatile to stop the compiler caching values or (in java1.5+,
+ * reordering access)
+ */
+ private volatile boolean stopped = false;
+ /**
+ * Error string.
+ * {@value}
+ */
+ public static final String ERROR_INVALID_TIMEOUT = "timeout less than 1.";
+
+ /**
+ * Constructor for Watchdog.
+ * @param timeout the timeout to use in milliseconds (must be &gt;= 1).
+ */
+ public Watchdog(long timeout) {
+ if (timeout < 1) {
+ throw new IllegalArgumentException(ERROR_INVALID_TIMEOUT);
+ }
+ this.timeout = timeout;
+ }
+
+ /**
+ * Add a timeout observer.
+ * @param to the timeout observer to add.
+ */
+ public void addTimeoutObserver(TimeoutObserver to) {
+ //no need to synchronize, as Vector is always synchronized
+ observers.addElement(to);
+ }
+
+ /**
+ * Remove a timeout observer.
+ * @param to the timeout observer to remove.
+ */
+ public void removeTimeoutObserver(TimeoutObserver to) {
+ //no need to synchronize, as Vector is always synchronized
+ observers.removeElement(to);
+ }
+
+ /**
+ * Inform the observers that a timeout has occurred.
+ * This happens in the watchdog thread.
+ */
+ protected final void fireTimeoutOccured() {
+ Enumeration e = observers.elements();
+ while (e.hasMoreElements()) {
+ ((TimeoutObserver) e.nextElement()).timeoutOccured(this);
+ }
+ }
+
+ /**
+ * Start the watch dog.
+ */
+ public synchronized void start() {
+ stopped = false;
+ Thread t = new Thread(this, "WATCHDOG");
+ t.setDaemon(true);
+ t.start();
+ }
+
+ /**
+ * Stop the watch dog.
+ */
+ public synchronized void stop() {
+ stopped = true;
+ notifyAll();
+ }
+
+ /**
+ * The run method of the watch dog thread.
+ * This simply does a wait for the timeout time, and
+ * if the stop flag has not been set when the wait has returned or
+ * has been interrupted, the watch dog listeners are informed.
+ */
+ public synchronized void run() {
+ long now = System.currentTimeMillis();
+ final long until = now + timeout;
+
+ try {
+ while (!stopped && until > now) {
+ wait(until - now);
+ now = System.currentTimeMillis();
+ }
+ } catch (InterruptedException e) {
+ // Ignore exception
+ }
+ if (!stopped) {
+ fireTimeoutOccured();
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/WeakishReference.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/WeakishReference.java
new file mode 100644
index 00000000..92f322fb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/WeakishReference.java
@@ -0,0 +1,91 @@
+/*
+ * 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.tools.ant.util;
+
+
+import java.lang.ref.WeakReference;
+
+/**
+ * These classes are part of some code to reduce memory leaks by only
+ * retaining weak references to things
+ * on Java1.2+, and yet still work (with leaky hard references) on Java1.1.
+ * Now that Ant is 1.2+ only,
+ * life is simpler and none of the classes are needed any more.
+ *
+ * They are only retained in case a third-party task uses them
+ * @since ant1.6
+ * @see org.apache.tools.ant.util.optional.WeakishReference12
+ * @deprecated deprecated 1.7; will be removed in Ant1.8
+ * Just use {@link java.lang.ref.WeakReference} directly.
+ */
+public class WeakishReference {
+
+
+ private WeakReference weakref;
+
+ /**
+ * create a new soft reference, which is bound to a
+ * Weak reference inside
+ *
+ * @param reference
+ * @see java.lang.ref.WeakReference
+ */
+ WeakishReference(Object reference) {
+ this.weakref = new WeakReference(reference);
+ }
+
+ /**
+ * Returns this reference object's referent. If this reference object has
+ * been cleared, then this method returns <code>null</code>.
+ *
+ * @return The object to which this reference refers, or
+ * <code>null</code> if this reference object has been cleared.
+ */
+ public Object get() {
+ return weakref.get();
+ }
+
+ /**
+ * create the appropriate type of reference for the java version
+ * @param object the object that the reference will refer to.
+ * @return reference to the Object.
+ */
+ public static WeakishReference createReference(Object object) {
+ return new WeakishReference(object);
+ }
+
+
+ /**
+ * This was a hard reference for Java 1.1. Since Ant1.7,
+ * @deprecated since 1.7.
+ * Hopefully nobody is using this.
+ */
+ public static class HardReference extends WeakishReference {
+
+ /**
+ * constructor.
+ * @param object the object that the reference will refer to.
+ */
+ public HardReference(Object object) {
+ super(object);
+ }
+
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/WorkerAnt.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/WorkerAnt.java
new file mode 100644
index 00000000..288d74dd
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/WorkerAnt.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.tools.ant.util;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * A worker ant executes a single task in a background thread.
+ * After the run, any exception thrown is turned into a buildexception, which can be
+ * rethrown, the finished attribute is set, then notifyAll() is called,
+ * so that anyone waiting on the same notify object gets woken up.
+ * <p>
+ * This class is effectively a superset of
+ * {@link org.apache.tools.ant.taskdefs.Parallel.TaskRunnable}
+ *
+ * @since Ant 1.8
+ */
+
+public class WorkerAnt extends Thread {
+
+ private Task task;
+ private Object notify;
+ private volatile boolean finished = false;
+ private volatile BuildException buildException;
+ private volatile Throwable exception;
+
+ /**
+ * Error message if invoked with no task
+ */
+ public static final String ERROR_NO_TASK = "No task defined";
+
+
+ /**
+ * Create the worker.
+ * <p>
+ * This does not start the thread, merely configures it.
+ * @param task the task
+ * @param notify what to notify
+ */
+ public WorkerAnt(Task task, Object notify) {
+ this.task = task;
+ this.notify = notify != null ? notify : this;
+ }
+
+ /**
+ * Create the worker, using the worker as the notification point.
+ * <p>
+ * This does not start the thread, merely configures it.
+ * @param task the task
+ */
+ public WorkerAnt(Task task) {
+ this(task, null);
+ }
+
+ /**
+ * Get any build exception.
+ * This would seem to be oversynchronised, but know that Java pre-1.5 can
+ * reorder volatile access.
+ * The synchronized attribute is to force an ordering.
+ *
+ * @return the exception or null
+ */
+ public synchronized BuildException getBuildException() {
+ return buildException;
+ }
+
+ /**
+ * Get whatever was thrown, which may or may not be a buildException.
+ * Assertion: getException() instanceof BuildException &lt;=&gt; getBuildException()==getException()
+ * @return the exception.
+ */
+ public synchronized Throwable getException() {
+ return exception;
+ }
+
+
+ /**
+ * Get the task
+ * @return the task
+ */
+ public Task getTask() {
+ return task;
+ }
+
+
+ /**
+ * Query the task/thread for being finished.
+ * This would seem to be oversynchronised, but know that Java pre-1.5 can
+ * reorder volatile access.
+ * The synchronized attribute is to force an ordering.
+ * @return true if the task is finished.
+ */
+ public synchronized boolean isFinished() {
+ return finished;
+ }
+
+ /**
+ * Block on the notify object and so wait until the thread is finished.
+ * @param timeout timeout in milliseconds
+ * @throws InterruptedException if the execution was interrupted
+ */
+ public void waitUntilFinished(long timeout) throws InterruptedException {
+ synchronized (notify) {
+ if (!finished) {
+ notify.wait(timeout);
+ }
+ }
+ }
+
+ /**
+ * Raise an exception if one was caught
+ *
+ * @throws BuildException if one has been picked up
+ */
+ public void rethrowAnyBuildException() {
+ BuildException ex = getBuildException();
+ if (ex != null) {
+ throw ex;
+ }
+ }
+
+
+ /**
+ * Handle a caught exception, by recording it and possibly wrapping it
+ * in a BuildException for later rethrowing.
+ * @param thrown what was caught earlier
+ */
+ private synchronized void caught(Throwable thrown) {
+ exception = thrown;
+ buildException = (thrown instanceof BuildException)
+ ? (BuildException) thrown
+ : new BuildException(thrown);
+ }
+
+ /**
+ * Run the task, which is skipped if null.
+ * When invoked again, the task is re-run.
+ */
+ public void run() {
+ try {
+ if (task != null) {
+ task.execute();
+ }
+ } catch (Throwable thrown) {
+ caught(thrown);
+ } finally {
+ synchronized (notify) {
+ finished = true;
+ //reset the task.
+ //wake up our owner, if it is waiting
+ notify.notifyAll();
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/XMLFragment.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/XMLFragment.java
new file mode 100644
index 00000000..36a6158e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/XMLFragment.java
@@ -0,0 +1,155 @@
+/*
+ * 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.tools.ant.util;
+
+import org.apache.tools.ant.DynamicConfiguratorNS;
+import org.apache.tools.ant.DynamicElementNS;
+import org.apache.tools.ant.ProjectComponent;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+
+/**
+ * Use this class as a nested element if you want to get a literal DOM
+ * fragment of something nested into your task/type.
+ *
+ * <p>This is useful for tasks that want to deal with the "real" XML
+ * from the build file instead of objects.</p>
+ *
+ * <p>Code heavily influenced by code written by Dominique Devienne.</p>
+ *
+ * @since Ant 1.7
+ */
+public class XMLFragment extends ProjectComponent implements DynamicElementNS {
+
+ private Document doc;
+ private DocumentFragment fragment;
+
+ /**
+ * Constructor for XMLFragment object.
+ */
+ public XMLFragment() {
+ doc = JAXPUtils.getDocumentBuilder().newDocument();
+ fragment = doc.createDocumentFragment();
+ }
+
+ /**
+ * @return the DocumentFragment that corresponds to the nested
+ * structure.
+ */
+ public DocumentFragment getFragment() {
+ return fragment;
+ }
+
+ /**
+ * Add nested text, expanding properties as we go
+ * @param s the text to add
+ */
+ public void addText(String s) {
+ addText(fragment, s);
+ }
+
+ /**
+ * Creates a nested element.
+ * @param uri the uri of the nested element
+ * @param name the localname of the nested element
+ * @param qName the qualified name of the nested element
+ * @return an object that the element is applied to
+ */
+ public Object createDynamicElement(String uri, String name, String qName) {
+ Element e = null;
+ if (uri.equals("")) {
+ e = doc.createElement(name);
+ } else {
+ e = doc.createElementNS(uri, qName);
+ }
+ fragment.appendChild(e);
+ return new Child(e);
+ }
+
+ /**
+ * Add text to a node.
+ * @param n node
+ * @param s value
+ */
+ private void addText(Node n, String s) {
+ s = getProject().replaceProperties(s);
+ //only text nodes that are non null after property expansion are added
+ if (s != null && !s.trim().equals("")) {
+ Text t = doc.createTextNode(s.trim());
+ n.appendChild(t);
+ }
+ }
+
+ /**
+ * An object to handle (recursively) nested elements.
+ */
+ public class Child implements DynamicConfiguratorNS {
+ private Element e;
+
+ Child(Element e) {
+ this.e = e;
+ }
+
+ /**
+ * Add nested text.
+ * @param s the text to add
+ */
+ public void addText(String s) {
+ XMLFragment.this.addText(e, s);
+ }
+
+ /**
+ * Sets the attribute
+ * @param uri the uri of the attribute
+ * @param name the localname of the attribute
+ * @param qName the qualified name of the attribute
+ * @param value the value of the attribute
+ */
+ public void setDynamicAttribute(
+ String uri, String name, String qName, String value) {
+ if (uri.equals("")) {
+ e.setAttribute(name, value);
+ } else {
+ e.setAttributeNS(uri, qName, value);
+ }
+ }
+
+ /**
+ * Creates a nested element.
+ * @param uri the uri of the nested element
+ * @param name the localname of the nested element
+ * @param qName the qualified name of the nested element
+ * @return an object that the element is applied to
+ */
+ public Object createDynamicElement(String uri, String name, String qName) {
+ Element e2 = null;
+ if (uri.equals("")) {
+ e2 = doc.createElement(name);
+ } else {
+ e2 = doc.createElementNS(uri, qName);
+ }
+ e.appendChild(e2);
+ return new Child(e2);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/XmlConstants.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/XmlConstants.java
new file mode 100644
index 00000000..d8eaa02e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/XmlConstants.java
@@ -0,0 +1,65 @@
+/*
+ * 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.tools.ant.util;
+
+/**
+ * XML Parser constants, all kept in one place for ease of reuse
+ * @see <a href="http://xml.apache.org/xerces-j/features.html">Xerces features</a>
+ * @see <a href="http://xml.apache.org/xerces-j/properties.html">Xerces properties</a>
+ * @see <a href=
+ * "http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html#package_description"
+ * >SAX.</a>
+ */
+
+public class XmlConstants {
+
+ private XmlConstants() {
+ }
+
+ /** property for location of xml schema */
+ public static final String PROPERTY_SCHEMA_LOCATION =
+ "http://apache.org/xml/properties/schema/external-schemaLocation";
+ /** property for location of no-name schema */
+ public static final String PROPERTY_NO_NAMESPACE_SCHEMA_LOCATION =
+ "http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation";
+ /** property for full validation */
+ public static final String FEATURE_XSD_FULL_VALIDATION =
+ "http://apache.org/xml/features/validation/schema-full-checking";
+ /** property for xsd */
+ public static final String FEATURE_XSD = "http://apache.org/xml/features/validation/schema";
+
+ /** property for validation */
+ public static final String FEATURE_VALIDATION = "http://xml.org/sax/features/validation";
+ /** property for namespace support */
+ public static final String FEATURE_NAMESPACES = "http://xml.org/sax/features/namespaces";
+ /** property for schema language */
+ public static final String FEATURE_JAXP12_SCHEMA_LANGUAGE =
+ "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
+ /** property for schema source */
+ public static final String FEATURE_JAXP12_SCHEMA_SOURCE =
+ "http://java.sun.com/xml/jaxp/properties/schemaSource";
+ /** the namespace for XML schema */
+ public static final String URI_XSD =
+ "http://www.w3.org/2001/XMLSchema";
+ /** the sax external entities feature */
+ public static final String FEATURE_EXTERNAL_ENTITIES =
+ "http://xml.org/sax/features/external-general-entities";
+ /** the apache.org/xml disallow doctype decl feature */
+ public static final String FEATURE_DISALLOW_DTD =
+ "http://apache.org/xml/features/disallow-doctype-decl";
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/AbstractAnalyzer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/AbstractAnalyzer.java
new file mode 100644
index 00000000..5c95d75b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/AbstractAnalyzer.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.tools.ant.util.depend;
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.zip.ZipFile;
+
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.VectorSet;
+
+/**
+ * An abstract implementation of the analyzer interface providing support
+ * for the bulk of interface methods.
+ *
+ */
+public abstract class AbstractAnalyzer implements DependencyAnalyzer {
+ /** Maximum number of loops for looking for indirect dependencies. */
+ public static final int MAX_LOOPS = 1000;
+
+ /** The source path for the source files */
+ private Path sourcePath = new Path(null);
+
+ /** The classpath containg dirs and jars of class files */
+ private Path classPath = new Path(null);
+
+ /** The list of root classes */
+ private final Vector<String> rootClasses = new VectorSet<String>();
+
+ /** true if dependencies have been determined */
+ private boolean determined = false;
+
+ /** the list of File objects that the root classes depend upon */
+ private Vector<File> fileDependencies;
+ /** the list of java classes the root classes depend upon */
+ private Vector<String> classDependencies;
+
+ /** true if indirect dependencies should be gathered */
+ private boolean closure = true;
+
+ /** Setup the analyzer */
+ protected AbstractAnalyzer() {
+ reset();
+ }
+
+ /**
+ * Set the closure flag. If this flag is true the analyzer will traverse
+ * all class relationships until it has collected the entire set of
+ * direct and indirect dependencies
+ *
+ * @param closure true if dependencies should be traversed to determine
+ * indirect dependencies.
+ */
+ public void setClosure(boolean closure) {
+ this.closure = closure;
+ }
+
+ /**
+ * Get the list of files in the file system upon which the root classes
+ * depend. The files will be either the classfiles or jar files upon
+ * which the root classes depend.
+ *
+ * @return an enumeration of File instances.
+ */
+ public Enumeration<File> getFileDependencies() {
+ if (!supportsFileDependencies()) {
+ throw new RuntimeException("File dependencies are not supported "
+ + "by this analyzer");
+ }
+ if (!determined) {
+ determineDependencies(fileDependencies, classDependencies);
+ }
+ return fileDependencies.elements();
+ }
+
+ /**
+ * Get the list of classes upon which root classes depend. This is a
+ * list of Java classnames in dot notation.
+ *
+ * @return an enumeration of Strings, each being the name of a Java
+ * class in dot notation.
+ */
+ public Enumeration<String> getClassDependencies() {
+ if (!determined) {
+ determineDependencies(fileDependencies, classDependencies);
+ }
+ return classDependencies.elements();
+ }
+
+ /**
+ * Get the file that contains the class definition
+ *
+ * @param classname the name of the required class
+ * @return the file instance, zip or class, containing the
+ * class or null if the class could not be found.
+ * @exception IOException if the files in the classpath cannot be read.
+ */
+ public File getClassContainer(String classname) throws IOException {
+ String classLocation = classname.replace('.', '/') + ".class";
+ // we look through the classpath elements. If the element is a dir
+ // we look for the file. IF it is a zip, we look for the zip entry
+ return getResourceContainer(classLocation, classPath.list());
+ }
+
+ /**
+ * Get the file that contains the class source.
+ *
+ * @param classname the name of the required class
+ * @return the file instance, zip or java, containing the
+ * source or null if the source for the class could not be found.
+ * @exception IOException if the files in the sourcepath cannot be read.
+ */
+ public File getSourceContainer(String classname) throws IOException {
+ String sourceLocation = classname.replace('.', '/') + ".java";
+
+ // we look through the source path elements. If the element is a dir
+ // we look for the file. If it is a zip, we look for the zip entry.
+ // This isn't normal for source paths but we get it for free
+ return getResourceContainer(sourceLocation, sourcePath.list());
+ }
+
+ /**
+ * Add a source path to the source path used by this analyzer. The
+ * elements in the given path contain the source files for the classes
+ * being analyzed. Not all analyzers will use this information.
+ *
+ * @param sourcePath The Path instance specifying the source path
+ * elements.
+ */
+ public void addSourcePath(Path sourcePath) {
+ if (sourcePath == null) {
+ return;
+ }
+ this.sourcePath.append(sourcePath);
+ this.sourcePath.setProject(sourcePath.getProject());
+ }
+
+ /**
+ * Add a classpath to the classpath being used by the analyzer. The
+ * classpath contains the binary classfiles for the classes being
+ * analyzed The elements may either be the directories or jar files.Not
+ * all analyzers will use this information.
+ *
+ * @param classPath the Path instance specifying the classpath elements
+ */
+ public void addClassPath(Path classPath) {
+ if (classPath == null) {
+ return;
+ }
+
+ this.classPath.append(classPath);
+ this.classPath.setProject(classPath.getProject());
+ }
+
+ /**
+ * Add a root class. The root classes are used to drive the
+ * determination of dependency information. The analyzer will start at
+ * the root classes and add dependencies from there.
+ *
+ * @param className the name of the class in Java dot notation.
+ */
+ public void addRootClass(String className) {
+ if (className == null) {
+ return;
+ }
+ if (!rootClasses.contains(className)) {
+ rootClasses.addElement(className);
+ }
+ }
+
+ /**
+ * Configure an aspect of the analyzer. The set of aspects that are
+ * supported is specific to each analyzer instance.
+ *
+ * @param name the name of the aspect being configured
+ * @param info the configuration info.
+ */
+ public void config(String name, Object info) {
+ // do nothing by default
+ }
+
+ /**
+ * Reset the dependency list. This will reset the determined
+ * dependencies and the also list of root classes.
+ */
+ public void reset() {
+ rootClasses.removeAllElements();
+ determined = false;
+ fileDependencies = new Vector<File>();
+ classDependencies = new Vector<String>();
+ }
+
+ /**
+ * Get an enumeration of the root classes
+ *
+ * @return an enumeration of Strings, each of which is a class name
+ * for a root class.
+ */
+ protected Enumeration<String> getRootClasses() {
+ return rootClasses.elements();
+ }
+
+ /**
+ * Indicate if the analyzer is required to follow
+ * indirect class relationships.
+ *
+ * @return true if indirect relationships should be followed.
+ */
+ protected boolean isClosureRequired() {
+ return closure;
+ }
+
+ /**
+ * Determine the dependencies of the current set of root classes
+ *
+ * @param files a vector into which Files upon which the root classes
+ * depend should be placed.
+ * @param classes a vector of Strings into which the names of classes
+ * upon which the root classes depend should be placed.
+ */
+ protected abstract void determineDependencies(Vector<File> files, Vector<String> classes);
+
+ /**
+ * Indicate if the particular subclass supports file dependency
+ * information.
+ *
+ * @return true if file dependencies are supported.
+ */
+ protected abstract boolean supportsFileDependencies();
+
+ /**
+ * Get the file that contains the resource
+ *
+ * @param resourceLocation the name of the required resource.
+ * @param paths the paths which will be searched for the resource.
+ * @return the file instance, zip or class, containing the
+ * class or null if the class could not be found.
+ * @exception IOException if the files in the given paths cannot be read.
+ */
+ private File getResourceContainer(String resourceLocation, String[] paths)
+ throws IOException {
+ for (int i = 0; i < paths.length; ++i) {
+ File element = new File(paths[i]);
+ if (!element.exists()) {
+ continue;
+ }
+ if (element.isDirectory()) {
+ File resource = new File(element, resourceLocation);
+ if (resource.exists()) {
+ return resource;
+ }
+ } else {
+ // must be a zip of some sort
+ ZipFile zipFile = null;
+ try {
+ zipFile = new ZipFile(element);
+ if (zipFile.getEntry(resourceLocation) != null) {
+ return element;
+ }
+ } finally {
+ if (zipFile != null) {
+ zipFile.close();
+ }
+ }
+ }
+ }
+ return null;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/DependencyAnalyzer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/DependencyAnalyzer.java
new file mode 100644
index 00000000..1415e2e4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/DependencyAnalyzer.java
@@ -0,0 +1,130 @@
+/*
+ * 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.tools.ant.util.depend;
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.tools.ant.types.Path;
+
+/**
+ * A dependency analyzer analyzes dependencies between Java classes to
+ * determine the minimal set of classes which are required by a set of
+ * &quot;root&quot; classes. Different implementations of this interface can
+ * use different strategies and libraries to determine the required set. For
+ * example, some analyzers will use class files while others might use
+ * source files. Analyzer specific configuration is catered for through a
+ * generic configure method
+ *
+ */
+public interface DependencyAnalyzer {
+ /**
+ * Add a source path to the source path used by this analyzer. The
+ * elements in the given path contain the source files for the classes
+ * being analyzed. Not all analyzers will use this information.
+ *
+ * @param sourcePath The Path instance specifying the source path
+ * elements.
+ */
+ void addSourcePath(Path sourcePath);
+
+ /**
+ * Add a classpath to the classpath being used by the analyzer. The
+ * classpath contains the binary classfiles for the classes being
+ * analyzed The elements may either be the directories or jar files.Not
+ * all analyzers will use this information.
+ *
+ * @param classpath the Path instance specifying the classpath elements
+ */
+ void addClassPath(Path classpath);
+
+ /**
+ * Add a root class. The root classes are used to drive the
+ * determination of dependency information. The analyzer will start at
+ * the root classes and add dependencies from there.
+ *
+ * @param classname the name of the class in Java dot notation.
+ */
+ void addRootClass(String classname);
+
+ /**
+ * Get the list of files in the file system upon which the root classes
+ * depend. The files will be either the classfiles or jar files upon
+ * which the root classes depend.
+ *
+ * @return an enumeration of File instances.
+ */
+ Enumeration<File> getFileDependencies();
+
+ /**
+ * Get the list of classes upon which root classes depend. This is a
+ * list of Java classnames in dot notation.
+ *
+ * @return an enumeration of Strings, each being the name of a Java
+ * class in dot notation.
+ */
+ Enumeration<String> getClassDependencies();
+
+
+ /**
+ * Reset the dependency list. This will reset the determined
+ * dependencies and the also list of root classes.
+ */
+ void reset();
+
+ /**
+ * Configure an aspect of the analyzer. The set of aspects that are
+ * supported is specific to each analyzer instance.
+ *
+ * @param name the name of the aspect being configured
+ * @param info the configuration information.
+ */
+ void config(String name, Object info);
+
+ /**
+ * Set the closure flag. If this flag is true the analyzer will traverse
+ * all class relationships until it has collected the entire set of
+ * direct and indirect dependencies
+ *
+ * @param closure true if dependencies should be traversed to determine
+ * indirect dependencies.
+ */
+ void setClosure(boolean closure);
+
+
+ /**
+ * Get the file that contains the class definition
+ *
+ * @param classname the name of the required class
+ * @return the file instance, zip or class, containing the
+ * class or null if the class could not be found.
+ * @exception IOException if the files in the classpath cannot be read.
+ */
+ File getClassContainer(String classname) throws IOException;
+
+ /**
+ * Get the file that contains the class source.
+ *
+ * @param classname the name of the required class
+ * @return the file instance, zip or java, containing the
+ * source or null if the source for the class could not be found.
+ * @exception IOException if the files in the sourcepath cannot be read.
+ */
+ File getSourceContainer(String classname) throws IOException;
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/bcel/AncestorAnalyzer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/bcel/AncestorAnalyzer.java
new file mode 100644
index 00000000..2bd2a6cf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/bcel/AncestorAnalyzer.java
@@ -0,0 +1,144 @@
+/*
+ * 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.tools.ant.util.depend.bcel;
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.depend.AbstractAnalyzer;
+
+/**
+ * A dependency analyzer which returns superclass and superinterface
+ * dependencies.
+ *
+ */
+public class AncestorAnalyzer extends AbstractAnalyzer {
+
+ /**
+ * Default constructor
+ *
+ * Causes the BCEL classes to load to ensure BCEL dependencies can
+ * be satisfied
+ */
+ public AncestorAnalyzer() {
+ // force BCEL classes to load now
+ try {
+ new ClassParser("force");
+ } catch (Exception e) {
+ // all released versions of BCEL may throw an IOException
+ // here, but BCEL's trunk does no longer declare to do so
+ if (!(e instanceof IOException)) {
+ throw new BuildException(e);
+ }
+ // ignore IOException like we've always done
+ }
+ }
+
+ /**
+ * Determine the dependencies of the configured root classes.
+ *
+ * @param files a vector to be populated with the files which contain
+ * the dependency classes
+ * @param classes a vector to be populated with the names of the
+ * dependency classes.
+ */
+ protected void determineDependencies(Vector<File> files, Vector<String> classes) {
+ // we get the root classes and build up a set of
+ // classes upon which they depend
+ Hashtable<String, String> dependencies = new Hashtable<String, String>();
+ Hashtable<File, File> containers = new Hashtable<File, File>();
+ Hashtable<String, String> toAnalyze = new Hashtable<String, String>();
+ Hashtable<String, String> nextAnalyze = new Hashtable<String, String>();
+
+ for (Enumeration<String> e = getRootClasses(); e.hasMoreElements();) {
+ String classname = e.nextElement();
+ toAnalyze.put(classname, classname);
+ }
+
+ int count = 0;
+ int maxCount = isClosureRequired() ? MAX_LOOPS : 2;
+ while (toAnalyze.size() != 0 && count++ < maxCount) {
+ nextAnalyze.clear();
+ for (String classname : toAnalyze.keySet()) {
+ dependencies.put(classname, classname);
+ try {
+ File container = getClassContainer(classname);
+ if (container == null) {
+ continue;
+ }
+ containers.put(container, container);
+
+ ClassParser parser = null;
+ if (container.getName().endsWith(".class")) {
+ parser = new ClassParser(container.getPath());
+ } else {
+ parser = new ClassParser(container.getPath(),
+ classname.replace('.', '/') + ".class");
+ }
+
+ JavaClass javaClass = parser.parse();
+ String[] interfaces = javaClass.getInterfaceNames();
+ for (int i = 0; i < interfaces.length; ++i) {
+ String interfaceName = interfaces[i];
+ if (!dependencies.containsKey(interfaceName)) {
+ nextAnalyze.put(interfaceName, interfaceName);
+ }
+ }
+
+ if (javaClass.isClass()) {
+ String superClass = javaClass.getSuperclassName();
+ if (!dependencies.containsKey(superClass)) {
+ nextAnalyze.put(superClass, superClass);
+ }
+ }
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+
+ Hashtable<String, String> temp = toAnalyze;
+ toAnalyze = nextAnalyze;
+ nextAnalyze = temp;
+ }
+
+ files.removeAllElements();
+ for (File f : containers.keySet()) {
+ files.add(f);
+ }
+
+ classes.removeAllElements();
+ for (String dependency : dependencies.keySet()) {
+ classes.add(dependency);
+ }
+ }
+
+ /**
+ * Indicate if this analyzer can determine dependent files.
+ *
+ * @return true if the analyzer provides dependency file information.
+ */
+ protected boolean supportsFileDependencies() {
+ return true;
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/bcel/DependencyVisitor.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/bcel/DependencyVisitor.java
new file mode 100644
index 00000000..d31d4534
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/bcel/DependencyVisitor.java
@@ -0,0 +1,192 @@
+/*
+ * 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.tools.ant.util.depend.bcel;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+import org.apache.bcel.classfile.ConstantClass;
+import org.apache.bcel.classfile.ConstantNameAndType;
+import org.apache.bcel.classfile.ConstantPool;
+import org.apache.bcel.classfile.EmptyVisitor;
+import org.apache.bcel.classfile.Field;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Method;
+
+/**
+ * A BCEL visitor implementation to collect class dependency information
+ *
+ */
+public class DependencyVisitor extends EmptyVisitor {
+ /** The collected dependencies */
+ private final Hashtable<String, String> dependencies = new Hashtable<String, String>();
+ /**
+ * The current class's constant pool - used to determine class names
+ * from class references.
+ */
+ private ConstantPool constantPool;
+
+ /**
+ * Get the dependencies collected by this visitor
+ *
+ * @return a Enumeration of classnames, being the classes upon which the
+ * visited classes depend.
+ */
+ public Enumeration<String> getDependencies() {
+ return dependencies.keys();
+ }
+
+ /** Clear the current set of collected dependencies. */
+ public void clearDependencies() {
+ dependencies.clear();
+ }
+
+ /**
+ * Visit the constant pool of a class
+ *
+ * @param constantPool the constant pool of the class being visited.
+ */
+ public void visitConstantPool(final ConstantPool constantPool) {
+ this.constantPool = constantPool;
+ }
+
+ /**
+ * Visit a class reference
+ *
+ * @param constantClass the constantClass entry for the class reference
+ */
+ public void visitConstantClass(final ConstantClass constantClass) {
+ final String classname
+ = constantClass.getConstantValue(constantPool).toString();
+ addSlashClass(classname);
+ }
+
+ /**
+ * Visit a name and type ref
+ *
+ * Look for class references in this
+ *
+ * @param obj the name and type reference being visited.
+ */
+ public void visitConstantNameAndType(final ConstantNameAndType obj) {
+ final String name = obj.getName(constantPool);
+ if (obj.getSignature(constantPool).equals("Ljava/lang/Class;")
+ && name.startsWith("class$")) {
+ String classname
+ = name.substring("class$".length()).replace('$', '.');
+ // does the class have a package structure
+ final int index = classname.lastIndexOf(".");
+ if (index > 0) {
+ char start;
+ // check if the package structure is more than 1 level deep
+ final int index2 = classname.lastIndexOf(".", index - 1);
+ if (index2 != -1) {
+ // class name has more than 1 package level 'com.company.Class'
+ start = classname.charAt(index2 + 1);
+ } else {
+ // class name has only 1 package level 'package.Class'
+ start = classname.charAt(0);
+ }
+ // Check to see if it's an inner class 'com.company.Class$Inner'
+ // CheckStyle:MagicNumber OFF
+ if ((start > 0x40) && (start < 0x5B)) {
+ // first letter of the previous segment of the class name 'Class'
+ // is upper case ascii. so according to the spec it's an inner class
+ classname = classname.substring(0, index) + "$"
+ + classname.substring(index + 1);
+ addClass(classname);
+ } else {
+ // Add the class in dotted notation 'com.company.Class'
+ addClass(classname);
+ }
+ // CheckStyle:MagicNumber ON
+ } else {
+ // Add a class with no package 'Class'
+ addClass(classname);
+ }
+ }
+ }
+
+ /**
+ * Visit a field of the class.
+ *
+ * @param field the field being visited
+ */
+ public void visitField(final Field field) {
+ addClasses(field.getSignature());
+ }
+
+ /**
+ * Visit a Java class
+ *
+ * @param javaClass the class being visited.
+ */
+ public void visitJavaClass(final JavaClass javaClass) {
+ addClass(javaClass.getClassName());
+ }
+
+ /**
+ * Visit a method of the current class
+ *
+ * @param method the method being visited.
+ */
+ public void visitMethod(final Method method) {
+ final String signature = method.getSignature();
+ final int pos = signature.indexOf(")");
+ addClasses(signature.substring(1, pos));
+ addClasses(signature.substring(pos + 1));
+ }
+
+ /**
+ * Add a classname to the list of dependency classes
+ *
+ * @param classname the class to be added to the list of dependencies.
+ */
+ void addClass(final String classname) {
+ dependencies.put(classname, classname);
+ }
+
+ /**
+ * Add all the classes from a descriptor string.
+ *
+ * @param string the descriptor string, being descriptors separated by
+ * ';' characters.
+ */
+ private void addClasses(final String string) {
+ final StringTokenizer tokens = new StringTokenizer(string, ";");
+ while (tokens.hasMoreTokens()) {
+ final String descriptor = tokens.nextToken();
+ final int pos = descriptor.indexOf('L');
+ if (pos != -1) {
+ addSlashClass(descriptor.substring(pos + 1));
+ }
+ }
+ }
+
+ /**
+ * Adds a class name in slash format
+ * (for example org/apache/tools/ant/Main).
+ *
+ * @param classname the class name in slash format
+ */
+ private void addSlashClass(final String classname) {
+ addClass(classname.replace('/', '.'));
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/bcel/FullAnalyzer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/bcel/FullAnalyzer.java
new file mode 100644
index 00000000..f270fd48
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/depend/bcel/FullAnalyzer.java
@@ -0,0 +1,136 @@
+/*
+ * 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.tools.ant.util.depend.bcel;
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.DescendingVisitor;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.depend.AbstractAnalyzer;
+
+/**
+ * An analyzer capable fo traversing all class - class relationships.
+ *
+ */
+public class FullAnalyzer extends AbstractAnalyzer {
+ /**
+ * Default constructor
+ *
+ * Causes the BCEL classes to load to ensure BCEL dependencies can
+ * be satisfied
+ */
+ public FullAnalyzer() {
+ // force BCEL classes to load now
+ try {
+ new ClassParser("force");
+ } catch (Exception e) {
+ // all released versions of BCEL may throw an IOException
+ // here, but BCEL's trunk does no longer declare to do so
+ if (!(e instanceof IOException)) {
+ throw new BuildException(e);
+ }
+ // ignore IOException like we've always done
+ }
+ }
+
+ /**
+ * Determine the dependencies of the configured root classes.
+ *
+ * @param files a vector to be populated with the files which contain
+ * the dependency classes
+ * @param classes a vector to be populated with the names of the
+ * dependency classes.
+ */
+ protected void determineDependencies(Vector<File> files, Vector<String> classes) {
+ // we get the root classes and build up a set of
+ // classes upon which they depend
+ Hashtable<String, String> dependencies = new Hashtable<String, String>();
+ Hashtable<File, File> containers = new Hashtable<File, File>();
+ Hashtable<String, String> toAnalyze = new Hashtable<String, String>();
+ for (Enumeration<String> e = getRootClasses(); e.hasMoreElements();) {
+ String classname = e.nextElement();
+ toAnalyze.put(classname, classname);
+ }
+
+ int count = 0;
+ int maxCount = isClosureRequired() ? MAX_LOOPS : 2;
+ while (toAnalyze.size() != 0 && count++ < maxCount) {
+ DependencyVisitor dependencyVisitor = new DependencyVisitor();
+ for (String classname : toAnalyze.keySet()) {
+ dependencies.put(classname, classname);
+ try {
+ File container = getClassContainer(classname);
+ if (container == null) {
+ continue;
+ }
+ containers.put(container, container);
+
+ ClassParser parser = null;
+ if (container.getName().endsWith(".class")) {
+ parser = new ClassParser(container.getPath());
+ } else {
+ parser = new ClassParser(container.getPath(),
+ classname.replace('.', '/') + ".class");
+ }
+
+ JavaClass javaClass = parser.parse();
+ DescendingVisitor traverser
+ = new DescendingVisitor(javaClass, dependencyVisitor);
+ traverser.visit();
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+
+ toAnalyze.clear();
+
+ // now recover all the dependencies collected and add to the list.
+ Enumeration<String> depsEnum = dependencyVisitor.getDependencies();
+ while (depsEnum.hasMoreElements()) {
+ String className = depsEnum.nextElement();
+ if (!dependencies.containsKey(className)) {
+ toAnalyze.put(className, className);
+ }
+ }
+ }
+
+ files.removeAllElements();
+ for (File f : containers.keySet()) {
+ files.add(f);
+ }
+
+ classes.removeAllElements();
+ for (String dependency : dependencies.keySet()) {
+ classes.add(dependency);
+ }
+ }
+
+ /**
+ * Indicate if this analyzer can determine dependent files.
+ *
+ * @return true if the analyzer provides dependency file information.
+ */
+ protected boolean supportsFileDependencies() {
+ return true;
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/facade/FacadeTaskHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/facade/FacadeTaskHelper.java
new file mode 100644
index 00000000..4fb4341b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/facade/FacadeTaskHelper.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.tools.ant.util.facade;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Helper class for facade implementations - encapsulates treatment of
+ * explicit implementation choices, magic properties and
+ * implementation specific command line arguments.
+ *
+ *
+ * @since Ant 1.5
+ */
+public class FacadeTaskHelper {
+
+ /**
+ * Command line arguments.
+ */
+ private List<ImplementationSpecificArgument> args = new ArrayList<ImplementationSpecificArgument>();
+
+ /**
+ * The explicitly chosen implementation.
+ */
+ private String userChoice;
+
+ /**
+ * The magic property to consult.
+ */
+ private String magicValue;
+
+ /**
+ * The default value.
+ */
+ private String defaultValue;
+
+ /**
+ * User specified path used as classpath when loading the implementation.
+ */
+ private Path implementationClasspath;
+
+ /**
+ * @param defaultValue The default value for the implementation.
+ * Must not be null.
+ */
+ public FacadeTaskHelper(String defaultValue) {
+ this(defaultValue, null);
+ }
+
+ /**
+ * @param defaultValue The default value for the implementation.
+ * Must not be null.
+ * @param magicValue the value of a magic property that may hold a user.
+ * choice. May be null.
+ */
+ public FacadeTaskHelper(String defaultValue, String magicValue) {
+ this.defaultValue = defaultValue;
+ this.magicValue = magicValue;
+ }
+
+ /**
+ * Used to set the value of the magic property.
+ * @param magicValue the value of a magic property that may hold a user.
+ */
+ public void setMagicValue(String magicValue) {
+ this.magicValue = magicValue;
+ }
+
+ /**
+ * Used for explicit user choices.
+ * @param userChoice the explicitly chosen implementation.
+ */
+ public void setImplementation(String userChoice) {
+ this.userChoice = userChoice;
+ }
+
+ /**
+ * Retrieves the implementation.
+ * @return the implementation.
+ */
+ public String getImplementation() {
+ return userChoice != null ? userChoice
+ : (magicValue != null ? magicValue
+ : defaultValue);
+ }
+
+ /**
+ * Retrieves the explicit user choice.
+ * @return the explicit user choice.
+ */
+ public String getExplicitChoice() {
+ return userChoice;
+ }
+
+ /**
+ * Command line argument.
+ * @param arg an argument to add.
+ */
+ public void addImplementationArgument(ImplementationSpecificArgument arg) {
+ args.add(arg);
+ }
+
+ /**
+ * Retrieves the command line arguments enabled for the current
+ * facade implementation.
+ * @return an array of command line arguments.
+ */
+ public String[] getArgs() {
+ List<String> tmp = new ArrayList<String>(args.size());
+ for (ImplementationSpecificArgument arg : args) {
+ String[] curr = arg.getParts(getImplementation());
+ if (curr != null) {
+ for (int i = 0; i < curr.length; i++) {
+ tmp.add(curr[i]);
+ }
+ }
+ }
+ String[] res = new String[tmp.size()];
+ return (String[]) tmp.toArray(res);
+ }
+
+ /**
+ * Tests whether the implementation has been chosen by the user
+ * (either via a magic property or explicitly.
+ * @return true if magic or user choice has be set.
+ * @since Ant 1.5.2
+ */
+ public boolean hasBeenSet() {
+ return userChoice != null || magicValue != null;
+ }
+
+ /**
+ * The classpath to use when loading the implementation.
+ *
+ * @param project the current project
+ * @return a Path instance that may be appended to
+ * @since Ant 1.8.0
+ */
+ public Path getImplementationClasspath(Project project) {
+ if (implementationClasspath == null) {
+ implementationClasspath = new Path(project);
+ }
+ return implementationClasspath;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/facade/ImplementationSpecificArgument.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/facade/ImplementationSpecificArgument.java
new file mode 100644
index 00000000..ba7f14a0
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/facade/ImplementationSpecificArgument.java
@@ -0,0 +1,61 @@
+/*
+ * 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.tools.ant.util.facade;
+
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Extension of Commandline.Argument with a new attribute that chooses
+ * a specific implementation of the facade.
+ *
+ *
+ * @since Ant 1.5
+ */
+public class ImplementationSpecificArgument extends Commandline.Argument {
+ private String impl;
+
+ /** Constructor for ImplementationSpecificArgument. */
+ public ImplementationSpecificArgument() {
+ super();
+ }
+
+ /**
+ * Set the implementation this argument is for.
+ * @param impl the implementation this command line argument is for.
+ */
+ public void setImplementation(String impl) {
+ this.impl = impl;
+ }
+
+ /**
+ * Return the parts this Argument consists of, if the
+ * implementation matches the chosen implementation.
+ * @see org.apache.tools.ant.types.Commandline.Argument#getParts()
+ * @param chosenImpl the implementation to check against.
+ * @return the parts if the implementation matches or an zero length
+ * array if not.
+ */
+ public final String[] getParts(String chosenImpl) {
+ if (impl == null || impl.equals(chosenImpl)) {
+ return super.getParts();
+ } else {
+ return new String[0];
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/java15/ProxyDiagnostics.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/java15/ProxyDiagnostics.java
new file mode 100644
index 00000000..bd2e7cac
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/java15/ProxyDiagnostics.java
@@ -0,0 +1,108 @@
+/*
+ * 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.tools.ant.util.java15;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * This class exists to create a string that tells diagnostics about the current
+ * state of proxy diagnostics.
+ * It does this in its toString operator.
+ * Java1.5+ is needed to compile this class; its interface is classic typeless
+ * Java.
+ * @since Ant 1.7
+ */
+public class ProxyDiagnostics {
+
+ private String destination;
+
+ private URI destURI;
+
+ /** {@value} */
+ public static final String DEFAULT_DESTINATION = "http://ant.apache.org/";
+
+ /**
+ * create a diagnostics binding for a specific URI
+ * @param destination dest to bind to
+ * @throws BuildException if the URI is malformed.
+ */
+ public ProxyDiagnostics(String destination) {
+ this.destination = destination;
+ try {
+ this.destURI = new URI(destination);
+ } catch (URISyntaxException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * create a proxy diagnostics tool bound to
+ * {@link #DEFAULT_DESTINATION}
+ */
+ public ProxyDiagnostics() {
+ this(DEFAULT_DESTINATION);
+ }
+
+ /**
+ * Get the diagnostics for proxy information.
+ * @return the information.
+ */
+ public String toString() {
+ ProxySelector selector = ProxySelector.getDefault();
+ List list = selector.select(destURI);
+ StringBuffer result = new StringBuffer();
+ Iterator proxies = list.listIterator();
+ while (proxies.hasNext()) {
+ Proxy proxy = (Proxy) proxies.next();
+ SocketAddress address = proxy.address();
+ if (address == null) {
+ result.append("Direct connection\n");
+ } else {
+ result.append(proxy.toString());
+ if (address instanceof InetSocketAddress) {
+ InetSocketAddress ina = (InetSocketAddress) address;
+ result.append(' ');
+ result.append(ina.getHostName());
+ result.append(':');
+ result.append(ina.getPort());
+ if (ina.isUnresolved()) {
+ result.append(" [unresolved]");
+ } else {
+ InetAddress addr = ina.getAddress();
+ result.append(" [");
+ result.append(addr.getHostAddress());
+ result.append(']');
+ }
+ }
+ result.append('\n');
+ }
+ }
+ return result.toString();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/JavaxScriptRunner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/JavaxScriptRunner.java
new file mode 100644
index 00000000..a24537ac
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/JavaxScriptRunner.java
@@ -0,0 +1,163 @@
+/*
+ * 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.tools.ant.util.optional;
+
+import java.util.Iterator;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.ReflectWrapper;
+import org.apache.tools.ant.util.ScriptRunnerBase;
+
+/**
+ * This class is used to run scripts using JSR 223.
+ * @since Ant 1.7.0
+ */
+public class JavaxScriptRunner extends ScriptRunnerBase {
+ private ReflectWrapper engine;
+
+ /**
+ * Get the name of the manager prefix.
+ * @return "javax"
+ */
+ public String getManagerName() {
+ return "javax";
+ }
+
+ /** {@inheritDoc}. */
+ public boolean supportsLanguage() {
+ if (engine != null) {
+ return true;
+ }
+ checkLanguage();
+ ClassLoader origLoader = replaceContextLoader();
+ try {
+ return createEngine() != null;
+ } catch (Exception ex) {
+ return false;
+ } finally {
+ restoreContextLoader(origLoader);
+ }
+ }
+
+ /**
+ * Do the work to run the script.
+ *
+ * @param execName the name that will be passed to the
+ * scripting engine for this script execution.
+ *
+ * @exception BuildException if something goes wrong executing the script.
+ */
+ public void executeScript(String execName) throws BuildException {
+ evaluateScript(execName);
+ }
+
+ /**
+ * Do the work to eval the script.
+ *
+ * @param execName the name that will be passed to the
+ * scripting engine for this script execution.
+ * @return the result of the evaluation
+ * @exception BuildException if something goes wrong executing the script.
+ */
+ public Object evaluateScript(String execName) throws BuildException {
+ checkLanguage();
+ ClassLoader origLoader = replaceContextLoader();
+ try {
+ ReflectWrapper engine = createEngine();
+ if (engine == null) {
+ throw new BuildException(
+ "Unable to create javax script engine for "
+ + getLanguage());
+ }
+ for (Iterator i = getBeans().keySet().iterator(); i.hasNext();) {
+ String key = (String) i.next();
+ Object value = getBeans().get(key);
+ if ("FX".equalsIgnoreCase(getLanguage())) {
+ engine.invoke(
+ "put", String.class, key
+ + ":" + value.getClass().getName(),
+ Object.class, value);
+ } else {
+ engine.invoke(
+ "put", String.class, key,
+ Object.class, value);
+ }
+ }
+ // execute the script
+ return engine.invoke("eval", String.class, getScript());
+ } catch (BuildException be) {
+ //catch and rethrow build exceptions
+
+ // this may be a BuildException wrapping a ScriptException
+ // deeply wrapping yet another BuildException - for
+ // example because of self.fail() - see
+ // https://issues.apache.org/bugzilla/show_bug.cgi?id=47509
+ throw unwrap(be);
+ } catch (Exception be) {
+ //any other exception? Get its cause
+ Throwable t = be;
+ Throwable te = be.getCause();
+ if (te != null) {
+ if (te instanceof BuildException) {
+ throw (BuildException) te;
+ } else {
+ t = te;
+ }
+ }
+ throw new BuildException(t);
+ } finally {
+ restoreContextLoader(origLoader);
+ }
+ }
+
+ private ReflectWrapper createEngine() throws Exception {
+ if (engine != null) {
+ return engine;
+ }
+ ReflectWrapper manager = new ReflectWrapper(
+ getClass().getClassLoader(), "javax.script.ScriptEngineManager");
+ Object e = manager.invoke(
+ "getEngineByName", String.class, getLanguage());
+ if (e == null) {
+ return null;
+ }
+ ReflectWrapper ret = new ReflectWrapper(e);
+ if (getKeepEngine()) {
+ this.engine = ret;
+ }
+ return ret;
+ }
+
+ /**
+ * Traverse a Throwable's cause(s) and return the BuildException
+ * most deeply nested into it - if any.
+ */
+ private static BuildException unwrap(Throwable t) {
+ BuildException deepest =
+ t instanceof BuildException ? (BuildException) t : null;
+ Throwable current = t;
+ while (current.getCause() != null) {
+ current = current.getCause();
+ if (current instanceof BuildException) {
+ deepest = (BuildException) current;
+ }
+ }
+ return deepest;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/NoExitSecurityManager.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/NoExitSecurityManager.java
new file mode 100644
index 00000000..e704ab29
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/NoExitSecurityManager.java
@@ -0,0 +1,51 @@
+/*
+ * 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.tools.ant.util.optional;
+
+import java.security.Permission;
+
+import org.apache.tools.ant.ExitException;
+
+/**
+ * This is intended as a replacement for the default system manager.
+ * The goal is to intercept System.exit calls and make it throw an
+ * exception instead so that a System.exit in a task does not
+ * fully terminate Ant.
+ *
+ * @see ExitException
+ */
+public class NoExitSecurityManager extends SecurityManager {
+
+ /**
+ * Override SecurityManager#checkExit.
+ * This throws an ExitException(status) exception.
+ * @param status the exit status
+ */
+ public void checkExit(int status) {
+ throw new ExitException(status);
+ }
+
+ /**
+ * Override SecurityManager#checkPermission.
+ * This does nothing.
+ * @param perm the requested permission.
+ */
+ public void checkPermission(Permission perm) {
+ // no permission here
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/ScriptRunner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/ScriptRunner.java
new file mode 100644
index 00000000..0f4cd1f9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/ScriptRunner.java
@@ -0,0 +1,177 @@
+/*
+ * 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.tools.ant.util.optional;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import org.apache.bsf.BSFEngine;
+import org.apache.bsf.BSFException;
+import org.apache.bsf.BSFManager;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.ReflectUtil;
+import org.apache.tools.ant.util.ScriptRunnerBase;
+
+/**
+ * This class is used to run BSF scripts
+ *
+ */
+public class ScriptRunner extends ScriptRunnerBase {
+ // Register Groovy ourselves, since BSF did not
+ // natively support it in versions previous to 1.2.4.
+ static {
+ BSFManager.registerScriptingEngine(
+ "groovy",
+ "org.codehaus.groovy.bsf.GroovyEngine",
+ new String[] {"groovy", "gy"});
+ }
+
+ private BSFEngine engine;
+ private BSFManager manager;
+
+ /**
+ * Get the name of the manager prefix.
+ * @return "bsf"
+ */
+ public String getManagerName() {
+ return "bsf";
+ }
+
+ /**
+ * Check if bsf supports the language.
+ * @return true if bsf can create an engine for this language.
+ */
+ public boolean supportsLanguage() {
+ Hashtable table = (Hashtable) ReflectUtil.getField(
+ new BSFManager(), "registeredEngines");
+ String engineClassName = (String) table.get(getLanguage());
+ if (engineClassName == null) {
+ getProject().log(
+ "This is no BSF engine class for language '"
+ + getLanguage() + "'",
+ Project.MSG_VERBOSE);
+ return false;
+ }
+ try {
+ getScriptClassLoader().loadClass(engineClassName);
+ return true;
+ } catch (Throwable ex) {
+ getProject().log(
+ "unable to create BSF engine class for language '"
+ + getLanguage() + "'",
+ ex,
+ Project.MSG_VERBOSE);
+ return false;
+ }
+ }
+
+ /**
+ * Do the work.
+ *
+ * @param execName the name that will be passed to BSF for this script execution.
+ * @exception BuildException if something goes wrong executing the script.
+ */
+ public void executeScript(String execName) throws BuildException {
+ checkLanguage();
+ ClassLoader origLoader = replaceContextLoader();
+ try {
+ BSFManager m = createManager();
+ declareBeans(m);
+ // execute the script
+ if (engine == null) {
+ m.exec(getLanguage(), execName, 0, 0, getScript());
+ } else {
+ engine.exec(execName, 0, 0, getScript());
+ }
+ } catch (BSFException be) {
+ throw getBuildException(be);
+ } finally {
+ restoreContextLoader(origLoader);
+ }
+ }
+
+ /**
+ * Evaluate the script.
+ *
+ * @param execName the name that will be passed to BSF for this script execution.
+ * @return the result of the evaluation
+ * @exception BuildException if something goes wrong executing the script.
+ */
+ public Object evaluateScript(String execName) throws BuildException {
+ checkLanguage();
+ ClassLoader origLoader = replaceContextLoader();
+ try {
+ BSFManager m = createManager();
+ declareBeans(m);
+ // execute the script
+ if (engine == null) {
+ return m.eval(getLanguage(), execName, 0, 0, getScript());
+ }
+ return engine.eval(execName, 0, 0, getScript());
+ } catch (BSFException be) {
+ throw getBuildException(be);
+ } finally {
+ restoreContextLoader(origLoader);
+ }
+ }
+
+ /**
+ * Get/create a BuildException from a BSFException.
+ * @param be BSFException to convert.
+ * @return BuildException the converted exception.
+ */
+ private BuildException getBuildException(BSFException be) {
+ Throwable t = be;
+ Throwable te = be.getTargetException();
+ if (te instanceof BuildException) {
+ return (BuildException) te;
+ }
+ return new BuildException(te == null ? t : te);
+ }
+
+ private void declareBeans(BSFManager m) throws BSFException {
+ for (Iterator i = getBeans().keySet().iterator(); i.hasNext();) {
+ String key = (String) i.next();
+ Object value = getBeans().get(key);
+ if (value != null) {
+ m.declareBean(key, value, value.getClass());
+ } else {
+ // BSF uses a hashtable to store values
+ // so cannot declareBean with a null value
+ // So need to remove any bean of this name as
+ // that bean should not be visible
+ m.undeclareBean(key);
+ }
+ }
+ }
+
+ private BSFManager createManager() throws BSFException {
+ if (manager != null) {
+ return manager;
+ }
+ BSFManager m = new BSFManager();
+ m.setClassLoader(getScriptClassLoader());
+ if (getKeepEngine()) {
+ BSFEngine e = manager.loadScriptingEngine(getLanguage());
+ this.manager = m;
+ this.engine = e;
+ }
+ return m;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/WeakishReference12.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/WeakishReference12.java
new file mode 100644
index 00000000..4cd12788
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/optional/WeakishReference12.java
@@ -0,0 +1,45 @@
+/*
+ * 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.tools.ant.util.optional;
+
+import org.apache.tools.ant.util.WeakishReference;
+
+/**
+ * This is a reference that really is is Weak, as it uses the
+ * appropriate java.lang.ref class.
+ * @deprecated since 1.7.
+ * Just use {@link java.lang.ref.WeakReference} directly.
+ * Note that in ant1.7 is parent was changed to extend HardReference.
+ * This is because the latter has access to the (package scoped)
+ * WeakishReference(Object) constructor, and both that and this are thin
+ * facades on the underlying no-longer-abstract base class.
+ */
+public class WeakishReference12 extends WeakishReference.HardReference {
+
+
+ /**
+ * create a new soft reference, which is bound to a
+ * Weak reference inside
+ * @param reference the object to reference.
+ * @see java.lang.ref.WeakReference
+ */
+ public WeakishReference12(Object reference) {
+ super(reference);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaOroMatcher.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaOroMatcher.java
new file mode 100644
index 00000000..5c43376c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaOroMatcher.java
@@ -0,0 +1,170 @@
+/*
+ * 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.tools.ant.util.regexp;
+
+import java.util.Vector;
+
+import org.apache.oro.text.regex.MatchResult;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.Perl5Compiler;
+import org.apache.oro.text.regex.Perl5Matcher;
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Implementation of RegexpMatcher for Jakarta-ORO.
+ *
+ */
+public class JakartaOroMatcher implements RegexpMatcher {
+
+ private String pattern;
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected final Perl5Compiler compiler = new Perl5Compiler();
+ protected final Perl5Matcher matcher = new Perl5Matcher();
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Constructor for JakartaOroMatcher.
+ */
+ public JakartaOroMatcher() {
+ }
+
+ /**
+ * Set the regexp pattern from the String description.
+ * @param pattern the pattern to match
+ */
+ public void setPattern(final String pattern) {
+ this.pattern = pattern;
+ }
+
+ /**
+ * Get a String representation of the regexp pattern
+ * @return the pattern
+ */
+ public String getPattern() {
+ return this.pattern;
+ }
+
+ /**
+ * Get a compiled representation of the regexp pattern
+ * @param options the options
+ * @return the compiled pattern
+ * @throws BuildException on error
+ */
+ protected Pattern getCompiledPattern(final int options)
+ throws BuildException {
+ try {
+ // compute the compiler options based on the input options first
+ final Pattern p = compiler.compile(pattern, getCompilerOptions(options));
+ return p;
+ } catch (final Exception e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Does the given argument match the pattern using default options?
+ * @param argument the string to match against
+ * @return true if the pattern matches
+ * @throws BuildException on error
+ */
+ public boolean matches(final String argument) throws BuildException {
+ return matches(argument, MATCH_DEFAULT);
+ }
+
+ /**
+ * Does the given argument match the pattern?
+ * @param input the string to match against
+ * @param options the regex options to use
+ * @return true if the pattern matches
+ * @throws BuildException on error
+ */
+ public boolean matches(final String input, final int options)
+ throws BuildException {
+ final Pattern p = getCompiledPattern(options);
+ return matcher.contains(input, p);
+ }
+
+ /**
+ * Returns a Vector of matched groups found in the argument
+ * using default options.
+ *
+ * <p>Group 0 will be the full match, the rest are the
+ * parenthesized subexpressions</p>.
+ *
+ * @param argument the string to match against
+ * @return the vector of groups
+ * @throws BuildException on error
+ */
+ public Vector getGroups(final String argument) throws BuildException {
+ return getGroups(argument, MATCH_DEFAULT);
+ }
+
+ /**
+ * Returns a Vector of matched groups found in the argument.
+ *
+ * <p>Group 0 will be the full match, the rest are the
+ * parenthesized subexpressions</p>.
+ *
+ * @param input the string to match against
+ * @param options the regex options to use
+ * @return the vector of groups
+ * @throws BuildException on error
+ */
+ public Vector getGroups(final String input, final int options)
+ throws BuildException {
+ if (!matches(input, options)) {
+ return null;
+ }
+ final Vector v = new Vector();
+ final MatchResult mr = matcher.getMatch();
+ final int cnt = mr.groups();
+ for (int i = 0; i < cnt; i++) {
+ String match = mr.group(i);
+ // treat non-matching groups as empty matches
+ if (match == null) {
+ match = "";
+ }
+ v.addElement(match);
+ }
+ return v;
+ }
+
+ /**
+ * Convert the generic options to the regex compiler specific options.
+ * @param options the generic options
+ * @return the specific options
+ */
+ protected int getCompilerOptions(final int options) {
+ int cOptions = Perl5Compiler.DEFAULT_MASK;
+
+ if (RegexpUtil.hasFlag(options, MATCH_CASE_INSENSITIVE)) {
+ cOptions |= Perl5Compiler.CASE_INSENSITIVE_MASK;
+ }
+ if (RegexpUtil.hasFlag(options, MATCH_MULTILINE)) {
+ cOptions |= Perl5Compiler.MULTILINE_MASK;
+ }
+ if (RegexpUtil.hasFlag(options, MATCH_SINGLELINE)) {
+ cOptions |= Perl5Compiler.SINGLELINE_MASK;
+ }
+
+ return cOptions;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaOroRegexp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaOroRegexp.java
new file mode 100644
index 00000000..529a78ae
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaOroRegexp.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.tools.ant.util.regexp;
+
+import org.apache.oro.text.regex.Perl5Substitution;
+import org.apache.oro.text.regex.Substitution;
+import org.apache.oro.text.regex.Util;
+import org.apache.tools.ant.BuildException;
+
+/***
+ * Regular expression implementation using the Jakarta Oro package
+ */
+public class JakartaOroRegexp extends JakartaOroMatcher implements Regexp {
+
+ private static final int DECIMAL = 10;
+
+ /** Constructor for JakartaOroRegexp */
+ public JakartaOroRegexp() {
+ super();
+ }
+
+ /**
+ * Perform a substitution on the regular expression.
+ * @param input The string to substitute on
+ * @param argument The string which defines the substitution
+ * @param options The list of options for the match and replace.
+ * @return the result of the operation
+ * @throws BuildException on error
+ */
+ public String substitute(final String input, final String argument, final int options)
+ throws BuildException {
+ // translate \1 to $1 so that the Perl5Substitution will work
+ final StringBuffer subst = new StringBuffer();
+ for (int i = 0; i < argument.length(); i++) {
+ char c = argument.charAt(i);
+ if (c == '$') {
+ subst.append('\\');
+ subst.append('$');
+ } else if (c == '\\') {
+ if (++i < argument.length()) {
+ c = argument.charAt(i);
+ final int value = Character.digit(c, DECIMAL);
+ if (value > -1) {
+ subst.append("$").append(value);
+ } else {
+ subst.append(c);
+ }
+ } else {
+ // TODO - should throw an exception instead?
+ subst.append('\\');
+ }
+ } else {
+ subst.append(c);
+ }
+ }
+
+ // Do the substitution
+ final Substitution s =
+ new Perl5Substitution(subst.toString(),
+ Perl5Substitution.INTERPOLATE_ALL);
+ return Util.substitute(matcher,
+ getCompiledPattern(options),
+ s,
+ input,
+ getSubsOptions(options));
+ }
+
+ /**
+ * Convert ant regexp substitution option to oro options.
+ *
+ * @param options the ant regexp options
+ * @return the oro substition options
+ */
+ protected int getSubsOptions(final int options) {
+ final boolean replaceAll = RegexpUtil.hasFlag(options, REPLACE_ALL);
+ int subsOptions = 1;
+ if (replaceAll) {
+ subsOptions = Util.SUBSTITUTE_ALL;
+ }
+ return subsOptions;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpMatcher.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpMatcher.java
new file mode 100644
index 00000000..3e14415d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpMatcher.java
@@ -0,0 +1,161 @@
+/*
+ * 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.tools.ant.util.regexp;
+
+import java.util.Vector;
+import org.apache.regexp.RE;
+import org.apache.regexp.RESyntaxException;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Implementation of RegexpMatcher for Jakarta-Regexp.
+ *
+ */
+public class JakartaRegexpMatcher implements RegexpMatcher {
+
+ private String pattern;
+
+ /**
+ * Set the regexp pattern from the String description.
+ * @param pattern the pattern to match
+ */
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+
+ /**
+ * Get a String representation of the regexp pattern
+ * @return the pattern
+ */
+ public String getPattern() {
+ return pattern;
+ }
+
+ /**
+ * Compile the pattern.
+ *
+ * @param options the ant regexp options
+ * @return a compiled pattern
+ * @exception BuildException if an error occurs
+ */
+ protected RE getCompiledPattern(int options)
+ throws BuildException {
+ int cOptions = getCompilerOptions(options);
+ try {
+ RE reg = new RE(pattern);
+ reg.setMatchFlags(cOptions);
+ return reg;
+ } catch (RESyntaxException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Does the given argument match the pattern?
+ * @param argument the string to match against
+ * @return true if the pattern matches
+ * @throws BuildException on error
+ */
+ public boolean matches(String argument) throws BuildException {
+ return matches(argument, MATCH_DEFAULT);
+ }
+
+ /**
+ * Does the given argument match the pattern?
+ * @param input the string to match against
+ * @param options the regex options to use
+ * @return true if the pattern matches
+ * @throws BuildException on error
+ */
+ public boolean matches(String input, int options)
+ throws BuildException {
+ return matches(input, getCompiledPattern(options));
+ }
+
+ private boolean matches(String input, RE reg) {
+ return reg.match(input);
+ }
+
+ /**
+ * Returns a Vector of matched groups found in the argument
+ * using default options.
+ *
+ * <p>Group 0 will be the full match, the rest are the
+ * parenthesized subexpressions</p>.
+ *
+ * @param argument the string to match against
+ * @return the vector of groups
+ * @throws BuildException on error
+ */
+ public Vector getGroups(String argument) throws BuildException {
+ return getGroups(argument, MATCH_DEFAULT);
+ }
+
+ /**
+ * Returns a Vector of matched groups found in the argument.
+ *
+ * <p>Group 0 will be the full match, the rest are the
+ * parenthesized subexpressions</p>.
+ *
+ * @param input the string to match against
+ * @param options the regex options to use
+ * @return the vector of groups
+ * @throws BuildException on error
+ */
+ public Vector getGroups(String input, int options)
+ throws BuildException {
+ RE reg = getCompiledPattern(options);
+ if (!matches(input, reg)) {
+ return null;
+ }
+ Vector v = new Vector();
+ int cnt = reg.getParenCount();
+ for (int i = 0; i < cnt; i++) {
+ String match = reg.getParen(i);
+ // treat non-matching groups as empty matches
+ if (match == null) {
+ match = "";
+ }
+ v.addElement(match);
+ }
+ return v;
+ }
+
+ /**
+ * Convert the generic options to the regex compiler specific options.
+ * @param options the generic options
+ * @return the specific options
+ */
+ protected int getCompilerOptions(int options) {
+ int cOptions = RE.MATCH_NORMAL;
+
+ if (RegexpUtil.hasFlag(options, MATCH_CASE_INSENSITIVE)) {
+ cOptions |= RE.MATCH_CASEINDEPENDENT;
+ }
+ if (RegexpUtil.hasFlag(options, MATCH_MULTILINE)) {
+ cOptions |= RE.MATCH_MULTILINE;
+ }
+ if (RegexpUtil.hasFlag(options, MATCH_SINGLELINE)) {
+ cOptions |= RE.MATCH_SINGLELINE;
+ }
+
+ return cOptions;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpRegexp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpRegexp.java
new file mode 100644
index 00000000..865f424a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpRegexp.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.tools.ant.util.regexp;
+
+import java.util.Vector;
+import org.apache.regexp.RE;
+import org.apache.tools.ant.BuildException;
+
+/***
+ * Regular expression implementation using the Jakarta Regexp package
+ */
+public class JakartaRegexpRegexp extends JakartaRegexpMatcher
+ implements Regexp {
+
+ private static final int DECIMAL = 10;
+
+ /** Constructor for JakartaRegexpRegexp */
+ public JakartaRegexpRegexp() {
+ super();
+ }
+
+ /**
+ * Convert ant regexp substitution option to apache regex options.
+ *
+ * @param options the ant regexp options
+ * @return the apache regex substition options
+ */
+ protected int getSubsOptions(int options) {
+ int subsOptions = RE.REPLACE_FIRSTONLY;
+ if (RegexpUtil.hasFlag(options, REPLACE_ALL)) {
+ subsOptions = RE.REPLACE_ALL;
+ }
+ return subsOptions;
+ }
+
+ /**
+ * Perform a substitution on the regular expression.
+ * @param input The string to substitute on
+ * @param argument The string which defines the substitution
+ * @param options The list of options for the match and replace.
+ * @return the result of the operation
+ * @throws BuildException on error
+ */
+ public String substitute(String input, String argument, int options)
+ throws BuildException {
+ Vector v = getGroups(input, options);
+
+ // replace \1 with the corresponding group
+ StringBuffer result = new StringBuffer();
+ for (int i = 0; i < argument.length(); i++) {
+ char c = argument.charAt(i);
+ if (c == '\\') {
+ if (++i < argument.length()) {
+ c = argument.charAt(i);
+ int value = Character.digit(c, DECIMAL);
+ if (value > -1) {
+ result.append((String) v.elementAt(value));
+ } else {
+ result.append(c);
+ }
+ } else {
+ // TODO - should throw an exception instead?
+ result.append('\\');
+ }
+ } else {
+ result.append(c);
+ }
+ }
+ argument = result.toString();
+
+ RE reg = getCompiledPattern(options);
+ int sOptions = getSubsOptions(options);
+ return reg.subst(input, argument, sOptions);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpMatcher.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpMatcher.java
new file mode 100644
index 00000000..8c241d4e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpMatcher.java
@@ -0,0 +1,170 @@
+/*
+ * 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.tools.ant.util.regexp;
+
+import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Implementation of RegexpMatcher for the built-in regexp matcher of
+ * JDK 1.4. UNIX_LINES option is enabled as a default.
+ *
+ */
+public class Jdk14RegexpMatcher implements RegexpMatcher {
+
+ private String pattern;
+
+ /** Constructor for JakartaOroRegexp */
+ public Jdk14RegexpMatcher() {
+ }
+
+ /**
+ * Set the regexp pattern from the String description.
+ * @param pattern the pattern to match
+ */
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+
+ /**
+ * Get a String representation of the regexp pattern
+ * @return the pattern
+ * @throws BuildException on error
+ */
+ public String getPattern() {
+ return pattern;
+ }
+
+ /**
+ * Get a compiled representation of the regexp pattern
+ * @param options the options
+ * @return the compiled pattern
+ * @throws BuildException on error
+ */
+ protected Pattern getCompiledPattern(int options)
+ throws BuildException {
+ int cOptions = getCompilerOptions(options);
+ try {
+ Pattern p = Pattern.compile(this.pattern, cOptions);
+ return p;
+ } catch (PatternSyntaxException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Does the given argument match the pattern using default options?
+ * @param argument the string to match against
+ * @return true if the pattern matches
+ * @throws BuildException on error
+ */
+ public boolean matches(String argument) throws BuildException {
+ return matches(argument, MATCH_DEFAULT);
+ }
+
+ /**
+ * Does the given argument match the pattern?
+ * @param input the string to match against
+ * @param options the regex options to use
+ * @return true if the pattern matches
+ * @throws BuildException on error
+ */
+ public boolean matches(String input, int options)
+ throws BuildException {
+ try {
+ Pattern p = getCompiledPattern(options);
+ return p.matcher(input).find();
+ } catch (Exception e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Returns a Vector of matched groups found in the argument
+ * using default options.
+ *
+ * <p>Group 0 will be the full match, the rest are the
+ * parenthesized subexpressions</p>.
+ *
+ * @param argument the string to match against
+ * @return the vector of groups
+ * @throws BuildException on error
+ */
+ public Vector getGroups(String argument) throws BuildException {
+ return getGroups(argument, MATCH_DEFAULT);
+ }
+
+ /**
+ * Returns a Vector of matched groups found in the argument.
+ *
+ * <p>Group 0 will be the full match, the rest are the
+ * parenthesized subexpressions</p>.
+ *
+ * @param input the string to match against
+ * @param options the regex options to use
+ * @return the vector of groups
+ * @throws BuildException on error
+ */
+ public Vector getGroups(String input, int options)
+ throws BuildException {
+ Pattern p = getCompiledPattern(options);
+ Matcher matcher = p.matcher(input);
+ if (!matcher.find()) {
+ return null;
+ }
+ Vector v = new Vector();
+ int cnt = matcher.groupCount();
+ for (int i = 0; i <= cnt; i++) {
+ String match = matcher.group(i);
+ // treat non-matching groups as empty matches
+ if (match == null) {
+ match = "";
+ }
+ v.addElement(match);
+ }
+ return v;
+ }
+
+ /**
+ * Convert the generic options to the regex compiler specific options.
+ * @param options the generic options
+ * @return the specific options
+ */
+ protected int getCompilerOptions(int options) {
+ // be strict about line separator
+ int cOptions = Pattern.UNIX_LINES;
+
+ if (RegexpUtil.hasFlag(options, MATCH_CASE_INSENSITIVE)) {
+ cOptions |= Pattern.CASE_INSENSITIVE;
+ }
+ if (RegexpUtil.hasFlag(options, MATCH_MULTILINE)) {
+ cOptions |= Pattern.MULTILINE;
+ }
+ if (RegexpUtil.hasFlag(options, MATCH_SINGLELINE)) {
+ cOptions |= Pattern.DOTALL;
+ }
+
+ return cOptions;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpRegexp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpRegexp.java
new file mode 100644
index 00000000..3ca80706
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpRegexp.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.tools.ant.util.regexp;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.tools.ant.BuildException;
+
+/***
+ * Regular expression implementation using the JDK 1.4 regular expression package
+ */
+public class Jdk14RegexpRegexp extends Jdk14RegexpMatcher implements Regexp {
+
+ private static final int DECIMAL = 10;
+
+ /** Constructor for Jdk14RegexpRegexp */
+ public Jdk14RegexpRegexp() {
+ super();
+ }
+
+ /**
+ * Convert ant regexp substitution option to jdk1.4 options.
+ *
+ * @param options the ant regexp options
+ * @return the jdk14 substition options
+ */
+ protected int getSubsOptions(int options) {
+ int subsOptions = REPLACE_FIRST;
+ if (RegexpUtil.hasFlag(options, REPLACE_ALL)) {
+ subsOptions = REPLACE_ALL;
+ }
+ return subsOptions;
+ }
+
+ /**
+ * Perform a substitution on the regular expression.
+ * @param input The string to substitute on
+ * @param argument The string which defines the substitution
+ * @param options The list of options for the match and replace.
+ * @return the result of the operation
+ * @throws BuildException on error
+ */
+ public String substitute(String input, String argument, int options)
+ throws BuildException {
+ // translate \1 to $(1) so that the Matcher will work
+ StringBuffer subst = new StringBuffer();
+ for (int i = 0; i < argument.length(); i++) {
+ char c = argument.charAt(i);
+ if (c == '$') {
+ subst.append('\\');
+ subst.append('$');
+ } else if (c == '\\') {
+ if (++i < argument.length()) {
+ c = argument.charAt(i);
+ int value = Character.digit(c, DECIMAL);
+ if (value > -1) {
+ subst.append("$").append(value);
+ } else {
+ subst.append(c);
+ }
+ } else {
+ // TODO - should throw an exception instead?
+ subst.append('\\');
+ }
+ } else {
+ subst.append(c);
+ }
+ }
+ argument = subst.toString();
+
+ int sOptions = getSubsOptions(options);
+ Pattern p = getCompiledPattern(options);
+ StringBuffer sb = new StringBuffer();
+
+ Matcher m = p.matcher(input);
+ if (RegexpUtil.hasFlag(sOptions, REPLACE_ALL)) {
+ sb.append(m.replaceAll(argument));
+ } else {
+ boolean res = m.find();
+ if (res) {
+ m.appendReplacement(sb, argument);
+ m.appendTail(sb);
+ } else {
+ sb.append(input);
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/Regexp.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/Regexp.java
new file mode 100644
index 00000000..1c7816a1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/Regexp.java
@@ -0,0 +1,50 @@
+/*
+ * 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.tools.ant.util.regexp;
+
+import org.apache.tools.ant.BuildException;
+
+/***
+ * Interface which represents a regular expression, and the operations
+ * that can be performed on it.
+ *
+ */
+public interface Regexp extends RegexpMatcher {
+
+ /**
+ * Replace only the first occurrence of the regular expression
+ */
+ int REPLACE_FIRST = 0x00000001;
+
+ /**
+ * Replace all occurrences of the regular expression
+ */
+ int REPLACE_ALL = 0x00000010;
+
+ /**
+ * Perform a substitution on the regular expression.
+ * @param input The string to substitute on
+ * @param argument The string which defines the substitution
+ * @param options The list of options for the match and replace. See the
+ * MATCH_ and REPLACE_ constants above.
+ * @return the result of the operation
+ * @throws BuildException on error
+ */
+ String substitute(String input, String argument, int options)
+ throws BuildException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpFactory.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpFactory.java
new file mode 100644
index 00000000..7077a216
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpFactory.java
@@ -0,0 +1,85 @@
+/*
+ * 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.tools.ant.util.regexp;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.ClasspathUtils;
+
+/***
+ * Regular expression factory, which will create Regexp objects. The
+ * actual implementation class depends on the System or Ant Property:
+ * <code>ant.regexp.regexpimpl</code>.
+ *
+ */
+public class RegexpFactory extends RegexpMatcherFactory {
+
+ /** Constructor for RegexpFactory */
+ public RegexpFactory() {
+ }
+
+ /***
+ * Create a new regular expression matcher instance.
+ * @return the matcher instance
+ * @throws BuildException on error
+ */
+ public Regexp newRegexp() throws BuildException {
+ return newRegexp(null);
+ }
+
+ /***
+ * Create a new regular expression matcher instance.
+ *
+ * @param p Project whose ant.regexp.regexpimpl property will be used.
+ * @return the matcher instance
+ * @throws BuildException on error
+ */
+ public Regexp newRegexp(Project p) throws BuildException {
+ String systemDefault = null;
+ if (p == null) {
+ systemDefault = System.getProperty(MagicNames.REGEXP_IMPL);
+ } else {
+ systemDefault = p.getProperty(MagicNames.REGEXP_IMPL);
+ }
+
+ if (systemDefault != null) {
+ return createRegexpInstance(systemDefault);
+ // TODO should we silently catch possible exceptions and try to
+ // load a different implementation?
+ }
+
+ return new Jdk14RegexpRegexp();
+ }
+
+ /**
+ * Wrapper over RegexpMatcherFactory.createInstance that ensures that
+ * we are dealing with a Regexp implementation.
+ * @param classname the name of the class to use.
+ * @return the instance.
+ * @throws BuildException if there is a problem.
+ * @since 1.3
+ *
+ * @see RegexpMatcherFactory#createInstance(String)
+ */
+ protected Regexp createRegexpInstance(String classname) throws BuildException {
+ return (Regexp) ClasspathUtils.newInstance(classname, RegexpFactory.class.getClassLoader(),
+ Regexp.class);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpMatcher.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpMatcher.java
new file mode 100644
index 00000000..7938d8be
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpMatcher.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.tools.ant.util.regexp;
+
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Interface describing a regular expression matcher.
+ *
+ */
+public interface RegexpMatcher {
+
+ /***
+ * Default Mask (case insensitive, neither multiline nor
+ * singleline specified).
+ */
+ int MATCH_DEFAULT = 0x00000000;
+
+ /***
+ * Perform a case insensitive match
+ */
+ int MATCH_CASE_INSENSITIVE = 0x00000100;
+
+ /***
+ * Treat the input as a multiline input
+ */
+ int MATCH_MULTILINE = 0x00001000;
+
+ /***
+ * Treat the input as singleline input ('.' matches newline)
+ */
+ int MATCH_SINGLELINE = 0x00010000;
+
+
+ /**
+ * Set the regexp pattern from the String description.
+ * @param pattern the pattern to match
+ * @throws BuildException on error
+ */
+ void setPattern(String pattern) throws BuildException;
+
+ /**
+ * Get a String representation of the regexp pattern
+ * @return the pattern
+ * @throws BuildException on error
+ */
+ String getPattern() throws BuildException;
+
+ /**
+ * Does the given argument match the pattern?
+ * @param argument the string to match against
+ * @return true if the pattern matches
+ * @throws BuildException on error
+ */
+ boolean matches(String argument) throws BuildException;
+
+ /**
+ * Returns a Vector of matched groups found in the argument
+ * using default options.
+ *
+ * <p>Group 0 will be the full match, the rest are the
+ * parenthesized subexpressions</p>.
+ *
+ * @param argument the string to match against
+ * @return the vector of groups
+ * @throws BuildException on error
+ */
+ Vector getGroups(String argument) throws BuildException;
+
+ /***
+ * Does this regular expression match the input, given
+ * certain options
+ * @param input The string to check for a match
+ * @param options The list of options for the match. See the
+ * MATCH_ constants above.
+ * @return true if the pattern matches
+ * @throws BuildException on error
+ */
+ boolean matches(String input, int options) throws BuildException;
+
+ /***
+ * Get the match groups from this regular expression. The return
+ * type of the elements is always String.
+ * @param input The string to check for a match
+ * @param options The list of options for the match. See the
+ * MATCH_ constants above.
+ * @return the vector of groups
+ * @throws BuildException on error
+ */
+ Vector getGroups(String input, int options) throws BuildException;
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpMatcherFactory.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpMatcherFactory.java
new file mode 100644
index 00000000..a0a8a155
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpMatcherFactory.java
@@ -0,0 +1,114 @@
+/*
+ * 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.tools.ant.util.regexp;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.ClasspathUtils;
+
+/**
+ * Simple Factory Class that produces an implementation of RegexpMatcher based on the system
+ * property <code>ant.regexp.regexpimpl</code> and the classes available.
+ *
+ * <p>
+ * In a more general framework this class would be abstract and have a static newInstance method.
+ * </p>
+ *
+ */
+public class RegexpMatcherFactory {
+
+ /** Constructor for RegexpMatcherFactory. */
+ public RegexpMatcherFactory() {
+ }
+
+ /***
+ * Create a new regular expression instance.
+ * @return the matcher
+ * @throws BuildException on error
+ */
+ public RegexpMatcher newRegexpMatcher() throws BuildException {
+ return newRegexpMatcher(null);
+ }
+
+ /***
+ * Create a new regular expression instance.
+ *
+ * @param p Project whose ant.regexp.regexpimpl property will be used.
+ * @return the matcher
+ * @throws BuildException on error
+ */
+ public RegexpMatcher newRegexpMatcher(Project p) throws BuildException {
+ String systemDefault = null;
+ if (p == null) {
+ systemDefault = System.getProperty(MagicNames.REGEXP_IMPL);
+ } else {
+ systemDefault = p.getProperty(MagicNames.REGEXP_IMPL);
+ }
+
+ if (systemDefault != null) {
+ return createInstance(systemDefault);
+ // TODO should we silently catch possible exceptions and try to
+ // load a different implementation?
+ }
+
+ return new Jdk14RegexpMatcher();
+ }
+
+ /**
+ * Create an instance of a matcher from a classname.
+ *
+ * @param className a <code>String</code> value
+ * @return a <code>RegexpMatcher</code> value
+ * @exception BuildException if an error occurs
+ */
+ protected RegexpMatcher createInstance(String className) throws BuildException {
+ return (RegexpMatcher) ClasspathUtils.newInstance(className, RegexpMatcherFactory.class
+ .getClassLoader(), RegexpMatcher.class);
+ }
+
+ /**
+ * Test if a particular class is available to be used.
+ *
+ * @param className a <code>String</code> value
+ * @exception BuildException if an error occurs
+ */
+ protected void testAvailability(String className) throws BuildException {
+ try {
+ Class.forName(className);
+ } catch (Throwable t) {
+ throw new BuildException(t);
+ }
+ }
+
+ /**
+ * Checks if a RegExp-Matcher is available.
+ * @param project The project to check for (may be <code>null</code>)
+ * @return <code>true</code> if available otherwise <code>false</code>
+ */
+ public static boolean regexpMatcherPresent(Project project) {
+ try {
+ // The factory throws a BuildException if no usable matcher
+ // cannot be instantiated. We dont need the matcher itself here.
+ new RegexpMatcherFactory().newRegexpMatcher(project);
+ return true;
+ } catch (Throwable ex) {
+ return false;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpUtil.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpUtil.java
new file mode 100644
index 00000000..ebd85fab
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/regexp/RegexpUtil.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.tools.ant.util.regexp;
+
+// CheckStyle:HideUtilityClassConstructorCheck OFF - bc
+
+/***
+ * Regular expression utilities class which handles flag operations.
+ *
+ */
+public class RegexpUtil {
+
+ /**
+ * Check the options has a particular flag set.
+ *
+ * @param options an <code>int</code> value
+ * @param flag an <code>int</code> value
+ * @return true if the flag is set
+ */
+ public static boolean hasFlag(int options, int flag) {
+ return ((options & flag) > 0);
+ }
+
+ /**
+ * Remove a particular flag from an int value contains the option flags.
+ *
+ * @param options an <code>int</code> value
+ * @param flag an <code>int</code> value
+ * @return the options with the flag unset
+ */
+ public static int removeFlag(int options, int flag) {
+ return (options & (0xFFFFFFFF - flag));
+ }
+
+ /**
+ * convert regex option flag characters to regex options
+ * <dl>
+ * <li>g - Regexp.REPLACE_ALL</li>
+ * <li>i - RegexpMatcher.MATCH_CASE_INSENSITIVE</li>
+ * <li>m - RegexpMatcher.MATCH_MULTILINE</li>
+ * <li>s - RegexpMatcher.MATCH_SINGLELINE</li>
+ * </dl>
+ * @param flags the string containing the flags
+ * @return the Regexp option bits
+ * @since Ant 1.8.2
+ */
+ public static int asOptions(String flags) {
+ int options = RegexpMatcher.MATCH_DEFAULT;
+ if (flags != null) {
+ options = asOptions(flags.indexOf('i') == -1,
+ flags.indexOf('m') != -1,
+ flags.indexOf('s') != -1);
+ if (flags.indexOf('g') != -1) {
+ options |= Regexp.REPLACE_ALL;
+ }
+ }
+ return options;
+ }
+
+ /**
+ * Convert flag to regex options.
+ *
+ * @param caseSensitive opposite of RegexpMatcher.MATCH_CASE_INSENSITIVE
+ * @return the Regexp option bits
+ * @since Ant 1.8.2
+ */
+ public static int asOptions(boolean caseSensitive) {
+ return asOptions(caseSensitive, false, false);
+ }
+
+ /**
+ * Convert flags to regex options.
+ *
+ * @param caseSensitive opposite of RegexpMatcher.MATCH_CASE_INSENSITIVE
+ * @param multiLine RegexpMatcher.MATCH_MULTILINE
+ * @param singleLine RegexpMatcher.MATCH_SINGLELINE
+ * @return the Regexp option bits
+ * @since Ant 1.8.2
+ */
+ public static int asOptions(boolean caseSensitive, boolean multiLine,
+ boolean singleLine) {
+ int options = RegexpMatcher.MATCH_DEFAULT;
+ if (!caseSensitive) {
+ options = options | RegexpMatcher.MATCH_CASE_INSENSITIVE;
+ }
+ if (multiLine) {
+ options = options | RegexpMatcher.MATCH_MULTILINE;
+ }
+ if (singleLine) {
+ options = options | RegexpMatcher.MATCH_SINGLELINE;
+ }
+ return options;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/version.txt b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/version.txt
new file mode 100644
index 00000000..0496e181
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/version.txt
@@ -0,0 +1,2 @@
+VERSION=${project.version}
+DATE=${TODAY}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/BZip2Constants.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/BZip2Constants.java
new file mode 100644
index 00000000..3a511a7a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/BZip2Constants.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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Keiron Liddle, Aftex Software
+ * <keiron@aftexsw.com> to whom the Ant project is very grateful for his
+ * great code.
+ */
+
+package org.apache.tools.bzip2;
+
+/**
+ * Base class for both the compress and decompress classes.
+ * Holds common arrays, and static data.
+ * <p>
+ * This interface is public for historical purposes.
+ * You should have no need to use it.
+ * </p>
+ */
+public interface BZip2Constants {
+
+ int baseBlockSize = 100000;
+ int MAX_ALPHA_SIZE = 258;
+ int MAX_CODE_LEN = 23;
+ int RUNA = 0;
+ int RUNB = 1;
+ int N_GROUPS = 6;
+ int G_SIZE = 50;
+ int N_ITERS = 4;
+ int MAX_SELECTORS = (2 + (900000 / G_SIZE));
+ int NUM_OVERSHOOT_BYTES = 20;
+
+ /**
+ * This array really shouldn't be here.
+ * Again, for historical purposes it is.
+ *
+ * <p>FIXME: This array should be in a private or package private
+ * location, since it could be modified by malicious code.</p>
+ */
+ int[] rNums = {
+ 619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
+ 985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
+ 733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
+ 419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
+ 878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
+ 862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
+ 150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
+ 170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
+ 73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
+ 909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
+ 641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
+ 161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
+ 382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
+ 98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
+ 227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
+ 469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
+ 184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
+ 715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
+ 951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
+ 652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
+ 645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
+ 609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
+ 653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
+ 411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
+ 170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
+ 857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
+ 669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
+ 944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
+ 344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
+ 897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
+ 433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
+ 686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
+ 946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
+ 978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
+ 680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
+ 707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
+ 297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
+ 134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
+ 343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
+ 140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
+ 170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
+ 369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
+ 804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
+ 896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
+ 661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
+ 768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
+ 61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
+ 372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
+ 780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
+ 920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
+ 645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
+ 936, 638
+ };
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/BlockSort.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/BlockSort.java
new file mode 100644
index 00000000..eb9066ee
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/BlockSort.java
@@ -0,0 +1,1081 @@
+/*
+ * 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.tools.bzip2;
+
+import java.util.BitSet;
+
+/**
+ * Encapsulates the Burrows-Wheeler sorting algorithm needed by {@link
+ * CBZip2OutputStream}.
+ *
+ * <p>This class is based on a Java port of Julian Seward's
+ * blocksort.c in his libbzip2</p>
+ *
+ * <p>The Burrows-Wheeler transform is a reversible transform of the
+ * original data that is supposed to group similar bytes close to
+ * each other. The idea is to sort all permutations of the input and
+ * only keep the last byte of each permutation. E.g. for "Commons
+ * Compress" you'd get:</p>
+ *
+ * <pre>
+ * CompressCommons
+ * Commons Compress
+ * CompressCommons
+ * essCommons Compr
+ * mmons CompressCo
+ * mons CompressCom
+ * mpressCommons Co
+ * ns CompressCommo
+ * ommons CompressC
+ * ompressCommons C
+ * ons CompressComm
+ * pressCommons Com
+ * ressCommons Comp
+ * s CompressCommon
+ * sCommons Compres
+ * ssCommons Compre
+ * </pre>
+ *
+ * <p>Which results in a new text "ss romooCCmmpnse", in adition the
+ * index of the first line that contained the original text is kept -
+ * in this case it is 1. The idea is that in a long English text all
+ * permutations that start with "he" are likely suffixes of a "the" and
+ * thus they end in "t" leading to a larger block of "t"s that can
+ * better be compressed by the subsequent Move-to-Front, run-length
+ * und Huffman encoding steps.</p>
+ *
+ * <p>For more information see for example:</p>
+ * <ul>
+ * <li><a
+ * href="http://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-124.pdf">Burrows,
+ * M. and Wheeler, D.: A Block-sorting Lossless Data Compression
+ * Algorithm</a></li>
+ * <li><a href="http://webglimpse.net/pubs/suffix.pdf">Manber, U. and
+ * Myers, G.: Suffix arrays: A new method for on-line string
+ * searches</a></li>
+ * <li><a
+ * href="http://www.cs.tufts.edu/~nr/comp150fp/archive/bob-sedgewick/fast-strings.pdf">Bentley,
+ * J.L. and Sedgewick, R.: Fast Algorithms for Sorting and Searching
+ * Strings</a></li>
+ * </ul>
+ *
+ * @NotThreadSafe
+ */
+class BlockSort {
+
+ /*
+ * Some of the constructs used in the C code cannot be ported
+ * literally to Java - for example macros, unsigned types. Some
+ * code has been hand-tuned to improve performance. In order to
+ * avoid memory pressure some structures are reused for several
+ * blocks and some memory is even shared between sorting and the
+ * MTF stage even though either algorithm uses it for its own
+ * purpose.
+ *
+ * Comments preserved from the actual C code are prefixed with
+ * "LBZ2:".
+ */
+
+ /*
+ * 2012-05-20 Stefan Bodewig:
+ *
+ * This class seems to mix several revisions of libbzip2's code.
+ * The mainSort function and those used by it look closer to the
+ * 0.9.5 version but show some variations introduced later. At
+ * the same time the logic of Compress 1.4 to randomize the block
+ * on bad input has been dropped after libbzip2 0.9.0 and replaced
+ * by a fallback sorting algorithm.
+ *
+ * I've added the fallbackSort function of 1.0.6 and tried to
+ * integrate it with the existing code without touching too much.
+ * I've also removed the now unused randomization code.
+ */
+
+ /*
+ * LBZ2: If you are ever unlucky/improbable enough to get a stack
+ * overflow whilst sorting, increase the following constant and
+ * try again. In practice I have never seen the stack go above 27
+ * elems, so the following limit seems very generous.
+ */
+ private static final int QSORT_STACK_SIZE = 1000;
+
+ private static final int FALLBACK_QSORT_STACK_SIZE = 100;
+
+ private static final int STACK_SIZE =
+ QSORT_STACK_SIZE < FALLBACK_QSORT_STACK_SIZE
+ ? FALLBACK_QSORT_STACK_SIZE : QSORT_STACK_SIZE;
+
+ /*
+ * Used when sorting. If too many long comparisons happen, we stop sorting,
+ * and use fallbackSort instead.
+ */
+ private int workDone;
+ private int workLimit;
+ private boolean firstAttempt;
+
+ private final int[] stack_ll = new int[STACK_SIZE]; // 4000 byte
+ private final int[] stack_hh = new int[STACK_SIZE]; // 4000 byte
+ private final int[] stack_dd = new int[QSORT_STACK_SIZE]; // 4000 byte
+
+ private final int[] mainSort_runningOrder = new int[256]; // 1024 byte
+ private final int[] mainSort_copy = new int[256]; // 1024 byte
+ private final boolean[] mainSort_bigDone = new boolean[256]; // 256 byte
+
+ private final int[] ftab = new int[65537]; // 262148 byte
+
+ /**
+ * Array instance identical to Data's sfmap, both are used only
+ * temporarily and indepently, so we do not need to allocate
+ * additional memory.
+ */
+ private final char[] quadrant;
+
+ BlockSort(final CBZip2OutputStream.Data data) {
+ this.quadrant = data.sfmap;
+ }
+
+ void blockSort(final CBZip2OutputStream.Data data, final int last) {
+ this.workLimit = WORK_FACTOR * last;
+ this.workDone = 0;
+ this.firstAttempt = true;
+
+ if (last + 1 < 10000) {
+ fallbackSort(data, last);
+ } else {
+ mainSort(data, last);
+
+ if (this.firstAttempt && (this.workDone > this.workLimit)) {
+ fallbackSort(data, last);
+ }
+ }
+
+ final int[] fmap = data.fmap;
+ data.origPtr = -1;
+ for (int i = 0; i <= last; i++) {
+ if (fmap[i] == 0) {
+ data.origPtr = i;
+ break;
+ }
+ }
+
+ // assert (data.origPtr != -1) : data.origPtr;
+ }
+
+ /**
+ * Adapt fallbackSort to the expected interface of the rest of the
+ * code, in particular deal with the fact that block starts at
+ * offset 1 (in libbzip2 1.0.6 it starts at 0).
+ */
+ final void fallbackSort(final CBZip2OutputStream.Data data,
+ final int last) {
+ data.block[0] = data.block[last + 1];
+ fallbackSort(data.fmap, data.block, last + 1);
+ for (int i = 0; i < last + 1; i++) {
+ --data.fmap[i];
+ }
+ for (int i = 0; i < last + 1; i++) {
+ if (data.fmap[i] == -1) {
+ data.fmap[i] = last;
+ break;
+ }
+ }
+ }
+
+/*---------------------------------------------*/
+
+/*---------------------------------------------*/
+/*--- LBZ2: Fallback O(N log(N)^2) sorting ---*/
+/*--- algorithm, for repetitive blocks ---*/
+/*---------------------------------------------*/
+
+ /*
+ * This is the fallback sorting algorithm libbzip2 1.0.6 uses for
+ * repetitive or very short inputs.
+ *
+ * The idea is inspired by Manber-Myers string suffix sorting
+ * algorithm. First a bucket sort places each permutation of the
+ * block into a bucket based on its first byte. Permutations are
+ * represented by pointers to their first character kept in
+ * (partially) sorted order inside the array ftab.
+ *
+ * The next step visits all buckets in order and performs a
+ * quicksort on all permutations of the bucket based on the index
+ * of the bucket the second byte of the permutation belongs to,
+ * thereby forming new buckets. When arrived here the
+ * permutations are sorted up to the second character and we have
+ * buckets of permutations that are identical up to two
+ * characters.
+ *
+ * Repeat the step of quicksorting each bucket, now based on the
+ * bucket holding the sequence of the third and forth character
+ * leading to four byte buckets. Repeat this doubling of bucket
+ * sizes until all buckets only contain single permutations or the
+ * bucket size exceeds the block size.
+ *
+ * I.e.
+ *
+ * "abraba" form three buckets for the chars "a", "b", and "r" in
+ * the first step with
+ *
+ * fmap = { 'a:' 5, 3, 0, 'b:' 4, 1, 'r', 2 }
+ *
+ * when looking at the bucket of "a"s the second characters are in
+ * the buckets that start with fmap-index 0 (rolled over), 3 and 3
+ * respectively, forming two new buckets "aa" and "ab", so we get
+ *
+ * fmap = { 'aa:' 5, 'ab:' 3, 0, 'ba:' 4, 'br': 1, 'ra:' 2 }
+ *
+ * since the last bucket only contained a single item it didn't
+ * have to be sorted at all.
+ *
+ * There now is just one bucket with more than one permutation
+ * that remains to be sorted. For the permutation that starts
+ * with index 3 the third and forth char are in bucket 'aa' at
+ * index 0 and for the one starting at block index 0 they are in
+ * bucket 'ra' with sort index 5. The fully sorted order then becomes.
+ *
+ * fmap = { 5, 3, 0, 4, 1, 2 }
+ *
+ */
+
+ /**
+ * @param fmap points to the index of the starting point of a
+ * permutation inside the block of data in the current
+ * partially sorted order
+ * @param eclass points from the index of a character inside the
+ * block to the first index in fmap that contains the
+ * bucket of its suffix that is sorted in this step.
+ * @param lo lower boundary of the fmap-interval to be sorted
+ * @param hi upper boundary of the fmap-interval to be sorted
+ */
+ private void fallbackSimpleSort(int[] fmap,
+ int[] eclass,
+ int lo,
+ int hi) {
+ if (lo == hi) {
+ return;
+ }
+
+ int j;
+ if (hi - lo > 3) {
+ for (int i = hi - 4; i >= lo; i--) {
+ int tmp = fmap[i];
+ int ec_tmp = eclass[tmp];
+ for (j = i + 4; j <= hi && ec_tmp > eclass[fmap[j]];
+ j += 4) {
+ fmap[j - 4] = fmap[j];
+ }
+ fmap[j - 4] = tmp;
+ }
+ }
+
+ for (int i = hi - 1; i >= lo; i--) {
+ int tmp = fmap[i];
+ int ec_tmp = eclass[tmp];
+ for (j = i + 1; j <= hi && ec_tmp > eclass[fmap[j]]; j++) {
+ fmap[j - 1] = fmap[j];
+ }
+ fmap[j-1] = tmp;
+ }
+ }
+
+ private static final int FALLBACK_QSORT_SMALL_THRESH = 10;
+
+ /**
+ * swaps two values in fmap
+ */
+ private void fswap(int[] fmap, int zz1, int zz2) {
+ int zztmp = fmap[zz1];
+ fmap[zz1] = fmap[zz2];
+ fmap[zz2] = zztmp;
+ }
+
+ /**
+ * swaps two intervals starting at yyp1 and yyp2 of length yyn inside fmap.
+ */
+ private void fvswap(int[] fmap, int yyp1, int yyp2, int yyn) {
+ while (yyn > 0) {
+ fswap(fmap, yyp1, yyp2);
+ yyp1++; yyp2++; yyn--;
+ }
+ }
+
+ private int fmin(int a, int b) {
+ return a < b ? a : b;
+ }
+
+ private void fpush(int sp, int lz, int hz) {
+ stack_ll[sp] = lz;
+ stack_hh[sp] = hz;
+ }
+
+ private int[] fpop(int sp) {
+ return new int[] { stack_ll[sp], stack_hh[sp] };
+ }
+
+ /**
+ * @param fmap points to the index of the starting point of a
+ * permutation inside the block of data in the current
+ * partially sorted order
+ * @param eclass points from the index of a character inside the
+ * block to the first index in fmap that contains the
+ * bucket of its suffix that is sorted in this step.
+ * @param loSt lower boundary of the fmap-interval to be sorted
+ * @param hiSt upper boundary of the fmap-interval to be sorted
+ */
+ private void fallbackQSort3(int[] fmap,
+ int[] eclass,
+ int loSt,
+ int hiSt) {
+ int lo, unLo, ltLo, hi, unHi, gtHi, n;
+
+ long r = 0;
+ int sp = 0;
+ fpush(sp++, loSt, hiSt);
+
+ while (sp > 0) {
+ int[] s = fpop(--sp);
+ lo = s[0]; hi = s[1];
+
+ if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) {
+ fallbackSimpleSort(fmap, eclass, lo, hi);
+ continue;
+ }
+
+ /* LBZ2: Random partitioning. Median of 3 sometimes fails to
+ avoid bad cases. Median of 9 seems to help but
+ looks rather expensive. This too seems to work but
+ is cheaper. Guidance for the magic constants
+ 7621 and 32768 is taken from Sedgewick's algorithms
+ book, chapter 35.
+ */
+ r = ((r * 7621) + 1) % 32768;
+ long r3 = r % 3, med;
+ if (r3 == 0) {
+ med = eclass[fmap[lo]];
+ } else if (r3 == 1) {
+ med = eclass[fmap[(lo + hi) >>> 1]];
+ } else {
+ med = eclass[fmap[hi]];
+ }
+
+ unLo = ltLo = lo;
+ unHi = gtHi = hi;
+
+ // looks like the ternary partition attributed to Wegner
+ // in the cited Sedgewick paper
+ while (true) {
+ while (true) {
+ if (unLo > unHi) {
+ break;
+ }
+ n = eclass[fmap[unLo]] - (int) med;
+ if (n == 0) {
+ fswap(fmap, unLo, ltLo);
+ ltLo++; unLo++;
+ continue;
+ }
+ if (n > 0) {
+ break;
+ }
+ unLo++;
+ }
+ while (true) {
+ if (unLo > unHi) {
+ break;
+ }
+ n = eclass[fmap[unHi]] - (int) med;
+ if (n == 0) {
+ fswap(fmap, unHi, gtHi);
+ gtHi--; unHi--;
+ continue;
+ }
+ if (n < 0) {
+ break;
+ }
+ unHi--;
+ }
+ if (unLo > unHi) {
+ break;
+ }
+ fswap(fmap, unLo, unHi); unLo++; unHi--;
+ }
+
+ if (gtHi < ltLo) {
+ continue;
+ }
+
+ n = fmin(ltLo - lo, unLo - ltLo);
+ fvswap(fmap, lo, unLo - n, n);
+ int m = fmin(hi - gtHi, gtHi - unHi);
+ fvswap(fmap, unHi + 1, hi - m + 1, m);
+
+ n = lo + unLo - ltLo - 1;
+ m = hi - (gtHi - unHi) + 1;
+
+ if (n - lo > hi - m) {
+ fpush(sp++, lo, n);
+ fpush(sp++, m, hi);
+ } else {
+ fpush(sp++, m, hi);
+ fpush(sp++, lo, n);
+ }
+ }
+ }
+
+
+/*---------------------------------------------*/
+
+ private int[] eclass;
+
+ private int[] getEclass() {
+ return eclass == null
+ ? (eclass = new int[quadrant.length / 2]) : eclass;
+ }
+
+ /*
+ * The C code uses an array of ints (each int holding 32 flags) to
+ * represents the bucket-start flags (bhtab). It also contains
+ * optimizations to skip over 32 consecutively set or
+ * consecutively unset bits on word boundaries at once. For now
+ * I've chosen to use the simpler but potentially slower code
+ * using BitSet - also in the hope that using the BitSet#nextXXX
+ * methods may be fast enough.
+ */
+
+ /**
+ * @param fmap points to the index of the starting point of a
+ * permutation inside the block of data in the current
+ * partially sorted order
+ * @param block the original data
+ * @param nblock size of the block
+ * @param off offset of first byte to sort in block
+ */
+ final void fallbackSort(int[] fmap, byte[] block, int nblock) {
+ final int[] ftab = new int[257];
+ int H, i, j, k, l, r, cc, cc1;
+ int nNotDone;
+ int nBhtab;
+ final int[] eclass = getEclass();
+
+ for (i = 0; i < nblock; i++) {
+ eclass[i] = 0;
+ }
+ /*--
+ LBZ2: Initial 1-char radix sort to generate
+ initial fmap and initial BH bits.
+ --*/
+ for (i = 0; i < nblock; i++) {
+ ftab[block[i] & 0xff]++;
+ }
+ for (i = 1; i < 257; i++) {
+ ftab[i] += ftab[i - 1];
+ }
+
+ for (i = 0; i < nblock; i++) {
+ j = block[i] & 0xff;
+ k = ftab[j] - 1;
+ ftab[j] = k;
+ fmap[k] = i;
+ }
+
+ nBhtab = 64 + nblock;
+ BitSet bhtab = new BitSet(nBhtab);
+ for (i = 0; i < 256; i++) {
+ bhtab.set(ftab[i]);
+ }
+
+ /*--
+ LBZ2: Inductively refine the buckets. Kind-of an
+ "exponential radix sort" (!), inspired by the
+ Manber-Myers suffix array construction algorithm.
+ --*/
+
+ /*-- LBZ2: set sentinel bits for block-end detection --*/
+ for (i = 0; i < 32; i++) {
+ bhtab.set(nblock + 2 * i);
+ bhtab.clear(nblock + 2 * i + 1);
+ }
+
+ /*-- LBZ2: the log(N) loop --*/
+ H = 1;
+ while (true) {
+
+ j = 0;
+ for (i = 0; i < nblock; i++) {
+ if (bhtab.get(i)) {
+ j = i;
+ }
+ k = fmap[i] - H;
+ if (k < 0) {
+ k += nblock;
+ }
+ eclass[k] = j;
+ }
+
+ nNotDone = 0;
+ r = -1;
+ while (true) {
+
+ /*-- LBZ2: find the next non-singleton bucket --*/
+ k = r + 1;
+ k = bhtab.nextClearBit(k);
+ l = k - 1;
+ if (l >= nblock) {
+ break;
+ }
+ k = bhtab.nextSetBit(k + 1);
+ r = k - 1;
+ if (r >= nblock) {
+ break;
+ }
+
+ /*-- LBZ2: now [l, r] bracket current bucket --*/
+ if (r > l) {
+ nNotDone += (r - l + 1);
+ fallbackQSort3(fmap, eclass, l, r);
+
+ /*-- LBZ2: scan bucket and generate header bits-- */
+ cc = -1;
+ for (i = l; i <= r; i++) {
+ cc1 = eclass[fmap[i]];
+ if (cc != cc1) {
+ bhtab.set(i);
+ cc = cc1;
+ }
+ }
+ }
+ }
+
+ H *= 2;
+ if (H > nblock || nNotDone == 0) {
+ break;
+ }
+ }
+ }
+
+/*---------------------------------------------*/
+
+ /*
+ * LBZ2: Knuth's increments seem to work better than Incerpi-Sedgewick here.
+ * Possibly because the number of elems to sort is usually small, typically
+ * &lt;= 20.
+ */
+ private static final int[] INCS = { 1, 4, 13, 40, 121, 364, 1093, 3280,
+ 9841, 29524, 88573, 265720, 797161,
+ 2391484 };
+
+ /**
+ * This is the most hammered method of this class.
+ *
+ * <p>
+ * This is the version using unrolled loops. Normally I never use such ones
+ * in Java code. The unrolling has shown a noticeable performance improvement
+ * on JRE 1.4.2 (Linux i586 / HotSpot Client). Of course it depends on the
+ * JIT compiler of the vm.
+ * </p>
+ */
+ private boolean mainSimpleSort(final CBZip2OutputStream.Data dataShadow,
+ final int lo, final int hi, final int d,
+ final int lastShadow) {
+ final int bigN = hi - lo + 1;
+ if (bigN < 2) {
+ return this.firstAttempt && (this.workDone > this.workLimit);
+ }
+
+ int hp = 0;
+ while (INCS[hp] < bigN) {
+ hp++;
+ }
+
+ final int[] fmap = dataShadow.fmap;
+ final char[] quadrant = this.quadrant;
+ final byte[] block = dataShadow.block;
+ final int lastPlus1 = lastShadow + 1;
+ final boolean firstAttemptShadow = this.firstAttempt;
+ final int workLimitShadow = this.workLimit;
+ int workDoneShadow = this.workDone;
+
+ // Following block contains unrolled code which could be shortened by
+ // coding it in additional loops.
+
+ HP: while (--hp >= 0) {
+ final int h = INCS[hp];
+ final int mj = lo + h - 1;
+
+ for (int i = lo + h; i <= hi;) {
+ // copy
+ for (int k = 3; (i <= hi) && (--k >= 0); i++) {
+ final int v = fmap[i];
+ final int vd = v + d;
+ int j = i;
+
+ // for (int a;
+ // (j > mj) && mainGtU((a = fmap[j - h]) + d, vd,
+ // block, quadrant, lastShadow);
+ // j -= h) {
+ // fmap[j] = a;
+ // }
+ //
+ // unrolled version:
+
+ // start inline mainGTU
+ boolean onceRunned = false;
+ int a = 0;
+
+ HAMMER: while (true) {
+ if (onceRunned) {
+ fmap[j] = a;
+ if ((j -= h) <= mj) {
+ break HAMMER;
+ }
+ } else {
+ onceRunned = true;
+ }
+
+ a = fmap[j - h];
+ int i1 = a + d;
+ int i2 = vd;
+
+ // following could be done in a loop, but
+ // unrolled it for performance:
+ if (block[i1 + 1] == block[i2 + 1]) {
+ if (block[i1 + 2] == block[i2 + 2]) {
+ if (block[i1 + 3] == block[i2 + 3]) {
+ if (block[i1 + 4] == block[i2 + 4]) {
+ if (block[i1 + 5] == block[i2 + 5]) {
+ if (block[(i1 += 6)] == block[(i2 += 6)]) {
+ int x = lastShadow;
+ X: while (x > 0) {
+ x -= 4;
+
+ if (block[i1 + 1] == block[i2 + 1]) {
+ if (quadrant[i1] == quadrant[i2]) {
+ if (block[i1 + 2] == block[i2 + 2]) {
+ if (quadrant[i1 + 1] == quadrant[i2 + 1]) {
+ if (block[i1 + 3] == block[i2 + 3]) {
+ if (quadrant[i1 + 2] == quadrant[i2 + 2]) {
+ if (block[i1 + 4] == block[i2 + 4]) {
+ if (quadrant[i1 + 3] == quadrant[i2 + 3]) {
+ if ((i1 += 4) >= lastPlus1) {
+ i1 -= lastPlus1;
+ }
+ if ((i2 += 4) >= lastPlus1) {
+ i2 -= lastPlus1;
+ }
+ workDoneShadow++;
+ continue X;
+ } else if ((quadrant[i1 + 3] > quadrant[i2 + 3])) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+ } else if ((block[i1 + 4] & 0xff) > (block[i2 + 4] & 0xff)) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+ } else if ((quadrant[i1 + 2] > quadrant[i2 + 2])) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+ } else if ((block[i1 + 3] & 0xff) > (block[i2 + 3] & 0xff)) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+ } else if ((quadrant[i1 + 1] > quadrant[i2 + 1])) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+ } else if ((block[i1 + 2] & 0xff) > (block[i2 + 2] & 0xff)) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+ } else if ((quadrant[i1] > quadrant[i2])) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+ } else if ((block[i1 + 1] & 0xff) > (block[i2 + 1] & 0xff)) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+
+ }
+ break HAMMER;
+ } // while x > 0
+ else {
+ if ((block[i1] & 0xff) > (block[i2] & 0xff)) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+ }
+ } else if ((block[i1 + 5] & 0xff) > (block[i2 + 5] & 0xff)) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+ } else if ((block[i1 + 4] & 0xff) > (block[i2 + 4] & 0xff)) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+ } else if ((block[i1 + 3] & 0xff) > (block[i2 + 3] & 0xff)) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+ } else if ((block[i1 + 2] & 0xff) > (block[i2 + 2] & 0xff)) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+ } else if ((block[i1 + 1] & 0xff) > (block[i2 + 1] & 0xff)) {
+ continue HAMMER;
+ } else {
+ break HAMMER;
+ }
+
+ } // HAMMER
+ // end inline mainGTU
+
+ fmap[j] = v;
+ }
+
+ if (firstAttemptShadow && (i <= hi)
+ && (workDoneShadow > workLimitShadow)) {
+ break HP;
+ }
+ }
+ }
+
+ this.workDone = workDoneShadow;
+ return firstAttemptShadow && (workDoneShadow > workLimitShadow);
+ }
+
+/*--
+ LBZ2: The following is an implementation of
+ an elegant 3-way quicksort for strings,
+ described in a paper "Fast Algorithms for
+ Sorting and Searching Strings", by Robert
+ Sedgewick and Jon L. Bentley.
+--*/
+
+ private static void vswap(int[] fmap, int p1, int p2, int n) {
+ n += p1;
+ while (p1 < n) {
+ int t = fmap[p1];
+ fmap[p1++] = fmap[p2];
+ fmap[p2++] = t;
+ }
+ }
+
+ private static byte med3(byte a, byte b, byte c) {
+ return (a < b) ? (b < c ? b : a < c ? c : a) : (b > c ? b : a > c ? c
+ : a);
+ }
+
+ private static final int SMALL_THRESH = 20;
+ private static final int DEPTH_THRESH = 10;
+ private static final int WORK_FACTOR = 30;
+
+ /**
+ * Method "mainQSort3", file "blocksort.c", BZip2 1.0.2
+ */
+ private void mainQSort3(final CBZip2OutputStream.Data dataShadow,
+ final int loSt, final int hiSt, final int dSt,
+ final int last) {
+ final int[] stack_ll = this.stack_ll;
+ final int[] stack_hh = this.stack_hh;
+ final int[] stack_dd = this.stack_dd;
+ final int[] fmap = dataShadow.fmap;
+ final byte[] block = dataShadow.block;
+
+ stack_ll[0] = loSt;
+ stack_hh[0] = hiSt;
+ stack_dd[0] = dSt;
+
+ for (int sp = 1; --sp >= 0;) {
+ final int lo = stack_ll[sp];
+ final int hi = stack_hh[sp];
+ final int d = stack_dd[sp];
+
+ if ((hi - lo < SMALL_THRESH) || (d > DEPTH_THRESH)) {
+ if (mainSimpleSort(dataShadow, lo, hi, d, last)) {
+ return;
+ }
+ } else {
+ final int d1 = d + 1;
+ final int med = med3(block[fmap[lo] + d1],
+ block[fmap[hi] + d1], block[fmap[(lo + hi) >>> 1] + d1]) & 0xff;
+
+ int unLo = lo;
+ int unHi = hi;
+ int ltLo = lo;
+ int gtHi = hi;
+
+ while (true) {
+ while (unLo <= unHi) {
+ final int n = (block[fmap[unLo] + d1] & 0xff)
+ - med;
+ if (n == 0) {
+ final int temp = fmap[unLo];
+ fmap[unLo++] = fmap[ltLo];
+ fmap[ltLo++] = temp;
+ } else if (n < 0) {
+ unLo++;
+ } else {
+ break;
+ }
+ }
+
+ while (unLo <= unHi) {
+ final int n = (block[fmap[unHi] + d1] & 0xff)
+ - med;
+ if (n == 0) {
+ final int temp = fmap[unHi];
+ fmap[unHi--] = fmap[gtHi];
+ fmap[gtHi--] = temp;
+ } else if (n > 0) {
+ unHi--;
+ } else {
+ break;
+ }
+ }
+
+ if (unLo <= unHi) {
+ final int temp = fmap[unLo];
+ fmap[unLo++] = fmap[unHi];
+ fmap[unHi--] = temp;
+ } else {
+ break;
+ }
+ }
+
+ if (gtHi < ltLo) {
+ stack_ll[sp] = lo;
+ stack_hh[sp] = hi;
+ stack_dd[sp] = d1;
+ sp++;
+ } else {
+ int n = ((ltLo - lo) < (unLo - ltLo)) ? (ltLo - lo)
+ : (unLo - ltLo);
+ vswap(fmap, lo, unLo - n, n);
+ int m = ((hi - gtHi) < (gtHi - unHi)) ? (hi - gtHi)
+ : (gtHi - unHi);
+ vswap(fmap, unLo, hi - m + 1, m);
+
+ n = lo + unLo - ltLo - 1;
+ m = hi - (gtHi - unHi) + 1;
+
+ stack_ll[sp] = lo;
+ stack_hh[sp] = n;
+ stack_dd[sp] = d;
+ sp++;
+
+ stack_ll[sp] = n + 1;
+ stack_hh[sp] = m - 1;
+ stack_dd[sp] = d1;
+ sp++;
+
+ stack_ll[sp] = m;
+ stack_hh[sp] = hi;
+ stack_dd[sp] = d;
+ sp++;
+ }
+ }
+ }
+ }
+
+ private static final int SETMASK = (1 << 21);
+ private static final int CLEARMASK = (~SETMASK);
+
+ final void mainSort(final CBZip2OutputStream.Data dataShadow,
+ final int lastShadow) {
+ final int[] runningOrder = this.mainSort_runningOrder;
+ final int[] copy = this.mainSort_copy;
+ final boolean[] bigDone = this.mainSort_bigDone;
+ final int[] ftab = this.ftab;
+ final byte[] block = dataShadow.block;
+ final int[] fmap = dataShadow.fmap;
+ final char[] quadrant = this.quadrant;
+ final int workLimitShadow = this.workLimit;
+ final boolean firstAttemptShadow = this.firstAttempt;
+
+ // LBZ2: Set up the 2-byte frequency table
+ for (int i = 65537; --i >= 0;) {
+ ftab[i] = 0;
+ }
+
+ /*
+ * In the various block-sized structures, live data runs from 0 to
+ * last+NUM_OVERSHOOT_BYTES inclusive. First, set up the overshoot area
+ * for block.
+ */
+ for (int i = 0; i < BZip2Constants.NUM_OVERSHOOT_BYTES; i++) {
+ block[lastShadow + i + 2] = block[(i % (lastShadow + 1)) + 1];
+ }
+ for (int i = lastShadow + BZip2Constants.NUM_OVERSHOOT_BYTES +1; --i >= 0;) {
+ quadrant[i] = 0;
+ }
+ block[0] = block[lastShadow + 1];
+
+ // LBZ2: Complete the initial radix sort:
+
+ int c1 = block[0] & 0xff;
+ for (int i = 0; i <= lastShadow; i++) {
+ final int c2 = block[i + 1] & 0xff;
+ ftab[(c1 << 8) + c2]++;
+ c1 = c2;
+ }
+
+ for (int i = 1; i <= 65536; i++) {
+ ftab[i] += ftab[i - 1];
+ }
+
+ c1 = block[1] & 0xff;
+ for (int i = 0; i < lastShadow; i++) {
+ final int c2 = block[i + 2] & 0xff;
+ fmap[--ftab[(c1 << 8) + c2]] = i;
+ c1 = c2;
+ }
+
+ fmap[--ftab[((block[lastShadow + 1] & 0xff) << 8) + (block[1] & 0xff)]] = lastShadow;
+
+ /*
+ * LBZ2: Now ftab contains the first loc of every small bucket. Calculate the
+ * running order, from smallest to largest big bucket.
+ */
+ for (int i = 256; --i >= 0;) {
+ bigDone[i] = false;
+ runningOrder[i] = i;
+ }
+
+ for (int h = 364; h != 1;) {
+ h /= 3;
+ for (int i = h; i <= 255; i++) {
+ final int vv = runningOrder[i];
+ final int a = ftab[(vv + 1) << 8] - ftab[vv << 8];
+ final int b = h - 1;
+ int j = i;
+ for (int ro = runningOrder[j - h]; (ftab[(ro + 1) << 8] - ftab[ro << 8]) > a; ro = runningOrder[j
+ - h]) {
+ runningOrder[j] = ro;
+ j -= h;
+ if (j <= b) {
+ break;
+ }
+ }
+ runningOrder[j] = vv;
+ }
+ }
+
+ /*
+ * LBZ2: The main sorting loop.
+ */
+ for (int i = 0; i <= 255; i++) {
+ /*
+ * LBZ2: Process big buckets, starting with the least full.
+ */
+ final int ss = runningOrder[i];
+
+ // Step 1:
+ /*
+ * LBZ2: Complete the big bucket [ss] by quicksorting any unsorted small
+ * buckets [ss, j]. Hopefully previous pointer-scanning phases have
+ * already completed many of the small buckets [ss, j], so we don't
+ * have to sort them at all.
+ */
+ for (int j = 0; j <= 255; j++) {
+ final int sb = (ss << 8) + j;
+ final int ftab_sb = ftab[sb];
+ if ((ftab_sb & SETMASK) != SETMASK) {
+ final int lo = ftab_sb & CLEARMASK;
+ final int hi = (ftab[sb + 1] & CLEARMASK) - 1;
+ if (hi > lo) {
+ mainQSort3(dataShadow, lo, hi, 2, lastShadow);
+ if (firstAttemptShadow
+ && (this.workDone > workLimitShadow)) {
+ return;
+ }
+ }
+ ftab[sb] = ftab_sb | SETMASK;
+ }
+ }
+
+ // Step 2:
+ // LBZ2: Now scan this big bucket so as to synthesise the
+ // sorted order for small buckets [t, ss] for all t != ss.
+
+ for (int j = 0; j <= 255; j++) {
+ copy[j] = ftab[(j << 8) + ss] & CLEARMASK;
+ }
+
+ for (int j = ftab[ss << 8] & CLEARMASK, hj = (ftab[(ss + 1) << 8] & CLEARMASK); j < hj; j++) {
+ final int fmap_j = fmap[j];
+ c1 = block[fmap_j] & 0xff;
+ if (!bigDone[c1]) {
+ fmap[copy[c1]] = (fmap_j == 0) ? lastShadow : (fmap_j - 1);
+ copy[c1]++;
+ }
+ }
+
+ for (int j = 256; --j >= 0;) {
+ ftab[(j << 8) + ss] |= SETMASK;
+ }
+
+ // Step 3:
+ /*
+ * LBZ2: The ss big bucket is now done. Record this fact, and update the
+ * quadrant descriptors. Remember to update quadrants in the
+ * overshoot area too, if necessary. The "if (i < 255)" test merely
+ * skips this updating for the last bucket processed, since updating
+ * for the last bucket is pointless.
+ */
+ bigDone[ss] = true;
+
+ if (i < 255) {
+ final int bbStart = ftab[ss << 8] & CLEARMASK;
+ final int bbSize = (ftab[(ss + 1) << 8] & CLEARMASK) - bbStart;
+ int shifts = 0;
+
+ while ((bbSize >> shifts) > 65534) {
+ shifts++;
+ }
+
+ for (int j = 0; j < bbSize; j++) {
+ final int a2update = fmap[bbStart + j];
+ final char qVal = (char) (j >> shifts);
+ quadrant[a2update] = qVal;
+ if (a2update < BZip2Constants.NUM_OVERSHOOT_BYTES) {
+ quadrant[a2update + lastShadow + 1] = qVal;
+ }
+ }
+ }
+
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/CBZip2InputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/CBZip2InputStream.java
new file mode 100644
index 00000000..62315d12
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/CBZip2InputStream.java
@@ -0,0 +1,1062 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Keiron Liddle, Aftex Software
+ * <keiron@aftexsw.com> to whom the Ant project is very grateful for his
+ * great code.
+ */
+package org.apache.tools.bzip2;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * An input stream that decompresses from the BZip2 format (without the file
+ * header chars) to be read as any other stream.
+ *
+ * <p>The decompression requires large amounts of memory. Thus you
+ * should call the {@link #close() close()} method as soon as
+ * possible, to force <tt>CBZip2InputStream</tt> to release the
+ * allocated memory. See {@link CBZip2OutputStream
+ * CBZip2OutputStream} for information about memory usage.</p>
+ *
+ * <p><tt>CBZip2InputStream</tt> reads bytes from the compressed
+ * source stream via the single byte {@link java.io.InputStream#read()
+ * read()} method exclusively. Thus you should consider to use a
+ * buffered source stream.</p>
+ *
+ * <p>Instances of this class are not threadsafe.</p>
+ */
+public class CBZip2InputStream extends InputStream implements BZip2Constants {
+
+ /**
+ * Index of the last char in the block, so the block size == last + 1.
+ */
+ private int last;
+
+ /**
+ * Index in zptr[] of original string after sorting.
+ */
+ private int origPtr;
+
+ /**
+ * always: in the range 0 .. 9.
+ * The current block size is 100000 * this number.
+ */
+ private int blockSize100k;
+
+ private boolean blockRandomised;
+
+ private int bsBuff;
+ private int bsLive;
+ private final CRC crc = new CRC();
+
+ private int nInUse;
+
+ private InputStream in;
+ private final boolean decompressConcatenated;
+
+ private int currentChar = -1;
+
+ private static final int EOF = 0;
+ private static final int START_BLOCK_STATE = 1;
+ private static final int RAND_PART_A_STATE = 2;
+ private static final int RAND_PART_B_STATE = 3;
+ private static final int RAND_PART_C_STATE = 4;
+ private static final int NO_RAND_PART_A_STATE = 5;
+ private static final int NO_RAND_PART_B_STATE = 6;
+ private static final int NO_RAND_PART_C_STATE = 7;
+
+ private int currentState = START_BLOCK_STATE;
+
+ private int storedBlockCRC, storedCombinedCRC;
+ private int computedBlockCRC, computedCombinedCRC;
+
+ // Variables used by setup* methods exclusively
+
+ private int su_count;
+ private int su_ch2;
+ private int su_chPrev;
+ private int su_i2;
+ private int su_j2;
+ private int su_rNToGo;
+ private int su_rTPos;
+ private int su_tPos;
+ private char su_z;
+
+ /**
+ * All memory intensive stuff.
+ * This field is initialized by initBlock().
+ */
+ private CBZip2InputStream.Data data;
+
+ /**
+ * Constructs a new CBZip2InputStream which decompresses bytes read from
+ * the specified stream. This doesn't suppprt decompressing
+ * concatenated .bz2 files.
+ *
+ * <p>Although BZip2 headers are marked with the magic
+ * <tt>"Bz"</tt> this constructor expects the next byte in the
+ * stream to be the first one after the magic. Thus callers have
+ * to skip the first two bytes. Otherwise this constructor will
+ * throw an exception. </p>
+ *
+ * @throws IOException
+ * if the stream content is malformed or an I/O error occurs.
+ * @throws NullPointerException
+ * if <tt>in == null</tt>
+ */
+ public CBZip2InputStream(final InputStream in) throws IOException {
+ this(in, false);
+ }
+
+ /**
+ * Constructs a new CBZip2InputStream which decompresses bytes
+ * read from the specified stream.
+ *
+ * <p>Although BZip2 headers are marked with the magic
+ * <tt>"Bz"</tt> this constructor expects the next byte in the
+ * stream to be the first one after the magic. Thus callers have
+ * to skip the first two bytes. Otherwise this constructor will
+ * throw an exception. </p>
+ *
+ * @param in the InputStream from which this object should be created
+ * @param decompressConcatenated
+ * if true, decompress until the end of the input;
+ * if false, stop after the first .bz2 stream and
+ * leave the input position to point to the next
+ * byte after the .bz2 stream
+ *
+ * @throws IOException
+ * if the stream content is malformed or an I/O error occurs.
+ * @throws NullPointerException
+ * if <tt>in == null</tt>
+ */
+ public CBZip2InputStream(final InputStream in,
+ final boolean decompressConcatenated)
+ throws IOException {
+ super();
+
+ this.in = in;
+ this.decompressConcatenated = decompressConcatenated;
+
+ init(true);
+ initBlock();
+ setupBlock();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int read() throws IOException {
+ if (this.in != null) {
+ return read0();
+ } else {
+ throw new IOException("stream closed");
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.io.InputStream#read(byte[], int, int)
+ */
+ @Override
+ public int read(final byte[] dest, final int offs, final int len)
+ throws IOException {
+ if (offs < 0) {
+ throw new IndexOutOfBoundsException("offs(" + offs + ") < 0.");
+ }
+ if (len < 0) {
+ throw new IndexOutOfBoundsException("len(" + len + ") < 0.");
+ }
+ if (offs + len > dest.length) {
+ throw new IndexOutOfBoundsException("offs(" + offs + ") + len("
+ + len + ") > dest.length("
+ + dest.length + ").");
+ }
+ if (this.in == null) {
+ throw new IOException("stream closed");
+ }
+
+ final int hi = offs + len;
+ int destOffs = offs;
+ for (int b; (destOffs < hi) && ((b = read0()) >= 0);) {
+ dest[destOffs++] = (byte) b;
+ }
+
+ return (destOffs == offs) ? -1 : (destOffs - offs);
+ }
+
+ private void makeMaps() {
+ final boolean[] inUse = this.data.inUse;
+ final byte[] seqToUnseq = this.data.seqToUnseq;
+
+ int nInUseShadow = 0;
+
+ for (int i = 0; i < 256; i++) {
+ if (inUse[i]) {
+ seqToUnseq[nInUseShadow++] = (byte) i;
+ }
+ }
+
+ this.nInUse = nInUseShadow;
+ }
+
+ private int read0() throws IOException {
+ final int retChar = this.currentChar;
+
+ switch (this.currentState) {
+ case EOF:
+ return -1;
+
+ case START_BLOCK_STATE:
+ throw new IllegalStateException();
+
+ case RAND_PART_A_STATE:
+ throw new IllegalStateException();
+
+ case RAND_PART_B_STATE:
+ setupRandPartB();
+ break;
+
+ case RAND_PART_C_STATE:
+ setupRandPartC();
+ break;
+
+ case NO_RAND_PART_A_STATE:
+ throw new IllegalStateException();
+
+ case NO_RAND_PART_B_STATE:
+ setupNoRandPartB();
+ break;
+
+ case NO_RAND_PART_C_STATE:
+ setupNoRandPartC();
+ break;
+
+ default:
+ throw new IllegalStateException();
+ }
+
+ return retChar;
+ }
+
+ private boolean init(boolean isFirstStream) throws IOException {
+ if (null == in) {
+ throw new IOException("No InputStream");
+ }
+
+ if (isFirstStream) {
+ if (in.available() == 0) {
+ throw new IOException("Empty InputStream");
+ }
+ } else {
+ int magic0 = this.in.read();
+ if (magic0 == -1) {
+ return false;
+ }
+ int magic1 = this.in.read();
+ if (magic0 != 'B' || magic1 != 'Z') {
+ throw new IOException("Garbage after a valid BZip2 stream");
+ }
+ }
+
+ int magic2 = this.in.read();
+ if (magic2 != 'h') {
+ throw new IOException(isFirstStream
+ ? "Stream is not in the BZip2 format"
+ : "Garbage after a valid BZip2 stream");
+ }
+
+ int blockSize = this.in.read();
+ if ((blockSize < '1') || (blockSize > '9')) {
+ throw new IOException("Stream is not BZip2 formatted: illegal "
+ + "blocksize " + (char) blockSize);
+ }
+
+ this.blockSize100k = blockSize - '0';
+
+ this.bsLive = 0;
+ this.computedCombinedCRC = 0;
+
+ return true;
+ }
+
+ private void initBlock() throws IOException {
+ char magic0;
+ char magic1;
+ char magic2;
+ char magic3;
+ char magic4;
+ char magic5;
+
+ while (true) {
+ // Get the block magic bytes.
+ magic0 = bsGetUByte();
+ magic1 = bsGetUByte();
+ magic2 = bsGetUByte();
+ magic3 = bsGetUByte();
+ magic4 = bsGetUByte();
+ magic5 = bsGetUByte();
+
+ // If isn't end of stream magic, break out of the loop.
+ if (magic0 != 0x17 || magic1 != 0x72 || magic2 != 0x45
+ || magic3 != 0x38 || magic4 != 0x50 || magic5 != 0x90) {
+ break;
+ }
+
+ // End of stream was reached. Check the combined CRC and
+ // advance to the next .bz2 stream if decoding concatenated
+ // streams.
+ if (complete()) {
+ return;
+ }
+ }
+
+ if (magic0 != 0x31 || // '1'
+ magic1 != 0x41 || // ')'
+ magic2 != 0x59 || // 'Y'
+ magic3 != 0x26 || // '&'
+ magic4 != 0x53 || // 'S'
+ magic5 != 0x59 // 'Y'
+ ) {
+ this.currentState = EOF;
+ throw new IOException("bad block header");
+ } else {
+ this.storedBlockCRC = bsGetInt();
+ this.blockRandomised = bsR(1) == 1;
+
+ /**
+ * Allocate data here instead in constructor, so we do not
+ * allocate it if the input file is empty.
+ */
+ if (this.data == null) {
+ this.data = new Data(this.blockSize100k);
+ }
+
+ // currBlockNo++;
+ getAndMoveToFrontDecode();
+
+ this.crc.initialiseCRC();
+ this.currentState = START_BLOCK_STATE;
+ }
+ }
+
+ private void endBlock() throws IOException {
+ this.computedBlockCRC = this.crc.getFinalCRC();
+
+ // A bad CRC is considered a fatal error.
+ if (this.storedBlockCRC != this.computedBlockCRC) {
+ // make next blocks readable without error
+ // (repair feature, not yet documented, not tested)
+ this.computedCombinedCRC
+ = (this.storedCombinedCRC << 1)
+ | (this.storedCombinedCRC >>> 31);
+ this.computedCombinedCRC ^= this.storedBlockCRC;
+
+ reportCRCError();
+ }
+
+ this.computedCombinedCRC
+ = (this.computedCombinedCRC << 1)
+ | (this.computedCombinedCRC >>> 31);
+ this.computedCombinedCRC ^= this.computedBlockCRC;
+ }
+
+ private boolean complete() throws IOException {
+ this.storedCombinedCRC = bsGetInt();
+ this.currentState = EOF;
+ this.data = null;
+
+ if (this.storedCombinedCRC != this.computedCombinedCRC) {
+ reportCRCError();
+ }
+
+ // Look for the next .bz2 stream if decompressing
+ // concatenated files.
+ return !decompressConcatenated || !init(false);
+ }
+
+ @Override
+ public void close() throws IOException {
+ InputStream inShadow = this.in;
+ if (inShadow != null) {
+ try {
+ if (inShadow != System.in) {
+ inShadow.close();
+ }
+ } finally {
+ this.data = null;
+ this.in = null;
+ }
+ }
+ }
+
+ private int bsR(final int n) throws IOException {
+ int bsLiveShadow = this.bsLive;
+ int bsBuffShadow = this.bsBuff;
+
+ if (bsLiveShadow < n) {
+ final InputStream inShadow = this.in;
+ do {
+ int thech = inShadow.read();
+
+ if (thech < 0) {
+ throw new IOException("unexpected end of stream");
+ }
+
+ bsBuffShadow = (bsBuffShadow << 8) | thech;
+ bsLiveShadow += 8;
+ } while (bsLiveShadow < n);
+
+ this.bsBuff = bsBuffShadow;
+ }
+
+ this.bsLive = bsLiveShadow - n;
+ return (bsBuffShadow >> (bsLiveShadow - n)) & ((1 << n) - 1);
+ }
+
+ private boolean bsGetBit() throws IOException {
+ int bsLiveShadow = this.bsLive;
+ int bsBuffShadow = this.bsBuff;
+
+ if (bsLiveShadow < 1) {
+ int thech = this.in.read();
+
+ if (thech < 0) {
+ throw new IOException("unexpected end of stream");
+ }
+
+ bsBuffShadow = (bsBuffShadow << 8) | thech;
+ bsLiveShadow += 8;
+ this.bsBuff = bsBuffShadow;
+ }
+
+ this.bsLive = bsLiveShadow - 1;
+ return ((bsBuffShadow >> (bsLiveShadow - 1)) & 1) != 0;
+ }
+
+ private char bsGetUByte() throws IOException {
+ return (char) bsR(8);
+ }
+
+ private int bsGetInt() throws IOException {
+ return (((((bsR(8) << 8) | bsR(8)) << 8) | bsR(8)) << 8) | bsR(8);
+ }
+
+ /**
+ * Called by createHuffmanDecodingTables() exclusively.
+ */
+ private static void hbCreateDecodeTables(final int[] limit,
+ final int[] base,
+ final int[] perm,
+ final char[] length,
+ final int minLen,
+ final int maxLen,
+ final int alphaSize) {
+ for (int i = minLen, pp = 0; i <= maxLen; i++) {
+ for (int j = 0; j < alphaSize; j++) {
+ if (length[j] == i) {
+ perm[pp++] = j;
+ }
+ }
+ }
+
+ for (int i = MAX_CODE_LEN; --i > 0;) {
+ base[i] = 0;
+ limit[i] = 0;
+ }
+
+ for (int i = 0; i < alphaSize; i++) {
+ base[length[i] + 1]++;
+ }
+
+ for (int i = 1, b = base[0]; i < MAX_CODE_LEN; i++) {
+ b += base[i];
+ base[i] = b;
+ }
+
+ for (int i = minLen, vec = 0, b = base[i]; i <= maxLen; i++) {
+ final int nb = base[i + 1];
+ vec += nb - b;
+ b = nb;
+ limit[i] = vec - 1;
+ vec <<= 1;
+ }
+
+ for (int i = minLen + 1; i <= maxLen; i++) {
+ base[i] = ((limit[i - 1] + 1) << 1) - base[i];
+ }
+ }
+
+ private void recvDecodingTables() throws IOException {
+ final Data dataShadow = this.data;
+ final boolean[] inUse = dataShadow.inUse;
+ final byte[] pos = dataShadow.recvDecodingTables_pos;
+ final byte[] selector = dataShadow.selector;
+ final byte[] selectorMtf = dataShadow.selectorMtf;
+
+ int inUse16 = 0;
+
+ /* Receive the mapping table */
+ for (int i = 0; i < 16; i++) {
+ if (bsGetBit()) {
+ inUse16 |= 1 << i;
+ }
+ }
+
+ for (int i = 256; --i >= 0;) {
+ inUse[i] = false;
+ }
+
+ for (int i = 0; i < 16; i++) {
+ if ((inUse16 & (1 << i)) != 0) {
+ final int i16 = i << 4;
+ for (int j = 0; j < 16; j++) {
+ if (bsGetBit()) {
+ inUse[i16 + j] = true;
+ }
+ }
+ }
+ }
+
+ makeMaps();
+ final int alphaSize = this.nInUse + 2;
+
+ /* Now the selectors */
+ final int nGroups = bsR(3);
+ final int nSelectors = bsR(15);
+
+ for (int i = 0; i < nSelectors; i++) {
+ int j = 0;
+ while (bsGetBit()) {
+ j++;
+ }
+ selectorMtf[i] = (byte) j;
+ }
+
+ /* Undo the MTF values for the selectors. */
+ for (int v = nGroups; --v >= 0;) {
+ pos[v] = (byte) v;
+ }
+
+ for (int i = 0; i < nSelectors; i++) {
+ int v = selectorMtf[i] & 0xff;
+ final byte tmp = pos[v];
+ while (v > 0) {
+ // nearly all times v is zero, 4 in most other cases
+ pos[v] = pos[v - 1];
+ v--;
+ }
+ pos[0] = tmp;
+ selector[i] = tmp;
+ }
+
+ final char[][] len = dataShadow.temp_charArray2d;
+
+ /* Now the coding tables */
+ for (int t = 0; t < nGroups; t++) {
+ int curr = bsR(5);
+ final char[] len_t = len[t];
+ for (int i = 0; i < alphaSize; i++) {
+ while (bsGetBit()) {
+ curr += bsGetBit() ? -1 : 1;
+ }
+ len_t[i] = (char) curr;
+ }
+ }
+
+ // finally create the Huffman tables
+ createHuffmanDecodingTables(alphaSize, nGroups);
+ }
+
+ /**
+ * Called by recvDecodingTables() exclusively.
+ */
+ private void createHuffmanDecodingTables(final int alphaSize,
+ final int nGroups) {
+ final Data dataShadow = this.data;
+ final char[][] len = dataShadow.temp_charArray2d;
+ final int[] minLens = dataShadow.minLens;
+ final int[][] limit = dataShadow.limit;
+ final int[][] base = dataShadow.base;
+ final int[][] perm = dataShadow.perm;
+
+ for (int t = 0; t < nGroups; t++) {
+ int minLen = 32;
+ int maxLen = 0;
+ final char[] len_t = len[t];
+ for (int i = alphaSize; --i >= 0;) {
+ final char lent = len_t[i];
+ if (lent > maxLen) {
+ maxLen = lent;
+ }
+ if (lent < minLen) {
+ minLen = lent;
+ }
+ }
+ hbCreateDecodeTables(limit[t], base[t], perm[t], len[t], minLen,
+ maxLen, alphaSize);
+ minLens[t] = minLen;
+ }
+ }
+
+ private void getAndMoveToFrontDecode() throws IOException {
+ this.origPtr = bsR(24);
+ recvDecodingTables();
+
+ final InputStream inShadow = this.in;
+ final Data dataShadow = this.data;
+ final byte[] ll8 = dataShadow.ll8;
+ final int[] unzftab = dataShadow.unzftab;
+ final byte[] selector = dataShadow.selector;
+ final byte[] seqToUnseq = dataShadow.seqToUnseq;
+ final char[] yy = dataShadow.getAndMoveToFrontDecode_yy;
+ final int[] minLens = dataShadow.minLens;
+ final int[][] limit = dataShadow.limit;
+ final int[][] base = dataShadow.base;
+ final int[][] perm = dataShadow.perm;
+ final int limitLast = this.blockSize100k * 100000;
+
+ /*
+ Setting up the unzftab entries here is not strictly
+ necessary, but it does save having to do it later
+ in a separate pass, and so saves a block's worth of
+ cache misses.
+ */
+ for (int i = 256; --i >= 0;) {
+ yy[i] = (char) i;
+ unzftab[i] = 0;
+ }
+
+ int groupNo = 0;
+ int groupPos = G_SIZE - 1;
+ final int eob = this.nInUse + 1;
+ int nextSym = getAndMoveToFrontDecode0(0);
+ int bsBuffShadow = this.bsBuff;
+ int bsLiveShadow = this.bsLive;
+ int lastShadow = -1;
+ int zt = selector[groupNo] & 0xff;
+ int[] base_zt = base[zt];
+ int[] limit_zt = limit[zt];
+ int[] perm_zt = perm[zt];
+ int minLens_zt = minLens[zt];
+
+ while (nextSym != eob) {
+ if ((nextSym == RUNA) || (nextSym == RUNB)) {
+ int s = -1;
+
+ for (int n = 1; true; n <<= 1) {
+ if (nextSym == RUNA) {
+ s += n;
+ } else if (nextSym == RUNB) {
+ s += n << 1;
+ } else {
+ break;
+ }
+
+ if (groupPos == 0) {
+ groupPos = G_SIZE - 1;
+ zt = selector[++groupNo] & 0xff;
+ base_zt = base[zt];
+ limit_zt = limit[zt];
+ perm_zt = perm[zt];
+ minLens_zt = minLens[zt];
+ } else {
+ groupPos--;
+ }
+
+ int zn = minLens_zt;
+
+ // Inlined:
+ // int zvec = bsR(zn);
+ while (bsLiveShadow < zn) {
+ final int thech = inShadow.read();
+ if (thech >= 0) {
+ bsBuffShadow = (bsBuffShadow << 8) | thech;
+ bsLiveShadow += 8;
+ continue;
+ } else {
+ throw new IOException("unexpected end of stream");
+ }
+ }
+ int zvec = (bsBuffShadow >> (bsLiveShadow - zn)) & ((1 << zn) - 1);
+ bsLiveShadow -= zn;
+
+ while (zvec > limit_zt[zn]) {
+ zn++;
+ while (bsLiveShadow < 1) {
+ final int thech = inShadow.read();
+ if (thech >= 0) {
+ bsBuffShadow = (bsBuffShadow << 8) | thech;
+ bsLiveShadow += 8;
+ continue;
+ } else {
+ throw new IOException("unexpected end of stream");
+ }
+ }
+ bsLiveShadow--;
+ zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1);
+ }
+ nextSym = perm_zt[zvec - base_zt[zn]];
+ }
+
+ final byte ch = seqToUnseq[yy[0]];
+ unzftab[ch & 0xff] += s + 1;
+
+ while (s-- >= 0) {
+ ll8[++lastShadow] = ch;
+ }
+
+ if (lastShadow >= limitLast) {
+ throw new IOException("block overrun");
+ }
+ } else {
+ if (++lastShadow >= limitLast) {
+ throw new IOException("block overrun");
+ }
+
+ final char tmp = yy[nextSym - 1];
+ unzftab[seqToUnseq[tmp] & 0xff]++;
+ ll8[lastShadow] = seqToUnseq[tmp];
+
+ /*
+ This loop is hammered during decompression,
+ hence avoid native method call overhead of
+ System.arraycopy for very small ranges to copy.
+ */
+ if (nextSym <= 16) {
+ for (int j = nextSym - 1; j > 0;) {
+ yy[j] = yy[--j];
+ }
+ } else {
+ System.arraycopy(yy, 0, yy, 1, nextSym - 1);
+ }
+
+ yy[0] = tmp;
+
+ if (groupPos == 0) {
+ groupPos = G_SIZE - 1;
+ zt = selector[++groupNo] & 0xff;
+ base_zt = base[zt];
+ limit_zt = limit[zt];
+ perm_zt = perm[zt];
+ minLens_zt = minLens[zt];
+ } else {
+ groupPos--;
+ }
+
+ int zn = minLens_zt;
+
+ // Inlined:
+ // int zvec = bsR(zn);
+ while (bsLiveShadow < zn) {
+ final int thech = inShadow.read();
+ if (thech >= 0) {
+ bsBuffShadow = (bsBuffShadow << 8) | thech;
+ bsLiveShadow += 8;
+ continue;
+ } else {
+ throw new IOException("unexpected end of stream");
+ }
+ }
+ int zvec = (bsBuffShadow >> (bsLiveShadow - zn)) & ((1 << zn) - 1);
+ bsLiveShadow -= zn;
+
+ while (zvec > limit_zt[zn]) {
+ zn++;
+ while (bsLiveShadow < 1) {
+ final int thech = inShadow.read();
+ if (thech >= 0) {
+ bsBuffShadow = (bsBuffShadow << 8) | thech;
+ bsLiveShadow += 8;
+ continue;
+ } else {
+ throw new IOException("unexpected end of stream");
+ }
+ }
+ bsLiveShadow--;
+ zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1);
+ }
+ nextSym = perm_zt[zvec - base_zt[zn]];
+ }
+ }
+
+ this.last = lastShadow;
+ this.bsLive = bsLiveShadow;
+ this.bsBuff = bsBuffShadow;
+ }
+
+ private int getAndMoveToFrontDecode0(final int groupNo)
+ throws IOException {
+ final InputStream inShadow = this.in;
+ final Data dataShadow = this.data;
+ final int zt = dataShadow.selector[groupNo] & 0xff;
+ final int[] limit_zt = dataShadow.limit[zt];
+ int zn = dataShadow.minLens[zt];
+ int zvec = bsR(zn);
+ int bsLiveShadow = this.bsLive;
+ int bsBuffShadow = this.bsBuff;
+
+ while (zvec > limit_zt[zn]) {
+ zn++;
+ while (bsLiveShadow < 1) {
+ final int thech = inShadow.read();
+
+ if (thech >= 0) {
+ bsBuffShadow = (bsBuffShadow << 8) | thech;
+ bsLiveShadow += 8;
+ continue;
+ } else {
+ throw new IOException("unexpected end of stream");
+ }
+ }
+ bsLiveShadow--;
+ zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1);
+ }
+
+ this.bsLive = bsLiveShadow;
+ this.bsBuff = bsBuffShadow;
+
+ return dataShadow.perm[zt][zvec - dataShadow.base[zt][zn]];
+ }
+
+ private void setupBlock() throws IOException {
+ if (this.data == null) {
+ return;
+ }
+
+ final int[] cftab = this.data.cftab;
+ final int[] tt = this.data.initTT(this.last + 1);
+ final byte[] ll8 = this.data.ll8;
+ cftab[0] = 0;
+ System.arraycopy(this.data.unzftab, 0, cftab, 1, 256);
+
+ for (int i = 1, c = cftab[0]; i <= 256; i++) {
+ c += cftab[i];
+ cftab[i] = c;
+ }
+
+ for (int i = 0, lastShadow = this.last; i <= lastShadow; i++) {
+ tt[cftab[ll8[i] & 0xff]++] = i;
+ }
+
+ if ((this.origPtr < 0) || (this.origPtr >= tt.length)) {
+ throw new IOException("stream corrupted");
+ }
+
+ this.su_tPos = tt[this.origPtr];
+ this.su_count = 0;
+ this.su_i2 = 0;
+ this.su_ch2 = 256; /* not a char and not EOF */
+
+ if (this.blockRandomised) {
+ this.su_rNToGo = 0;
+ this.su_rTPos = 0;
+ setupRandPartA();
+ } else {
+ setupNoRandPartA();
+ }
+ }
+
+ private void setupRandPartA() throws IOException {
+ if (this.su_i2 <= this.last) {
+ this.su_chPrev = this.su_ch2;
+ int su_ch2Shadow = this.data.ll8[this.su_tPos] & 0xff;
+ this.su_tPos = this.data.tt[this.su_tPos];
+ if (this.su_rNToGo == 0) {
+ this.su_rNToGo = BZip2Constants.rNums[this.su_rTPos] - 1;
+ if (++this.su_rTPos == 512) {
+ this.su_rTPos = 0;
+ }
+ } else {
+ this.su_rNToGo--;
+ }
+ this.su_ch2 = su_ch2Shadow ^= (this.su_rNToGo == 1) ? 1 : 0;
+ this.su_i2++;
+ this.currentChar = su_ch2Shadow;
+ this.currentState = RAND_PART_B_STATE;
+ this.crc.updateCRC(su_ch2Shadow);
+ } else {
+ endBlock();
+ initBlock();
+ setupBlock();
+ }
+ }
+
+ private void setupNoRandPartA() throws IOException {
+ if (this.su_i2 <= this.last) {
+ this.su_chPrev = this.su_ch2;
+ int su_ch2Shadow = this.data.ll8[this.su_tPos] & 0xff;
+ this.su_ch2 = su_ch2Shadow;
+ this.su_tPos = this.data.tt[this.su_tPos];
+ this.su_i2++;
+ this.currentChar = su_ch2Shadow;
+ this.currentState = NO_RAND_PART_B_STATE;
+ this.crc.updateCRC(su_ch2Shadow);
+ } else {
+ this.currentState = NO_RAND_PART_A_STATE;
+ endBlock();
+ initBlock();
+ setupBlock();
+ }
+ }
+
+ private void setupRandPartB() throws IOException {
+ if (this.su_ch2 != this.su_chPrev) {
+ this.currentState = RAND_PART_A_STATE;
+ this.su_count = 1;
+ setupRandPartA();
+ } else if (++this.su_count >= 4) {
+ this.su_z = (char) (this.data.ll8[this.su_tPos] & 0xff);
+ this.su_tPos = this.data.tt[this.su_tPos];
+ if (this.su_rNToGo == 0) {
+ this.su_rNToGo = BZip2Constants.rNums[this.su_rTPos] - 1;
+ if (++this.su_rTPos == 512) {
+ this.su_rTPos = 0;
+ }
+ } else {
+ this.su_rNToGo--;
+ }
+ this.su_j2 = 0;
+ this.currentState = RAND_PART_C_STATE;
+ if (this.su_rNToGo == 1) {
+ this.su_z ^= 1;
+ }
+ setupRandPartC();
+ } else {
+ this.currentState = RAND_PART_A_STATE;
+ setupRandPartA();
+ }
+ }
+
+ private void setupRandPartC() throws IOException {
+ if (this.su_j2 < this.su_z) {
+ this.currentChar = this.su_ch2;
+ this.crc.updateCRC(this.su_ch2);
+ this.su_j2++;
+ } else {
+ this.currentState = RAND_PART_A_STATE;
+ this.su_i2++;
+ this.su_count = 0;
+ setupRandPartA();
+ }
+ }
+
+ private void setupNoRandPartB() throws IOException {
+ if (this.su_ch2 != this.su_chPrev) {
+ this.su_count = 1;
+ setupNoRandPartA();
+ } else if (++this.su_count >= 4) {
+ this.su_z = (char) (this.data.ll8[this.su_tPos] & 0xff);
+ this.su_tPos = this.data.tt[this.su_tPos];
+ this.su_j2 = 0;
+ setupNoRandPartC();
+ } else {
+ setupNoRandPartA();
+ }
+ }
+
+ private void setupNoRandPartC() throws IOException {
+ if (this.su_j2 < this.su_z) {
+ int su_ch2Shadow = this.su_ch2;
+ this.currentChar = su_ch2Shadow;
+ this.crc.updateCRC(su_ch2Shadow);
+ this.su_j2++;
+ this.currentState = NO_RAND_PART_C_STATE;
+ } else {
+ this.su_i2++;
+ this.su_count = 0;
+ setupNoRandPartA();
+ }
+ }
+
+ private static final class Data extends Object {
+
+ // (with blockSize 900k)
+ final boolean[] inUse = new boolean[256]; // 256 byte
+
+ final byte[] seqToUnseq = new byte[256]; // 256 byte
+ final byte[] selector = new byte[MAX_SELECTORS]; // 18002 byte
+ final byte[] selectorMtf = new byte[MAX_SELECTORS]; // 18002 byte
+
+ /**
+ * Freq table collected to save a pass over the data during
+ * decompression.
+ */
+ final int[] unzftab = new int[256]; // 1024 byte
+
+ final int[][] limit = new int[N_GROUPS][MAX_ALPHA_SIZE]; // 6192 byte
+ final int[][] base = new int[N_GROUPS][MAX_ALPHA_SIZE]; // 6192 byte
+ final int[][] perm = new int[N_GROUPS][MAX_ALPHA_SIZE]; // 6192 byte
+ final int[] minLens = new int[N_GROUPS]; // 24 byte
+
+ final int[] cftab = new int[257]; // 1028 byte
+ final char[] getAndMoveToFrontDecode_yy = new char[256]; // 512 byte
+ final char[][] temp_charArray2d = new char[N_GROUPS][MAX_ALPHA_SIZE]; // 3096 byte
+ final byte[] recvDecodingTables_pos = new byte[N_GROUPS]; // 6 byte
+ //---------------
+ // 60798 byte
+
+ int[] tt; // 3600000 byte
+ byte[] ll8; // 900000 byte
+ //---------------
+ // 4560782 byte
+ //===============
+
+ Data(int blockSize100k) {
+ super();
+
+ this.ll8 = new byte[blockSize100k * BZip2Constants.baseBlockSize];
+ }
+
+ /**
+ * Initializes the {@link #tt} array.
+ *
+ * This method is called when the required length of the array
+ * is known. I don't initialize it at construction time to
+ * avoid unnecessary memory allocation when compressing small
+ * files.
+ */
+ final int[] initTT(int length) {
+ int[] ttShadow = this.tt;
+
+ // tt.length should always be >= length, but theoretically
+ // it can happen, if the compressor mixed small and large
+ // blocks. Normally only the last block will be smaller
+ // than others.
+ if ((ttShadow == null) || (ttShadow.length < length)) {
+ this.tt = ttShadow = new int[length];
+ }
+
+ return ttShadow;
+ }
+
+ }
+
+ private static void reportCRCError() throws IOException {
+ // The clean way would be to throw an exception.
+ //throw new IOException("crc error");
+
+ // Just print a message, like the previous versions of this class did
+ System.err.println("BZip2 CRC error");
+ }
+
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/CBZip2OutputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/CBZip2OutputStream.java
new file mode 100644
index 00000000..01e23424
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/CBZip2OutputStream.java
@@ -0,0 +1,1580 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Keiron Liddle, Aftex Software
+ * <keiron@aftexsw.com> to whom the Ant project is very grateful for his
+ * great code.
+ */
+
+package org.apache.tools.bzip2;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An output stream that compresses into the BZip2 format (without the file
+ * header chars) into another stream.
+ *
+ * <p>
+ * The compression requires large amounts of memory. Thus you should call the
+ * {@link #close() close()} method as soon as possible, to force
+ * <tt>CBZip2OutputStream</tt> to release the allocated memory.
+ * </p>
+ *
+ * <p> You can shrink the amount of allocated memory and maybe raise
+ * the compression speed by choosing a lower blocksize, which in turn
+ * may cause a lower compression ratio. You can avoid unnecessary
+ * memory allocation by avoiding using a blocksize which is bigger
+ * than the size of the input. </p>
+ *
+ * <p> You can compute the memory usage for compressing by the
+ * following formula: </p>
+ *
+ * <pre>
+ * &lt;code&gt;400k + (9 * blocksize)&lt;/code&gt;.
+ * </pre>
+ *
+ * <p> To get the memory required for decompression by {@link
+ * CBZip2InputStream CBZip2InputStream} use </p>
+ *
+ * <pre>
+ * &lt;code&gt;65k + (5 * blocksize)&lt;/code&gt;.
+ * </pre>
+ *
+ * <table width="100%" border="1">
+ * <colgroup> <col width="33%" /> <col width="33%" /> <col width="33%" />
+ * </colgroup>
+ * <tr>
+ * <th colspan="3">Memory usage by blocksize</th>
+ * </tr>
+ * <tr>
+ * <th align="right">Blocksize</th> <th align="right">Compression<br>
+ * memory usage</th> <th align="right">Decompression<br>
+ * memory usage</th>
+ * </tr>
+ * <tr>
+ * <td align="right">100k</td>
+ * <td align="right">1300k</td>
+ * <td align="right">565k</td>
+ * </tr>
+ * <tr>
+ * <td align="right">200k</td>
+ * <td align="right">2200k</td>
+ * <td align="right">1065k</td>
+ * </tr>
+ * <tr>
+ * <td align="right">300k</td>
+ * <td align="right">3100k</td>
+ * <td align="right">1565k</td>
+ * </tr>
+ * <tr>
+ * <td align="right">400k</td>
+ * <td align="right">4000k</td>
+ * <td align="right">2065k</td>
+ * </tr>
+ * <tr>
+ * <td align="right">500k</td>
+ * <td align="right">4900k</td>
+ * <td align="right">2565k</td>
+ * </tr>
+ * <tr>
+ * <td align="right">600k</td>
+ * <td align="right">5800k</td>
+ * <td align="right">3065k</td>
+ * </tr>
+ * <tr>
+ * <td align="right">700k</td>
+ * <td align="right">6700k</td>
+ * <td align="right">3565k</td>
+ * </tr>
+ * <tr>
+ * <td align="right">800k</td>
+ * <td align="right">7600k</td>
+ * <td align="right">4065k</td>
+ * </tr>
+ * <tr>
+ * <td align="right">900k</td>
+ * <td align="right">8500k</td>
+ * <td align="right">4565k</td>
+ * </tr>
+ * </table>
+ *
+ * <p>
+ * For decompression <tt>CBZip2InputStream</tt> allocates less memory if the
+ * bzipped input is smaller than one block.
+ * </p>
+ *
+ * <p>
+ * Instances of this class are not threadsafe.
+ * </p>
+ *
+ * <p>
+ * TODO: Update to BZip2 1.0.1
+ * </p>
+ *
+ */
+public class CBZip2OutputStream extends OutputStream
+ implements BZip2Constants {
+
+ /**
+ * The minimum supported blocksize <tt> == 1</tt>.
+ */
+ public static final int MIN_BLOCKSIZE = 1;
+
+ /**
+ * The maximum supported blocksize <tt> == 9</tt>.
+ */
+ public static final int MAX_BLOCKSIZE = 9;
+
+ /**
+ * This constant is accessible by subclasses for historical
+ * purposes. If you don't know what it means then you don't need
+ * it.
+ */
+ protected static final int SETMASK = (1 << 21);
+
+ /**
+ * This constant is accessible by subclasses for historical
+ * purposes. If you don't know what it means then you don't need
+ * it.
+ */
+ protected static final int CLEARMASK = (~SETMASK);
+
+ /**
+ * This constant is accessible by subclasses for historical
+ * purposes. If you don't know what it means then you don't need
+ * it.
+ */
+ protected static final int GREATER_ICOST = 15;
+
+ /**
+ * This constant is accessible by subclasses for historical
+ * purposes. If you don't know what it means then you don't need
+ * it.
+ */
+ protected static final int LESSER_ICOST = 0;
+
+ /**
+ * This constant is accessible by subclasses for historical
+ * purposes. If you don't know what it means then you don't need
+ * it.
+ */
+ protected static final int SMALL_THRESH = 20;
+
+ /**
+ * This constant is accessible by subclasses for historical
+ * purposes. If you don't know what it means then you don't need
+ * it.
+ */
+ protected static final int DEPTH_THRESH = 10;
+
+ /**
+ * This constant is accessible by subclasses for historical
+ * purposes. If you don't know what it means then you don't need
+ * it.
+ */
+ protected static final int WORK_FACTOR = 30;
+
+ /**
+ * This constant is accessible by subclasses for historical
+ * purposes. If you don't know what it means then you don't need
+ * it.
+ * <p> If you are ever unlucky/improbable enough to get a stack
+ * overflow whilst sorting, increase the following constant and
+ * try again. In practice I have never seen the stack go above 27
+ * elems, so the following limit seems very generous. </p>
+ */
+ protected static final int QSORT_STACK_SIZE = 1000;
+
+ /**
+ * Knuth's increments seem to work better than Incerpi-Sedgewick here.
+ * Possibly because the number of elems to sort is usually small, typically
+ * &lt;= 20.
+ */
+ private static final int[] INCS = { 1, 4, 13, 40, 121, 364, 1093, 3280,
+ 9841, 29524, 88573, 265720, 797161,
+ 2391484 };
+
+ /**
+ * This method is accessible by subclasses for historical
+ * purposes. If you don't know what it does then you don't need
+ * it.
+ */
+ protected static void hbMakeCodeLengths(char[] len, int[] freq,
+ int alphaSize, int maxLen) {
+ /*
+ * Nodes and heap entries run from 1. Entry 0 for both the heap and
+ * nodes is a sentinel.
+ */
+ final int[] heap = new int[MAX_ALPHA_SIZE * 2];
+ final int[] weight = new int[MAX_ALPHA_SIZE * 2];
+ final int[] parent = new int[MAX_ALPHA_SIZE * 2];
+
+ for (int i = alphaSize; --i >= 0;) {
+ weight[i + 1] = (freq[i] == 0 ? 1 : freq[i]) << 8;
+ }
+
+ for (boolean tooLong = true; tooLong;) {
+ tooLong = false;
+
+ int nNodes = alphaSize;
+ int nHeap = 0;
+ heap[0] = 0;
+ weight[0] = 0;
+ parent[0] = -2;
+
+ for (int i = 1; i <= alphaSize; i++) {
+ parent[i] = -1;
+ nHeap++;
+ heap[nHeap] = i;
+
+ int zz = nHeap;
+ int tmp = heap[zz];
+ while (weight[tmp] < weight[heap[zz >> 1]]) {
+ heap[zz] = heap[zz >> 1];
+ zz >>= 1;
+ }
+ heap[zz] = tmp;
+ }
+
+ // assert (nHeap < (MAX_ALPHA_SIZE + 2)) : nHeap;
+
+ while (nHeap > 1) {
+ int n1 = heap[1];
+ heap[1] = heap[nHeap];
+ nHeap--;
+
+ int yy = 0;
+ int zz = 1;
+ int tmp = heap[1];
+
+ while (true) {
+ yy = zz << 1;
+
+ if (yy > nHeap) {
+ break;
+ }
+
+ if ((yy < nHeap)
+ && (weight[heap[yy + 1]] < weight[heap[yy]])) {
+ yy++;
+ }
+
+ if (weight[tmp] < weight[heap[yy]]) {
+ break;
+ }
+
+ heap[zz] = heap[yy];
+ zz = yy;
+ }
+
+ heap[zz] = tmp;
+
+ int n2 = heap[1];
+ heap[1] = heap[nHeap];
+ nHeap--;
+
+ yy = 0;
+ zz = 1;
+ tmp = heap[1];
+
+ while (true) {
+ yy = zz << 1;
+
+ if (yy > nHeap) {
+ break;
+ }
+
+ if ((yy < nHeap)
+ && (weight[heap[yy + 1]] < weight[heap[yy]])) {
+ yy++;
+ }
+
+ if (weight[tmp] < weight[heap[yy]]) {
+ break;
+ }
+
+ heap[zz] = heap[yy];
+ zz = yy;
+ }
+
+ heap[zz] = tmp;
+ nNodes++;
+ parent[n1] = parent[n2] = nNodes;
+
+ final int weight_n1 = weight[n1];
+ final int weight_n2 = weight[n2];
+ weight[nNodes] = (((weight_n1 & 0xffffff00)
+ + (weight_n2 & 0xffffff00))
+ |
+ (1 + (((weight_n1 & 0x000000ff)
+ > (weight_n2 & 0x000000ff))
+ ? (weight_n1 & 0x000000ff)
+ : (weight_n2 & 0x000000ff))
+ ));
+
+ parent[nNodes] = -1;
+ nHeap++;
+ heap[nHeap] = nNodes;
+
+ tmp = 0;
+ zz = nHeap;
+ tmp = heap[zz];
+ final int weight_tmp = weight[tmp];
+ while (weight_tmp < weight[heap[zz >> 1]]) {
+ heap[zz] = heap[zz >> 1];
+ zz >>= 1;
+ }
+ heap[zz] = tmp;
+
+ }
+
+ // assert (nNodes < (MAX_ALPHA_SIZE * 2)) : nNodes;
+
+ for (int i = 1; i <= alphaSize; i++) {
+ int j = 0;
+ int k = i;
+
+ for (int parent_k; (parent_k = parent[k]) >= 0;) {
+ k = parent_k;
+ j++;
+ }
+
+ len[i - 1] = (char) j;
+ if (j > maxLen) {
+ tooLong = true;
+ }
+ }
+
+ if (tooLong) {
+ for (int i = 1; i < alphaSize; i++) {
+ int j = weight[i] >> 8;
+ j = 1 + (j >> 1);
+ weight[i] = j << 8;
+ }
+ }
+ }
+ }
+
+ private static void hbMakeCodeLengths(final byte[] len, final int[] freq,
+ final Data dat, final int alphaSize,
+ final int maxLen) {
+ /*
+ * Nodes and heap entries run from 1. Entry 0 for both the heap and
+ * nodes is a sentinel.
+ */
+ final int[] heap = dat.heap;
+ final int[] weight = dat.weight;
+ final int[] parent = dat.parent;
+
+ for (int i = alphaSize; --i >= 0;) {
+ weight[i + 1] = (freq[i] == 0 ? 1 : freq[i]) << 8;
+ }
+
+ for (boolean tooLong = true; tooLong;) {
+ tooLong = false;
+
+ int nNodes = alphaSize;
+ int nHeap = 0;
+ heap[0] = 0;
+ weight[0] = 0;
+ parent[0] = -2;
+
+ for (int i = 1; i <= alphaSize; i++) {
+ parent[i] = -1;
+ nHeap++;
+ heap[nHeap] = i;
+
+ int zz = nHeap;
+ int tmp = heap[zz];
+ while (weight[tmp] < weight[heap[zz >> 1]]) {
+ heap[zz] = heap[zz >> 1];
+ zz >>= 1;
+ }
+ heap[zz] = tmp;
+ }
+
+ while (nHeap > 1) {
+ int n1 = heap[1];
+ heap[1] = heap[nHeap];
+ nHeap--;
+
+ int yy = 0;
+ int zz = 1;
+ int tmp = heap[1];
+
+ while (true) {
+ yy = zz << 1;
+
+ if (yy > nHeap) {
+ break;
+ }
+
+ if ((yy < nHeap)
+ && (weight[heap[yy + 1]] < weight[heap[yy]])) {
+ yy++;
+ }
+
+ if (weight[tmp] < weight[heap[yy]]) {
+ break;
+ }
+
+ heap[zz] = heap[yy];
+ zz = yy;
+ }
+
+ heap[zz] = tmp;
+
+ int n2 = heap[1];
+ heap[1] = heap[nHeap];
+ nHeap--;
+
+ yy = 0;
+ zz = 1;
+ tmp = heap[1];
+
+ while (true) {
+ yy = zz << 1;
+
+ if (yy > nHeap) {
+ break;
+ }
+
+ if ((yy < nHeap)
+ && (weight[heap[yy + 1]] < weight[heap[yy]])) {
+ yy++;
+ }
+
+ if (weight[tmp] < weight[heap[yy]]) {
+ break;
+ }
+
+ heap[zz] = heap[yy];
+ zz = yy;
+ }
+
+ heap[zz] = tmp;
+ nNodes++;
+ parent[n1] = parent[n2] = nNodes;
+
+ final int weight_n1 = weight[n1];
+ final int weight_n2 = weight[n2];
+ weight[nNodes] = ((weight_n1 & 0xffffff00)
+ + (weight_n2 & 0xffffff00))
+ | (1 + (((weight_n1 & 0x000000ff)
+ > (weight_n2 & 0x000000ff))
+ ? (weight_n1 & 0x000000ff)
+ : (weight_n2 & 0x000000ff)));
+
+ parent[nNodes] = -1;
+ nHeap++;
+ heap[nHeap] = nNodes;
+
+ tmp = 0;
+ zz = nHeap;
+ tmp = heap[zz];
+ final int weight_tmp = weight[tmp];
+ while (weight_tmp < weight[heap[zz >> 1]]) {
+ heap[zz] = heap[zz >> 1];
+ zz >>= 1;
+ }
+ heap[zz] = tmp;
+
+ }
+
+ for (int i = 1; i <= alphaSize; i++) {
+ int j = 0;
+ int k = i;
+
+ for (int parent_k; (parent_k = parent[k]) >= 0;) {
+ k = parent_k;
+ j++;
+ }
+
+ len[i - 1] = (byte) j;
+ if (j > maxLen) {
+ tooLong = true;
+ }
+ }
+
+ if (tooLong) {
+ for (int i = 1; i < alphaSize; i++) {
+ int j = weight[i] >> 8;
+ j = 1 + (j >> 1);
+ weight[i] = j << 8;
+ }
+ }
+ }
+ }
+
+ /**
+ * Index of the last char in the block, so the block size == last + 1.
+ */
+ private int last;
+
+ /**
+ * Always: in the range 0 .. 9. The current block size is 100000 * this
+ * number.
+ */
+ private final int blockSize100k;
+
+ private int bsBuff;
+ private int bsLive;
+ private final CRC crc = new CRC();
+
+ private int nInUse;
+
+ private int nMTF;
+
+ private int currentChar = -1;
+ private int runLength = 0;
+
+ private int blockCRC;
+ private int combinedCRC;
+ private final int allowableBlockSize;
+
+ /**
+ * All memory intensive stuff.
+ */
+ private Data data;
+ private BlockSort blockSorter;
+
+ private OutputStream out;
+
+ /**
+ * Chooses a blocksize based on the given length of the data to compress.
+ *
+ * @return The blocksize, between {@link #MIN_BLOCKSIZE} and
+ * {@link #MAX_BLOCKSIZE} both inclusive. For a negative
+ * <tt>inputLength</tt> this method returns <tt>MAX_BLOCKSIZE</tt>
+ * always.
+ *
+ * @param inputLength
+ * The length of the data which will be compressed by
+ * <tt>CBZip2OutputStream</tt>.
+ */
+ public static int chooseBlockSize(long inputLength) {
+ return (inputLength > 0) ? (int) Math
+ .min((inputLength / 132000) + 1, 9) : MAX_BLOCKSIZE;
+ }
+
+ /**
+ * Constructs a new <tt>CBZip2OutputStream</tt> with a blocksize of 900k.
+ *
+ * <p>
+ * <b>Attention: </b>The caller is responsible to write the two BZip2 magic
+ * bytes <tt>"BZ"</tt> to the specified stream prior to calling this
+ * constructor.
+ * </p>
+ *
+ * @param out *
+ * the destination stream.
+ *
+ * @throws IOException
+ * if an I/O error occurs in the specified stream.
+ * @throws NullPointerException
+ * if <code>out == null</code>.
+ */
+ public CBZip2OutputStream(final OutputStream out) throws IOException {
+ this(out, MAX_BLOCKSIZE);
+ }
+
+ /**
+ * Constructs a new <tt>CBZip2OutputStream</tt> with specified blocksize.
+ *
+ * <p>
+ * <b>Attention: </b>The caller is responsible to write the two BZip2 magic
+ * bytes <tt>"BZ"</tt> to the specified stream prior to calling this
+ * constructor.
+ * </p>
+ *
+ *
+ * @param out
+ * the destination stream.
+ * @param blockSize
+ * the blockSize as 100k units.
+ *
+ * @throws IOException
+ * if an I/O error occurs in the specified stream.
+ * @throws IllegalArgumentException
+ * if <code>(blockSize &lt; 1) || (blockSize &gt; 9)</code>.
+ * @throws NullPointerException
+ * if <code>out == null</code>.
+ *
+ * @see #MIN_BLOCKSIZE
+ * @see #MAX_BLOCKSIZE
+ */
+ public CBZip2OutputStream(final OutputStream out, final int blockSize)
+ throws IOException {
+ super();
+
+ if (blockSize < 1) {
+ throw new IllegalArgumentException("blockSize(" + blockSize
+ + ") < 1");
+ }
+ if (blockSize > 9) {
+ throw new IllegalArgumentException("blockSize(" + blockSize
+ + ") > 9");
+ }
+
+ this.blockSize100k = blockSize;
+ this.out = out;
+
+ /* 20 is just a paranoia constant */
+ this.allowableBlockSize = (this.blockSize100k * BZip2Constants.baseBlockSize) - 20;
+ init();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void write(final int b) throws IOException {
+ if (this.out != null) {
+ write0(b);
+ } else {
+ throw new IOException("closed");
+ }
+ }
+
+ /**
+ * Writes the current byte to the buffer, run-length encoding it
+ * if it has been repeated at least four times (the first step
+ * RLEs sequences of four identical bytes).
+ *
+ * <p>Flushes the current block before writing data if it is
+ * full.</p>
+ *
+ * <p>"write to the buffer" means adding to data.buffer starting
+ * two steps "after" this.last - initially starting at index 1
+ * (not 0) - and updating this.last to point to the last index
+ * written minus 1.</p>
+ */
+ private void writeRun() throws IOException {
+ final int lastShadow = this.last;
+
+ if (lastShadow < this.allowableBlockSize) {
+ final int currentCharShadow = this.currentChar;
+ final Data dataShadow = this.data;
+ dataShadow.inUse[currentCharShadow] = true;
+ final byte ch = (byte) currentCharShadow;
+
+ int runLengthShadow = this.runLength;
+ this.crc.updateCRC(currentCharShadow, runLengthShadow);
+
+ switch (runLengthShadow) {
+ case 1:
+ dataShadow.block[lastShadow + 2] = ch;
+ this.last = lastShadow + 1;
+ break;
+
+ case 2:
+ dataShadow.block[lastShadow + 2] = ch;
+ dataShadow.block[lastShadow + 3] = ch;
+ this.last = lastShadow + 2;
+ break;
+
+ case 3: {
+ final byte[] block = dataShadow.block;
+ block[lastShadow + 2] = ch;
+ block[lastShadow + 3] = ch;
+ block[lastShadow + 4] = ch;
+ this.last = lastShadow + 3;
+ }
+ break;
+
+ default: {
+ runLengthShadow -= 4;
+ dataShadow.inUse[runLengthShadow] = true;
+ final byte[] block = dataShadow.block;
+ block[lastShadow + 2] = ch;
+ block[lastShadow + 3] = ch;
+ block[lastShadow + 4] = ch;
+ block[lastShadow + 5] = ch;
+ block[lastShadow + 6] = (byte) runLengthShadow;
+ this.last = lastShadow + 5;
+ }
+ break;
+
+ }
+ } else {
+ endBlock();
+ initBlock();
+ writeRun();
+ }
+ }
+
+ /**
+ * Overridden to close the stream.
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ finish();
+ super.finalize();
+ }
+
+
+ public void finish() throws IOException {
+ if (out != null) {
+ try {
+ if (this.runLength > 0) {
+ writeRun();
+ }
+ this.currentChar = -1;
+ endBlock();
+ endCompression();
+ } finally {
+ this.out = null;
+ this.data = null;
+ this.blockSorter = null;
+ }
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (out != null) {
+ OutputStream outShadow = this.out;
+ finish();
+ outShadow.close();
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ OutputStream outShadow = this.out;
+ if (outShadow != null) {
+ outShadow.flush();
+ }
+ }
+
+ private void init() throws IOException {
+ // write magic: done by caller who created this stream
+ // this.out.write('B');
+ // this.out.write('Z');
+
+ this.data = new Data(this.blockSize100k);
+ this.blockSorter = new BlockSort(this.data);
+
+ /*
+ * Write `magic' bytes h indicating file-format == huffmanised, followed
+ * by a digit indicating blockSize100k.
+ */
+ bsPutUByte('h');
+ bsPutUByte('0' + this.blockSize100k);
+
+ this.combinedCRC = 0;
+ initBlock();
+ }
+
+ private void initBlock() {
+ // blockNo++;
+ this.crc.initialiseCRC();
+ this.last = -1;
+ // ch = 0;
+
+ boolean[] inUse = this.data.inUse;
+ for (int i = 256; --i >= 0;) {
+ inUse[i] = false;
+ }
+ }
+
+ private void endBlock() throws IOException {
+ this.blockCRC = this.crc.getFinalCRC();
+ this.combinedCRC = (this.combinedCRC << 1) | (this.combinedCRC >>> 31);
+ this.combinedCRC ^= this.blockCRC;
+
+ // empty block at end of file
+ if (this.last == -1) {
+ return;
+ }
+
+ /* sort the block and establish posn of original string */
+ blockSort();
+
+ /*
+ * A 6-byte block header, the value chosen arbitrarily as 0x314159265359
+ * :-). A 32 bit value does not really give a strong enough guarantee
+ * that the value will not appear by chance in the compressed
+ * datastream. Worst-case probability of this event, for a 900k block,
+ * is about 2.0e-3 for 32 bits, 1.0e-5 for 40 bits and 4.0e-8 for 48
+ * bits. For a compressed file of size 100Gb -- about 100000 blocks --
+ * only a 48-bit marker will do. NB: normal compression/ decompression
+ * donot rely on these statistical properties. They are only important
+ * when trying to recover blocks from damaged files.
+ */
+ bsPutUByte(0x31);
+ bsPutUByte(0x41);
+ bsPutUByte(0x59);
+ bsPutUByte(0x26);
+ bsPutUByte(0x53);
+ bsPutUByte(0x59);
+
+ /* Now the block's CRC, so it is in a known place. */
+ bsPutInt(this.blockCRC);
+
+ /* Now a single bit indicating no randomisation. */
+ bsW(1, 0);
+
+ /* Finally, block's contents proper. */
+ moveToFrontCodeAndSend();
+ }
+
+ private void endCompression() throws IOException {
+ /*
+ * Now another magic 48-bit number, 0x177245385090, to indicate the end
+ * of the last block. (sqrt(pi), if you want to know. I did want to use
+ * e, but it contains too much repetition -- 27 18 28 18 28 46 -- for me
+ * to feel statistically comfortable. Call me paranoid.)
+ */
+ bsPutUByte(0x17);
+ bsPutUByte(0x72);
+ bsPutUByte(0x45);
+ bsPutUByte(0x38);
+ bsPutUByte(0x50);
+ bsPutUByte(0x90);
+
+ bsPutInt(this.combinedCRC);
+ bsFinishedWithStream();
+ }
+
+ /**
+ * Returns the blocksize parameter specified at construction time.
+ */
+ public final int getBlockSize() {
+ return this.blockSize100k;
+ }
+
+ @Override
+ public void write(final byte[] buf, int offs, final int len)
+ throws IOException {
+ if (offs < 0) {
+ throw new IndexOutOfBoundsException("offs(" + offs + ") < 0.");
+ }
+ if (len < 0) {
+ throw new IndexOutOfBoundsException("len(" + len + ") < 0.");
+ }
+ if (offs + len > buf.length) {
+ throw new IndexOutOfBoundsException("offs(" + offs + ") + len("
+ + len + ") > buf.length("
+ + buf.length + ").");
+ }
+ if (this.out == null) {
+ throw new IOException("stream closed");
+ }
+
+ for (int hi = offs + len; offs < hi;) {
+ write0(buf[offs++]);
+ }
+ }
+
+ /**
+ * Keeps track of the last bytes written and implicitly performs
+ * run-length encoding as the first step of the bzip2 algorithm.
+ */
+ private void write0(int b) throws IOException {
+ if (this.currentChar != -1) {
+ b &= 0xff;
+ if (this.currentChar == b) {
+ if (++this.runLength > 254) {
+ writeRun();
+ this.currentChar = -1;
+ this.runLength = 0;
+ }
+ // else nothing to do
+ } else {
+ writeRun();
+ this.runLength = 1;
+ this.currentChar = b;
+ }
+ } else {
+ this.currentChar = b & 0xff;
+ this.runLength++;
+ }
+ }
+
+ private static void hbAssignCodes(final int[] code, final byte[] length,
+ final int minLen, final int maxLen,
+ final int alphaSize) {
+ int vec = 0;
+ for (int n = minLen; n <= maxLen; n++) {
+ for (int i = 0; i < alphaSize; i++) {
+ if ((length[i] & 0xff) == n) {
+ code[i] = vec;
+ vec++;
+ }
+ }
+ vec <<= 1;
+ }
+ }
+
+ private void bsFinishedWithStream() throws IOException {
+ while (this.bsLive > 0) {
+ int ch = this.bsBuff >> 24;
+ this.out.write(ch); // write 8-bit
+ this.bsBuff <<= 8;
+ this.bsLive -= 8;
+ }
+ }
+
+ private void bsW(final int n, final int v) throws IOException {
+ final OutputStream outShadow = this.out;
+ int bsLiveShadow = this.bsLive;
+ int bsBuffShadow = this.bsBuff;
+
+ while (bsLiveShadow >= 8) {
+ outShadow.write(bsBuffShadow >> 24); // write 8-bit
+ bsBuffShadow <<= 8;
+ bsLiveShadow -= 8;
+ }
+
+ this.bsBuff = bsBuffShadow | (v << (32 - bsLiveShadow - n));
+ this.bsLive = bsLiveShadow + n;
+ }
+
+ private void bsPutUByte(final int c) throws IOException {
+ bsW(8, c);
+ }
+
+ private void bsPutInt(final int u) throws IOException {
+ bsW(8, (u >> 24) & 0xff);
+ bsW(8, (u >> 16) & 0xff);
+ bsW(8, (u >> 8) & 0xff);
+ bsW(8, u & 0xff);
+ }
+
+ private void sendMTFValues() throws IOException {
+ final byte[][] len = this.data.sendMTFValues_len;
+ final int alphaSize = this.nInUse + 2;
+
+ for (int t = N_GROUPS; --t >= 0;) {
+ byte[] len_t = len[t];
+ for (int v = alphaSize; --v >= 0;) {
+ len_t[v] = GREATER_ICOST;
+ }
+ }
+
+ /* Decide how many coding tables to use */
+ // assert (this.nMTF > 0) : this.nMTF;
+ final int nGroups = (this.nMTF < 200) ? 2 : (this.nMTF < 600) ? 3
+ : (this.nMTF < 1200) ? 4 : (this.nMTF < 2400) ? 5 : 6;
+
+ /* Generate an initial set of coding tables */
+ sendMTFValues0(nGroups, alphaSize);
+
+ /*
+ * Iterate up to N_ITERS times to improve the tables.
+ */
+ final int nSelectors = sendMTFValues1(nGroups, alphaSize);
+
+ /* Compute MTF values for the selectors. */
+ sendMTFValues2(nGroups, nSelectors);
+
+ /* Assign actual codes for the tables. */
+ sendMTFValues3(nGroups, alphaSize);
+
+ /* Transmit the mapping table. */
+ sendMTFValues4();
+
+ /* Now the selectors. */
+ sendMTFValues5(nGroups, nSelectors);
+
+ /* Now the coding tables. */
+ sendMTFValues6(nGroups, alphaSize);
+
+ /* And finally, the block data proper */
+ sendMTFValues7();
+ }
+
+ private void sendMTFValues0(final int nGroups, final int alphaSize) {
+ final byte[][] len = this.data.sendMTFValues_len;
+ final int[] mtfFreq = this.data.mtfFreq;
+
+ int remF = this.nMTF;
+ int gs = 0;
+
+ for (int nPart = nGroups; nPart > 0; nPart--) {
+ final int tFreq = remF / nPart;
+ int ge = gs - 1;
+ int aFreq = 0;
+
+ for (final int a = alphaSize - 1; (aFreq < tFreq) && (ge < a);) {
+ aFreq += mtfFreq[++ge];
+ }
+
+ if ((ge > gs) && (nPart != nGroups) && (nPart != 1)
+ && (((nGroups - nPart) & 1) != 0)) {
+ aFreq -= mtfFreq[ge--];
+ }
+
+ final byte[] len_np = len[nPart - 1];
+ for (int v = alphaSize; --v >= 0;) {
+ if ((v >= gs) && (v <= ge)) {
+ len_np[v] = LESSER_ICOST;
+ } else {
+ len_np[v] = GREATER_ICOST;
+ }
+ }
+
+ gs = ge + 1;
+ remF -= aFreq;
+ }
+ }
+
+ private int sendMTFValues1(final int nGroups, final int alphaSize) {
+ final Data dataShadow = this.data;
+ final int[][] rfreq = dataShadow.sendMTFValues_rfreq;
+ final int[] fave = dataShadow.sendMTFValues_fave;
+ final short[] cost = dataShadow.sendMTFValues_cost;
+ final char[] sfmap = dataShadow.sfmap;
+ final byte[] selector = dataShadow.selector;
+ final byte[][] len = dataShadow.sendMTFValues_len;
+ final byte[] len_0 = len[0];
+ final byte[] len_1 = len[1];
+ final byte[] len_2 = len[2];
+ final byte[] len_3 = len[3];
+ final byte[] len_4 = len[4];
+ final byte[] len_5 = len[5];
+ final int nMTFShadow = this.nMTF;
+
+ int nSelectors = 0;
+
+ for (int iter = 0; iter < N_ITERS; iter++) {
+ for (int t = nGroups; --t >= 0;) {
+ fave[t] = 0;
+ int[] rfreqt = rfreq[t];
+ for (int i = alphaSize; --i >= 0;) {
+ rfreqt[i] = 0;
+ }
+ }
+
+ nSelectors = 0;
+
+ for (int gs = 0; gs < this.nMTF;) {
+ /* Set group start & end marks. */
+
+ /*
+ * Calculate the cost of this group as coded by each of the
+ * coding tables.
+ */
+
+ final int ge = Math.min(gs + G_SIZE - 1, nMTFShadow - 1);
+
+ if (nGroups == N_GROUPS) {
+ // unrolled version of the else-block
+
+ short cost0 = 0;
+ short cost1 = 0;
+ short cost2 = 0;
+ short cost3 = 0;
+ short cost4 = 0;
+ short cost5 = 0;
+
+ for (int i = gs; i <= ge; i++) {
+ final int icv = sfmap[i];
+ cost0 += len_0[icv] & 0xff;
+ cost1 += len_1[icv] & 0xff;
+ cost2 += len_2[icv] & 0xff;
+ cost3 += len_3[icv] & 0xff;
+ cost4 += len_4[icv] & 0xff;
+ cost5 += len_5[icv] & 0xff;
+ }
+
+ cost[0] = cost0;
+ cost[1] = cost1;
+ cost[2] = cost2;
+ cost[3] = cost3;
+ cost[4] = cost4;
+ cost[5] = cost5;
+
+ } else {
+ for (int t = nGroups; --t >= 0;) {
+ cost[t] = 0;
+ }
+
+ for (int i = gs; i <= ge; i++) {
+ final int icv = sfmap[i];
+ for (int t = nGroups; --t >= 0;) {
+ cost[t] += len[t][icv] & 0xff;
+ }
+ }
+ }
+
+ /*
+ * Find the coding table which is best for this group, and
+ * record its identity in the selector table.
+ */
+ int bt = -1;
+ for (int t = nGroups, bc = 999999999; --t >= 0;) {
+ final int cost_t = cost[t];
+ if (cost_t < bc) {
+ bc = cost_t;
+ bt = t;
+ }
+ }
+
+ fave[bt]++;
+ selector[nSelectors] = (byte) bt;
+ nSelectors++;
+
+ /*
+ * Increment the symbol frequencies for the selected table.
+ */
+ final int[] rfreq_bt = rfreq[bt];
+ for (int i = gs; i <= ge; i++) {
+ rfreq_bt[sfmap[i]]++;
+ }
+
+ gs = ge + 1;
+ }
+
+ /*
+ * Recompute the tables based on the accumulated frequencies.
+ */
+ for (int t = 0; t < nGroups; t++) {
+ hbMakeCodeLengths(len[t], rfreq[t], this.data, alphaSize, 20);
+ }
+ }
+
+ return nSelectors;
+ }
+
+ private void sendMTFValues2(final int nGroups, final int nSelectors) {
+ // assert (nGroups < 8) : nGroups;
+
+ final Data dataShadow = this.data;
+ byte[] pos = dataShadow.sendMTFValues2_pos;
+
+ for (int i = nGroups; --i >= 0;) {
+ pos[i] = (byte) i;
+ }
+
+ for (int i = 0; i < nSelectors; i++) {
+ final byte ll_i = dataShadow.selector[i];
+ byte tmp = pos[0];
+ int j = 0;
+
+ while (ll_i != tmp) {
+ j++;
+ byte tmp2 = tmp;
+ tmp = pos[j];
+ pos[j] = tmp2;
+ }
+
+ pos[0] = tmp;
+ dataShadow.selectorMtf[i] = (byte) j;
+ }
+ }
+
+ private void sendMTFValues3(final int nGroups, final int alphaSize) {
+ int[][] code = this.data.sendMTFValues_code;
+ byte[][] len = this.data.sendMTFValues_len;
+
+ for (int t = 0; t < nGroups; t++) {
+ int minLen = 32;
+ int maxLen = 0;
+ final byte[] len_t = len[t];
+ for (int i = alphaSize; --i >= 0;) {
+ final int l = len_t[i] & 0xff;
+ if (l > maxLen) {
+ maxLen = l;
+ }
+ if (l < minLen) {
+ minLen = l;
+ }
+ }
+
+ // assert (maxLen <= 20) : maxLen;
+ // assert (minLen >= 1) : minLen;
+
+ hbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize);
+ }
+ }
+
+ private void sendMTFValues4() throws IOException {
+ final boolean[] inUse = this.data.inUse;
+ final boolean[] inUse16 = this.data.sentMTFValues4_inUse16;
+
+ for (int i = 16; --i >= 0;) {
+ inUse16[i] = false;
+ final int i16 = i * 16;
+ for (int j = 16; --j >= 0;) {
+ if (inUse[i16 + j]) {
+ inUse16[i] = true;
+ }
+ }
+ }
+
+ for (int i = 0; i < 16; i++) {
+ bsW(1, inUse16[i] ? 1 : 0);
+ }
+
+ final OutputStream outShadow = this.out;
+ int bsLiveShadow = this.bsLive;
+ int bsBuffShadow = this.bsBuff;
+
+ for (int i = 0; i < 16; i++) {
+ if (inUse16[i]) {
+ final int i16 = i * 16;
+ for (int j = 0; j < 16; j++) {
+ // inlined: bsW(1, inUse[i16 + j] ? 1 : 0);
+ while (bsLiveShadow >= 8) {
+ outShadow.write(bsBuffShadow >> 24); // write 8-bit
+ bsBuffShadow <<= 8;
+ bsLiveShadow -= 8;
+ }
+ if (inUse[i16 + j]) {
+ bsBuffShadow |= 1 << (32 - bsLiveShadow - 1);
+ }
+ bsLiveShadow++;
+ }
+ }
+ }
+
+ this.bsBuff = bsBuffShadow;
+ this.bsLive = bsLiveShadow;
+ }
+
+ private void sendMTFValues5(final int nGroups, final int nSelectors)
+ throws IOException {
+ bsW(3, nGroups);
+ bsW(15, nSelectors);
+
+ final OutputStream outShadow = this.out;
+ final byte[] selectorMtf = this.data.selectorMtf;
+
+ int bsLiveShadow = this.bsLive;
+ int bsBuffShadow = this.bsBuff;
+
+ for (int i = 0; i < nSelectors; i++) {
+ for (int j = 0, hj = selectorMtf[i] & 0xff; j < hj; j++) {
+ // inlined: bsW(1, 1);
+ while (bsLiveShadow >= 8) {
+ outShadow.write(bsBuffShadow >> 24);
+ bsBuffShadow <<= 8;
+ bsLiveShadow -= 8;
+ }
+ bsBuffShadow |= 1 << (32 - bsLiveShadow - 1);
+ bsLiveShadow++;
+ }
+
+ // inlined: bsW(1, 0);
+ while (bsLiveShadow >= 8) {
+ outShadow.write(bsBuffShadow >> 24);
+ bsBuffShadow <<= 8;
+ bsLiveShadow -= 8;
+ }
+ // bsBuffShadow |= 0 << (32 - bsLiveShadow - 1);
+ bsLiveShadow++;
+ }
+
+ this.bsBuff = bsBuffShadow;
+ this.bsLive = bsLiveShadow;
+ }
+
+ private void sendMTFValues6(final int nGroups, final int alphaSize)
+ throws IOException {
+ final byte[][] len = this.data.sendMTFValues_len;
+ final OutputStream outShadow = this.out;
+
+ int bsLiveShadow = this.bsLive;
+ int bsBuffShadow = this.bsBuff;
+
+ for (int t = 0; t < nGroups; t++) {
+ byte[] len_t = len[t];
+ int curr = len_t[0] & 0xff;
+
+ // inlined: bsW(5, curr);
+ while (bsLiveShadow >= 8) {
+ outShadow.write(bsBuffShadow >> 24); // write 8-bit
+ bsBuffShadow <<= 8;
+ bsLiveShadow -= 8;
+ }
+ bsBuffShadow |= curr << (32 - bsLiveShadow - 5);
+ bsLiveShadow += 5;
+
+ for (int i = 0; i < alphaSize; i++) {
+ int lti = len_t[i] & 0xff;
+ while (curr < lti) {
+ // inlined: bsW(2, 2);
+ while (bsLiveShadow >= 8) {
+ outShadow.write(bsBuffShadow >> 24); // write 8-bit
+ bsBuffShadow <<= 8;
+ bsLiveShadow -= 8;
+ }
+ bsBuffShadow |= 2 << (32 - bsLiveShadow - 2);
+ bsLiveShadow += 2;
+
+ curr++; /* 10 */
+ }
+
+ while (curr > lti) {
+ // inlined: bsW(2, 3);
+ while (bsLiveShadow >= 8) {
+ outShadow.write(bsBuffShadow >> 24); // write 8-bit
+ bsBuffShadow <<= 8;
+ bsLiveShadow -= 8;
+ }
+ bsBuffShadow |= 3 << (32 - bsLiveShadow - 2);
+ bsLiveShadow += 2;
+
+ curr--; /* 11 */
+ }
+
+ // inlined: bsW(1, 0);
+ while (bsLiveShadow >= 8) {
+ outShadow.write(bsBuffShadow >> 24); // write 8-bit
+ bsBuffShadow <<= 8;
+ bsLiveShadow -= 8;
+ }
+ // bsBuffShadow |= 0 << (32 - bsLiveShadow - 1);
+ bsLiveShadow++;
+ }
+ }
+
+ this.bsBuff = bsBuffShadow;
+ this.bsLive = bsLiveShadow;
+ }
+
+ private void sendMTFValues7() throws IOException {
+ final Data dataShadow = this.data;
+ final byte[][] len = dataShadow.sendMTFValues_len;
+ final int[][] code = dataShadow.sendMTFValues_code;
+ final OutputStream outShadow = this.out;
+ final byte[] selector = dataShadow.selector;
+ final char[] sfmap = dataShadow.sfmap;
+ final int nMTFShadow = this.nMTF;
+
+ int selCtr = 0;
+
+ int bsLiveShadow = this.bsLive;
+ int bsBuffShadow = this.bsBuff;
+
+ for (int gs = 0; gs < nMTFShadow;) {
+ final int ge = Math.min(gs + G_SIZE - 1, nMTFShadow - 1);
+ final int selector_selCtr = selector[selCtr] & 0xff;
+ final int[] code_selCtr = code[selector_selCtr];
+ final byte[] len_selCtr = len[selector_selCtr];
+
+ while (gs <= ge) {
+ final int sfmap_i = sfmap[gs];
+
+ //
+ // inlined: bsW(len_selCtr[sfmap_i] & 0xff,
+ // code_selCtr[sfmap_i]);
+ //
+ while (bsLiveShadow >= 8) {
+ outShadow.write(bsBuffShadow >> 24);
+ bsBuffShadow <<= 8;
+ bsLiveShadow -= 8;
+ }
+ final int n = len_selCtr[sfmap_i] & 0xFF;
+ bsBuffShadow |= code_selCtr[sfmap_i] << (32 - bsLiveShadow - n);
+ bsLiveShadow += n;
+
+ gs++;
+ }
+
+ gs = ge + 1;
+ selCtr++;
+ }
+
+ this.bsBuff = bsBuffShadow;
+ this.bsLive = bsLiveShadow;
+ }
+
+ private void moveToFrontCodeAndSend() throws IOException {
+ bsW(24, this.data.origPtr);
+ generateMTFValues();
+ sendMTFValues();
+ }
+
+ private void blockSort() {
+ blockSorter.blockSort(data, last);
+ }
+
+ /*
+ * Performs Move-To-Front on the Burrows-Wheeler transformed
+ * buffer, storing the MTFed data in data.sfmap in RUNA/RUNB
+ * run-length-encoded form.
+ *
+ * <p>Keeps track of byte frequencies in data.mtfFreq at the same time.</p>
+ */
+ private void generateMTFValues() {
+ final int lastShadow = this.last;
+ final Data dataShadow = this.data;
+ final boolean[] inUse = dataShadow.inUse;
+ final byte[] block = dataShadow.block;
+ final int[] fmap = dataShadow.fmap;
+ final char[] sfmap = dataShadow.sfmap;
+ final int[] mtfFreq = dataShadow.mtfFreq;
+ final byte[] unseqToSeq = dataShadow.unseqToSeq;
+ final byte[] yy = dataShadow.generateMTFValues_yy;
+
+ // make maps
+ int nInUseShadow = 0;
+ for (int i = 0; i < 256; i++) {
+ if (inUse[i]) {
+ unseqToSeq[i] = (byte) nInUseShadow;
+ nInUseShadow++;
+ }
+ }
+ this.nInUse = nInUseShadow;
+
+ final int eob = nInUseShadow + 1;
+
+ for (int i = eob; i >= 0; i--) {
+ mtfFreq[i] = 0;
+ }
+
+ for (int i = nInUseShadow; --i >= 0;) {
+ yy[i] = (byte) i;
+ }
+
+ int wr = 0;
+ int zPend = 0;
+
+ for (int i = 0; i <= lastShadow; i++) {
+ final byte ll_i = unseqToSeq[block[fmap[i]] & 0xff];
+ byte tmp = yy[0];
+ int j = 0;
+
+ while (ll_i != tmp) {
+ j++;
+ byte tmp2 = tmp;
+ tmp = yy[j];
+ yy[j] = tmp2;
+ }
+ yy[0] = tmp;
+
+ if (j == 0) {
+ zPend++;
+ } else {
+ if (zPend > 0) {
+ zPend--;
+ while (true) {
+ if ((zPend & 1) == 0) {
+ sfmap[wr] = RUNA;
+ wr++;
+ mtfFreq[RUNA]++;
+ } else {
+ sfmap[wr] = RUNB;
+ wr++;
+ mtfFreq[RUNB]++;
+ }
+
+ if (zPend >= 2) {
+ zPend = (zPend - 2) >> 1;
+ } else {
+ break;
+ }
+ }
+ zPend = 0;
+ }
+ sfmap[wr] = (char) (j + 1);
+ wr++;
+ mtfFreq[j + 1]++;
+ }
+ }
+
+ if (zPend > 0) {
+ zPend--;
+ while (true) {
+ if ((zPend & 1) == 0) {
+ sfmap[wr] = RUNA;
+ wr++;
+ mtfFreq[RUNA]++;
+ } else {
+ sfmap[wr] = RUNB;
+ wr++;
+ mtfFreq[RUNB]++;
+ }
+
+ if (zPend >= 2) {
+ zPend = (zPend - 2) >> 1;
+ } else {
+ break;
+ }
+ }
+ }
+
+ sfmap[wr] = (char) eob;
+ mtfFreq[eob]++;
+ this.nMTF = wr + 1;
+ }
+
+ static final class Data extends Object {
+
+ // with blockSize 900k
+ /* maps unsigned byte => "does it occur in block" */
+ final boolean[] inUse = new boolean[256]; // 256 byte
+ final byte[] unseqToSeq = new byte[256]; // 256 byte
+ final int[] mtfFreq = new int[MAX_ALPHA_SIZE]; // 1032 byte
+ final byte[] selector = new byte[MAX_SELECTORS]; // 18002 byte
+ final byte[] selectorMtf = new byte[MAX_SELECTORS]; // 18002 byte
+
+ final byte[] generateMTFValues_yy = new byte[256]; // 256 byte
+ final byte[][] sendMTFValues_len = new byte[N_GROUPS][MAX_ALPHA_SIZE]; // 1548
+ // byte
+ final int[][] sendMTFValues_rfreq = new int[N_GROUPS][MAX_ALPHA_SIZE]; // 6192
+ // byte
+ final int[] sendMTFValues_fave = new int[N_GROUPS]; // 24 byte
+ final short[] sendMTFValues_cost = new short[N_GROUPS]; // 12 byte
+ final int[][] sendMTFValues_code = new int[N_GROUPS][MAX_ALPHA_SIZE]; // 6192
+ // byte
+ final byte[] sendMTFValues2_pos = new byte[N_GROUPS]; // 6 byte
+ final boolean[] sentMTFValues4_inUse16 = new boolean[16]; // 16 byte
+
+ final int[] heap = new int[MAX_ALPHA_SIZE + 2]; // 1040 byte
+ final int[] weight = new int[MAX_ALPHA_SIZE * 2]; // 2064 byte
+ final int[] parent = new int[MAX_ALPHA_SIZE * 2]; // 2064 byte
+
+ // ------------
+ // 333408 byte
+
+ /* holds the RLEd block of original data starting at index 1.
+ * After sorting the last byte added to the buffer is at index
+ * 0. */
+ final byte[] block; // 900021 byte
+ /* maps index in Burrows-Wheeler transformed block => index of
+ * byte in original block */
+ final int[] fmap; // 3600000 byte
+ final char[] sfmap; // 3600000 byte
+ // ------------
+ // 8433529 byte
+ // ============
+
+ /**
+ * Index of original line in Burrows-Wheeler table.
+ *
+ * <p>This is the index in fmap that points to the last byte
+ * of the original data.</p>
+ */
+ int origPtr;
+
+ Data(int blockSize100k) {
+ super();
+
+ final int n = blockSize100k * BZip2Constants.baseBlockSize;
+ this.block = new byte[(n + 1 + NUM_OVERSHOOT_BYTES)];
+ this.fmap = new int[n];
+ this.sfmap = new char[2 * n];
+ }
+
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/CRC.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/CRC.java
new file mode 100644
index 00000000..0102c8e3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/bzip2/CRC.java
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Keiron Liddle, Aftex Software
+ * <keiron@aftexsw.com> to whom the Ant project is very grateful for his
+ * great code.
+ */
+
+package org.apache.tools.bzip2;
+
+/**
+ * A simple class the hold and calculate the CRC for sanity checking
+ * of the data.
+ *
+ */
+final class CRC {
+ static final int crc32Table[] = {
+ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
+ 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
+ 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+ 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+ 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
+ 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+ 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
+ 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
+ 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
+ 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
+ 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+ 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
+ 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+ 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+ 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
+ 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
+ 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+ 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+ 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
+ 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+ 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
+ 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
+ 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
+ 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
+ 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+ 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
+ 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+ 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+ 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
+ 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
+ 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+ 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+ 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
+ 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+ 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
+ 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
+ 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
+ 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
+ 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+ 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
+ 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+ 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+ 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
+ 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
+ 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+ 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+ 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
+ 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+ 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
+ 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
+ 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
+ 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
+ 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+ 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
+ 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+ 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+ 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
+ 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
+ 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+ 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+ };
+
+ CRC() {
+ initialiseCRC();
+ }
+
+ void initialiseCRC() {
+ globalCrc = 0xffffffff;
+ }
+
+ int getFinalCRC() {
+ return ~globalCrc;
+ }
+
+ int getGlobalCRC() {
+ return globalCrc;
+ }
+
+ void setGlobalCRC(int newCrc) {
+ globalCrc = newCrc;
+ }
+
+ void updateCRC(int inCh) {
+ int temp = (globalCrc >> 24) ^ inCh;
+ if (temp < 0) {
+ temp = 256 + temp;
+ }
+ globalCrc = (globalCrc << 8) ^ CRC.crc32Table[temp];
+ }
+
+ void updateCRC(int inCh, int repeat) {
+ int globalCrcShadow = this.globalCrc;
+ while (repeat-- > 0) {
+ int temp = (globalCrcShadow >> 24) ^ inCh;
+ globalCrcShadow = (globalCrcShadow << 8) ^ crc32Table[(temp >= 0)
+ ? temp
+ : (temp + 256)];
+ }
+ this.globalCrc = globalCrcShadow;
+ }
+
+ int globalCrc;
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/mail/ErrorInQuitException.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/mail/ErrorInQuitException.java
new file mode 100644
index 00000000..6f78a142
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/mail/ErrorInQuitException.java
@@ -0,0 +1,43 @@
+/*
+ * 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.tools.mail;
+
+import java.io.IOException;
+
+/**
+ * Specialized IOException that get thrown if SMTP's QUIT command fails.
+ *
+ * <p>This seems to happen with some version of MS Exchange that
+ * doesn't respond with a 221 code immediately. See <a
+ * href="http://nagoya.apache.org/bugzilla/show_bug.cgi?id=5273">Bug
+ * report 5273</a>.</p>
+ *
+ */
+public class ErrorInQuitException extends IOException {
+
+ /**
+ * Initialise from an IOException
+ *
+ * @param e the IO Exception.
+ */
+ public ErrorInQuitException(IOException e) {
+ super(e.getMessage());
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/mail/MailMessage.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/mail/MailMessage.java
new file mode 100644
index 00000000..b4173a96
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/mail/MailMessage.java
@@ -0,0 +1,526 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * The original version of this class was donated by Jason Hunter,
+ * who wrote the class as part of the com.oreilly.servlet
+ * package for his book "Java Servlet Programming" (O'Reilly).
+ * See http://www.servlets.com.
+ *
+ */
+
+package org.apache.tools.mail;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * A class to help send SMTP email.
+ * This class is an improvement on the sun.net.smtp.SmtpClient class
+ * found in the JDK. This version has extra functionality, and can be used
+ * with JVMs that did not extend from the JDK. It's not as robust as
+ * the JavaMail Standard Extension classes, but it's easier to use and
+ * easier to install, and has an Open Source license.
+ * <p>
+ * It can be used like this:
+ * <blockquote><pre>
+ * String mailhost = "localhost"; // or another mail host
+ * String from = "Mail Message Servlet &lt;MailMessage@server.com&gt;";
+ * String to = "to@you.com";
+ * String cc1 = "cc1@you.com";
+ * String cc2 = "cc2@you.com";
+ * String bcc = "bcc@you.com";
+ * &nbsp;
+ * MailMessage msg = new MailMessage(mailhost);
+ * msg.setPort(25);
+ * msg.from(from);
+ * msg.to(to);
+ * msg.cc(cc1);
+ * msg.cc(cc2);
+ * msg.bcc(bcc);
+ * msg.setSubject("Test subject");
+ * PrintStream out = msg.getPrintStream();
+ * &nbsp;
+ * Enumeration enum = req.getParameterNames();
+ * while (enum.hasMoreElements()) {
+ * String name = (String)enum.nextElement();
+ * String value = req.getParameter(name);
+ * out.println(name + " = " + value);
+ * }
+ * &nbsp;
+ * msg.sendAndClose();
+ * </pre></blockquote>
+ * <p>
+ * Be sure to set the from address, then set the recipient
+ * addresses, then set the subject and other headers, then get the
+ * PrintStream, then write the message, and finally send and close.
+ * The class does minimal error checking internally; it counts on the mail
+ * host to complain if there's any malformatted input or out of order
+ * execution.
+ * <p>
+ * An attachment mechanism based on RFC 1521 could be implemented on top of
+ * this class. In the meanwhile, JavaMail is the best solution for sending
+ * email with attachments.
+ * <p>
+ * Still to do:
+ * <ul>
+ * <li>Figure out how to close the connection in case of error
+ * </ul>
+ *
+ * @version 1.1, 2000/03/19, added angle brackets to address, helps some servers
+ * version 1.0, 1999/12/29
+ */
+public class MailMessage {
+
+ /** default mailhost */
+ public static final String DEFAULT_HOST = "localhost";
+
+ /** default port for SMTP: 25 */
+ public static final int DEFAULT_PORT = 25;
+
+ /** host name for the mail server */
+ private String host;
+
+ /** host port for the mail server */
+ private int port = DEFAULT_PORT;
+
+ /** sender email address */
+ private String from;
+
+ /** list of email addresses to reply to */
+ private Vector replyto;
+
+ /** list of email addresses to send to */
+ private Vector to;
+
+ /** list of email addresses to cc to */
+ private Vector cc;
+
+ /** headers to send in the mail */
+ private Vector headersKeys;
+ private Vector headersValues;
+
+ private MailPrintStream out;
+
+ private SmtpResponseReader in;
+
+ private Socket socket;
+ private static final int OK_READY = 220;
+ private static final int OK_HELO = 250;
+ private static final int OK_FROM = 250;
+ private static final int OK_RCPT_1 = 250;
+ private static final int OK_RCPT_2 = 251;
+ private static final int OK_DATA = 354;
+ private static final int OK_DOT = 250;
+ private static final int OK_QUIT = 221;
+
+ /**
+ * Constructs a new MailMessage to send an email.
+ * Use localhost as the mail server with port 25.
+ *
+ * @exception IOException if there's any problem contacting the mail server
+ */
+ public MailMessage() throws IOException {
+ this(DEFAULT_HOST, DEFAULT_PORT);
+ }
+
+ /**
+ * Constructs a new MailMessage to send an email.
+ * Use the given host as the mail server with port 25.
+ *
+ * @param host the mail server to use
+ * @exception IOException if there's any problem contacting the mail server
+ */
+ public MailMessage(String host) throws IOException {
+ this(host, DEFAULT_PORT);
+ }
+
+ /**
+ * Constructs a new MailMessage to send an email.
+ * Use the given host and port as the mail server.
+ *
+ * @param host the mail server to use
+ * @param port the port to connect to
+ * @exception IOException if there's any problem contacting the mail server
+ */
+ public MailMessage(String host, int port) throws IOException {
+ this.port = port;
+ this.host = host;
+ replyto = new Vector();
+ to = new Vector();
+ cc = new Vector();
+ headersKeys = new Vector();
+ headersValues = new Vector();
+ connect();
+ sendHelo();
+ }
+
+ /**
+ * Set the port to connect to the SMTP host.
+ * @param port the port to use for connection.
+ * @see #DEFAULT_PORT
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ * Sets the from address. Also sets the "From" header. This method should
+ * be called only once.
+ * @param from the from address
+ * @exception IOException if there's any problem reported by the mail server
+ */
+ public void from(String from) throws IOException {
+ sendFrom(from);
+ this.from = from;
+ }
+
+ /**
+ * Sets the replyto address
+ * This method may be
+ * called multiple times.
+ * @param rto the replyto address
+ *
+ */
+ public void replyto(String rto) {
+ this.replyto.addElement(rto);
+ }
+
+ /**
+ * Sets the to address. Also sets the "To" header. This method may be
+ * called multiple times.
+ *
+ * @param to the to address
+ * @exception IOException if there's any problem reported by the mail server
+ */
+ public void to(String to) throws IOException {
+ sendRcpt(to);
+ this.to.addElement(to);
+ }
+
+ /**
+ * Sets the cc address. Also sets the "Cc" header. This method may be
+ * called multiple times.
+ *
+ * @param cc the cc address
+ * @exception IOException if there's any problem reported by the mail server
+ */
+ public void cc(String cc) throws IOException {
+ sendRcpt(cc);
+ this.cc.addElement(cc);
+ }
+
+ /**
+ * Sets the bcc address. Does NOT set any header since it's a *blind* copy.
+ * This method may be called multiple times.
+ *
+ * @param bcc the bcc address
+ * @exception IOException if there's any problem reported by the mail server
+ */
+ public void bcc(String bcc) throws IOException {
+ sendRcpt(bcc);
+ // No need to keep track of Bcc'd addresses
+ }
+
+ /**
+ * Sets the subject of the mail message. Actually sets the "Subject"
+ * header.
+ * @param subj the subject of the mail message
+ */
+ public void setSubject(String subj) {
+ setHeader("Subject", subj);
+ }
+
+ /**
+ * Sets the named header to the given value. RFC 822 provides the rules for
+ * what text may constitute a header name and value.
+ * @param name name of the header
+ * @param value contents of the header
+ */
+ public void setHeader(String name, String value) {
+ // Blindly trust the user doesn't set any invalid headers
+ headersKeys.add(name);
+ headersValues.add(value);
+ }
+
+ /**
+ * Returns a PrintStream that can be used to write the body of the message.
+ * A stream is used since email bodies are byte-oriented. A writer can
+ * be wrapped on top if necessary for internationalization.
+ * This is actually done in Message.java
+ *
+ * @return a printstream containing the data and the headers of the email
+ * @exception IOException if there's any problem reported by the mail server
+ * @see org.apache.tools.ant.taskdefs.email.Message
+ */
+ public PrintStream getPrintStream() throws IOException {
+ setFromHeader();
+ setReplyToHeader();
+ setToHeader();
+ setCcHeader();
+ setHeader("X-Mailer", "org.apache.tools.mail.MailMessage (ant.apache.org)");
+ sendData();
+ flushHeaders();
+ return out;
+ }
+
+
+ // RFC 822 s4.1: "From:" header must be sent
+ // We rely on error checking by the MTA
+ void setFromHeader() {
+ setHeader("From", from);
+ }
+
+ // RFC 822 s4.1: "Reply-To:" header is optional
+ void setReplyToHeader() {
+ if (!replyto.isEmpty()) {
+ setHeader("Reply-To", vectorToList(replyto));
+ }
+ }
+
+ void setToHeader() {
+ if (!to.isEmpty()) {
+ setHeader("To", vectorToList(to));
+ }
+ }
+
+ void setCcHeader() {
+ if (!cc.isEmpty()) {
+ setHeader("Cc", vectorToList(cc));
+ }
+ }
+
+ String vectorToList(Vector v) {
+ StringBuffer buf = new StringBuffer();
+ Enumeration e = v.elements();
+ while (e.hasMoreElements()) {
+ buf.append(e.nextElement());
+ if (e.hasMoreElements()) {
+ buf.append(", ");
+ }
+ }
+ return buf.toString();
+ }
+
+ void flushHeaders() throws IOException {
+ // RFC 822 s4.1:
+ // "Header fields are NOT required to occur in any particular order,
+ // except that the message body MUST occur AFTER the headers"
+ // (the same section specifies a recommended order, which we ignore)
+ final int size = headersKeys.size();
+ for (int i = 0; i < size; i++) {
+ String name = (String) headersKeys.elementAt(i);
+ String value = (String) headersValues.elementAt(i);
+ out.println(name + ": " + value);
+ }
+ out.println();
+ out.flush();
+ }
+
+ /**
+ * Sends the message and closes the connection to the server.
+ * The MailMessage object cannot be reused.
+ *
+ * @exception IOException if there's any problem reported by the mail server
+ */
+ public void sendAndClose() throws IOException {
+ try {
+ sendDot();
+ sendQuit();
+ } finally {
+ disconnect();
+ }
+ }
+
+ // Make a limited attempt to extract a sanitized email address
+ // Prefer text in <brackets>, ignore anything in (parentheses)
+ static String sanitizeAddress(String s) {
+ int paramDepth = 0;
+ int start = 0;
+ int end = 0;
+ int len = s.length();
+
+ for (int i = 0; i < len; i++) {
+ char c = s.charAt(i);
+ if (c == '(') {
+ paramDepth++;
+ if (start == 0) {
+ end = i; // support "address (name)"
+ }
+ } else if (c == ')') {
+ paramDepth--;
+ if (end == 0) {
+ start = i + 1; // support "(name) address"
+ }
+ } else if (paramDepth == 0 && c == '<') {
+ start = i + 1;
+ } else if (paramDepth == 0 && c == '>') {
+ end = i;
+ }
+ }
+
+ if (end == 0) {
+ end = len;
+ }
+
+ return s.substring(start, end);
+ }
+
+ // * * * * * Raw protocol methods below here * * * * *
+
+ void connect() throws IOException {
+ socket = new Socket(host, port);
+ out = new MailPrintStream(
+ new BufferedOutputStream(
+ socket.getOutputStream()));
+ in = new SmtpResponseReader(socket.getInputStream());
+ getReady();
+ }
+
+ void getReady() throws IOException {
+ String response = in.getResponse();
+ int[] ok = {OK_READY};
+ if (!isResponseOK(response, ok)) {
+ throw new IOException(
+ "Didn't get introduction from server: " + response);
+ }
+ }
+ void sendHelo() throws IOException {
+ String local = InetAddress.getLocalHost().getHostName();
+ int[] ok = {OK_HELO};
+ send("HELO " + local, ok);
+ }
+ void sendFrom(String from) throws IOException {
+ int[] ok = {OK_FROM};
+ send("MAIL FROM: " + "<" + sanitizeAddress(from) + ">", ok);
+ }
+ void sendRcpt(String rcpt) throws IOException {
+ int[] ok = {OK_RCPT_1, OK_RCPT_2};
+ send("RCPT TO: " + "<" + sanitizeAddress(rcpt) + ">", ok);
+ }
+
+ void sendData() throws IOException {
+ int[] ok = {OK_DATA};
+ send("DATA", ok);
+ }
+
+ void sendDot() throws IOException {
+ int[] ok = {OK_DOT};
+ send("\r\n.", ok); // make sure dot is on new line
+ }
+
+ void sendQuit() throws IOException {
+ int[] ok = {OK_QUIT};
+ try {
+ send("QUIT", ok);
+ } catch (IOException e) {
+ throw new ErrorInQuitException(e);
+ }
+ }
+
+ void send(String msg, int[] ok) throws IOException {
+ out.rawPrint(msg + "\r\n"); // raw supports <CRLF>.<CRLF>
+ String response = in.getResponse();
+ if (!isResponseOK(response, ok)) {
+ throw new IOException("Unexpected reply to command: "
+ + msg + ": " + response);
+ }
+ }
+
+ boolean isResponseOK(String response, int[] ok) {
+ // Check that the response is one of the valid codes
+ for (int i = 0; i < ok.length; i++) {
+ if (response.startsWith("" + ok[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void disconnect() throws IOException {
+ if (out != null) {
+ out.close();
+ }
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+}
+
+/**
+ * This PrintStream subclass makes sure that <CRLF>. becomes <CRLF>..
+ * per RFC 821. It also ensures that new lines are always \r\n.
+*/
+class MailPrintStream extends PrintStream {
+
+ private int lastChar;
+
+ public MailPrintStream(OutputStream out) {
+ super(out, true); // deprecated, but email is byte-oriented
+ }
+
+ // Mac does \n\r, but that's tough to distinguish from Windows \r\n\r\n.
+ // Don't tackle that problem right now.
+ public void write(int b) {
+ if (b == '\n' && lastChar != '\r') {
+ rawWrite('\r'); // ensure always \r\n
+ rawWrite(b);
+ } else if (b == '.' && lastChar == '\n') {
+ rawWrite('.'); // add extra dot
+ rawWrite(b);
+ } else {
+ rawWrite(b);
+ }
+ lastChar = b;
+ }
+
+ public void write(byte[] buf, int off, int len) {
+ for (int i = 0; i < len; i++) {
+ write(buf[off + i]);
+ }
+ }
+
+ void rawWrite(int b) {
+ super.write(b);
+ }
+
+ void rawPrint(String s) {
+ int len = s.length();
+ for (int i = 0; i < len; i++) {
+ rawWrite(s.charAt(i));
+ }
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/mail/SmtpResponseReader.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/mail/SmtpResponseReader.java
new file mode 100644
index 00000000..c1693f49
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/mail/SmtpResponseReader.java
@@ -0,0 +1,106 @@
+/*
+ * 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.tools.mail;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+/**
+ * A wrapper around the raw input from the SMTP server that assembles
+ * multi line responses into a single String.
+ *
+ * <p>The same rules used here would apply to FTP and other Telnet
+ * based protocols as well.</p>
+ *
+ */
+public class SmtpResponseReader {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected BufferedReader reader = null;
+ // CheckStyle:VisibilityModifier ON
+ private StringBuffer result = new StringBuffer();
+
+ /**
+ * Wrap this input stream.
+ * @param in the stream to wrap.
+ */
+ public SmtpResponseReader(InputStream in) {
+ reader = new BufferedReader(new InputStreamReader(in));
+ }
+
+ /**
+ * Read until the server indicates that the response is complete.
+ *
+ * @return Responsecode (3 digits) + Blank + Text from all
+ * response line concatenated (with blanks replacing the \r\n
+ * sequences).
+ * @throws IOException on error.
+ */
+ public String getResponse() throws IOException {
+ result.setLength(0);
+ String line = reader.readLine();
+ // CheckStyle:MagicNumber OFF
+ if (line != null && line.length() >= 3) {
+ result.append(line.substring(0, 3));
+ result.append(" ");
+ }
+ // CheckStyle:MagicNumber ON
+
+ while (line != null) {
+ append(line);
+ if (!hasMoreLines(line)) {
+ break;
+ }
+ line = reader.readLine();
+ }
+ return result.toString().trim();
+ }
+
+ /**
+ * Closes the underlying stream.
+ * @throws IOException on error.
+ */
+ public void close() throws IOException {
+ reader.close();
+ }
+
+ /**
+ * Should we expect more input?
+ * @param line the line to check.
+ * @return true if there are more lines to check.
+ */
+ protected boolean hasMoreLines(String line) {
+ // CheckStyle:MagicNumber OFF
+ return line.length() > 3 && line.charAt(3) == '-';
+ // CheckStyle:MagicNumber ON
+ }
+
+ /**
+ * Append the text from this line of the resonse.
+ */
+ private void append(String line) {
+ // CheckStyle:MagicNumber OFF
+ if (line.length() > 4) {
+ result.append(line.substring(4));
+ result.append(" ");
+ }
+ // CheckStyle:MagicNumber ON
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarArchiveSparseEntry.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarArchiveSparseEntry.java
new file mode 100644
index 00000000..2e76fb69
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarArchiveSparseEntry.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.tools.tar;
+
+import java.io.IOException;
+
+/**
+ * This class represents a sparse entry in a Tar archive.
+ *
+ * <p>
+ * The C structure for a sparse entry is:
+ * <pre>
+ * struct posix_header {
+ * struct sparse sp[21]; // TarConstants.SPARSELEN_GNU_SPARSE - offset 0
+ * char isextended; // TarConstants.ISEXTENDEDLEN_GNU_SPARSE - offset 504
+ * };
+ * </pre>
+ * Whereas, "struct sparse" is:
+ * <pre>
+ * struct sparse {
+ * char offset[12]; // offset 0
+ * char numbytes[12]; // offset 12
+ * };
+ * </pre>
+ */
+
+public class TarArchiveSparseEntry implements TarConstants {
+ /** If an extension sparse header follows. */
+ private boolean isExtended;
+
+ /**
+ * Construct an entry from an archive's header bytes. File is set
+ * to null.
+ *
+ * @param headerBuf The header bytes from a tar archive entry.
+ * @throws IOException on unknown format
+ */
+ public TarArchiveSparseEntry(byte[] headerBuf) throws IOException {
+ int offset = 0;
+ offset += SPARSELEN_GNU_SPARSE;
+ isExtended = TarUtils.parseBoolean(headerBuf, offset);
+ }
+
+ public boolean isExtended() {
+ return isExtended;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarBuffer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarBuffer.java
new file mode 100644
index 00000000..b089d9bf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarBuffer.java
@@ -0,0 +1,463 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Timothy Gerard Endres
+ * (time@ice.com) to whom the Ant project is very grateful for his great code.
+ */
+
+package org.apache.tools.tar;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+/**
+ * The TarBuffer class implements the tar archive concept
+ * of a buffered input stream. This concept goes back to the
+ * days of blocked tape drives and special io devices. In the
+ * Java universe, the only real function that this class
+ * performs is to ensure that files have the correct "block"
+ * size, or other tars will complain.
+ * <p>
+ * You should never have a need to access this class directly.
+ * TarBuffers are created by Tar IO Streams.
+ *
+ */
+
+public class TarBuffer {
+
+ /** Default record size */
+ public static final int DEFAULT_RCDSIZE = (512);
+
+ /** Default block size */
+ public static final int DEFAULT_BLKSIZE = (DEFAULT_RCDSIZE * 20);
+
+ private InputStream inStream;
+ private OutputStream outStream;
+ private final int blockSize;
+ private final int recordSize;
+ private final int recsPerBlock;
+ private final byte[] blockBuffer;
+
+ private int currBlkIdx;
+ private int currRecIdx;
+ private boolean debug;
+
+ /**
+ * Constructor for a TarBuffer on an input stream.
+ * @param inStream the input stream to use
+ */
+ public TarBuffer(InputStream inStream) {
+ this(inStream, TarBuffer.DEFAULT_BLKSIZE);
+ }
+
+ /**
+ * Constructor for a TarBuffer on an input stream.
+ * @param inStream the input stream to use
+ * @param blockSize the block size to use
+ */
+ public TarBuffer(InputStream inStream, int blockSize) {
+ this(inStream, blockSize, TarBuffer.DEFAULT_RCDSIZE);
+ }
+
+ /**
+ * Constructor for a TarBuffer on an input stream.
+ * @param inStream the input stream to use
+ * @param blockSize the block size to use
+ * @param recordSize the record size to use
+ */
+ public TarBuffer(InputStream inStream, int blockSize, int recordSize) {
+ this(inStream, null, blockSize, recordSize);
+ }
+
+ /**
+ * Constructor for a TarBuffer on an output stream.
+ * @param outStream the output stream to use
+ */
+ public TarBuffer(OutputStream outStream) {
+ this(outStream, TarBuffer.DEFAULT_BLKSIZE);
+ }
+
+ /**
+ * Constructor for a TarBuffer on an output stream.
+ * @param outStream the output stream to use
+ * @param blockSize the block size to use
+ */
+ public TarBuffer(OutputStream outStream, int blockSize) {
+ this(outStream, blockSize, TarBuffer.DEFAULT_RCDSIZE);
+ }
+
+ /**
+ * Constructor for a TarBuffer on an output stream.
+ * @param outStream the output stream to use
+ * @param blockSize the block size to use
+ * @param recordSize the record size to use
+ */
+ public TarBuffer(OutputStream outStream, int blockSize, int recordSize) {
+ this(null, outStream, blockSize, recordSize);
+ }
+
+ /**
+ * Private constructor to perform common setup.
+ */
+ private TarBuffer(InputStream inStream, OutputStream outStream, int blockSize, int recordSize) {
+ this.inStream = inStream;
+ this.outStream = outStream;
+ this.debug = false;
+ this.blockSize = blockSize;
+ this.recordSize = recordSize;
+ this.recsPerBlock = (this.blockSize / this.recordSize);
+ this.blockBuffer = new byte[this.blockSize];
+
+ if (this.inStream != null) {
+ this.currBlkIdx = -1;
+ this.currRecIdx = this.recsPerBlock;
+ } else {
+ this.currBlkIdx = 0;
+ this.currRecIdx = 0;
+ }
+ }
+
+ /**
+ * Get the TAR Buffer's block size. Blocks consist of multiple records.
+ * @return the block size
+ */
+ public int getBlockSize() {
+ return this.blockSize;
+ }
+
+ /**
+ * Get the TAR Buffer's record size.
+ * @return the record size
+ */
+ public int getRecordSize() {
+ return this.recordSize;
+ }
+
+ /**
+ * Set the debugging flag for the buffer.
+ *
+ * @param debug If true, print debugging output.
+ */
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ }
+
+ /**
+ * Determine if an archive record indicate End of Archive. End of
+ * archive is indicated by a record that consists entirely of null bytes.
+ *
+ * @param record The record data to check.
+ * @return true if the record data is an End of Archive
+ */
+ public boolean isEOFRecord(byte[] record) {
+ for (int i = 0, sz = getRecordSize(); i < sz; ++i) {
+ if (record[i] != 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Skip over a record on the input stream.
+ * @throws IOException on error
+ */
+ public void skipRecord() throws IOException {
+ if (debug) {
+ System.err.println("SkipRecord: recIdx = " + currRecIdx
+ + " blkIdx = " + currBlkIdx);
+ }
+
+ if (inStream == null) {
+ throw new IOException("reading (via skip) from an output buffer");
+ }
+
+ if (currRecIdx >= recsPerBlock && !readBlock()) {
+ return; // UNDONE
+ }
+
+ currRecIdx++;
+ }
+
+ /**
+ * Read a record from the input stream and return the data.
+ *
+ * @return The record data.
+ * @throws IOException on error
+ */
+ public byte[] readRecord() throws IOException {
+ if (debug) {
+ System.err.println("ReadRecord: recIdx = " + currRecIdx
+ + " blkIdx = " + currBlkIdx);
+ }
+
+ if (inStream == null) {
+ if (outStream == null) {
+ throw new IOException("input buffer is closed");
+ }
+ throw new IOException("reading from an output buffer");
+ }
+
+ if (currRecIdx >= recsPerBlock && !readBlock()) {
+ return null;
+ }
+
+ byte[] result = new byte[recordSize];
+
+ System.arraycopy(blockBuffer,
+ (currRecIdx * recordSize), result, 0,
+ recordSize);
+
+ currRecIdx++;
+
+ return result;
+ }
+
+ /**
+ * @return false if End-Of-File, else true
+ */
+ private boolean readBlock() throws IOException {
+ if (debug) {
+ System.err.println("ReadBlock: blkIdx = " + currBlkIdx);
+ }
+
+ if (inStream == null) {
+ throw new IOException("reading from an output buffer");
+ }
+
+ currRecIdx = 0;
+
+ int offset = 0;
+ int bytesNeeded = blockSize;
+
+ while (bytesNeeded > 0) {
+ long numBytes = inStream.read(blockBuffer, offset,
+ bytesNeeded);
+
+ //
+ // NOTE
+ // We have fit EOF, and the block is not full!
+ //
+ // This is a broken archive. It does not follow the standard
+ // blocking algorithm. However, because we are generous, and
+ // it requires little effort, we will simply ignore the error
+ // and continue as if the entire block were read. This does
+ // not appear to break anything upstream. We used to return
+ // false in this case.
+ //
+ // Thanks to 'Yohann.Roussel@alcatel.fr' for this fix.
+ //
+ if (numBytes == -1) {
+ if (offset == 0) {
+ // Ensure that we do not read gigabytes of zeros
+ // for a corrupt tar file.
+ // See http://issues.apache.org/bugzilla/show_bug.cgi?id=39924
+ return false;
+ }
+ // However, just leaving the unread portion of the buffer dirty does
+ // cause problems in some cases. This problem is described in
+ // http://issues.apache.org/bugzilla/show_bug.cgi?id=29877
+ //
+ // The solution is to fill the unused portion of the buffer with zeros.
+
+ Arrays.fill(blockBuffer, offset, offset + bytesNeeded, (byte) 0);
+
+ break;
+ }
+
+ offset += numBytes;
+ bytesNeeded -= numBytes;
+
+ if (numBytes != blockSize) {
+ if (debug) {
+ System.err.println("ReadBlock: INCOMPLETE READ "
+ + numBytes + " of " + blockSize
+ + " bytes read.");
+ }
+ }
+ }
+
+ currBlkIdx++;
+
+ return true;
+ }
+
+ /**
+ * Get the current block number, zero based.
+ *
+ * @return The current zero based block number.
+ */
+ public int getCurrentBlockNum() {
+ return currBlkIdx;
+ }
+
+ /**
+ * Get the current record number, within the current block, zero based.
+ * Thus, current offset = (currentBlockNum * recsPerBlk) + currentRecNum.
+ *
+ * @return The current zero based record number.
+ */
+ public int getCurrentRecordNum() {
+ return currRecIdx - 1;
+ }
+
+ /**
+ * Write an archive record to the archive.
+ *
+ * @param record The record data to write to the archive.
+ * @throws IOException on error
+ */
+ public void writeRecord(byte[] record) throws IOException {
+ if (debug) {
+ System.err.println("WriteRecord: recIdx = " + currRecIdx
+ + " blkIdx = " + currBlkIdx);
+ }
+
+ if (outStream == null) {
+ if (inStream == null){
+ throw new IOException("Output buffer is closed");
+ }
+ throw new IOException("writing to an input buffer");
+ }
+
+ if (record.length != recordSize) {
+ throw new IOException("record to write has length '"
+ + record.length
+ + "' which is not the record size of '"
+ + recordSize + "'");
+ }
+
+ if (currRecIdx >= recsPerBlock) {
+ writeBlock();
+ }
+
+ System.arraycopy(record, 0, blockBuffer,
+ (currRecIdx * recordSize),
+ recordSize);
+
+ currRecIdx++;
+ }
+
+ /**
+ * Write an archive record to the archive, where the record may be
+ * inside of a larger array buffer. The buffer must be "offset plus
+ * record size" long.
+ *
+ * @param buf The buffer containing the record data to write.
+ * @param offset The offset of the record data within buf.
+ * @throws IOException on error
+ */
+ public void writeRecord(byte[] buf, int offset) throws IOException {
+ if (debug) {
+ System.err.println("WriteRecord: recIdx = " + currRecIdx
+ + " blkIdx = " + currBlkIdx);
+ }
+
+ if (outStream == null) {
+ if (inStream == null){
+ throw new IOException("Output buffer is closed");
+ }
+ throw new IOException("writing to an input buffer");
+ }
+
+ if ((offset + recordSize) > buf.length) {
+ throw new IOException("record has length '" + buf.length
+ + "' with offset '" + offset
+ + "' which is less than the record size of '"
+ + recordSize + "'");
+ }
+
+ if (currRecIdx >= recsPerBlock) {
+ writeBlock();
+ }
+
+ System.arraycopy(buf, offset, blockBuffer,
+ (currRecIdx * recordSize),
+ recordSize);
+
+ currRecIdx++;
+ }
+
+ /**
+ * Write a TarBuffer block to the archive.
+ */
+ private void writeBlock() throws IOException {
+ if (debug) {
+ System.err.println("WriteBlock: blkIdx = " + currBlkIdx);
+ }
+
+ if (outStream == null) {
+ throw new IOException("writing to an input buffer");
+ }
+
+ outStream.write(blockBuffer, 0, blockSize);
+ outStream.flush();
+
+ currRecIdx = 0;
+ currBlkIdx++;
+ Arrays.fill(blockBuffer, (byte) 0);
+ }
+
+ /**
+ * Flush the current data block if it has any data in it.
+ */
+ void flushBlock() throws IOException {
+ if (debug) {
+ System.err.println("TarBuffer.flushBlock() called.");
+ }
+
+ if (outStream == null) {
+ throw new IOException("writing to an input buffer");
+ }
+
+ if (currRecIdx > 0) {
+ writeBlock();
+ }
+ }
+
+ /**
+ * Close the TarBuffer. If this is an output buffer, also flush the
+ * current block before closing.
+ * @throws IOException on error
+ */
+ public void close() throws IOException {
+ if (debug) {
+ System.err.println("TarBuffer.closeBuffer().");
+ }
+
+ if (outStream != null) {
+ flushBlock();
+
+ if (outStream != System.out
+ && outStream != System.err) {
+ outStream.close();
+
+ outStream = null;
+ }
+ } else if (inStream != null) {
+ if (inStream != System.in) {
+ inStream.close();
+ }
+ inStream = null;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarConstants.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarConstants.java
new file mode 100644
index 00000000..06d0faca
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarConstants.java
@@ -0,0 +1,293 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Timothy Gerard Endres
+ * (time@ice.com) to whom the Ant project is very grateful for his great code.
+ */
+
+package org.apache.tools.tar;
+
+/**
+ * This interface contains all the definitions used in the package.
+ *
+ * For tar formats (FORMAT_OLDGNU, FORMAT_POSIX, etc.) see GNU tar
+ * <I>tar.h</I> type <I>enum archive_format</I>
+ */
+// CheckStyle:InterfaceIsTypeCheck OFF (bc)
+public interface TarConstants {
+
+ /**
+ * GNU format as per before tar 1.12.
+ */
+ int FORMAT_OLDGNU = 2;
+
+ /**
+ * Pure Posix format.
+ */
+ int FORMAT_POSIX = 3;
+
+ /**
+ * The length of the name field in a header buffer.
+ */
+ int NAMELEN = 100;
+
+ /**
+ * The length of the mode field in a header buffer.
+ */
+ int MODELEN = 8;
+
+ /**
+ * The length of the user id field in a header buffer.
+ */
+ int UIDLEN = 8;
+
+ /**
+ * The length of the group id field in a header buffer.
+ */
+ int GIDLEN = 8;
+
+ /**
+ * The maximum value of gid/uid in a tar archive which can
+ * be expressed in octal char notation (that's 7 sevens, octal).
+ */
+ long MAXID = 07777777L;
+
+ /**
+ * The length of the checksum field in a header buffer.
+ */
+ int CHKSUMLEN = 8;
+
+ /**
+ * The length of the size field in a header buffer.
+ * Includes the trailing space or NUL.
+ */
+ int SIZELEN = 12;
+
+ /**
+ * The maximum size of a file in a tar archive
+ * which can be expressed in octal char notation (that's 11 sevens, octal).
+ */
+ long MAXSIZE = 077777777777L;
+
+ /** Offset of start of magic field within header record */
+ int MAGIC_OFFSET = 257;
+ /**
+ * The length of the magic field in a header buffer including the version.
+ */
+ int MAGICLEN = 8;
+
+ /**
+ * The length of the magic field in a header buffer.
+ */
+ int PURE_MAGICLEN = 6;
+
+ /** Offset of start of magic field within header record */
+ int VERSION_OFFSET = 263;
+ /**
+ * Previously this was regarded as part of "magic" field, but it
+ * is separate.
+ */
+ int VERSIONLEN = 2;
+
+ /**
+ * The length of the modification time field in a header buffer.
+ */
+ int MODTIMELEN = 12;
+
+ /**
+ * The length of the user name field in a header buffer.
+ */
+ int UNAMELEN = 32;
+
+ /**
+ * The length of the group name field in a header buffer.
+ */
+ int GNAMELEN = 32;
+
+ /**
+ * The length of each of the device fields (major and minor) in a header buffer.
+ */
+ int DEVLEN = 8;
+
+ /**
+ * Length of the prefix field.
+ *
+ */
+ int PREFIXLEN = 155;
+
+ /**
+ * The length of the access time field in an old GNU header buffer.
+ *
+ */
+ int ATIMELEN_GNU = 12;
+
+ /**
+ * The length of the created time field in an old GNU header buffer.
+ *
+ */
+ int CTIMELEN_GNU = 12;
+
+ /**
+ * The length of the multivolume start offset field in an old GNU header buffer.
+ *
+ */
+ int OFFSETLEN_GNU = 12;
+
+ /**
+ * The length of the long names field in an old GNU header buffer.
+ *
+ */
+ int LONGNAMESLEN_GNU = 4;
+
+ /**
+ * The length of the padding field in an old GNU header buffer.
+ *
+ */
+ int PAD2LEN_GNU = 1;
+
+ /**
+ * The sum of the length of all sparse headers in an old GNU header buffer.
+ *
+ */
+ int SPARSELEN_GNU = 96;
+
+ /**
+ * The length of the is extension field in an old GNU header buffer.
+ *
+ */
+ int ISEXTENDEDLEN_GNU = 1;
+
+ /**
+ * The length of the real size field in an old GNU header buffer.
+ *
+ */
+ int REALSIZELEN_GNU = 12;
+
+ /**
+ * The sum of the length of all sparse headers in a sparse header buffer.
+ *
+ */
+ int SPARSELEN_GNU_SPARSE = 504;
+
+ /**
+ * The length of the is extension field in a sparse header buffer.
+ *
+ */
+ int ISEXTENDEDLEN_GNU_SPARSE = 1;
+
+ /**
+ * LF_ constants represent the "link flag" of an entry, or more commonly,
+ * the "entry type". This is the "old way" of indicating a normal file.
+ */
+ byte LF_OLDNORM = 0;
+
+ /**
+ * Normal file type.
+ */
+ byte LF_NORMAL = (byte) '0';
+
+ /**
+ * Link file type.
+ */
+ byte LF_LINK = (byte) '1';
+
+ /**
+ * Symbolic link file type.
+ */
+ byte LF_SYMLINK = (byte) '2';
+
+ /**
+ * Character device file type.
+ */
+ byte LF_CHR = (byte) '3';
+
+ /**
+ * Block device file type.
+ */
+ byte LF_BLK = (byte) '4';
+
+ /**
+ * Directory file type.
+ */
+ byte LF_DIR = (byte) '5';
+
+ /**
+ * FIFO (pipe) file type.
+ */
+ byte LF_FIFO = (byte) '6';
+
+ /**
+ * Contiguous file type.
+ */
+ byte LF_CONTIG = (byte) '7';
+
+ /**
+ * Identifies the *next* file on the tape as having a long linkname.
+ */
+ byte LF_GNUTYPE_LONGLINK = (byte) 'K';
+
+ /**
+ * Identifies the *next* file on the tape as having a long name.
+ */
+ byte LF_GNUTYPE_LONGNAME = (byte) 'L';
+
+ /**
+ * Sparse file type.
+ */
+ byte LF_GNUTYPE_SPARSE = (byte) 'S';
+
+ // See "http://www.opengroup.org/onlinepubs/009695399/utilities/pax.html#tag_04_100_13_02"
+
+ /**
+ * Identifies the entry as a Pax extended header.
+ */
+ byte LF_PAX_EXTENDED_HEADER_LC = (byte) 'x';
+
+ /**
+ * Identifies the entry as a Pax extended header (SunOS tar -E).
+ */
+ byte LF_PAX_EXTENDED_HEADER_UC = (byte) 'X';
+
+ /**
+ * Identifies the entry as a Pax global extended header.
+ */
+ byte LF_PAX_GLOBAL_EXTENDED_HEADER = (byte) 'g';
+
+ String TMAGIC = "ustar";
+
+ /**
+ * The magic tag representing a POSIX tar archive.
+ */
+ String MAGIC_POSIX = "ustar\0";
+ String VERSION_POSIX = "00";
+
+ /**
+ * The magic tag representing a GNU tar archive.
+ */
+ String GNU_TMAGIC = "ustar ";
+ // Appear to be two possible GNU versions
+ String VERSION_GNU_SPACE = " \0";
+ String VERSION_GNU_ZERO = "0\0";
+
+ /**
+ * The name of the GNU tar entry which contains a long name.
+ */
+ String GNU_LONGLINK = "././@LongLink";
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarEntry.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarEntry.java
new file mode 100644
index 00000000..a9c96f13
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarEntry.java
@@ -0,0 +1,1149 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Timothy Gerard Endres
+ * (time@ice.com) to whom the Ant project is very grateful for his great code.
+ */
+
+package org.apache.tools.tar;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+import java.util.Locale;
+
+import org.apache.tools.zip.ZipEncoding;
+
+/**
+ * This class represents an entry in a Tar archive. It consists
+ * of the entry's header, as well as the entry's File. Entries
+ * can be instantiated in one of three ways, depending on how
+ * they are to be used.
+ * <p>
+ * TarEntries that are created from the header bytes read from
+ * an archive are instantiated with the TarEntry( byte[] )
+ * constructor. These entries will be used when extracting from
+ * or listing the contents of an archive. These entries have their
+ * header filled in using the header bytes. They also set the File
+ * to null, since they reference an archive entry not a file.
+ * <p>
+ * TarEntries that are created from Files that are to be written
+ * into an archive are instantiated with the TarEntry( File )
+ * constructor. These entries have their header filled in using
+ * the File's information. They also keep a reference to the File
+ * for convenience when writing entries.
+ * <p>
+ * Finally, TarEntries can be constructed from nothing but a name.
+ * This allows the programmer to construct the entry by hand, for
+ * instance when only an InputStream is available for writing to
+ * the archive, and the header information is constructed from
+ * other information. In this case the header fields are set to
+ * defaults and the File is set to null.
+ *
+ * <p>
+ * The C structure for a Tar Entry's header is:
+ * <pre>
+ * struct header {
+ * char name[NAMSIZ];
+ * char mode[8];
+ * char uid[8];
+ * char gid[8];
+ * char size[12];
+ * char mtime[12];
+ * char chksum[8];
+ * char linkflag;
+ * char linkname[NAMSIZ];
+ * char magic[8];
+ * char uname[TUNMLEN];
+ * char gname[TGNMLEN];
+ * char devmajor[8];
+ * char devminor[8];
+ * } header;
+ * All unused bytes are set to null.
+ * New-style GNU tar files are slightly different from the above.
+ * For values of size larger than 077777777777L (11 7s)
+ * or uid and gid larger than 07777777L (7 7s)
+ * the sign bit of the first byte is set, and the rest of the
+ * field is the binary representation of the number.
+ * See TarUtils.parseOctalOrBinary.
+ * </pre>
+ *
+ * <p>
+ * The C structure for a old GNU Tar Entry's header is:
+ * <pre>
+ * struct oldgnu_header {
+ * char unused_pad1[345]; // TarConstants.PAD1LEN_GNU - offset 0
+ * char atime[12]; // TarConstants.ATIMELEN_GNU - offset 345
+ * char ctime[12]; // TarConstants.CTIMELEN_GNU - offset 357
+ * char offset[12]; // TarConstants.OFFSETLEN_GNU - offset 369
+ * char longnames[4]; // TarConstants.LONGNAMESLEN_GNU - offset 381
+ * char unused_pad2; // TarConstants.PAD2LEN_GNU - offset 385
+ * struct sparse sp[4]; // TarConstants.SPARSELEN_GNU - offset 386
+ * char isextended; // TarConstants.ISEXTENDEDLEN_GNU - offset 482
+ * char realsize[12]; // TarConstants.REALSIZELEN_GNU - offset 483
+ * char unused_pad[17]; // TarConstants.PAD3LEN_GNU - offset 495
+ * };
+ * </pre>
+ * Whereas, "struct sparse" is:
+ * <pre>
+ * struct sparse {
+ * char offset[12]; // offset 0
+ * char numbytes[12]; // offset 12
+ * };
+ * </pre>
+ *
+ */
+
+public class TarEntry implements TarConstants {
+ /** The entry's name. */
+ private String name;
+
+ /** The entry's permission mode. */
+ private int mode;
+
+ /** The entry's user id. */
+ private long userId;
+
+ /** The entry's group id. */
+ private long groupId;
+
+ /** The entry's size. */
+ private long size;
+
+ /** The entry's modification time. */
+ private long modTime;
+
+ /** The entry's link flag. */
+ private byte linkFlag;
+
+ /** The entry's link name. */
+ private String linkName;
+
+ /** The entry's magic tag. */
+ private String magic;
+ /** The version of the format */
+ private String version;
+
+ /** The entry's user name. */
+ private String userName;
+
+ /** The entry's group name. */
+ private String groupName;
+
+ /** The entry's major device number. */
+ private int devMajor;
+
+ /** The entry's minor device number. */
+ private int devMinor;
+
+ /** If an extension sparse header follows. */
+ private boolean isExtended;
+
+ /** The entry's real size in case of a sparse file. */
+ private long realSize;
+
+ /** The entry's file reference */
+ private File file;
+
+ /** Maximum length of a user's name in the tar file */
+ public static final int MAX_NAMELEN = 31;
+
+ /** Default permissions bits for directories */
+ public static final int DEFAULT_DIR_MODE = 040755;
+
+ /** Default permissions bits for files */
+ public static final int DEFAULT_FILE_MODE = 0100644;
+
+ /** Convert millis to seconds */
+ public static final int MILLIS_PER_SECOND = 1000;
+
+ /**
+ * Construct an empty entry and prepares the header values.
+ */
+ private TarEntry() {
+ this.magic = MAGIC_POSIX;
+ this.version = VERSION_POSIX;
+ this.name = "";
+ this.linkName = "";
+
+ String user = System.getProperty("user.name", "");
+
+ if (user.length() > MAX_NAMELEN) {
+ user = user.substring(0, MAX_NAMELEN);
+ }
+
+ this.userId = 0;
+ this.groupId = 0;
+ this.userName = user;
+ this.groupName = "";
+ this.file = null;
+ }
+
+ /**
+ * Construct an entry with only a name. This allows the programmer
+ * to construct the entry's header "by hand". File is set to null.
+ *
+ * @param name the entry name
+ */
+ public TarEntry(String name) {
+ this(name, false);
+ }
+
+ /**
+ * Construct an entry with only a name. This allows the programmer
+ * to construct the entry's header "by hand". File is set to null.
+ *
+ * @param name the entry name
+ * @param preserveLeadingSlashes whether to allow leading slashes
+ * in the name.
+ */
+ public TarEntry(String name, boolean preserveLeadingSlashes) {
+ this();
+
+ name = normalizeFileName(name, preserveLeadingSlashes);
+ boolean isDir = name.endsWith("/");
+
+ this.devMajor = 0;
+ this.devMinor = 0;
+ this.name = name;
+ this.mode = isDir ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE;
+ this.linkFlag = isDir ? LF_DIR : LF_NORMAL;
+ this.userId = 0;
+ this.groupId = 0;
+ this.size = 0;
+ this.modTime = (new Date()).getTime() / MILLIS_PER_SECOND;
+ this.linkName = "";
+ this.userName = "";
+ this.groupName = "";
+ }
+
+ /**
+ * Construct an entry with a name and a link flag.
+ *
+ * @param name the entry name
+ * @param linkFlag the entry link flag.
+ */
+ public TarEntry(String name, byte linkFlag) {
+ this(name);
+ this.linkFlag = linkFlag;
+ if (linkFlag == LF_GNUTYPE_LONGNAME) {
+ magic = GNU_TMAGIC;
+ version = VERSION_GNU_SPACE;
+ }
+ }
+
+ /**
+ * Construct an entry for a file. File is set to file, and the
+ * header is constructed from information from the file.
+ * The name is set from the normalized file path.
+ *
+ * @param file The file that the entry represents.
+ */
+ public TarEntry(File file) {
+ this(file, file.getPath());
+ }
+
+ /**
+ * Construct an entry for a file. File is set to file, and the
+ * header is constructed from information from the file.
+ *
+ * @param file The file that the entry represents.
+ * @param fileName the name to be used for the entry.
+ */
+ public TarEntry(File file, String fileName) {
+ this();
+
+ String normalizedName = normalizeFileName(fileName, false);
+ this.file = file;
+
+ this.linkName = "";
+
+ if (file.isDirectory()) {
+ this.mode = DEFAULT_DIR_MODE;
+ this.linkFlag = LF_DIR;
+
+ int nameLength = normalizedName.length();
+ if (nameLength == 0 || normalizedName.charAt(nameLength - 1) != '/') {
+ this.name = normalizedName + "/";
+ } else {
+ this.name = normalizedName;
+ }
+ this.size = 0;
+ } else {
+ this.mode = DEFAULT_FILE_MODE;
+ this.linkFlag = LF_NORMAL;
+ this.size = file.length();
+ this.name = normalizedName;
+ }
+
+ this.modTime = file.lastModified() / MILLIS_PER_SECOND;
+ this.devMajor = 0;
+ this.devMinor = 0;
+ }
+
+ /**
+ * Construct an entry from an archive's header bytes. File is set
+ * to null.
+ *
+ * @param headerBuf The header bytes from a tar archive entry.
+ * @throws IllegalArgumentException if any of the numeric fields have an invalid format
+ */
+ public TarEntry(byte[] headerBuf) {
+ this();
+ parseTarHeader(headerBuf);
+ }
+
+ /**
+ * Construct an entry from an archive's header bytes. File is set
+ * to null.
+ *
+ * @param headerBuf The header bytes from a tar archive entry.
+ * @param encoding encoding to use for file names
+ * @throws IllegalArgumentException if any of the numeric fields have an invalid format
+ * @throws IOException if an error occurs during reading the archive
+ */
+ public TarEntry(byte[] headerBuf, ZipEncoding encoding)
+ throws IOException {
+ this();
+ parseTarHeader(headerBuf, encoding);
+ }
+
+ /**
+ * Determine if the two entries are equal. Equality is determined
+ * by the header names being equal.
+ *
+ * @param it Entry to be checked for equality.
+ * @return True if the entries are equal.
+ */
+ public boolean equals(TarEntry it) {
+ return getName().equals(it.getName());
+ }
+
+ /**
+ * Determine if the two entries are equal. Equality is determined
+ * by the header names being equal.
+ *
+ * @param it Entry to be checked for equality.
+ * @return True if the entries are equal.
+ */
+ @Override
+ public boolean equals(Object it) {
+ if (it == null || getClass() != it.getClass()) {
+ return false;
+ }
+ return equals((TarEntry) it);
+ }
+
+ /**
+ * Hashcodes are based on entry names.
+ *
+ * @return the entry hashcode
+ */
+ @Override
+ public int hashCode() {
+ return getName().hashCode();
+ }
+
+ /**
+ * Determine if the given entry is a descendant of this entry.
+ * Descendancy is determined by the name of the descendant
+ * starting with this entry's name.
+ *
+ * @param desc Entry to be checked as a descendant of this.
+ * @return True if entry is a descendant of this.
+ */
+ public boolean isDescendent(TarEntry desc) {
+ return desc.getName().startsWith(getName());
+ }
+
+ /**
+ * Get this entry's name.
+ *
+ * @return This entry's name.
+ */
+ public String getName() {
+ return name.toString();
+ }
+
+ /**
+ * Set this entry's name.
+ *
+ * @param name This entry's new name.
+ */
+ public void setName(String name) {
+ this.name = normalizeFileName(name, false);
+ }
+
+ /**
+ * Set the mode for this entry
+ *
+ * @param mode the mode for this entry
+ */
+ public void setMode(int mode) {
+ this.mode = mode;
+ }
+
+ /**
+ * Get this entry's link name.
+ *
+ * @return This entry's link name.
+ */
+ public String getLinkName() {
+ return linkName.toString();
+ }
+
+ /**
+ * Set this entry's link name.
+ *
+ * @param link the link name to use.
+ */
+ public void setLinkName(String link) {
+ this.linkName = link;
+ }
+
+ /**
+ * Get this entry's user id.
+ *
+ * @return This entry's user id.
+ * @deprecated use #getLongUserId instead as user ids can be
+ * bigger than {@link Integer#MAX_VALUE}
+ */
+ @Deprecated
+ public int getUserId() {
+ return (int) (userId & 0xffffffff);
+ }
+
+ /**
+ * Set this entry's user id.
+ *
+ * @param userId This entry's new user id.
+ */
+ public void setUserId(int userId) {
+ setUserId((long) userId);
+ }
+
+ /**
+ * Get this entry's user id.
+ *
+ * @return This entry's user id.
+ * @since 1.9.5
+ */
+ public long getLongUserId() {
+ return userId;
+ }
+
+ /**
+ * Set this entry's user id.
+ *
+ * @param userId This entry's new user id.
+ * @since 1.9.5
+ */
+ public void setUserId(long userId) {
+ this.userId = userId;
+ }
+
+ /**
+ * Get this entry's group id.
+ *
+ * @return This entry's group id.
+ * @deprecated use #getLongGroupId instead as group ids can be
+ * bigger than {@link Integer#MAX_VALUE}
+ */
+ @Deprecated
+ public int getGroupId() {
+ return (int) (groupId & 0xffffffff);
+ }
+
+ /**
+ * Set this entry's group id.
+ *
+ * @param groupId This entry's new group id.
+ */
+ public void setGroupId(int groupId) {
+ setGroupId((long) groupId);
+ }
+
+ /**
+ * Get this entry's group id.
+ *
+ * @return This entry's group id.
+ * @since 1.9.5
+ */
+ public long getLongGroupId() {
+ return groupId;
+ }
+
+ /**
+ * Set this entry's group id.
+ *
+ * @param groupId This entry's new group id.
+ * @since 1.9.5
+ */
+ public void setGroupId(long groupId) {
+ this.groupId = groupId;
+ }
+
+ /**
+ * Get this entry's user name.
+ *
+ * @return This entry's user name.
+ */
+ public String getUserName() {
+ return userName.toString();
+ }
+
+ /**
+ * Set this entry's user name.
+ *
+ * @param userName This entry's new user name.
+ */
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ /**
+ * Get this entry's group name.
+ *
+ * @return This entry's group name.
+ */
+ public String getGroupName() {
+ return groupName.toString();
+ }
+
+ /**
+ * Set this entry's group name.
+ *
+ * @param groupName This entry's new group name.
+ */
+ public void setGroupName(String groupName) {
+ this.groupName = groupName;
+ }
+
+ /**
+ * Convenience method to set this entry's group and user ids.
+ *
+ * @param userId This entry's new user id.
+ * @param groupId This entry's new group id.
+ */
+ public void setIds(int userId, int groupId) {
+ setUserId(userId);
+ setGroupId(groupId);
+ }
+
+ /**
+ * Convenience method to set this entry's group and user names.
+ *
+ * @param userName This entry's new user name.
+ * @param groupName This entry's new group name.
+ */
+ public void setNames(String userName, String groupName) {
+ setUserName(userName);
+ setGroupName(groupName);
+ }
+
+ /**
+ * Set this entry's modification time. The parameter passed
+ * to this method is in "Java time".
+ *
+ * @param time This entry's new modification time.
+ */
+ public void setModTime(long time) {
+ modTime = time / MILLIS_PER_SECOND;
+ }
+
+ /**
+ * Set this entry's modification time.
+ *
+ * @param time This entry's new modification time.
+ */
+ public void setModTime(Date time) {
+ modTime = time.getTime() / MILLIS_PER_SECOND;
+ }
+
+ /**
+ * Set this entry's modification time.
+ *
+ * @return time This entry's new modification time.
+ */
+ public Date getModTime() {
+ return new Date(modTime * MILLIS_PER_SECOND);
+ }
+
+ /**
+ * Get this entry's file.
+ *
+ * @return This entry's file.
+ */
+ public File getFile() {
+ return file;
+ }
+
+ /**
+ * Get this entry's mode.
+ *
+ * @return This entry's mode.
+ */
+ public int getMode() {
+ return mode;
+ }
+
+ /**
+ * Get this entry's file size.
+ *
+ * @return This entry's file size.
+ */
+ public long getSize() {
+ return size;
+ }
+
+ /**
+ * Set this entry's file size.
+ *
+ * @param size This entry's new file size.
+ * @throws IllegalArgumentException if the size is &lt; 0.
+ */
+ public void setSize(long size) {
+ if (size < 0) {
+ throw new IllegalArgumentException("Size is out of range: " + size);
+ }
+ this.size = size;
+ }
+
+ /**
+ * Get this entry's major device number.
+ *
+ * @return This entry's major device number.
+ */
+ public int getDevMajor() {
+ return devMajor;
+ }
+
+ /**
+ * Set this entry's major device number.
+ *
+ * @param devNo This entry's major device number.
+ * @throws IllegalArgumentException if the devNo is &lt; 0.
+ */
+ public void setDevMajor(int devNo) {
+ if (devNo < 0) {
+ throw new IllegalArgumentException("Major device number is out of "
+ + "range: " + devNo);
+ }
+ this.devMajor = devNo;
+ }
+
+ /**
+ * Get this entry's minor device number.
+ *
+ * @return This entry's minor device number.
+ */
+ public int getDevMinor() {
+ return devMinor;
+ }
+
+ /**
+ * Set this entry's minor device number.
+ *
+ * @param devNo This entry's minor device number.
+ * @throws IllegalArgumentException if the devNo is &lt; 0.
+ */
+ public void setDevMinor(int devNo) {
+ if (devNo < 0) {
+ throw new IllegalArgumentException("Minor device number is out of "
+ + "range: " + devNo);
+ }
+ this.devMinor = devNo;
+ }
+
+ /**
+ * Indicates in case of a sparse file if an extension sparse header
+ * follows.
+ *
+ * @return true if an extension sparse header follows.
+ */
+ public boolean isExtended() {
+ return isExtended;
+ }
+
+ /**
+ * Get this entry's real file size in case of a sparse file.
+ *
+ * @return This entry's real file size.
+ */
+ public long getRealSize() {
+ return realSize;
+ }
+
+ /**
+ * Indicate if this entry is a GNU sparse block.
+ *
+ * @return true if this is a sparse extension provided by GNU tar
+ */
+ public boolean isGNUSparse() {
+ return linkFlag == LF_GNUTYPE_SPARSE;
+ }
+
+ /**
+ * Indicate if this entry is a GNU long linkname block
+ *
+ * @return true if this is a long name extension provided by GNU tar
+ */
+ public boolean isGNULongLinkEntry() {
+ return linkFlag == LF_GNUTYPE_LONGLINK
+ && name.equals(GNU_LONGLINK);
+ }
+
+ /**
+ * Indicate if this entry is a GNU long name block
+ *
+ * @return true if this is a long name extension provided by GNU tar
+ */
+ public boolean isGNULongNameEntry() {
+ return linkFlag == LF_GNUTYPE_LONGNAME
+ && name.equals(GNU_LONGLINK);
+ }
+
+ /**
+ * Check if this is a Pax header.
+ *
+ * @return {@code true} if this is a Pax header.
+ */
+ public boolean isPaxHeader() {
+ return linkFlag == LF_PAX_EXTENDED_HEADER_LC
+ || linkFlag == LF_PAX_EXTENDED_HEADER_UC;
+ }
+
+ /**
+ * Check if this is a Pax header.
+ *
+ * @return {@code true} if this is a Pax header.
+ */
+ public boolean isGlobalPaxHeader() {
+ return linkFlag == LF_PAX_GLOBAL_EXTENDED_HEADER;
+ }
+
+ /**
+ * Return whether or not this entry represents a directory.
+ *
+ * @return True if this entry is a directory.
+ */
+ public boolean isDirectory() {
+ if (file != null) {
+ return file.isDirectory();
+ }
+
+ if (linkFlag == LF_DIR) {
+ return true;
+ }
+
+ if (getName().endsWith("/")) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if this is a "normal file".
+ * @return <i>true</i> if it is a 'normal' file
+ */
+ public boolean isFile() {
+ if (file != null) {
+ return file.isFile();
+ }
+ if (linkFlag == LF_OLDNORM || linkFlag == LF_NORMAL) {
+ return true;
+ }
+ return !getName().endsWith("/");
+ }
+
+ /**
+ * Check if this is a symbolic link entry.
+ * @return <i>true</i> if it is a symlink
+ */
+ public boolean isSymbolicLink() {
+ return linkFlag == LF_SYMLINK;
+ }
+
+ /**
+ * Check if this is a link entry.
+ * @return <i>true</i> if it is a link
+ */
+ public boolean isLink() {
+ return linkFlag == LF_LINK;
+ }
+
+ /**
+ * Check if this is a character device entry.
+ * @return <i>true</i> if it is a character device entry
+ */
+ public boolean isCharacterDevice() {
+ return linkFlag == LF_CHR;
+ }
+
+ /**
+ * @return <i>true</i> if this is a block device entry.
+ */
+ public boolean isBlockDevice() {
+ return linkFlag == LF_BLK;
+ }
+
+ /**
+ * @return <i>true</i> if this is a FIFO (pipe) entry.
+ */
+ public boolean isFIFO() {
+ return linkFlag == LF_FIFO;
+ }
+
+ /**
+ * If this entry represents a file, and the file is a directory, return
+ * an array of TarEntries for this entry's children.
+ *
+ * @return An array of TarEntry's for this entry's children.
+ */
+ public TarEntry[] getDirectoryEntries() {
+ if (file == null || !file.isDirectory()) {
+ return new TarEntry[0];
+ }
+
+ String[] list = file.list();
+ TarEntry[] result = new TarEntry[list.length];
+
+ for (int i = 0; i < list.length; ++i) {
+ result[i] = new TarEntry(new File(file, list[i]));
+ }
+
+ return result;
+ }
+
+ /**
+ * Write an entry's header information to a header buffer.
+ *
+ * <p>This method does not use the star/GNU tar/BSD tar extensions.</p>
+ *
+ * @param outbuf The tar entry header buffer to fill in.
+ */
+ public void writeEntryHeader(byte[] outbuf) {
+ try {
+ writeEntryHeader(outbuf, TarUtils.DEFAULT_ENCODING, false);
+ } catch (IOException ex) {
+ try {
+ writeEntryHeader(outbuf, TarUtils.FALLBACK_ENCODING, false);
+ } catch (IOException ex2) {
+ // impossible
+ throw new RuntimeException(ex2);
+ }
+ }
+ }
+
+ /**
+ * Write an entry's header information to a header buffer.
+ *
+ * @param outbuf The tar entry header buffer to fill in.
+ * @param encoding encoding to use when writing the file name.
+ * @param starMode whether to use the star/GNU tar/BSD tar
+ * extension for numeric fields if their value doesn't fit in the
+ * maximum size of standard tar archives
+ * @throws IOException if an error occurs while writing the archive
+ */
+ public void writeEntryHeader(byte[] outbuf, ZipEncoding encoding,
+ boolean starMode) throws IOException {
+ int offset = 0;
+
+ offset = TarUtils.formatNameBytes(name, outbuf, offset, NAMELEN,
+ encoding);
+ offset = writeEntryHeaderField(mode, outbuf, offset, MODELEN, starMode);
+ offset = writeEntryHeaderField(userId, outbuf, offset, UIDLEN,
+ starMode);
+ offset = writeEntryHeaderField(groupId, outbuf, offset, GIDLEN,
+ starMode);
+ offset = writeEntryHeaderField(size, outbuf, offset, SIZELEN, starMode);
+ offset = writeEntryHeaderField(modTime, outbuf, offset, MODTIMELEN,
+ starMode);
+
+ int csOffset = offset;
+
+ for (int c = 0; c < CHKSUMLEN; ++c) {
+ outbuf[offset++] = (byte) ' ';
+ }
+
+ outbuf[offset++] = linkFlag;
+ offset = TarUtils.formatNameBytes(linkName, outbuf, offset, NAMELEN,
+ encoding);
+ offset = TarUtils.formatNameBytes(magic, outbuf, offset, PURE_MAGICLEN);
+ offset = TarUtils.formatNameBytes(version, outbuf, offset, VERSIONLEN);
+ offset = TarUtils.formatNameBytes(userName, outbuf, offset, UNAMELEN,
+ encoding);
+ offset = TarUtils.formatNameBytes(groupName, outbuf, offset, GNAMELEN,
+ encoding);
+ offset = writeEntryHeaderField(devMajor, outbuf, offset, DEVLEN,
+ starMode);
+ offset = writeEntryHeaderField(devMinor, outbuf, offset, DEVLEN,
+ starMode);
+
+ while (offset < outbuf.length) {
+ outbuf[offset++] = 0;
+ }
+
+ long chk = TarUtils.computeCheckSum(outbuf);
+
+ TarUtils.formatCheckSumOctalBytes(chk, outbuf, csOffset, CHKSUMLEN);
+ }
+
+ private int writeEntryHeaderField(long value, byte[] outbuf, int offset,
+ int length, boolean starMode) {
+ if (!starMode && (value < 0
+ || value >= (1L << (3 * (length - 1))))) {
+ // value doesn't fit into field when written as octal
+ // number, will be written to PAX header or causes an
+ // error
+ return TarUtils.formatLongOctalBytes(0, outbuf, offset, length);
+ }
+ return TarUtils.formatLongOctalOrBinaryBytes(value, outbuf, offset,
+ length);
+ }
+
+ /**
+ * Parse an entry's header information from a header buffer.
+ *
+ * @param header The tar entry header buffer to get information from.
+ * @throws IllegalArgumentException if any of the numeric fields have an invalid format
+ */
+ public void parseTarHeader(byte[] header) {
+ try {
+ parseTarHeader(header, TarUtils.DEFAULT_ENCODING);
+ } catch (IOException ex) {
+ try {
+ parseTarHeader(header, TarUtils.DEFAULT_ENCODING, true);
+ } catch (IOException ex2) {
+ // not really possible
+ throw new RuntimeException(ex2);
+ }
+ }
+ }
+
+ /**
+ * Parse an entry's header information from a header buffer.
+ *
+ * @param header The tar entry header buffer to get information from.
+ * @param encoding encoding to use for file names
+ * @throws IllegalArgumentException if any of the numeric fields
+ * have an invalid format
+ * @throws IOException if an error occurs while reading the archive
+ */
+ public void parseTarHeader(byte[] header, ZipEncoding encoding)
+ throws IOException {
+ parseTarHeader(header, encoding, false);
+ }
+
+ private void parseTarHeader(byte[] header, ZipEncoding encoding,
+ final boolean oldStyle)
+ throws IOException {
+ int offset = 0;
+
+ name = oldStyle ? TarUtils.parseName(header, offset, NAMELEN)
+ : TarUtils.parseName(header, offset, NAMELEN, encoding);
+ offset += NAMELEN;
+ mode = (int) TarUtils.parseOctalOrBinary(header, offset, MODELEN);
+ offset += MODELEN;
+ userId = (int) TarUtils.parseOctalOrBinary(header, offset, UIDLEN);
+ offset += UIDLEN;
+ groupId = (int) TarUtils.parseOctalOrBinary(header, offset, GIDLEN);
+ offset += GIDLEN;
+ size = TarUtils.parseOctalOrBinary(header, offset, SIZELEN);
+ offset += SIZELEN;
+ modTime = TarUtils.parseOctalOrBinary(header, offset, MODTIMELEN);
+ offset += MODTIMELEN;
+ offset += CHKSUMLEN;
+ linkFlag = header[offset++];
+ linkName = oldStyle ? TarUtils.parseName(header, offset, NAMELEN)
+ : TarUtils.parseName(header, offset, NAMELEN, encoding);
+ offset += NAMELEN;
+ magic = TarUtils.parseName(header, offset, PURE_MAGICLEN);
+ offset += PURE_MAGICLEN;
+ version = TarUtils.parseName(header, offset, VERSIONLEN);
+ offset += VERSIONLEN;
+ userName = oldStyle ? TarUtils.parseName(header, offset, UNAMELEN)
+ : TarUtils.parseName(header, offset, UNAMELEN, encoding);
+ offset += UNAMELEN;
+ groupName = oldStyle ? TarUtils.parseName(header, offset, GNAMELEN)
+ : TarUtils.parseName(header, offset, GNAMELEN, encoding);
+ offset += GNAMELEN;
+ devMajor = (int) TarUtils.parseOctalOrBinary(header, offset, DEVLEN);
+ offset += DEVLEN;
+ devMinor = (int) TarUtils.parseOctalOrBinary(header, offset, DEVLEN);
+ offset += DEVLEN;
+
+ int type = evaluateType(header);
+ switch (type) {
+ case FORMAT_OLDGNU: {
+ offset += ATIMELEN_GNU;
+ offset += CTIMELEN_GNU;
+ offset += OFFSETLEN_GNU;
+ offset += LONGNAMESLEN_GNU;
+ offset += PAD2LEN_GNU;
+ offset += SPARSELEN_GNU;
+ isExtended = TarUtils.parseBoolean(header, offset);
+ offset += ISEXTENDEDLEN_GNU;
+ realSize = TarUtils.parseOctal(header, offset, REALSIZELEN_GNU);
+ offset += REALSIZELEN_GNU;
+ break;
+ }
+ case FORMAT_POSIX:
+ default: {
+ String prefix = oldStyle
+ ? TarUtils.parseName(header, offset, PREFIXLEN)
+ : TarUtils.parseName(header, offset, PREFIXLEN, encoding);
+ // SunOS tar -E does not add / to directory names, so fix
+ // up to be consistent
+ if (isDirectory() && !name.endsWith("/")) {
+ name = name + "/";
+ }
+ if (prefix.length() > 0) {
+ name = prefix + "/" + name;
+ }
+ }
+ }
+ }
+
+ /**
+ * Strips Windows' drive letter as well as any leading slashes,
+ * turns path separators into forward slahes.
+ */
+ private static String normalizeFileName(String fileName,
+ boolean preserveLeadingSlashes) {
+ String osname = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
+
+ if (osname != null) {
+
+ // Strip off drive letters!
+ // REVIEW Would a better check be "(File.separator == '\')"?
+
+ if (osname.startsWith("windows")) {
+ if (fileName.length() > 2) {
+ char ch1 = fileName.charAt(0);
+ char ch2 = fileName.charAt(1);
+
+ if (ch2 == ':'
+ && ((ch1 >= 'a' && ch1 <= 'z')
+ || (ch1 >= 'A' && ch1 <= 'Z'))) {
+ fileName = fileName.substring(2);
+ }
+ }
+ } else if (osname.indexOf("netware") > -1) {
+ int colon = fileName.indexOf(':');
+ if (colon != -1) {
+ fileName = fileName.substring(colon + 1);
+ }
+ }
+ }
+
+ fileName = fileName.replace(File.separatorChar, '/');
+
+ // No absolute pathnames
+ // Windows (and Posix?) paths can start with "\\NetworkDrive\",
+ // so we loop on starting /'s.
+ while (!preserveLeadingSlashes && fileName.startsWith("/")) {
+ fileName = fileName.substring(1);
+ }
+ return fileName;
+ }
+
+ /**
+ * Evaluate an entry's header format from a header buffer.
+ *
+ * @param header The tar entry header buffer to evaluate the format for.
+ * @return format type
+ */
+ private int evaluateType(byte[] header) {
+ if (matchAsciiBuffer(GNU_TMAGIC, header, MAGIC_OFFSET, PURE_MAGICLEN)) {
+ return FORMAT_OLDGNU;
+ }
+ if (matchAsciiBuffer(MAGIC_POSIX, header, MAGIC_OFFSET, PURE_MAGICLEN)) {
+ return FORMAT_POSIX;
+ }
+ return 0;
+ }
+
+ /**
+ * Check if buffer contents matches Ascii String.
+ *
+ * @param expected
+ * @param buffer
+ * @param offset
+ * @param length
+ * @return {@code true} if buffer is the same as the expected string
+ */
+ private static boolean matchAsciiBuffer(String expected, byte[] buffer,
+ int offset, int length) {
+ byte[] buffer1;
+ try {
+ buffer1 = expected.getBytes("ASCII");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e); // Should not happen
+ }
+ return isEqual(buffer1, 0, buffer1.length, buffer, offset, length,
+ false);
+ }
+
+ /**
+ * Compare byte buffers, optionally ignoring trailing nulls
+ *
+ * @param buffer1
+ * @param offset1
+ * @param length1
+ * @param buffer2
+ * @param offset2
+ * @param length2
+ * @param ignoreTrailingNulls
+ * @return {@code true} if buffer1 and buffer2 have same contents, having regard to trailing nulls
+ */
+ private static boolean isEqual(
+ final byte[] buffer1, final int offset1, final int length1,
+ final byte[] buffer2, final int offset2, final int length2,
+ boolean ignoreTrailingNulls) {
+ int minLen=length1 < length2 ? length1 : length2;
+ for (int i=0; i < minLen; i++) {
+ if (buffer1[offset1+i] != buffer2[offset2+i]) {
+ return false;
+ }
+ }
+ if (length1 == length2) {
+ return true;
+ }
+ if (ignoreTrailingNulls) {
+ if (length1 > length2){
+ for(int i = length2; i < length1; i++){
+ if (buffer1[offset1+i] != 0) {
+ return false;
+ }
+ }
+ } else {
+ for (int i = length1; i < length2; i++){
+ if (buffer2[offset2+i] != 0) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarInputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarInputStream.java
new file mode 100644
index 00000000..57827a2c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarInputStream.java
@@ -0,0 +1,660 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Timothy Gerard Endres
+ * (time@ice.com) to whom the Ant project is very grateful for his great code.
+ */
+
+package org.apache.tools.tar;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.tools.zip.ZipEncoding;
+import org.apache.tools.zip.ZipEncodingHelper;
+
+/**
+ * The TarInputStream reads a UNIX tar archive as an InputStream.
+ * methods are provided to position at each successive entry in
+ * the archive, and the read each entry as a normal input stream
+ * using read().
+ *
+ */
+public class TarInputStream extends FilterInputStream {
+ private static final int SMALL_BUFFER_SIZE = 256;
+ private static final int BUFFER_SIZE = 8 * 1024;
+ private static final int LARGE_BUFFER_SIZE = 32 * 1024;
+ private static final int BYTE_MASK = 0xFF;
+
+ private final byte[] SKIP_BUF = new byte[BUFFER_SIZE];
+ private final byte[] SMALL_BUF = new byte[SMALL_BUFFER_SIZE];
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected boolean debug;
+ protected boolean hasHitEOF;
+ protected long entrySize;
+ protected long entryOffset;
+ protected byte[] readBuf;
+ protected TarBuffer buffer;
+ protected TarEntry currEntry;
+
+ /**
+ * This contents of this array is not used at all in this class,
+ * it is only here to avoid repreated object creation during calls
+ * to the no-arg read method.
+ */
+ protected byte[] oneBuf;
+
+ // CheckStyle:VisibilityModifier ON
+
+ private final ZipEncoding encoding;
+
+ /**
+ * Constructor for TarInputStream.
+ * @param is the input stream to use
+ */
+ public TarInputStream(InputStream is) {
+ this(is, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE);
+ }
+
+ /**
+ * Constructor for TarInputStream.
+ * @param is the input stream to use
+ * @param encoding name of the encoding to use for file names
+ */
+ public TarInputStream(InputStream is, String encoding) {
+ this(is, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE, encoding);
+ }
+
+ /**
+ * Constructor for TarInputStream.
+ * @param is the input stream to use
+ * @param blockSize the block size to use
+ */
+ public TarInputStream(InputStream is, int blockSize) {
+ this(is, blockSize, TarBuffer.DEFAULT_RCDSIZE);
+ }
+
+ /**
+ * Constructor for TarInputStream.
+ * @param is the input stream to use
+ * @param blockSize the block size to use
+ * @param encoding name of the encoding to use for file names
+ */
+ public TarInputStream(InputStream is, int blockSize, String encoding) {
+ this(is, blockSize, TarBuffer.DEFAULT_RCDSIZE, encoding);
+ }
+
+ /**
+ * Constructor for TarInputStream.
+ * @param is the input stream to use
+ * @param blockSize the block size to use
+ * @param recordSize the record size to use
+ */
+ public TarInputStream(InputStream is, int blockSize, int recordSize) {
+ this(is, blockSize, recordSize, null);
+ }
+
+ /**
+ * Constructor for TarInputStream.
+ * @param is the input stream to use
+ * @param blockSize the block size to use
+ * @param recordSize the record size to use
+ * @param encoding name of the encoding to use for file names
+ */
+ public TarInputStream(InputStream is, int blockSize, int recordSize,
+ String encoding) {
+ super(is);
+ this.buffer = new TarBuffer(is, blockSize, recordSize);
+ this.readBuf = null;
+ this.oneBuf = new byte[1];
+ this.debug = false;
+ this.hasHitEOF = false;
+ this.encoding = ZipEncodingHelper.getZipEncoding(encoding);
+ }
+
+ /**
+ * Sets the debugging flag.
+ *
+ * @param debug True to turn on debugging.
+ */
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ buffer.setDebug(debug);
+ }
+
+ /**
+ * Closes this stream. Calls the TarBuffer's close() method.
+ * @throws IOException on error
+ */
+ @Override
+ public void close() throws IOException {
+ buffer.close();
+ }
+
+ /**
+ * Get the record size being used by this stream's TarBuffer.
+ *
+ * @return The TarBuffer record size.
+ */
+ public int getRecordSize() {
+ return buffer.getRecordSize();
+ }
+
+ /**
+ * Get the available data that can be read from the current
+ * entry in the archive. This does not indicate how much data
+ * is left in the entire archive, only in the current entry.
+ * This value is determined from the entry's size header field
+ * and the amount of data already read from the current entry.
+ * Integer.MAX_VALUE is returned in case more than Integer.MAX_VALUE
+ * bytes are left in the current entry in the archive.
+ *
+ * @return The number of available bytes for the current entry.
+ * @throws IOException for signature
+ */
+ @Override
+ public int available() throws IOException {
+ if (entrySize - entryOffset > Integer.MAX_VALUE) {
+ return Integer.MAX_VALUE;
+ }
+ return (int) (entrySize - entryOffset);
+ }
+
+ /**
+ * Skip bytes in the input buffer. This skips bytes in the
+ * current entry's data, not the entire archive, and will
+ * stop at the end of the current entry's data if the number
+ * to skip extends beyond that point.
+ *
+ * @param numToSkip The number of bytes to skip.
+ * @return the number actually skipped
+ * @throws IOException on error
+ */
+ @Override
+ public long skip(long numToSkip) throws IOException {
+ // REVIEW
+ // This is horribly inefficient, but it ensures that we
+ // properly skip over bytes via the TarBuffer...
+ //
+ long skip = numToSkip;
+ while (skip > 0) {
+ int realSkip = (int) (skip > SKIP_BUF.length
+ ? SKIP_BUF.length : skip);
+ int numRead = read(SKIP_BUF, 0, realSkip);
+ if (numRead == -1) {
+ break;
+ }
+ skip -= numRead;
+ }
+ return (numToSkip - skip);
+ }
+
+ /**
+ * Since we do not support marking just yet, we return false.
+ *
+ * @return False.
+ */
+ @Override
+ public boolean markSupported() {
+ return false;
+ }
+
+ /**
+ * Since we do not support marking just yet, we do nothing.
+ *
+ * @param markLimit The limit to mark.
+ */
+ @Override
+ public void mark(int markLimit) {
+ }
+
+ /**
+ * Since we do not support marking just yet, we do nothing.
+ */
+ @Override
+ public void reset() {
+ }
+
+ /**
+ * Get the next entry in this tar archive. This will skip
+ * over any remaining data in the current entry, if there
+ * is one, and place the input stream at the header of the
+ * next entry, and read the header and instantiate a new
+ * TarEntry from the header bytes and return that entry.
+ * If there are no more entries in the archive, null will
+ * be returned to indicate that the end of the archive has
+ * been reached.
+ *
+ * @return The next TarEntry in the archive, or null.
+ * @throws IOException on error
+ */
+ public TarEntry getNextEntry() throws IOException {
+ if (hasHitEOF) {
+ return null;
+ }
+
+ if (currEntry != null) {
+ long numToSkip = entrySize - entryOffset;
+
+ if (debug) {
+ System.err.println("TarInputStream: SKIP currENTRY '"
+ + currEntry.getName() + "' SZ "
+ + entrySize + " OFF "
+ + entryOffset + " skipping "
+ + numToSkip + " bytes");
+ }
+
+ while (numToSkip > 0) {
+ long skipped = skip(numToSkip);
+ if (skipped <= 0) {
+ throw new RuntimeException("failed to skip current tar"
+ + " entry");
+ }
+ numToSkip -= skipped;
+ }
+
+ readBuf = null;
+ }
+
+ byte[] headerBuf = getRecord();
+
+ if (hasHitEOF) {
+ currEntry = null;
+ return null;
+ }
+
+ try {
+ currEntry = new TarEntry(headerBuf, encoding);
+ } catch (IllegalArgumentException e) {
+ IOException ioe = new IOException("Error detected parsing the header");
+ ioe.initCause(e);
+ throw ioe;
+ }
+ if (debug) {
+ System.err.println("TarInputStream: SET CURRENTRY '"
+ + currEntry.getName()
+ + "' size = "
+ + currEntry.getSize());
+ }
+
+ entryOffset = 0;
+ entrySize = currEntry.getSize();
+
+ if (currEntry.isGNULongLinkEntry()) {
+ byte[] longLinkData = getLongNameData();
+ if (longLinkData == null) {
+ // Bugzilla: 40334
+ // Malformed tar file - long link entry name not followed by
+ // entry
+ return null;
+ }
+ currEntry.setLinkName(encoding.decode(longLinkData));
+ }
+
+ if (currEntry.isGNULongNameEntry()) {
+ byte[] longNameData = getLongNameData();
+ if (longNameData == null) {
+ // Bugzilla: 40334
+ // Malformed tar file - long entry name not followed by
+ // entry
+ return null;
+ }
+ currEntry.setName(encoding.decode(longNameData));
+ }
+
+ if (currEntry.isPaxHeader()){ // Process Pax headers
+ paxHeaders();
+ }
+
+ if (currEntry.isGNUSparse()){ // Process sparse files
+ readGNUSparse();
+ }
+
+ // If the size of the next element in the archive has changed
+ // due to a new size being reported in the posix header
+ // information, we update entrySize here so that it contains
+ // the correct value.
+ entrySize = currEntry.getSize();
+ return currEntry;
+ }
+
+ /**
+ * Get the next entry in this tar archive as longname data.
+ *
+ * @return The next entry in the archive as longname data, or null.
+ * @throws IOException on error
+ */
+ protected byte[] getLongNameData() throws IOException {
+ // read in the name
+ ByteArrayOutputStream longName = new ByteArrayOutputStream();
+ int length = 0;
+ while ((length = read(SMALL_BUF)) >= 0) {
+ longName.write(SMALL_BUF, 0, length);
+ }
+ getNextEntry();
+ if (currEntry == null) {
+ // Bugzilla: 40334
+ // Malformed tar file - long entry name not followed by entry
+ return null;
+ }
+ byte[] longNameData = longName.toByteArray();
+ // remove trailing null terminator(s)
+ length = longNameData.length;
+ while (length > 0 && longNameData[length - 1] == 0) {
+ --length;
+ }
+ if (length != longNameData.length) {
+ byte[] l = new byte[length];
+ System.arraycopy(longNameData, 0, l, 0, length);
+ longNameData = l;
+ }
+ return longNameData;
+ }
+
+ /**
+ * Get the next record in this tar archive. This will skip
+ * over any remaining data in the current entry, if there
+ * is one, and place the input stream at the header of the
+ * next entry.
+ * If there are no more entries in the archive, null will
+ * be returned to indicate that the end of the archive has
+ * been reached.
+ *
+ * @return The next header in the archive, or null.
+ * @throws IOException on error
+ */
+ private byte[] getRecord() throws IOException {
+ if (hasHitEOF) {
+ return null;
+ }
+
+ byte[] headerBuf = buffer.readRecord();
+
+ if (headerBuf == null) {
+ if (debug) {
+ System.err.println("READ NULL RECORD");
+ }
+ hasHitEOF = true;
+ } else if (buffer.isEOFRecord(headerBuf)) {
+ if (debug) {
+ System.err.println("READ EOF RECORD");
+ }
+ hasHitEOF = true;
+ }
+
+ return hasHitEOF ? null : headerBuf;
+ }
+
+ private void paxHeaders() throws IOException{
+ Map<String, String> headers = parsePaxHeaders(this);
+ getNextEntry(); // Get the actual file entry
+ applyPaxHeadersToCurrentEntry(headers);
+ }
+
+ Map<String, String> parsePaxHeaders(InputStream i) throws IOException {
+ Map<String, String> headers = new HashMap<String, String>();
+ // Format is "length keyword=value\n";
+ while(true){ // get length
+ int ch;
+ int len = 0;
+ int read = 0;
+ while((ch = i.read()) != -1) {
+ read++;
+ if (ch == ' '){ // End of length string
+ // Get keyword
+ ByteArrayOutputStream coll = new ByteArrayOutputStream();
+ while((ch = i.read()) != -1) {
+ read++;
+ if (ch == '='){ // end of keyword
+ String keyword = coll.toString("UTF-8");
+ // Get rest of entry
+ final int restLen = len - read;
+ byte[] rest = new byte[restLen];
+ int got = 0;
+ while (got < restLen && (ch = i.read()) != -1) {
+ rest[got++] = (byte) ch;
+ }
+ if (got != restLen) {
+ throw new IOException("Failed to read "
+ + "Paxheader. Expected "
+ + restLen
+ + " bytes, read "
+ + got);
+ }
+ // Drop trailing NL
+ String value = new String(rest, 0,
+ restLen - 1, "UTF-8");
+ headers.put(keyword, value);
+ break;
+ }
+ coll.write((byte) ch);
+ }
+ break; // Processed single header
+ }
+ len *= 10;
+ len += ch - '0';
+ }
+ if (ch == -1){ // EOF
+ break;
+ }
+ }
+ return headers;
+ }
+
+ private void applyPaxHeadersToCurrentEntry(Map<String, String> headers) {
+ /*
+ * The following headers are defined for Pax.
+ * atime, ctime, charset: cannot use these without changing TarEntry fields
+ * mtime
+ * comment
+ * gid, gname
+ * linkpath
+ * size
+ * uid,uname
+ * SCHILY.devminor, SCHILY.devmajor: don't have setters/getters for those
+ */
+ for (Entry<String, String> ent : headers.entrySet()){
+ String key = ent.getKey();
+ String val = ent.getValue();
+ if ("path".equals(key)){
+ currEntry.setName(val);
+ } else if ("linkpath".equals(key)){
+ currEntry.setLinkName(val);
+ } else if ("gid".equals(key)){
+ currEntry.setGroupId(Long.parseLong(val));
+ } else if ("gname".equals(key)){
+ currEntry.setGroupName(val);
+ } else if ("uid".equals(key)){
+ currEntry.setUserId(Long.parseLong(val));
+ } else if ("uname".equals(key)){
+ currEntry.setUserName(val);
+ } else if ("size".equals(key)){
+ currEntry.setSize(Long.parseLong(val));
+ } else if ("mtime".equals(key)){
+ currEntry.setModTime((long) (Double.parseDouble(val) * 1000));
+ } else if ("SCHILY.devminor".equals(key)){
+ currEntry.setDevMinor(Integer.parseInt(val));
+ } else if ("SCHILY.devmajor".equals(key)){
+ currEntry.setDevMajor(Integer.parseInt(val));
+ }
+ }
+ }
+
+ /**
+ * Adds the sparse chunks from the current entry to the sparse chunks,
+ * including any additional sparse entries following the current entry.
+ *
+ * @throws IOException on error
+ *
+ * @todo Sparse files get not yet really processed.
+ */
+ private void readGNUSparse() throws IOException {
+ /* we do not really process sparse files yet
+ sparses = new ArrayList();
+ sparses.addAll(currEntry.getSparses());
+ */
+ if (currEntry.isExtended()) {
+ TarArchiveSparseEntry entry;
+ do {
+ byte[] headerBuf = getRecord();
+ if (hasHitEOF) {
+ currEntry = null;
+ break;
+ }
+ entry = new TarArchiveSparseEntry(headerBuf);
+ /* we do not really process sparse files yet
+ sparses.addAll(entry.getSparses());
+ */
+ } while (entry.isExtended());
+ }
+ }
+
+ /**
+ * Reads a byte from the current tar archive entry.
+ *
+ * This method simply calls read( byte[], int, int ).
+ *
+ * @return The byte read, or -1 at EOF.
+ * @throws IOException on error
+ */
+ @Override
+ public int read() throws IOException {
+ int num = read(oneBuf, 0, 1);
+ return num == -1 ? -1 : (oneBuf[0]) & BYTE_MASK;
+ }
+
+ /**
+ * Reads bytes from the current tar archive entry.
+ *
+ * This method is aware of the boundaries of the current
+ * entry in the archive and will deal with them as if they
+ * were this stream's start and EOF.
+ *
+ * @param buf The buffer into which to place bytes read.
+ * @param offset The offset at which to place bytes read.
+ * @param numToRead The number of bytes to read.
+ * @return The number of bytes read, or -1 at EOF.
+ * @throws IOException on error
+ */
+ @Override
+ public int read(byte[] buf, int offset, int numToRead) throws IOException {
+ int totalRead = 0;
+
+ if (entryOffset >= entrySize) {
+ return -1;
+ }
+
+ if ((numToRead + entryOffset) > entrySize) {
+ numToRead = (int) (entrySize - entryOffset);
+ }
+
+ if (readBuf != null) {
+ int sz = (numToRead > readBuf.length) ? readBuf.length
+ : numToRead;
+
+ System.arraycopy(readBuf, 0, buf, offset, sz);
+
+ if (sz >= readBuf.length) {
+ readBuf = null;
+ } else {
+ int newLen = readBuf.length - sz;
+ byte[] newBuf = new byte[newLen];
+
+ System.arraycopy(readBuf, sz, newBuf, 0, newLen);
+
+ readBuf = newBuf;
+ }
+
+ totalRead += sz;
+ numToRead -= sz;
+ offset += sz;
+ }
+
+ while (numToRead > 0) {
+ byte[] rec = buffer.readRecord();
+
+ if (rec == null) {
+ // Unexpected EOF!
+ throw new IOException("unexpected EOF with " + numToRead
+ + " bytes unread");
+ }
+
+ int sz = numToRead;
+ int recLen = rec.length;
+
+ if (recLen > sz) {
+ System.arraycopy(rec, 0, buf, offset, sz);
+
+ readBuf = new byte[recLen - sz];
+
+ System.arraycopy(rec, sz, readBuf, 0, recLen - sz);
+ } else {
+ sz = recLen;
+
+ System.arraycopy(rec, 0, buf, offset, recLen);
+ }
+
+ totalRead += sz;
+ numToRead -= sz;
+ offset += sz;
+ }
+
+ entryOffset += totalRead;
+
+ return totalRead;
+ }
+
+ /**
+ * Copies the contents of the current tar archive entry directly into
+ * an output stream.
+ *
+ * @param out The OutputStream into which to write the entry's data.
+ * @throws IOException on error
+ */
+ public void copyEntryContents(OutputStream out) throws IOException {
+ byte[] buf = new byte[LARGE_BUFFER_SIZE];
+
+ while (true) {
+ int numRead = read(buf, 0, buf.length);
+
+ if (numRead == -1) {
+ break;
+ }
+
+ out.write(buf, 0, numRead);
+ }
+ }
+
+ /**
+ * Whether this class is able to read the given entry.
+ *
+ * <p>May return false if the current entry is a sparse file.</p>
+ */
+ public boolean canReadEntryData(TarEntry te) {
+ return !te.isGNUSparse();
+ }
+}
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarOutputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarOutputStream.java
new file mode 100644
index 00000000..032f7254
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarOutputStream.java
@@ -0,0 +1,657 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Timothy Gerard Endres
+ * (time@ice.com) to whom the Ant project is very grateful for his great code.
+ */
+
+package org.apache.tools.tar;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.nio.ByteBuffer;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.tools.zip.ZipEncoding;
+import org.apache.tools.zip.ZipEncodingHelper;
+
+/**
+ * The TarOutputStream writes a UNIX tar archive as an OutputStream.
+ * Methods are provided to put entries, and then write their contents
+ * by writing to this stream using write().
+ *
+ */
+public class TarOutputStream extends FilterOutputStream {
+ /** Fail if a long file name is required in the archive. */
+ public static final int LONGFILE_ERROR = 0;
+
+ /** Long paths will be truncated in the archive. */
+ public static final int LONGFILE_TRUNCATE = 1;
+
+ /** GNU tar extensions are used to store long file names in the archive. */
+ public static final int LONGFILE_GNU = 2;
+
+ /** POSIX/PAX extensions are used to store long file names in the archive. */
+ public static final int LONGFILE_POSIX = 3;
+
+ /** Fail if a big number (e.g. size &gt; 8GiB) is required in the archive. */
+ public static final int BIGNUMBER_ERROR = 0;
+
+ /** star/GNU tar/BSD tar extensions are used to store big number in the archive. */
+ public static final int BIGNUMBER_STAR = 1;
+
+ /** POSIX/PAX extensions are used to store big numbers in the archive. */
+ public static final int BIGNUMBER_POSIX = 2;
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected boolean debug;
+ protected long currSize;
+ protected String currName;
+ protected long currBytes;
+ protected byte[] oneBuf;
+ protected byte[] recordBuf;
+ protected int assemLen;
+ protected byte[] assemBuf;
+ protected TarBuffer buffer;
+ protected int longFileMode = LONGFILE_ERROR;
+ // CheckStyle:VisibilityModifier ON
+
+ private int bigNumberMode = BIGNUMBER_ERROR;
+
+ private boolean closed = false;
+
+ /** Indicates if putNextEntry has been called without closeEntry */
+ private boolean haveUnclosedEntry = false;
+
+ /** indicates if this archive is finished */
+ private boolean finished = false;
+
+ private final ZipEncoding encoding;
+
+ private boolean addPaxHeadersForNonAsciiNames = false;
+ private static final ZipEncoding ASCII =
+ ZipEncodingHelper.getZipEncoding("ASCII");
+
+ /**
+ * Constructor for TarInputStream.
+ * @param os the output stream to use
+ */
+ public TarOutputStream(OutputStream os) {
+ this(os, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE);
+ }
+
+ /**
+ * Constructor for TarInputStream.
+ * @param os the output stream to use
+ * @param encoding name of the encoding to use for file names
+ */
+ public TarOutputStream(OutputStream os, String encoding) {
+ this(os, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE, encoding);
+ }
+
+ /**
+ * Constructor for TarInputStream.
+ * @param os the output stream to use
+ * @param blockSize the block size to use
+ */
+ public TarOutputStream(OutputStream os, int blockSize) {
+ this(os, blockSize, TarBuffer.DEFAULT_RCDSIZE);
+ }
+
+ /**
+ * Constructor for TarInputStream.
+ * @param os the output stream to use
+ * @param blockSize the block size to use
+ * @param encoding name of the encoding to use for file names
+ */
+ public TarOutputStream(OutputStream os, int blockSize, String encoding) {
+ this(os, blockSize, TarBuffer.DEFAULT_RCDSIZE, encoding);
+ }
+
+ /**
+ * Constructor for TarInputStream.
+ * @param os the output stream to use
+ * @param blockSize the block size to use
+ * @param recordSize the record size to use
+ */
+ public TarOutputStream(OutputStream os, int blockSize, int recordSize) {
+ this(os, blockSize, recordSize, null);
+ }
+
+ /**
+ * Constructor for TarInputStream.
+ * @param os the output stream to use
+ * @param blockSize the block size to use
+ * @param recordSize the record size to use
+ * @param encoding name of the encoding to use for file names
+ */
+ public TarOutputStream(OutputStream os, int blockSize, int recordSize,
+ String encoding) {
+ super(os);
+ this.encoding = ZipEncodingHelper.getZipEncoding(encoding);
+
+ this.buffer = new TarBuffer(os, blockSize, recordSize);
+ this.debug = false;
+ this.assemLen = 0;
+ this.assemBuf = new byte[recordSize];
+ this.recordBuf = new byte[recordSize];
+ this.oneBuf = new byte[1];
+ }
+
+ /**
+ * Set the long file mode.
+ * This can be LONGFILE_ERROR(0), LONGFILE_TRUNCATE(1) or LONGFILE_GNU(2).
+ * This specifies the treatment of long file names (names &gt;= TarConstants.NAMELEN).
+ * Default is LONGFILE_ERROR.
+ * @param longFileMode the mode to use
+ */
+ public void setLongFileMode(int longFileMode) {
+ this.longFileMode = longFileMode;
+ }
+
+ /**
+ * Set the big number mode.
+ * This can be BIGNUMBER_ERROR(0), BIGNUMBER_POSIX(1) or BIGNUMBER_STAR(2).
+ * This specifies the treatment of big files (sizes &gt; TarConstants.MAXSIZE) and other numeric values to big to fit into a traditional tar header.
+ * Default is BIGNUMBER_ERROR.
+ * @param bigNumberMode the mode to use
+ */
+ public void setBigNumberMode(int bigNumberMode) {
+ this.bigNumberMode = bigNumberMode;
+ }
+
+ /**
+ * Whether to add a PAX extension header for non-ASCII file names.
+ */
+ public void setAddPaxHeadersForNonAsciiNames(boolean b) {
+ addPaxHeadersForNonAsciiNames = b;
+ }
+
+ /**
+ * Sets the debugging flag.
+ *
+ * @param debugF True to turn on debugging.
+ */
+ public void setDebug(boolean debugF) {
+ this.debug = debugF;
+ }
+
+ /**
+ * Sets the debugging flag in this stream's TarBuffer.
+ *
+ * @param debug True to turn on debugging.
+ */
+ public void setBufferDebug(boolean debug) {
+ buffer.setDebug(debug);
+ }
+
+ /**
+ * Ends the TAR archive without closing the underlying OutputStream.
+ *
+ * An archive consists of a series of file entries terminated by an
+ * end-of-archive entry, which consists of two 512 blocks of zero bytes.
+ * POSIX.1 requires two EOF records, like some other implementations.
+ *
+ * @throws IOException on error
+ */
+ public void finish() throws IOException {
+ if (finished) {
+ throw new IOException("This archive has already been finished");
+ }
+
+ if (haveUnclosedEntry) {
+ throw new IOException("This archives contains unclosed entries.");
+ }
+ writeEOFRecord();
+ writeEOFRecord();
+ buffer.flushBlock();
+ finished = true;
+ }
+
+ /**
+ * Ends the TAR archive and closes the underlying OutputStream.
+ * This means that finish() is called followed by calling the
+ * TarBuffer's close().
+ * @throws IOException on error
+ */
+ @Override
+ public void close() throws IOException {
+ if(!finished) {
+ finish();
+ }
+
+ if (!closed) {
+ buffer.close();
+ out.close();
+ closed = true;
+ }
+ }
+
+ /**
+ * Get the record size being used by this stream's TarBuffer.
+ *
+ * @return The TarBuffer record size.
+ */
+ public int getRecordSize() {
+ return buffer.getRecordSize();
+ }
+
+ /**
+ * Put an entry on the output stream. This writes the entry's
+ * header record and positions the output stream for writing
+ * the contents of the entry. Once this method is called, the
+ * stream is ready for calls to write() to write the entry's
+ * contents. Once the contents are written, closeEntry()
+ * <B>MUST</B> be called to ensure that all buffered data
+ * is completely written to the output stream.
+ *
+ * @param entry The TarEntry to be written to the archive.
+ * @throws IOException on error
+ */
+ public void putNextEntry(TarEntry entry) throws IOException {
+ if(finished) {
+ throw new IOException("Stream has already been finished");
+ }
+ Map<String, String> paxHeaders = new HashMap<String, String>();
+ final String entryName = entry.getName();
+ boolean paxHeaderContainsPath = handleLongName(entry, entryName, paxHeaders, "path",
+ TarConstants.LF_GNUTYPE_LONGNAME, "file name");
+
+ final String linkName = entry.getLinkName();
+ boolean paxHeaderContainsLinkPath = linkName != null && linkName.length() > 0
+ && handleLongName(entry, linkName, paxHeaders, "linkpath",
+ TarConstants.LF_GNUTYPE_LONGLINK, "link name");
+
+ if (bigNumberMode == BIGNUMBER_POSIX) {
+ addPaxHeadersForBigNumbers(paxHeaders, entry);
+ } else if (bigNumberMode != BIGNUMBER_STAR) {
+ failForBigNumbers(entry);
+ }
+
+ if (addPaxHeadersForNonAsciiNames && !paxHeaderContainsPath
+ && !ASCII.canEncode(entryName)) {
+ paxHeaders.put("path", entryName);
+ }
+
+ if (addPaxHeadersForNonAsciiNames && !paxHeaderContainsLinkPath
+ && (entry.isLink() || entry.isSymbolicLink())
+ && !ASCII.canEncode(linkName)) {
+ paxHeaders.put("linkpath", linkName);
+ }
+
+ if (paxHeaders.size() > 0) {
+ writePaxHeaders(entry, entryName, paxHeaders);
+ }
+
+ entry.writeEntryHeader(recordBuf, encoding,
+ bigNumberMode == BIGNUMBER_STAR);
+ buffer.writeRecord(recordBuf);
+
+ currBytes = 0;
+
+ if (entry.isDirectory()) {
+ currSize = 0;
+ } else {
+ currSize = entry.getSize();
+ }
+ currName = entryName;
+ haveUnclosedEntry = true;
+ }
+
+ /**
+ * Close an entry. This method MUST be called for all file
+ * entries that contain data. The reason is that we must
+ * buffer data written to the stream in order to satisfy
+ * the buffer's record based writes. Thus, there may be
+ * data fragments still being assembled that must be written
+ * to the output stream before this entry is closed and the
+ * next entry written.
+ * @throws IOException on error
+ */
+ public void closeEntry() throws IOException {
+ if (finished) {
+ throw new IOException("Stream has already been finished");
+ }
+ if (!haveUnclosedEntry){
+ throw new IOException("No current entry to close");
+ }
+ if (assemLen > 0) {
+ for (int i = assemLen; i < assemBuf.length; ++i) {
+ assemBuf[i] = 0;
+ }
+
+ buffer.writeRecord(assemBuf);
+
+ currBytes += assemLen;
+ assemLen = 0;
+ }
+
+ if (currBytes < currSize) {
+ throw new IOException("entry '" + currName + "' closed at '"
+ + currBytes
+ + "' before the '" + currSize
+ + "' bytes specified in the header were written");
+ }
+ haveUnclosedEntry = false;
+ }
+
+ /**
+ * Writes a byte to the current tar archive entry.
+ *
+ * This method simply calls read( byte[], int, int ).
+ *
+ * @param b The byte written.
+ * @throws IOException on error
+ */
+ @Override
+ public void write(int b) throws IOException {
+ oneBuf[0] = (byte) b;
+
+ write(oneBuf, 0, 1);
+ }
+
+ /**
+ * Writes bytes to the current tar archive entry.
+ *
+ * This method simply calls write( byte[], int, int ).
+ *
+ * @param wBuf The buffer to write to the archive.
+ * @throws IOException on error
+ */
+ @Override
+ public void write(byte[] wBuf) throws IOException {
+ write(wBuf, 0, wBuf.length);
+ }
+
+ /**
+ * Writes bytes to the current tar archive entry. This method
+ * is aware of the current entry and will throw an exception if
+ * you attempt to write bytes past the length specified for the
+ * current entry. The method is also (painfully) aware of the
+ * record buffering required by TarBuffer, and manages buffers
+ * that are not a multiple of recordsize in length, including
+ * assembling records from small buffers.
+ *
+ * @param wBuf The buffer to write to the archive.
+ * @param wOffset The offset in the buffer from which to get bytes.
+ * @param numToWrite The number of bytes to write.
+ * @throws IOException on error
+ */
+ @Override
+ public void write(byte[] wBuf, int wOffset, int numToWrite) throws IOException {
+ if ((currBytes + numToWrite) > currSize) {
+ throw new IOException("request to write '" + numToWrite
+ + "' bytes exceeds size in header of '"
+ + currSize + "' bytes for entry '"
+ + currName + "'");
+
+ //
+ // We have to deal with assembly!!!
+ // The programmer can be writing little 32 byte chunks for all
+ // we know, and we must assemble complete records for writing.
+ // REVIEW Maybe this should be in TarBuffer? Could that help to
+ // eliminate some of the buffer copying.
+ //
+ }
+
+ if (assemLen > 0) {
+ if ((assemLen + numToWrite) >= recordBuf.length) {
+ int aLen = recordBuf.length - assemLen;
+
+ System.arraycopy(assemBuf, 0, recordBuf, 0,
+ assemLen);
+ System.arraycopy(wBuf, wOffset, recordBuf,
+ assemLen, aLen);
+ buffer.writeRecord(recordBuf);
+
+ currBytes += recordBuf.length;
+ wOffset += aLen;
+ numToWrite -= aLen;
+ assemLen = 0;
+ } else {
+ System.arraycopy(wBuf, wOffset, assemBuf, assemLen,
+ numToWrite);
+
+ wOffset += numToWrite;
+ assemLen += numToWrite;
+ numToWrite = 0;
+ }
+ }
+
+ //
+ // When we get here we have EITHER:
+ // o An empty "assemble" buffer.
+ // o No bytes to write (numToWrite == 0)
+ //
+ while (numToWrite > 0) {
+ if (numToWrite < recordBuf.length) {
+ System.arraycopy(wBuf, wOffset, assemBuf, assemLen,
+ numToWrite);
+
+ assemLen += numToWrite;
+
+ break;
+ }
+
+ buffer.writeRecord(wBuf, wOffset);
+
+ int num = recordBuf.length;
+
+ currBytes += num;
+ numToWrite -= num;
+ wOffset += num;
+ }
+ }
+
+ /**
+ * Writes a PAX extended header with the given map as contents.
+ */
+ void writePaxHeaders(TarEntry entry,
+ String entryName,
+ Map<String, String> headers) throws IOException {
+ String name = "./PaxHeaders.X/" + stripTo7Bits(entryName);
+ if (name.length() >= TarConstants.NAMELEN) {
+ name = name.substring(0, TarConstants.NAMELEN - 1);
+ }
+ while (name.endsWith("/")) {
+ // TarEntry's constructor would think this is a directory
+ // and not allow any data to be written
+ name = name.substring(0, name.length() - 1);
+ }
+ TarEntry pex = new TarEntry(name,
+ TarConstants.LF_PAX_EXTENDED_HEADER_LC);
+ transferModTime(entry, pex);
+
+ StringWriter w = new StringWriter();
+ for (Map.Entry<String, String> h : headers.entrySet()) {
+ String key = h.getKey();
+ String value = h.getValue();
+ int len = key.length() + value.length()
+ + 3 /* blank, equals and newline */
+ + 2 /* guess 9 < actual length < 100 */;
+ String line = len + " " + key + "=" + value + "\n";
+ int actualLength = line.getBytes("UTF-8").length;
+ while (len != actualLength) {
+ // Adjust for cases where length < 10 or > 100
+ // or where UTF-8 encoding isn't a single octet
+ // per character.
+ // Must be in loop as size may go from 99 to 100 in
+ // first pass so we'd need a second.
+ len = actualLength;
+ line = len + " " + key + "=" + value + "\n";
+ actualLength = line.getBytes("UTF-8").length;
+ }
+ w.write(line);
+ }
+ byte[] data = w.toString().getBytes("UTF-8");
+ pex.setSize(data.length);
+ putNextEntry(pex);
+ write(data);
+ closeEntry();
+ }
+
+ private String stripTo7Bits(String name) {
+ final int length = name.length();
+ StringBuilder result = new StringBuilder(length);
+ for (int i = 0; i < length; i++) {
+ char stripped = (char) (name.charAt(i) & 0x7F);
+ if (stripped != 0) { // would be read as Trailing null
+ result.append(stripped);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Write an EOF (end of archive) record to the tar archive.
+ * An EOF record consists of a record of all zeros.
+ */
+ private void writeEOFRecord() throws IOException {
+ for (int i = 0; i < recordBuf.length; ++i) {
+ recordBuf[i] = 0;
+ }
+
+ buffer.writeRecord(recordBuf);
+ }
+
+ private void addPaxHeadersForBigNumbers(Map<String, String> paxHeaders,
+ TarEntry entry) {
+ addPaxHeaderForBigNumber(paxHeaders, "size", entry.getSize(),
+ TarConstants.MAXSIZE);
+ addPaxHeaderForBigNumber(paxHeaders, "gid", entry.getLongGroupId(),
+ TarConstants.MAXID);
+ addPaxHeaderForBigNumber(paxHeaders, "mtime",
+ entry.getModTime().getTime() / 1000,
+ TarConstants.MAXSIZE);
+ addPaxHeaderForBigNumber(paxHeaders, "uid", entry.getLongUserId(),
+ TarConstants.MAXID);
+ // star extensions by J\u00f6rg Schilling
+ addPaxHeaderForBigNumber(paxHeaders, "SCHILY.devmajor",
+ entry.getDevMajor(), TarConstants.MAXID);
+ addPaxHeaderForBigNumber(paxHeaders, "SCHILY.devminor",
+ entry.getDevMinor(), TarConstants.MAXID);
+ // there is no PAX header for file mode
+ failForBigNumber("mode", entry.getMode(), TarConstants.MAXID);
+ }
+
+ private void addPaxHeaderForBigNumber(Map<String, String> paxHeaders,
+ String header, long value,
+ long maxValue) {
+ if (value < 0 || value > maxValue) {
+ paxHeaders.put(header, String.valueOf(value));
+ }
+ }
+
+ private void failForBigNumbers(TarEntry entry) {
+ failForBigNumber("entry size", entry.getSize(), TarConstants.MAXSIZE);
+ failForBigNumberWithPosixMessage("group id", entry.getLongGroupId(), TarConstants.MAXID);
+ failForBigNumber("last modification time",
+ entry.getModTime().getTime() / 1000,
+ TarConstants.MAXSIZE);
+ failForBigNumber("user id", entry.getLongUserId(), TarConstants.MAXID);
+ failForBigNumber("mode", entry.getMode(), TarConstants.MAXID);
+ failForBigNumber("major device number", entry.getDevMajor(),
+ TarConstants.MAXID);
+ failForBigNumber("minor device number", entry.getDevMinor(),
+ TarConstants.MAXID);
+ }
+
+ private void failForBigNumber(String field, long value, long maxValue) {
+ failForBigNumber(field, value, maxValue, "");
+ }
+
+ private void failForBigNumberWithPosixMessage(String field, long value, long maxValue) {
+ failForBigNumber(field, value, maxValue, " Use STAR or POSIX extensions to overcome this limit");
+ }
+
+ private void failForBigNumber(String field, long value, long maxValue, String additionalMsg) {
+ if (value < 0 || value > maxValue) {
+ throw new RuntimeException(field + " '" + value
+ + "' is too big ( > "
+ + maxValue + " )");
+ }
+ }
+
+ /**
+ * Handles long file or link names according to the longFileMode setting.
+ *
+ * <p>I.e. if the given name is too long to be written to a plain
+ * tar header then
+ * <ul>
+ * <li>it creates a pax header who's name is given by the
+ * paxHeaderName parameter if longFileMode is POSIX</li>
+ * <li>it creates a GNU longlink entry who's type is given by
+ * the linkType parameter if longFileMode is GNU</li>
+ * <li>it throws an exception if longFileMode is ERROR</li>
+ * <li>it truncates the name if longFileMode is TRUNCATE</li>
+ * </ul></p>
+ *
+ * @param entry entry the name belongs to
+ * @param name the name to write
+ * @param paxHeaders current map of pax headers
+ * @param paxHeaderName name of the pax header to write
+ * @param linkType type of the GNU entry to write
+ * @param fieldName the name of the field
+ * @return whether a pax header has been written.
+ */
+ private boolean handleLongName(TarEntry entry , String name,
+ Map<String, String> paxHeaders,
+ String paxHeaderName, byte linkType, String fieldName)
+ throws IOException {
+ final ByteBuffer encodedName = encoding.encode(name);
+ final int len = encodedName.limit() - encodedName.position();
+ if (len >= TarConstants.NAMELEN) {
+
+ if (longFileMode == LONGFILE_POSIX) {
+ paxHeaders.put(paxHeaderName, name);
+ return true;
+ } else if (longFileMode == LONGFILE_GNU) {
+ // create a TarEntry for the LongLink, the contents
+ // of which are the link's name
+ TarEntry longLinkEntry =
+ new TarEntry(TarConstants.GNU_LONGLINK, linkType);
+
+ longLinkEntry.setSize(len + 1); // +1 for NUL
+ transferModTime(entry, longLinkEntry);
+ putNextEntry(longLinkEntry);
+ write(encodedName.array(), encodedName.arrayOffset(), len);
+ write(0); // NUL terminator
+ closeEntry();
+ } else if (longFileMode != LONGFILE_TRUNCATE) {
+ throw new RuntimeException(fieldName + " '" + name
+ + "' is too long ( > "
+ + TarConstants.NAMELEN + " bytes)");
+ }
+ }
+ return false;
+ }
+
+ private void transferModTime(TarEntry from, TarEntry to) {
+ Date fromModTime = from.getModTime();
+ long fromModTimeSeconds = fromModTime.getTime() / 1000;
+ if (fromModTimeSeconds < 0 || fromModTimeSeconds > TarConstants.MAXSIZE) {
+ fromModTime = new Date(0);
+ }
+ to.setModTime(fromModTime);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarUtils.java
new file mode 100644
index 00000000..12bd1da3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/tar/TarUtils.java
@@ -0,0 +1,564 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Timothy Gerard Endres
+ * (time@ice.com) to whom the Ant project is very grateful for his great code.
+ */
+
+package org.apache.tools.tar;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+
+import org.apache.tools.zip.ZipEncoding;
+import org.apache.tools.zip.ZipEncodingHelper;
+
+/**
+ * This class provides static utility methods to work with byte streams.
+ *
+ */
+// CheckStyle:HideUtilityClassConstructorCheck OFF (bc)
+public class TarUtils {
+
+ private static final int BYTE_MASK = 255;
+
+ static final ZipEncoding DEFAULT_ENCODING =
+ ZipEncodingHelper.getZipEncoding(null);
+
+ /**
+ * Encapsulates the algorithms used up to Ant 1.8 as ZipEncoding.
+ */
+ static final ZipEncoding FALLBACK_ENCODING = new ZipEncoding() {
+ public boolean canEncode(final String name) { return true; }
+
+ public ByteBuffer encode(final String name) {
+ final int length = name.length();
+ final byte[] buf = new byte[length];
+
+ // copy until end of input or output is reached.
+ for (int i = 0; i < length; ++i) {
+ buf[i] = (byte) name.charAt(i);
+ }
+ return ByteBuffer.wrap(buf);
+ }
+
+ public String decode(final byte[] buffer) {
+ final int length = buffer.length;
+ final StringBuilder result = new StringBuilder(length);
+
+ for (int i = 0; i < length; ++i) {
+ final byte b = buffer[i];
+ if (b == 0) { // Trailing null
+ break;
+ }
+ result.append((char) (b & 0xFF)); // Allow for sign-extension
+ }
+
+ return result.toString();
+ }
+ };
+
+ /** Private constructor to prevent instantiation of this utility class. */
+ private TarUtils(){
+ }
+
+ /**
+ * Parse an octal string from a buffer.
+ *
+ * <p>Leading spaces are ignored.
+ * The buffer must contain a trailing space or NUL,
+ * and may contain an additional trailing space or NUL.</p>
+ *
+ * <p>The input buffer is allowed to contain all NULs,
+ * in which case the method returns 0L
+ * (this allows for missing fields).</p>
+ *
+ * <p>To work-around some tar implementations that insert a
+ * leading NUL this method returns 0 if it detects a leading NUL
+ * since Ant 1.9.</p>
+ *
+ * @param buffer The buffer from which to parse.
+ * @param offset The offset into the buffer from which to parse.
+ * @param length The maximum number of bytes to parse - must be at least 2 bytes.
+ * @return The long value of the octal string.
+ * @throws IllegalArgumentException if the trailing space/NUL is missing or if a invalid byte is detected.
+ */
+ public static long parseOctal(final byte[] buffer, final int offset, final int length) {
+ long result = 0;
+ int end = offset + length;
+ int start = offset;
+
+ if (length < 2){
+ throw new IllegalArgumentException("Length "+length+" must be at least 2");
+ }
+
+ if (buffer[start] == 0) {
+ return 0L;
+ }
+
+ // Skip leading spaces
+ while (start < end){
+ if (buffer[start] == ' '){
+ start++;
+ } else {
+ break;
+ }
+ }
+
+ // Trim all trailing NULs and spaces.
+ // The ustar and POSIX tar specs require a trailing NUL or
+ // space but some implementations use the extra digit for big
+ // sizes/uids/gids ...
+ byte trailer = buffer[end - 1];
+ while (start < end && (trailer == 0 || trailer == ' ')) {
+ end--;
+ trailer = buffer[end - 1];
+ }
+
+ for ( ;start < end; start++) {
+ final byte currentByte = buffer[start];
+ // CheckStyle:MagicNumber OFF
+ if (currentByte < '0' || currentByte > '7'){
+ throw new IllegalArgumentException(
+ exceptionMessage(buffer, offset, length, start, currentByte));
+ }
+ result = (result << 3) + (currentByte - '0'); // convert from ASCII
+ // CheckStyle:MagicNumber ON
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the value contained in a byte buffer. If the most
+ * significant bit of the first byte in the buffer is set, this
+ * bit is ignored and the rest of the buffer is interpreted as a
+ * binary number. Otherwise, the buffer is interpreted as an
+ * octal number as per the parseOctal function above.
+ *
+ * @param buffer The buffer from which to parse.
+ * @param offset The offset into the buffer from which to parse.
+ * @param length The maximum number of bytes to parse.
+ * @return The long value of the octal or binary string.
+ * @throws IllegalArgumentException if the trailing space/NUL is
+ * missing or an invalid byte is detected in an octal number, or
+ * if a binary number would exceed the size of a signed long
+ * 64-bit integer.
+ */
+ public static long parseOctalOrBinary(final byte[] buffer, final int offset,
+ final int length) {
+
+ if ((buffer[offset] & 0x80) == 0) {
+ return parseOctal(buffer, offset, length);
+ }
+ final boolean negative = buffer[offset] == (byte) 0xff;
+ if (length < 9) {
+ return parseBinaryLong(buffer, offset, length, negative);
+ }
+ return parseBinaryBigInteger(buffer, offset, length, negative);
+ }
+
+ private static long parseBinaryLong(final byte[] buffer, final int offset,
+ final int length,
+ final boolean negative) {
+ if (length >= 9) {
+ throw new IllegalArgumentException("At offset " + offset + ", "
+ + length + " byte binary number"
+ + " exceeds maximum signed long"
+ + " value");
+ }
+ long val = 0;
+ for (int i = 1; i < length; i++) {
+ val = (val << 8) + (buffer[offset + i] & 0xff);
+ }
+ if (negative) {
+ // 2's complement
+ val--;
+ val ^= (long) Math.pow(2, (length - 1) * 8) - 1;
+ }
+ return negative ? -val : val;
+ }
+
+ private static long parseBinaryBigInteger(final byte[] buffer,
+ final int offset,
+ final int length,
+ final boolean negative) {
+ final byte[] remainder = new byte[length - 1];
+ System.arraycopy(buffer, offset + 1, remainder, 0, length - 1);
+ BigInteger val = new BigInteger(remainder);
+ if (negative) {
+ // 2's complement
+ val = val.add(BigInteger.valueOf(-1)).not();
+ }
+ if (val.bitLength() > 63) {
+ throw new IllegalArgumentException("At offset " + offset + ", "
+ + length + " byte binary number"
+ + " exceeds maximum signed long"
+ + " value");
+ }
+ return negative ? -val.longValue() : val.longValue();
+ }
+
+ /**
+ * Parse a boolean byte from a buffer.
+ * Leading spaces and NUL are ignored.
+ * The buffer may contain trailing spaces or NULs.
+ *
+ * @param buffer The buffer from which to parse.
+ * @param offset The offset into the buffer from which to parse.
+ * @return The boolean value of the bytes.
+ * @throws IllegalArgumentException if an invalid byte is detected.
+ */
+ public static boolean parseBoolean(final byte[] buffer, final int offset) {
+ return buffer[offset] == 1;
+ }
+
+ // Helper method to generate the exception message
+ private static String exceptionMessage(final byte[] buffer, final int offset,
+ final int length, final int current, final byte currentByte) {
+ // default charset is good enough for an exception message,
+ //
+ // the alternative was to modify parseOctal and
+ // parseOctalOrBinary to receive the ZipEncoding of the
+ // archive (deprecating the existing public methods, of
+ // course) and dealing with the fact that ZipEncoding#decode
+ // can throw an IOException which parseOctal* doesn't declare
+ String string = new String(buffer, offset, length);
+
+ string=string.replaceAll("\0", "{NUL}"); // Replace NULs to allow string to be printed
+ final String s = "Invalid byte "+currentByte+" at offset "+(current-offset)+" in '"+string+"' len="+length;
+ return s;
+ }
+
+ /**
+ * Parse an entry name from a buffer.
+ * Parsing stops when a NUL is found
+ * or the buffer length is reached.
+ *
+ * @param buffer The buffer from which to parse.
+ * @param offset The offset into the buffer from which to parse.
+ * @param length The maximum number of bytes to parse.
+ * @return The entry name.
+ */
+ public static String parseName(final byte[] buffer, final int offset, final int length) {
+ try {
+ return parseName(buffer, offset, length, DEFAULT_ENCODING);
+ } catch (final IOException ex) {
+ try {
+ return parseName(buffer, offset, length, FALLBACK_ENCODING);
+ } catch (final IOException ex2) {
+ // impossible
+ throw new RuntimeException(ex2);
+ }
+ }
+ }
+
+ /**
+ * Parse an entry name from a buffer.
+ * Parsing stops when a NUL is found
+ * or the buffer length is reached.
+ *
+ * @param buffer The buffer from which to parse.
+ * @param offset The offset into the buffer from which to parse.
+ * @param length The maximum number of bytes to parse.
+ * @param encoding name of the encoding to use for file names
+ * @return The entry name.
+ */
+ public static String parseName(final byte[] buffer, final int offset,
+ final int length,
+ final ZipEncoding encoding)
+ throws IOException {
+
+ int len = length;
+ for (; len > 0; len--) {
+ if (buffer[offset + len - 1] != 0) {
+ break;
+ }
+ }
+ if (len > 0) {
+ final byte[] b = new byte[len];
+ System.arraycopy(buffer, offset, b, 0, len);
+ return encoding.decode(b);
+ }
+ return "";
+ }
+
+ /**
+ * Copy a name into a buffer.
+ * Copies characters from the name into the buffer
+ * starting at the specified offset.
+ * If the buffer is longer than the name, the buffer
+ * is filled with trailing NULs.
+ * If the name is longer than the buffer,
+ * the output is truncated.
+ *
+ * @param name The header name from which to copy the characters.
+ * @param buf The buffer where the name is to be stored.
+ * @param offset The starting offset into the buffer
+ * @param length The maximum number of header bytes to copy.
+ * @return The updated offset, i.e. offset + length
+ */
+ public static int formatNameBytes(final String name, final byte[] buf, final int offset, final int length) {
+ try {
+ return formatNameBytes(name, buf, offset, length, DEFAULT_ENCODING);
+ } catch (final IOException ex) {
+ try {
+ return formatNameBytes(name, buf, offset, length,
+ FALLBACK_ENCODING);
+ } catch (final IOException ex2) {
+ // impossible
+ throw new RuntimeException(ex2);
+ }
+ }
+ }
+
+ /**
+ * Copy a name into a buffer.
+ * Copies characters from the name into the buffer
+ * starting at the specified offset.
+ * If the buffer is longer than the name, the buffer
+ * is filled with trailing NULs.
+ * If the name is longer than the buffer,
+ * the output is truncated.
+ *
+ * @param name The header name from which to copy the characters.
+ * @param buf The buffer where the name is to be stored.
+ * @param offset The starting offset into the buffer
+ * @param length The maximum number of header bytes to copy.
+ * @param encoding name of the encoding to use for file names
+ * @return The updated offset, i.e. offset + length
+ */
+ public static int formatNameBytes(final String name, final byte[] buf, final int offset,
+ final int length,
+ final ZipEncoding encoding)
+ throws IOException {
+ int len = name.length();
+ ByteBuffer b = encoding.encode(name);
+ while (b.limit() > length && len > 0) {
+ b = encoding.encode(name.substring(0, --len));
+ }
+ final int limit = b.limit() - b.position();
+ System.arraycopy(b.array(), b.arrayOffset(), buf, offset, limit);
+
+ // Pad any remaining output bytes with NUL
+ for (int i = limit; i < length; ++i) {
+ buf[offset + i] = 0;
+ }
+
+ return offset + length;
+ }
+
+ /**
+ * Fill buffer with unsigned octal number, padded with leading zeroes.
+ *
+ * @param value number to convert to octal - treated as unsigned
+ * @param buffer destination buffer
+ * @param offset starting offset in buffer
+ * @param length length of buffer to fill
+ * @throws IllegalArgumentException if the value will not fit in the buffer
+ */
+ public static void formatUnsignedOctalString(final long value, final byte[] buffer,
+ final int offset, final int length) {
+ int remaining = length;
+ remaining--;
+ if (value == 0) {
+ buffer[offset + remaining--] = (byte) '0';
+ } else {
+ long val = value;
+ for (; remaining >= 0 && val != 0; --remaining) {
+ // CheckStyle:MagicNumber OFF
+ buffer[offset + remaining] = (byte) ((byte) '0' + (byte) (val & 7));
+ val = val >>> 3;
+ // CheckStyle:MagicNumber ON
+ }
+ if (val != 0){
+ throw new IllegalArgumentException
+ (value+"="+Long.toOctalString(value)+ " will not fit in octal number buffer of length "+length);
+ }
+ }
+
+ for (; remaining >= 0; --remaining) { // leading zeros
+ buffer[offset + remaining] = (byte) '0';
+ }
+ }
+
+ /**
+ * Write an octal integer into a buffer.
+ *
+ * Uses {@link #formatUnsignedOctalString} to format
+ * the value as an octal string with leading zeros.
+ * The converted number is followed by space and NUL
+ *
+ * @param value The value to write
+ * @param buf The buffer to receive the output
+ * @param offset The starting offset into the buffer
+ * @param length The size of the output buffer
+ * @return The updated offset, i.e offset+length
+ * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
+ */
+ public static int formatOctalBytes(final long value, final byte[] buf, final int offset, final int length) {
+
+ int idx=length-2; // For space and trailing null
+ formatUnsignedOctalString(value, buf, offset, idx);
+
+ buf[offset + idx++] = (byte) ' '; // Trailing space
+ buf[offset + idx] = 0; // Trailing null
+
+ return offset + length;
+ }
+
+ /**
+ * Write an octal long integer into a buffer.
+ *
+ * Uses {@link #formatUnsignedOctalString} to format
+ * the value as an octal string with leading zeros.
+ * The converted number is followed by a space.
+ *
+ * @param value The value to write as octal
+ * @param buf The destinationbuffer.
+ * @param offset The starting offset into the buffer.
+ * @param length The length of the buffer
+ * @return The updated offset
+ * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
+ */
+ public static int formatLongOctalBytes(final long value, final byte[] buf, final int offset, final int length) {
+
+ final int idx=length-1; // For space
+
+ formatUnsignedOctalString(value, buf, offset, idx);
+ buf[offset + idx] = (byte) ' '; // Trailing space
+
+ return offset + length;
+ }
+
+ /**
+ * Write an long integer into a buffer as an octal string if this
+ * will fit, or as a binary number otherwise.
+ *
+ * Uses {@link #formatUnsignedOctalString} to format
+ * the value as an octal string with leading zeros.
+ * The converted number is followed by a space.
+ *
+ * @param value The value to write into the buffer.
+ * @param buf The destination buffer.
+ * @param offset The starting offset into the buffer.
+ * @param length The length of the buffer.
+ * @return The updated offset.
+ * @throws IllegalArgumentException if the value (and trailer)
+ * will not fit in the buffer.
+ */
+ public static int formatLongOctalOrBinaryBytes(
+ final long value, final byte[] buf, final int offset, final int length) {
+
+ // Check whether we are dealing with UID/GID or SIZE field
+ final long maxAsOctalChar = length == TarConstants.UIDLEN ? TarConstants.MAXID : TarConstants.MAXSIZE;
+
+ final boolean negative = value < 0;
+ if (!negative && value <= maxAsOctalChar) { // OK to store as octal chars
+ return formatLongOctalBytes(value, buf, offset, length);
+ }
+
+ if (length < 9) {
+ formatLongBinary(value, buf, offset, length, negative);
+ }
+ formatBigIntegerBinary(value, buf, offset, length, negative);
+
+ buf[offset] = (byte) (negative ? 0xff : 0x80);
+ return offset + length;
+ }
+
+ private static void formatLongBinary(final long value, final byte[] buf,
+ final int offset, final int length,
+ final boolean negative) {
+ final int bits = (length - 1) * 8;
+ final long max = 1L << bits;
+ long val = Math.abs(value);
+ if (val >= max) {
+ throw new IllegalArgumentException("Value " + value +
+ " is too large for " + length + " byte field.");
+ }
+ if (negative) {
+ val ^= max - 1;
+ val |= 0xff << bits;
+ val++;
+ }
+ for (int i = offset + length - 1; i >= offset; i--) {
+ buf[i] = (byte) val;
+ val >>= 8;
+ }
+ }
+
+ private static void formatBigIntegerBinary(final long value, final byte[] buf,
+ final int offset,
+ final int length,
+ final boolean negative) {
+ final BigInteger val = BigInteger.valueOf(value);
+ final byte[] b = val.toByteArray();
+ final int len = b.length;
+ final int off = offset + length - len;
+ System.arraycopy(b, 0, buf, off, len);
+ final byte fill = (byte) (negative ? 0xff : 0);
+ for (int i = offset + 1; i < off; i++) {
+ buf[i] = fill;
+ }
+ }
+
+ /**
+ * Writes an octal value into a buffer.
+ *
+ * Uses {@link #formatUnsignedOctalString} to format
+ * the value as an octal string with leading zeros.
+ * The converted number is followed by NUL and then space.
+ *
+ * @param value The value to convert
+ * @param buf The destination buffer
+ * @param offset The starting offset into the buffer.
+ * @param length The size of the buffer.
+ * @return The updated value of offset, i.e. offset+length
+ * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
+ */
+ public static int formatCheckSumOctalBytes(final long value, final byte[] buf, final int offset, final int length) {
+
+ int idx=length-2; // for NUL and space
+ formatUnsignedOctalString(value, buf, offset, idx);
+
+ buf[offset + idx++] = 0; // Trailing null
+ buf[offset + idx] = (byte) ' '; // Trailing space
+
+ return offset + length;
+ }
+
+ /**
+ * Compute the checksum of a tar entry header.
+ *
+ * @param buf The tar entry's header buffer.
+ * @return The computed checksum.
+ */
+ public static long computeCheckSum(final byte[] buf) {
+ long sum = 0;
+
+ for (final byte element : buf) {
+ sum += BYTE_MASK & element;
+ }
+
+ return sum;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/AbstractUnicodeExtraField.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/AbstractUnicodeExtraField.java
new file mode 100644
index 00000000..1014787d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/AbstractUnicodeExtraField.java
@@ -0,0 +1,183 @@
+/*
+ * 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.tools.zip;
+
+import java.io.UnsupportedEncodingException;
+import java.util.zip.CRC32;
+import java.util.zip.ZipException;
+
+/**
+ * A common base class for Unicode extra information extra fields.
+ */
+public abstract class AbstractUnicodeExtraField implements ZipExtraField {
+ private long nameCRC32;
+ private byte[] unicodeName;
+ private byte[] data;
+
+ protected AbstractUnicodeExtraField() {
+ }
+
+ /**
+ * Assemble as unicode extension from the name/comment and
+ * encoding of the original zip entry.
+ *
+ * @param text The file name or comment.
+ * @param bytes The encoded of the filename or comment in the zip
+ * file.
+ * @param off The offset of the encoded filename or comment in
+ * <code>bytes</code>.
+ * @param len The length of the encoded filename or commentin
+ * <code>bytes</code>.
+ */
+ protected AbstractUnicodeExtraField(final String text, final byte[] bytes, final int off,
+ final int len) {
+ final CRC32 crc32 = new CRC32();
+ crc32.update(bytes, off, len);
+ nameCRC32 = crc32.getValue();
+
+ try {
+ unicodeName = text.getBytes("UTF-8");
+ } catch (final UnsupportedEncodingException e) {
+ throw new RuntimeException("FATAL: UTF-8 encoding not supported.",
+ e);
+ }
+ }
+
+ /**
+ * Assemble as unicode extension from the name/comment and
+ * encoding of the original zip entry.
+ *
+ * @param text The file name or comment.
+ * @param bytes The encoded of the filename or comment in the zip
+ * file.
+ */
+ protected AbstractUnicodeExtraField(final String text, final byte[] bytes) {
+
+ this(text, bytes, 0, bytes.length);
+ }
+
+ private void assembleData() {
+ if (unicodeName == null) {
+ return;
+ }
+
+ data = new byte[5 + unicodeName.length];
+ // version 1
+ data[0] = 0x01;
+ System.arraycopy(ZipLong.getBytes(nameCRC32), 0, data, 1, 4);
+ System.arraycopy(unicodeName, 0, data, 5, unicodeName.length);
+ }
+
+ /**
+ * @return The CRC32 checksum of the filename or comment as
+ * encoded in the central directory of the zip file.
+ */
+ public long getNameCRC32() {
+ return nameCRC32;
+ }
+
+ /**
+ * @param nameCRC32 The CRC32 checksum of the filename as encoded
+ * in the central directory of the zip file to set.
+ */
+ public void setNameCRC32(final long nameCRC32) {
+ this.nameCRC32 = nameCRC32;
+ data = null;
+ }
+
+ /**
+ * @return The utf-8 encoded name.
+ */
+ public byte[] getUnicodeName() {
+ byte[] b = null;
+ if (unicodeName != null) {
+ b = new byte[unicodeName.length];
+ System.arraycopy(unicodeName, 0, b, 0, b.length);
+ }
+ return b;
+ }
+
+ /**
+ * @param unicodeName The utf-8 encoded name to set.
+ */
+ public void setUnicodeName(final byte[] unicodeName) {
+ if (unicodeName != null) {
+ this.unicodeName = new byte[unicodeName.length];
+ System.arraycopy(unicodeName, 0, this.unicodeName, 0,
+ unicodeName.length);
+ } else {
+ this.unicodeName = null;
+ }
+ data = null;
+ }
+
+ /** {@inheritDoc} */
+ public byte[] getCentralDirectoryData() {
+ if (data == null) {
+ this.assembleData();
+ }
+ byte[] b = null;
+ if (data != null) {
+ b = new byte[data.length];
+ System.arraycopy(data, 0, b, 0, b.length);
+ }
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ public ZipShort getCentralDirectoryLength() {
+ if (data == null) {
+ assembleData();
+ }
+ return new ZipShort(data.length);
+ }
+
+ /** {@inheritDoc} */
+ public byte[] getLocalFileDataData() {
+ return getCentralDirectoryData();
+ }
+
+ /** {@inheritDoc} */
+ public ZipShort getLocalFileDataLength() {
+ return getCentralDirectoryLength();
+ }
+
+ /** {@inheritDoc} */
+ public void parseFromLocalFileData(final byte[] buffer, final int offset, final int length)
+ throws ZipException {
+
+ if (length < 5) {
+ throw new ZipException("UniCode path extra data must have at least"
+ + " 5 bytes.");
+ }
+
+ final int version = buffer[offset];
+
+ if (version != 0x01) {
+ throw new ZipException("Unsupported version [" + version
+ + "] for UniCode path extra data.");
+ }
+
+ nameCRC32 = ZipLong.getValue(buffer, offset + 1);
+ unicodeName = new byte[length - 5];
+ System.arraycopy(buffer, offset + 5, unicodeName, 0, length - 5);
+ data = null;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/AsiExtraField.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/AsiExtraField.java
new file mode 100644
index 00000000..d2ca6910
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/AsiExtraField.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.tools.zip;
+
+import java.util.zip.CRC32;
+import java.util.zip.ZipException;
+
+/**
+ * Adds Unix file permission and UID/GID fields as well as symbolic
+ * link handling.
+ *
+ * <p>This class uses the ASi extra field in the format:
+ * <pre>
+ * Value Size Description
+ * ----- ---- -----------
+ * (Unix3) 0x756e Short tag for this extra block type
+ * TSize Short total data size for this block
+ * CRC Long CRC-32 of the remaining data
+ * Mode Short file permissions
+ * SizDev Long symlink'd size OR major/minor dev num
+ * UID Short user ID
+ * GID Short group ID
+ * (var.) variable symbolic link filename
+ * </pre>
+ * taken from appnote.iz (Info-ZIP note, 981119) found at <a
+ * href="ftp://ftp.uu.net/pub/archiving/zip/doc/">ftp://ftp.uu.net/pub/archiving/zip/doc/</a></p>
+
+ *
+ * <p>Short is two bytes and Long is four bytes in big endian byte and
+ * word order, device numbers are currently not supported.</p>
+ *
+ * <p>Since the documentation this class is based upon doesn't mention
+ * the character encoding of the file name at all, it is assumed that
+ * it uses the current platform's default encoding.</p>
+ */
+public class AsiExtraField implements ZipExtraField, UnixStat, Cloneable {
+
+ private static final ZipShort HEADER_ID = new ZipShort(0x756E);
+ private static final int WORD = 4;
+ /**
+ * Standard Unix stat(2) file mode.
+ *
+ * @since 1.1
+ */
+ private int mode = 0;
+ /**
+ * User ID.
+ *
+ * @since 1.1
+ */
+ private int uid = 0;
+ /**
+ * Group ID.
+ *
+ * @since 1.1
+ */
+ private int gid = 0;
+ /**
+ * File this entry points to, if it is a symbolic link.
+ *
+ * <p>empty string - if entry is not a symbolic link.</p>
+ *
+ * @since 1.1
+ */
+ private String link = "";
+ /**
+ * Is this an entry for a directory?
+ *
+ * @since 1.1
+ */
+ private boolean dirFlag = false;
+
+ /**
+ * Instance used to calculate checksums.
+ *
+ * @since 1.1
+ */
+ private CRC32 crc = new CRC32();
+
+ /** Constructor for AsiExtraField. */
+ public AsiExtraField() {
+ }
+
+ /**
+ * The Header-ID.
+ * @return the value for the header id for this extrafield
+ * @since 1.1
+ */
+ public ZipShort getHeaderId() {
+ return HEADER_ID;
+ }
+
+ /**
+ * Length of the extra field in the local file data - without
+ * Header-ID or length specifier.
+ * @return a <code>ZipShort</code> for the length of the data of this extra field
+ * @since 1.1
+ */
+ public ZipShort getLocalFileDataLength() {
+ return new ZipShort(WORD // CRC
+ + 2 // Mode
+ + WORD // SizDev
+ + 2 // UID
+ + 2 // GID
+ + getLinkedFile().getBytes().length);
+ // Uses default charset - see class Javadoc
+ }
+
+ /**
+ * Delegate to local file data.
+ * @return the centralDirectory length
+ * @since 1.1
+ */
+ public ZipShort getCentralDirectoryLength() {
+ return getLocalFileDataLength();
+ }
+
+ /**
+ * The actual data to put into local file data - without Header-ID
+ * or length specifier.
+ * @return get the data
+ * @since 1.1
+ */
+ public byte[] getLocalFileDataData() {
+ // CRC will be added later
+ byte[] data = new byte[getLocalFileDataLength().getValue() - WORD];
+ System.arraycopy(ZipShort.getBytes(getMode()), 0, data, 0, 2);
+
+ byte[] linkArray = getLinkedFile().getBytes(); // Uses default charset - see class Javadoc
+ // CheckStyle:MagicNumber OFF
+ System.arraycopy(ZipLong.getBytes(linkArray.length),
+ 0, data, 2, WORD);
+
+ System.arraycopy(ZipShort.getBytes(getUserId()),
+ 0, data, 6, 2);
+ System.arraycopy(ZipShort.getBytes(getGroupId()),
+ 0, data, 8, 2);
+
+ System.arraycopy(linkArray, 0, data, 10, linkArray.length);
+ // CheckStyle:MagicNumber ON
+
+ crc.reset();
+ crc.update(data);
+ long checksum = crc.getValue();
+
+ byte[] result = new byte[data.length + WORD];
+ System.arraycopy(ZipLong.getBytes(checksum), 0, result, 0, WORD);
+ System.arraycopy(data, 0, result, WORD, data.length);
+ return result;
+ }
+
+ /**
+ * Delegate to local file data.
+ * @return the local file data
+ * @since 1.1
+ */
+ public byte[] getCentralDirectoryData() {
+ return getLocalFileDataData();
+ }
+
+ /**
+ * Set the user id.
+ * @param uid the user id
+ * @since 1.1
+ */
+ public void setUserId(int uid) {
+ this.uid = uid;
+ }
+
+ /**
+ * Get the user id.
+ * @return the user id
+ * @since 1.1
+ */
+ public int getUserId() {
+ return uid;
+ }
+
+ /**
+ * Set the group id.
+ * @param gid the group id
+ * @since 1.1
+ */
+ public void setGroupId(int gid) {
+ this.gid = gid;
+ }
+
+ /**
+ * Get the group id.
+ * @return the group id
+ * @since 1.1
+ */
+ public int getGroupId() {
+ return gid;
+ }
+
+ /**
+ * Indicate that this entry is a symbolic link to the given filename.
+ *
+ * @param name Name of the file this entry links to, empty String
+ * if it is not a symbolic link.
+ *
+ * @since 1.1
+ */
+ public void setLinkedFile(String name) {
+ link = name;
+ mode = getMode(mode);
+ }
+
+ /**
+ * Name of linked file
+ *
+ * @return name of the file this entry links to if it is a
+ * symbolic link, the empty string otherwise.
+ *
+ * @since 1.1
+ */
+ public String getLinkedFile() {
+ return link;
+ }
+
+ /**
+ * Is this entry a symbolic link?
+ * @return true if this is a symbolic link
+ * @since 1.1
+ */
+ public boolean isLink() {
+ return getLinkedFile().length() != 0;
+ }
+
+ /**
+ * File mode of this file.
+ * @param mode the file mode
+ * @since 1.1
+ */
+ public void setMode(int mode) {
+ this.mode = getMode(mode);
+ }
+
+ /**
+ * File mode of this file.
+ * @return the file mode
+ * @since 1.1
+ */
+ public int getMode() {
+ return mode;
+ }
+
+ /**
+ * Indicate whether this entry is a directory.
+ * @param dirFlag if true, this entry is a directory
+ * @since 1.1
+ */
+ public void setDirectory(boolean dirFlag) {
+ this.dirFlag = dirFlag;
+ mode = getMode(mode);
+ }
+
+ /**
+ * Is this entry a directory?
+ * @return true if this entry is a directory
+ * @since 1.1
+ */
+ public boolean isDirectory() {
+ return dirFlag && !isLink();
+ }
+
+ /**
+ * Populate data from this array as if it was in local file data.
+ * @param data an array of bytes
+ * @param offset the start offset
+ * @param length the number of bytes in the array from offset
+ * @since 1.1
+ * @throws ZipException on error
+ */
+ public void parseFromLocalFileData(byte[] data, int offset, int length)
+ throws ZipException {
+
+ long givenChecksum = ZipLong.getValue(data, offset);
+ byte[] tmp = new byte[length - WORD];
+ System.arraycopy(data, offset + WORD, tmp, 0, length - WORD);
+ crc.reset();
+ crc.update(tmp);
+ long realChecksum = crc.getValue();
+ if (givenChecksum != realChecksum) {
+ throw new ZipException("bad CRC checksum "
+ + Long.toHexString(givenChecksum)
+ + " instead of "
+ + Long.toHexString(realChecksum));
+ }
+
+ int newMode = ZipShort.getValue(tmp, 0);
+ // CheckStyle:MagicNumber OFF
+ byte[] linkArray = new byte[(int) ZipLong.getValue(tmp, 2)];
+ uid = ZipShort.getValue(tmp, 6);
+ gid = ZipShort.getValue(tmp, 8);
+
+ if (linkArray.length == 0) {
+ link = "";
+ } else {
+ System.arraycopy(tmp, 10, linkArray, 0, linkArray.length);
+ link = new String(linkArray); // Uses default charset - see class Javadoc
+ }
+ // CheckStyle:MagicNumber ON
+ setDirectory((newMode & DIR_FLAG) != 0);
+ setMode(newMode);
+ }
+
+ /**
+ * Get the file mode for given permissions with the correct file type.
+ * @param mode the mode
+ * @return the type with the mode
+ * @since 1.1
+ */
+ protected int getMode(int mode) {
+ int type = FILE_FLAG;
+ if (isLink()) {
+ type = LINK_FLAG;
+ } else if (isDirectory()) {
+ type = DIR_FLAG;
+ }
+ return type | (mode & PERM_MASK);
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ AsiExtraField cloned = (AsiExtraField) super.clone();
+ cloned.crc = new CRC32();
+ return cloned;
+ } catch (CloneNotSupportedException cnfe) {
+ // impossible
+ throw new RuntimeException(cnfe);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/CentralDirectoryParsingZipExtraField.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/CentralDirectoryParsingZipExtraField.java
new file mode 100644
index 00000000..738ed625
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/CentralDirectoryParsingZipExtraField.java
@@ -0,0 +1,40 @@
+/*
+ * 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.tools.zip;
+
+import java.util.zip.ZipException;
+
+/**
+ * {@link ZipExtraField ZipExtraField} that knows how to parse central
+ * directory data.
+ *
+ * @since Ant 1.8.0
+ */
+public interface CentralDirectoryParsingZipExtraField extends ZipExtraField {
+ /**
+ * Populate data from this array as if it was in central directory data.
+ * @param data an array of bytes
+ * @param offset the start offset
+ * @param length the number of bytes in the array from offset
+ *
+ * @throws ZipException on error
+ */
+ void parseFromCentralDirectoryData(byte[] data, int offset, int length)
+ throws ZipException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ExtraFieldUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ExtraFieldUtils.java
new file mode 100644
index 00000000..a6c0118f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ExtraFieldUtils.java
@@ -0,0 +1,314 @@
+/*
+ * 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.tools.zip;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.zip.ZipException;
+
+/**
+ * ZipExtraField related methods
+ *
+ */
+// CheckStyle:HideUtilityClassConstructorCheck OFF (bc)
+public class ExtraFieldUtils {
+
+ private static final int WORD = 4;
+
+ /**
+ * Static registry of known extra fields.
+ *
+ * @since 1.1
+ */
+ private static final Map<ZipShort, Class<?>> implementations;
+
+ static {
+ implementations = new ConcurrentHashMap<ZipShort, Class<?>>();
+ register(AsiExtraField.class);
+ register(JarMarker.class);
+ register(UnicodePathExtraField.class);
+ register(UnicodeCommentExtraField.class);
+ register(Zip64ExtendedInformationExtraField.class);
+ }
+
+ /**
+ * Register a ZipExtraField implementation.
+ *
+ * <p>The given class must have a no-arg constructor and implement
+ * the {@link ZipExtraField ZipExtraField interface}.</p>
+ * @param c the class to register
+ *
+ * @since 1.1
+ */
+ public static void register(Class<?> c) {
+ try {
+ ZipExtraField ze = (ZipExtraField) c.newInstance();
+ implementations.put(ze.getHeaderId(), c);
+ } catch (ClassCastException cc) {
+ throw new RuntimeException(c + " doesn\'t implement ZipExtraField");
+ } catch (InstantiationException ie) {
+ throw new RuntimeException(c + " is not a concrete class");
+ } catch (IllegalAccessException ie) {
+ throw new RuntimeException(c + "\'s no-arg constructor is not public");
+ }
+ }
+
+ /**
+ * Create an instance of the appropriate ExtraField, falls back to
+ * {@link UnrecognizedExtraField UnrecognizedExtraField}.
+ * @param headerId the header identifier
+ * @return an instance of the appropriate ExtraField
+ * @exception InstantiationException if unable to instantiate the class
+ * @exception IllegalAccessException if not allowed to instantiate the class
+ * @since 1.1
+ */
+ public static ZipExtraField createExtraField(ZipShort headerId)
+ throws InstantiationException, IllegalAccessException {
+ Class<?> c = implementations.get(headerId);
+ if (c != null) {
+ return (ZipExtraField) c.newInstance();
+ }
+ UnrecognizedExtraField u = new UnrecognizedExtraField();
+ u.setHeaderId(headerId);
+ return u;
+ }
+
+ /**
+ * Split the array into ExtraFields and populate them with the
+ * given data as local file data, throwing an exception if the
+ * data cannot be parsed.
+ * @param data an array of bytes as it appears in local file data
+ * @return an array of ExtraFields
+ * @throws ZipException on error
+ */
+ public static ZipExtraField[] parse(byte[] data) throws ZipException {
+ return parse(data, true, UnparseableExtraField.THROW);
+ }
+
+ /**
+ * Split the array into ExtraFields and populate them with the
+ * given data, throwing an exception if the data cannot be parsed.
+ * @param data an array of bytes
+ * @param local whether data originates from the local file data
+ * or the central directory
+ * @return an array of ExtraFields
+ * @since 1.1
+ * @throws ZipException on error
+ */
+ public static ZipExtraField[] parse(byte[] data, boolean local)
+ throws ZipException {
+ return parse(data, local, UnparseableExtraField.THROW);
+ }
+
+ /**
+ * Split the array into ExtraFields and populate them with the
+ * given data.
+ * @param data an array of bytes
+ * @param local whether data originates from the local file data
+ * or the central directory
+ * @param onUnparseableData what to do if the extra field data
+ * cannot be parsed.
+ * @return an array of ExtraFields
+ * @throws ZipException on error
+ * @since Ant 1.8.1
+ */
+ public static ZipExtraField[] parse(byte[] data, boolean local,
+ UnparseableExtraField onUnparseableData)
+ throws ZipException {
+ List<ZipExtraField> v = new ArrayList<ZipExtraField>();
+ int start = 0;
+ LOOP:
+ while (start <= data.length - WORD) {
+ ZipShort headerId = new ZipShort(data, start);
+ int length = (new ZipShort(data, start + 2)).getValue();
+ if (start + WORD + length > data.length) {
+ switch(onUnparseableData.getKey()) {
+ case UnparseableExtraField.THROW_KEY:
+ throw new ZipException("bad extra field starting at "
+ + start + ". Block length of "
+ + length + " bytes exceeds remaining"
+ + " data of "
+ + (data.length - start - WORD)
+ + " bytes.");
+ case UnparseableExtraField.READ_KEY:
+ UnparseableExtraFieldData field =
+ new UnparseableExtraFieldData();
+ if (local) {
+ field.parseFromLocalFileData(data, start,
+ data.length - start);
+ } else {
+ field.parseFromCentralDirectoryData(data, start,
+ data.length - start);
+ }
+ v.add(field);
+ //$FALL-THROUGH$
+ case UnparseableExtraField.SKIP_KEY:
+ // since we cannot parse the data we must assume
+ // the extra field consumes the whole rest of the
+ // available data
+ break LOOP;
+ default:
+ throw new ZipException("unknown UnparseableExtraField key: "
+ + onUnparseableData.getKey());
+ }
+ }
+ try {
+ ZipExtraField ze = createExtraField(headerId);
+ if (local
+ || !(ze instanceof CentralDirectoryParsingZipExtraField)) {
+ ze.parseFromLocalFileData(data, start + WORD, length);
+ } else {
+ ((CentralDirectoryParsingZipExtraField) ze)
+ .parseFromCentralDirectoryData(data, start + WORD,
+ length);
+ }
+ v.add(ze);
+ } catch (InstantiationException ie) {
+ throw new ZipException(ie.getMessage());
+ } catch (IllegalAccessException iae) {
+ throw new ZipException(iae.getMessage());
+ }
+ start += (length + WORD);
+ }
+
+ ZipExtraField[] result = new ZipExtraField[v.size()];
+ return v.toArray(result);
+ }
+
+ /**
+ * Merges the local file data fields of the given ZipExtraFields.
+ * @param data an array of ExtraFiles
+ * @return an array of bytes
+ * @since 1.1
+ */
+ public static byte[] mergeLocalFileDataData(ZipExtraField[] data) {
+ final boolean lastIsUnparseableHolder = data.length > 0
+ && data[data.length - 1] instanceof UnparseableExtraFieldData;
+ int regularExtraFieldCount =
+ lastIsUnparseableHolder ? data.length - 1 : data.length;
+
+ int sum = WORD * regularExtraFieldCount;
+ for (ZipExtraField element : data) {
+ sum += element.getLocalFileDataLength().getValue();
+ }
+
+ byte[] result = new byte[sum];
+ int start = 0;
+ for (int i = 0; i < regularExtraFieldCount; i++) {
+ System.arraycopy(data[i].getHeaderId().getBytes(),
+ 0, result, start, 2);
+ System.arraycopy(data[i].getLocalFileDataLength().getBytes(),
+ 0, result, start + 2, 2);
+ byte[] local = data[i].getLocalFileDataData();
+ System.arraycopy(local, 0, result, start + WORD, local.length);
+ start += (local.length + WORD);
+ }
+ if (lastIsUnparseableHolder) {
+ byte[] local = data[data.length - 1].getLocalFileDataData();
+ System.arraycopy(local, 0, result, start, local.length);
+ }
+ return result;
+ }
+
+ /**
+ * Merges the central directory fields of the given ZipExtraFields.
+ * @param data an array of ExtraFields
+ * @return an array of bytes
+ * @since 1.1
+ */
+ public static byte[] mergeCentralDirectoryData(ZipExtraField[] data) {
+ final boolean lastIsUnparseableHolder = data.length > 0
+ && data[data.length - 1] instanceof UnparseableExtraFieldData;
+ int regularExtraFieldCount =
+ lastIsUnparseableHolder ? data.length - 1 : data.length;
+
+ int sum = WORD * regularExtraFieldCount;
+ for (ZipExtraField element : data) {
+ sum += element.getCentralDirectoryLength().getValue();
+ }
+ byte[] result = new byte[sum];
+ int start = 0;
+ for (int i = 0; i < regularExtraFieldCount; i++) {
+ System.arraycopy(data[i].getHeaderId().getBytes(),
+ 0, result, start, 2);
+ System.arraycopy(data[i].getCentralDirectoryLength().getBytes(),
+ 0, result, start + 2, 2);
+ byte[] local = data[i].getCentralDirectoryData();
+ System.arraycopy(local, 0, result, start + WORD, local.length);
+ start += (local.length + WORD);
+ }
+ if (lastIsUnparseableHolder) {
+ byte[] local = data[data.length - 1].getCentralDirectoryData();
+ System.arraycopy(local, 0, result, start, local.length);
+ }
+ return result;
+ }
+
+ /**
+ * "enum" for the possible actions to take if the extra field
+ * cannot be parsed.
+ */
+ public static final class UnparseableExtraField {
+ /**
+ * Key for "throw an exception" action.
+ */
+ public static final int THROW_KEY = 0;
+ /**
+ * Key for "skip" action.
+ */
+ public static final int SKIP_KEY = 1;
+ /**
+ * Key for "read" action.
+ */
+ public static final int READ_KEY = 2;
+
+ /**
+ * Throw an exception if field cannot be parsed.
+ */
+ public static final UnparseableExtraField THROW
+ = new UnparseableExtraField(THROW_KEY);
+
+ /**
+ * Skip the extra field entirely and don't make its data
+ * available - effectively removing the extra field data.
+ */
+ public static final UnparseableExtraField SKIP
+ = new UnparseableExtraField(SKIP_KEY);
+
+ /**
+ * Read the extra field data into an instance of {@link
+ * UnparseableExtraFieldData UnparseableExtraFieldData}.
+ */
+ public static final UnparseableExtraField READ
+ = new UnparseableExtraField(READ_KEY);
+
+ private final int key;
+
+ private UnparseableExtraField(int k) {
+ key = k;
+ }
+
+ /**
+ * Key of the action to take.
+ */
+ public int getKey() { return key; }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/FallbackZipEncoding.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/FallbackZipEncoding.java
new file mode 100644
index 00000000..3edbb8e1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/FallbackZipEncoding.java
@@ -0,0 +1,94 @@
+/*
+ * 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.tools.zip;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * A fallback ZipEncoding, which uses a java.io means to encode names.
+ *
+ * <p>This implementation is not favorable for encodings other than
+ * utf-8, because java.io encodes unmappable character as question
+ * marks leading to unreadable ZIP entries on some operating
+ * systems.</p>
+ *
+ * <p>Furthermore this implementation is unable to tell whether a
+ * given name can be safely encoded or not.</p>
+ *
+ * <p>This implementation acts as a last resort implementation, when
+ * neither {@link Simple8BitZipEnoding} nor {@link NioZipEncoding} is
+ * available.</p>
+ *
+ * <p>The methods of this class are reentrant.</p>
+ */
+class FallbackZipEncoding implements ZipEncoding {
+ private final String charset;
+
+ /**
+ * Construct a fallback zip encoding, which uses the platform's
+ * default charset.
+ */
+ public FallbackZipEncoding() {
+ this.charset = null;
+ }
+
+ /**
+ * Construct a fallback zip encoding, which uses the given charset.
+ *
+ * @param charset The name of the charset or {@code null} for
+ * the platform's default character set.
+ */
+ public FallbackZipEncoding(final String charset) {
+ this.charset = charset;
+ }
+
+ /**
+ * @see
+ * org.apache.tools.zip.ZipEncoding#canEncode(java.lang.String)
+ */
+ public boolean canEncode(final String name) {
+ return true;
+ }
+
+ /**
+ * @see
+ * org.apache.tools.zip.ZipEncoding#encode(java.lang.String)
+ */
+ public ByteBuffer encode(final String name) throws IOException {
+ if (this.charset == null) { // i.e. use default charset, see no-args constructor
+ return ByteBuffer.wrap(name.getBytes());
+ } else {
+ return ByteBuffer.wrap(name.getBytes(this.charset));
+ }
+ }
+
+ /**
+ * @see
+ * org.apache.tools.zip.ZipEncoding#decode(byte[])
+ */
+ public String decode(final byte[] data) throws IOException {
+ if (this.charset == null) { // i.e. use default charset, see no-args constructor
+ return new String(data);
+ } else {
+ return new String(data,this.charset);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/GeneralPurposeBit.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/GeneralPurposeBit.java
new file mode 100644
index 00000000..1d2255fa
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/GeneralPurposeBit.java
@@ -0,0 +1,194 @@
+/*
+ * 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.tools.zip;
+
+/**
+ * Parser/encoder for the "general purpose bit" field in ZIP's local
+ * file and central directory headers.
+ *
+ * @since Ant 1.9.0
+ */
+public final class GeneralPurposeBit implements Cloneable {
+ /**
+ * Indicates that the file is encrypted.
+ */
+ private static final int ENCRYPTION_FLAG = 1 << 0;
+
+ /**
+ * Indicates that a data descriptor stored after the file contents
+ * will hold CRC and size information.
+ */
+ private static final int DATA_DESCRIPTOR_FLAG = 1 << 3;
+
+ /**
+ * Indicates strong encryption.
+ */
+ private static final int STRONG_ENCRYPTION_FLAG = 1 << 6;
+
+ /**
+ * Indicates that filenames are written in utf-8.
+ *
+ * <p>The only reason this is public is that {@link
+ * ZipOutputStream#EFS_FLAG} was public in several versions of
+ * Apache Ant and we needed a substitute for it.</p>
+ */
+ public static final int UFT8_NAMES_FLAG = 1 << 11;
+
+ private boolean languageEncodingFlag = false;
+ private boolean dataDescriptorFlag = false;
+ private boolean encryptionFlag = false;
+ private boolean strongEncryptionFlag = false;
+
+ public GeneralPurposeBit() {
+ }
+
+ /**
+ * whether the current entry uses UTF8 for file name and comment.
+ */
+ public boolean usesUTF8ForNames() {
+ return languageEncodingFlag;
+ }
+
+ /**
+ * whether the current entry will use UTF8 for file name and comment.
+ */
+ public void useUTF8ForNames(boolean b) {
+ languageEncodingFlag = b;
+ }
+
+ /**
+ * whether the current entry uses the data descriptor to store CRC
+ * and size information
+ */
+ public boolean usesDataDescriptor() {
+ return dataDescriptorFlag;
+ }
+
+ /**
+ * whether the current entry will use the data descriptor to store
+ * CRC and size information
+ */
+ public void useDataDescriptor(boolean b) {
+ dataDescriptorFlag = b;
+ }
+
+ /**
+ * whether the current entry is encrypted
+ */
+ public boolean usesEncryption() {
+ return encryptionFlag;
+ }
+
+ /**
+ * whether the current entry will be encrypted
+ */
+ public void useEncryption(boolean b) {
+ encryptionFlag = b;
+ }
+
+ /**
+ * whether the current entry is encrypted using strong encryption
+ */
+ public boolean usesStrongEncryption() {
+ return encryptionFlag && strongEncryptionFlag;
+ }
+
+ /**
+ * whether the current entry will be encrypted using strong encryption
+ */
+ public void useStrongEncryption(boolean b) {
+ strongEncryptionFlag = b;
+ if (b) {
+ useEncryption(true);
+ }
+ }
+
+ /**
+ * Encodes the set bits in a form suitable for ZIP archives.
+ */
+ public byte[] encode() {
+ byte[] result = new byte[2];
+ encode(result, 0);
+ return result;
+ }
+
+ /**
+ * Encodes the set bits in a form suitable for ZIP archives.
+ *
+ * @param buf the output buffer
+ * @param offset
+ * The offset within the output buffer of the first byte to be written.
+ * must be non-negative and no larger than <tt>buf.length-2</tt>
+ */
+ public void encode(byte[] buf, int offset) {
+ ZipShort.putShort((dataDescriptorFlag ? DATA_DESCRIPTOR_FLAG : 0)
+ |
+ (languageEncodingFlag ? UFT8_NAMES_FLAG : 0)
+ |
+ (encryptionFlag ? ENCRYPTION_FLAG : 0)
+ |
+ (strongEncryptionFlag ? STRONG_ENCRYPTION_FLAG : 0)
+ , buf, offset);
+ }
+
+ /**
+ * Parses the supported flags from the given archive data.
+ * @param data local file header or a central directory entry.
+ * @param offset offset at which the general purpose bit starts
+ */
+ public static GeneralPurposeBit parse(final byte[] data, final int offset) {
+ final int generalPurposeFlag = ZipShort.getValue(data, offset);
+ GeneralPurposeBit b = new GeneralPurposeBit();
+ b.useDataDescriptor((generalPurposeFlag & DATA_DESCRIPTOR_FLAG) != 0);
+ b.useUTF8ForNames((generalPurposeFlag & UFT8_NAMES_FLAG) != 0);
+ b.useStrongEncryption((generalPurposeFlag & STRONG_ENCRYPTION_FLAG)
+ != 0);
+ b.useEncryption((generalPurposeFlag & ENCRYPTION_FLAG) != 0);
+ return b;
+ }
+
+ @Override
+ public int hashCode() {
+ return 3 * (7 * (13 * (17 * (encryptionFlag ? 1 : 0)
+ + (strongEncryptionFlag ? 1 : 0))
+ + (languageEncodingFlag ? 1 : 0))
+ + (dataDescriptorFlag ? 1 : 0));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof GeneralPurposeBit)) {
+ return false;
+ }
+ GeneralPurposeBit g = (GeneralPurposeBit) o;
+ return g.encryptionFlag == encryptionFlag
+ && g.strongEncryptionFlag == strongEncryptionFlag
+ && g.languageEncodingFlag == languageEncodingFlag
+ && g.dataDescriptorFlag == dataDescriptorFlag;
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException ex) {
+ // impossible
+ throw new RuntimeException("GeneralPurposeBit is not Cloneable?", ex);
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/JarMarker.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/JarMarker.java
new file mode 100644
index 00000000..c0633539
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/JarMarker.java
@@ -0,0 +1,108 @@
+/*
+ * 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.tools.zip;
+
+import java.util.zip.ZipException;
+
+/**
+ * If this extra field is added as the very first extra field of the
+ * archive, Solaris will consider it an executable jar file.
+ *
+ * @since Ant 1.6.3
+ */
+public final class JarMarker implements ZipExtraField {
+
+ private static final ZipShort ID = new ZipShort(0xCAFE);
+ private static final ZipShort NULL = new ZipShort(0);
+ private static final byte[] NO_BYTES = new byte[0];
+ private static final JarMarker DEFAULT = new JarMarker();
+
+ /** No-arg constructor */
+ public JarMarker() {
+ // empty
+ }
+
+ /**
+ * Since JarMarker is stateless we can always use the same instance.
+ * @return the DEFAULT jarmaker.
+ */
+ public static JarMarker getInstance() {
+ return DEFAULT;
+ }
+
+ /**
+ * The Header-ID.
+ * @return the header id
+ */
+ public ZipShort getHeaderId() {
+ return ID;
+ }
+
+ /**
+ * Length of the extra field in the local file data - without
+ * Header-ID or length specifier.
+ * @return 0
+ */
+ public ZipShort getLocalFileDataLength() {
+ return NULL;
+ }
+
+ /**
+ * Length of the extra field in the central directory - without
+ * Header-ID or length specifier.
+ * @return 0
+ */
+ public ZipShort getCentralDirectoryLength() {
+ return NULL;
+ }
+
+ /**
+ * The actual data to put into local file data - without Header-ID
+ * or length specifier.
+ * @return the data
+ * @since 1.1
+ */
+ public byte[] getLocalFileDataData() {
+ return NO_BYTES;
+ }
+
+ /**
+ * The actual data to put central directory - without Header-ID or
+ * length specifier.
+ * @return the data
+ */
+ public byte[] getCentralDirectoryData() {
+ return NO_BYTES;
+ }
+
+ /**
+ * Populate data from this array as if it was in local file data.
+ * @param data an array of bytes
+ * @param offset the start offset
+ * @param length the number of bytes in the array from offset
+ *
+ * @throws ZipException on error
+ */
+ public void parseFromLocalFileData(byte[] data, int offset, int length)
+ throws ZipException {
+ if (length != 0) {
+ throw new ZipException("JarMarker doesn't expect any data");
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/NioZipEncoding.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/NioZipEncoding.java
new file mode 100644
index 00000000..63d33ff5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/NioZipEncoding.java
@@ -0,0 +1,122 @@
+/*
+ * 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.tools.zip;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * A ZipEncoding, which uses a java.nio {@link
+ * java.nio.charset.Charset Charset} to encode names.
+ *
+ * <p>This implementation works for all cases under java-1.5 or
+ * later. However, in java-1.4, some charsets don't have a java.nio
+ * implementation, most notably the default ZIP encoding Cp437.</p>
+ *
+ * <p>The methods of this class are reentrant.</p>
+ */
+class NioZipEncoding implements ZipEncoding {
+ private final Charset charset;
+
+ /**
+ * Construct an NIO based zip encoding, which wraps the given
+ * charset.
+ *
+ * @param charset The NIO charset to wrap.
+ */
+ public NioZipEncoding(final Charset charset) {
+ this.charset = charset;
+ }
+
+ /**
+ * @see
+ * org.apache.tools.zip.ZipEncoding#canEncode(java.lang.String)
+ */
+ public boolean canEncode(final String name) {
+ final CharsetEncoder enc = this.charset.newEncoder();
+ enc.onMalformedInput(CodingErrorAction.REPORT);
+ enc.onUnmappableCharacter(CodingErrorAction.REPORT);
+
+ return enc.canEncode(name);
+ }
+
+ /**
+ * @see
+ * org.apache.tools.zip.ZipEncoding#encode(java.lang.String)
+ */
+ public ByteBuffer encode(final String name) {
+ final CharsetEncoder enc = this.charset.newEncoder();
+
+ enc.onMalformedInput(CodingErrorAction.REPORT);
+ enc.onUnmappableCharacter(CodingErrorAction.REPORT);
+
+ final CharBuffer cb = CharBuffer.wrap(name);
+ ByteBuffer out = ByteBuffer.allocate(name.length()
+ + (name.length() + 1) / 2);
+
+ while (cb.remaining() > 0) {
+ final CoderResult res = enc.encode(cb, out,true);
+
+ if (res.isUnmappable() || res.isMalformed()) {
+
+ // write the unmappable characters in utf-16
+ // pseudo-URL encoding style to ByteBuffer.
+ if (res.length() * 6 > out.remaining()) {
+ out = ZipEncodingHelper.growBuffer(out, out.position()
+ + res.length() * 6);
+ }
+
+ for (int i=0; i<res.length(); ++i) {
+ ZipEncodingHelper.appendSurrogate(out,cb.get());
+ }
+
+ } else if (res.isOverflow()) {
+
+ out = ZipEncodingHelper.growBuffer(out, 0);
+
+ } else if (res.isUnderflow()) {
+
+ enc.flush(out);
+ break;
+
+ }
+ }
+
+ out.limit(out.position());
+ out.rewind();
+ return out;
+ }
+
+ /**
+ * @see
+ * org.apache.tools.zip.ZipEncoding#decode(byte[])
+ */
+ public String decode(final byte[] data) throws IOException {
+ return this.charset.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT)
+ .decode(ByteBuffer.wrap(data)).toString();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Simple8BitZipEncoding.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Simple8BitZipEncoding.java
new file mode 100644
index 00000000..57a838ca
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Simple8BitZipEncoding.java
@@ -0,0 +1,274 @@
+/*
+ * 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.tools.zip;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This ZipEncoding implementation implements a simple 8bit character
+ * set, which mets the following restrictions:
+ *
+ * <ul>
+ * <li>Characters 0x0000 to 0x007f are encoded as the corresponding
+ * byte values 0x00 to 0x7f.</li>
+ * <li>All byte codes from 0x80 to 0xff are mapped to a unique unicode
+ * character in the range 0x0080 to 0x7fff. (No support for
+ * UTF-16 surrogates)
+ * </ul>
+ *
+ * <p>These restrictions most notably apply to the most prominent
+ * omissions of java-1.4's {@link java.nio.charset.Charset Charset}
+ * implementation, Cp437 and Cp850.</p>
+ *
+ * <p>The methods of this class are reentrant.</p>
+ */
+class Simple8BitZipEncoding implements ZipEncoding {
+
+ /**
+ * A character entity, which is put to the reverse mapping table
+ * of a simple encoding.
+ */
+ private static final class Simple8BitChar implements Comparable<Simple8BitChar> {
+ public final char unicode;
+ public final byte code;
+
+ Simple8BitChar(final byte code, final char unicode) {
+ this.code = code;
+ this.unicode = unicode;
+ }
+
+ public int compareTo(final Simple8BitChar a) {
+ return this.unicode - a.unicode;
+ }
+
+ @Override
+ public String toString() {
+ return "0x" + Integer.toHexString(0xffff & unicode)
+ + "->0x" + Integer.toHexString(0xff & code);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o instanceof Simple8BitChar) {
+ final Simple8BitChar other = (Simple8BitChar) o;
+ return unicode == other.unicode && code == other.code;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return unicode;
+ }
+ }
+
+ /**
+ * The characters for byte values of 128 to 255 stored as an array of
+ * 128 chars.
+ */
+ private final char[] highChars;
+
+ /**
+ * A list of {@link Simple8BitChar} objects sorted by the unicode
+ * field. This list is used to binary search reverse mapping of
+ * unicode characters with a character code greater than 127.
+ */
+ private final List<Simple8BitChar> reverseMapping;
+
+ /**
+ * @param highChars The characters for byte values of 128 to 255
+ * stored as an array of 128 chars.
+ */
+ public Simple8BitZipEncoding(final char[] highChars) {
+ this.highChars = highChars.clone();
+ final List<Simple8BitChar> temp =
+ new ArrayList<Simple8BitChar>(this.highChars.length);
+
+ byte code = 127;
+
+ for (int i = 0; i < this.highChars.length; ++i) {
+ temp.add(new Simple8BitChar(++code, this.highChars[i]));
+ }
+
+ Collections.sort(temp);
+ this.reverseMapping = Collections.unmodifiableList(temp);
+ }
+
+ /**
+ * Return the character code for a given encoded byte.
+ *
+ * @param b The byte to decode.
+ * @return The associated character value.
+ */
+ public char decodeByte(final byte b) {
+ // code 0-127
+ if (b >= 0) {
+ return (char) b;
+ }
+
+ // byte is signed, so 128 == -128 and 255 == -1
+ return this.highChars[128 + b];
+ }
+
+ /**
+ * @param c The character to encode.
+ * @return Whether the given unicode character is covered by this encoding.
+ */
+ public boolean canEncodeChar(final char c) {
+
+ if (c >= 0 && c < 128) {
+ return true;
+ }
+
+ final Simple8BitChar r = this.encodeHighChar(c);
+ return r != null;
+ }
+
+ /**
+ * Pushes the encoded form of the given character to the given byte buffer.
+ *
+ * @param bb The byte buffer to write to.
+ * @param c The character to encode.
+ * @return Whether the given unicode character is covered by this encoding.
+ * If {@code false} is returned, nothing is pushed to the
+ * byte buffer.
+ */
+ public boolean pushEncodedChar(final ByteBuffer bb, final char c) {
+
+ if (c >= 0 && c < 128) {
+ bb.put((byte) c);
+ return true;
+ }
+
+ final Simple8BitChar r = this.encodeHighChar(c);
+ if (r == null) {
+ return false;
+ }
+ bb.put(r.code);
+ return true;
+ }
+
+ /**
+ * @param c A unicode character in the range from 0x0080 to 0x7f00
+ * @return A Simple8BitChar, if this character is covered by this encoding.
+ * A {@code null} value is returned, if this character is not
+ * covered by this encoding.
+ */
+ private Simple8BitChar encodeHighChar(final char c) {
+ // for performance an simplicity, yet another reincarnation of
+ // binary search...
+ int i0 = 0;
+ int i1 = this.reverseMapping.size();
+
+ while (i1 > i0) {
+
+ final int i = i0 + (i1 - i0) / 2;
+
+ final Simple8BitChar m = this.reverseMapping.get(i);
+
+ if (m.unicode == c) {
+ return m;
+ }
+
+ if (m.unicode < c) {
+ i0 = i + 1;
+ } else {
+ i1 = i;
+ }
+ }
+
+ if (i0 >= this.reverseMapping.size()) {
+ return null;
+ }
+
+ final Simple8BitChar r = this.reverseMapping.get(i0);
+
+ if (r.unicode != c) {
+ return null;
+ }
+
+ return r;
+ }
+
+ /**
+ * @see
+ * org.apache.tools.zip.ZipEncoding#canEncode(java.lang.String)
+ */
+ public boolean canEncode(final String name) {
+
+ for (int i=0;i<name.length();++i) {
+
+ final char c = name.charAt(i);
+
+ if (!this.canEncodeChar(c)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @see
+ * org.apache.tools.zip.ZipEncoding#encode(java.lang.String)
+ */
+ public ByteBuffer encode(final String name) {
+ ByteBuffer out = ByteBuffer.allocate(name.length()
+ + 6 + (name.length() + 1) / 2);
+
+ for (int i=0;i<name.length();++i) {
+
+ final char c = name.charAt(i);
+
+ if (out.remaining() < 6) {
+ out = ZipEncodingHelper.growBuffer(out,out.position() + 6);
+ }
+
+ if (!this.pushEncodedChar(out,c)) {
+
+ ZipEncodingHelper.appendSurrogate(out,c);
+ }
+ }
+
+ out.limit(out.position());
+ out.rewind();
+ return out;
+ }
+
+ /**
+ * @see
+ * org.apache.tools.zip.ZipEncoding#decode(byte[])
+ */
+ public String decode(final byte[] data) throws IOException {
+ final char [] ret = new char[data.length];
+
+ for (int i=0;i<data.length;++i) {
+ ret[i] = this.decodeByte(data[i]);
+ }
+
+ return new String(ret);
+ }
+
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnicodeCommentExtraField.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnicodeCommentExtraField.java
new file mode 100644
index 00000000..51550fe7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnicodeCommentExtraField.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.tools.zip;
+
+/**
+ * Info-ZIP Unicode Comment Extra Field (0x6375):
+ *
+ * <p>Stores the UTF-8 version of the file comment as stored in the
+ * central directory header.</p>
+ *
+ * <p>See {@link
+ * "http://www.pkware.com/documents/casestudies/APPNOTE.TXT PKWARE's
+ * APPNOTE.TXT, section 4.6.8"}.</p>
+ *
+ */
+public class UnicodeCommentExtraField extends AbstractUnicodeExtraField {
+
+ public static final ZipShort UCOM_ID = new ZipShort(0x6375);
+
+ public UnicodeCommentExtraField () {
+ }
+
+ /**
+ * Assemble as unicode comment extension from the name given as
+ * text as well as the encoded bytes actually written to the archive.
+ *
+ * @param text The file name
+ * @param bytes the bytes actually written to the archive
+ * @param off The offset of the encoded comment in <code>bytes</code>.
+ * @param len The length of the encoded comment or comment in
+ * <code>bytes</code>.
+ */
+ public UnicodeCommentExtraField(final String text, final byte[] bytes, final int off,
+ final int len) {
+ super(text, bytes, off, len);
+ }
+
+ /**
+ * Assemble as unicode comment extension from the comment given as
+ * text as well as the bytes actually written to the archive.
+ *
+ * @param comment The file comment
+ * @param bytes the bytes actually written to the archive
+ */
+ public UnicodeCommentExtraField(final String comment, final byte[] bytes) {
+ super(comment, bytes);
+ }
+
+ /** {@inheritDoc} */
+ public ZipShort getHeaderId() {
+ return UCOM_ID;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnicodePathExtraField.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnicodePathExtraField.java
new file mode 100644
index 00000000..8b045a1e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnicodePathExtraField.java
@@ -0,0 +1,67 @@
+/*
+ * 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.tools.zip;
+
+/**
+ * Info-ZIP Unicode Path Extra Field (0x7075):
+ *
+ * <p>Stores the UTF-8 version of the file name field as stored in the
+ * local header and central directory header.</p>
+ *
+ * <p>See {@link
+ * "http://www.pkware.com/documents/casestudies/APPNOTE.TXT PKWARE's
+ * APPNOTE.TXT, section 4.6.9"}.</p>
+ */
+public class UnicodePathExtraField extends AbstractUnicodeExtraField {
+
+ public static final ZipShort UPATH_ID = new ZipShort(0x7075);
+
+ public UnicodePathExtraField () {
+ }
+
+ /**
+ * Assemble as unicode path extension from the name given as
+ * text as well as the encoded bytes actually written to the archive.
+ *
+ * @param text The file name
+ * @param bytes the bytes actually written to the archive
+ * @param off The offset of the encoded filename in <code>bytes</code>.
+ * @param len The length of the encoded filename or comment in
+ * <code>bytes</code>.
+ */
+ public UnicodePathExtraField(final String text, final byte[] bytes, final int off, final int len) {
+ super(text, bytes, off, len);
+ }
+
+ /**
+ * Assemble as unicode path extension from the name given as
+ * text as well as the encoded bytes actually written to the archive.
+ *
+ * @param name The file name
+ * @param bytes the bytes actually written to the archive
+ */
+ public UnicodePathExtraField(final String name, final byte[] bytes) {
+ super(name, bytes);
+ }
+
+ /** {@inheritDoc} */
+ public ZipShort getHeaderId() {
+ return UPATH_ID;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnixStat.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnixStat.java
new file mode 100644
index 00000000..2ca0674b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnixStat.java
@@ -0,0 +1,76 @@
+/*
+ * 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.tools.zip;
+
+/**
+ * Constants from stat.h on Unix systems.
+ *
+ */
+// CheckStyle:InterfaceIsTypeCheck OFF - backward compatible
+public interface UnixStat {
+
+ /**
+ * Bits used for permissions (and sticky bit)
+ *
+ * @since 1.1
+ */
+ int PERM_MASK = 07777;
+ /**
+ * Indicates symbolic links.
+ *
+ * @since 1.1
+ */
+ int LINK_FLAG = 0120000;
+ /**
+ * Indicates plain files.
+ *
+ * @since 1.1
+ */
+ int FILE_FLAG = 0100000;
+ /**
+ * Indicates directories.
+ *
+ * @since 1.1
+ */
+ int DIR_FLAG = 040000;
+
+ // ----------------------------------------------------------
+ // somewhat arbitrary choices that are quite common for shared
+ // installations
+ // -----------------------------------------------------------
+
+ /**
+ * Default permissions for symbolic links.
+ *
+ * @since 1.1
+ */
+ int DEFAULT_LINK_PERM = 0777;
+ /**
+ * Default permissions for directories.
+ *
+ * @since 1.1
+ */
+ int DEFAULT_DIR_PERM = 0755;
+ /**
+ * Default permissions for plain files.
+ *
+ * @since 1.1
+ */
+ int DEFAULT_FILE_PERM = 0644;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnparseableExtraFieldData.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnparseableExtraFieldData.java
new file mode 100644
index 00000000..92d30020
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnparseableExtraFieldData.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.tools.zip;
+
+/**
+ * Wrapper for extra field data that doesn't conform to the recommended format of header-tag + size + data.
+ *
+ * <p>The header-id is artificial (and not listed as a known ID in
+ * {@link <a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">
+ * APPNOTE.TXT</a>}). Since it isn't used anywhere except to satisfy the
+ * ZipExtraField contract it shouldn't matter anyway.</p>
+ *
+ * @since Ant 1.8.1
+ */
+public final class UnparseableExtraFieldData
+ implements CentralDirectoryParsingZipExtraField {
+
+ private static final ZipShort HEADER_ID = new ZipShort(0xACC1);
+
+ private byte[] localFileData;
+ private byte[] centralDirectoryData;
+
+ /**
+ * The Header-ID.
+ *
+ * @return a completely arbitrary value that should be ignored.
+ */
+ public ZipShort getHeaderId() {
+ return HEADER_ID;
+ }
+
+ /**
+ * Length of the complete extra field in the local file data.
+ *
+ * @return The LocalFileDataLength value
+ */
+ public ZipShort getLocalFileDataLength() {
+ return new ZipShort(localFileData == null ? 0 : localFileData.length);
+ }
+
+ /**
+ * Length of the complete extra field in the central directory.
+ *
+ * @return The CentralDirectoryLength value
+ */
+ public ZipShort getCentralDirectoryLength() {
+ return centralDirectoryData == null
+ ? getLocalFileDataLength()
+ : new ZipShort(centralDirectoryData.length);
+ }
+
+ /**
+ * The actual data to put into local file data.
+ *
+ * @return The LocalFileDataData value
+ */
+ public byte[] getLocalFileDataData() {
+ return ZipUtil.copy(localFileData);
+ }
+
+ /**
+ * The actual data to put into central directory.
+ *
+ * @return The CentralDirectoryData value
+ */
+ public byte[] getCentralDirectoryData() {
+ return centralDirectoryData == null
+ ? getLocalFileDataData() : ZipUtil.copy(centralDirectoryData);
+ }
+
+ /**
+ * Populate data from this array as if it was in local file data.
+ *
+ * @param buffer the buffer to read data from
+ * @param offset offset into buffer to read data
+ * @param length the length of data
+ */
+ public void parseFromLocalFileData(byte[] buffer, int offset, int length) {
+ localFileData = new byte[length];
+ System.arraycopy(buffer, offset, localFileData, 0, length);
+ }
+
+ /**
+ * Populate data from this array as if it was in central directory data.
+ *
+ * @param buffer the buffer to read data from
+ * @param offset offset into buffer to read data
+ * @param length the length of data
+ */
+ public void parseFromCentralDirectoryData(byte[] buffer, int offset,
+ int length) {
+ centralDirectoryData = new byte[length];
+ System.arraycopy(buffer, offset, centralDirectoryData, 0, length);
+ if (localFileData == null) {
+ parseFromLocalFileData(buffer, offset, length);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnrecognizedExtraField.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnrecognizedExtraField.java
new file mode 100644
index 00000000..0e4262de
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnrecognizedExtraField.java
@@ -0,0 +1,154 @@
+/*
+ * 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.tools.zip;
+
+/**
+ * Simple placeholder for all those extra fields we don't want to deal
+ * with.
+ *
+ * <p>Assumes local file data and central directory entries are
+ * identical - unless told the opposite.</p>
+ *
+ */
+public class UnrecognizedExtraField
+ implements CentralDirectoryParsingZipExtraField {
+
+ /**
+ * The Header-ID.
+ *
+ * @since 1.1
+ */
+ private ZipShort headerId;
+
+ /**
+ * Set the header id.
+ * @param headerId the header id to use
+ */
+ public void setHeaderId(ZipShort headerId) {
+ this.headerId = headerId;
+ }
+
+ /**
+ * Get the header id.
+ * @return the header id
+ */
+ public ZipShort getHeaderId() {
+ return headerId;
+ }
+
+ /**
+ * Extra field data in local file data - without
+ * Header-ID or length specifier.
+ *
+ * @since 1.1
+ */
+ private byte[] localData;
+
+ /**
+ * Set the extra field data in the local file data -
+ * without Header-ID or length specifier.
+ * @param data the field data to use
+ */
+ public void setLocalFileDataData(byte[] data) {
+ localData = ZipUtil.copy(data);
+ }
+
+ /**
+ * Get the length of the local data.
+ * @return the length of the local data
+ */
+ public ZipShort getLocalFileDataLength() {
+ return new ZipShort(localData.length);
+ }
+
+ /**
+ * Get the local data.
+ * @return the local data
+ */
+ public byte[] getLocalFileDataData() {
+ return ZipUtil.copy(localData);
+ }
+
+ /**
+ * Extra field data in central directory - without
+ * Header-ID or length specifier.
+ *
+ * @since 1.1
+ */
+ private byte[] centralData;
+
+ /**
+ * Set the extra field data in central directory.
+ * @param data the data to use
+ */
+ public void setCentralDirectoryData(byte[] data) {
+ centralData = ZipUtil.copy(data);
+ }
+
+ /**
+ * Get the central data length.
+ * If there is no central data, get the local file data length.
+ * @return the central data length
+ */
+ public ZipShort getCentralDirectoryLength() {
+ if (centralData != null) {
+ return new ZipShort(centralData.length);
+ }
+ return getLocalFileDataLength();
+ }
+
+ /**
+ * Get the central data.
+ * @return the central data if present, else return the local file data
+ */
+ public byte[] getCentralDirectoryData() {
+ if (centralData != null) {
+ return ZipUtil.copy(centralData);
+ }
+ return getLocalFileDataData();
+ }
+
+ /**
+ * @param data the array of bytes.
+ * @param offset the source location in the data array.
+ * @param length the number of bytes to use in the data array.
+ * @see ZipExtraField#parseFromLocalFileData(byte[], int, int)
+ */
+ public void parseFromLocalFileData(byte[] data, int offset, int length) {
+ byte[] tmp = new byte[length];
+ System.arraycopy(data, offset, tmp, 0, length);
+ setLocalFileDataData(tmp);
+ }
+
+ /**
+ * @param data the array of bytes.
+ * @param offset the source location in the data array.
+ * @param length the number of bytes to use in the data array.
+ */
+ public void parseFromCentralDirectoryData(byte[] data, int offset,
+ int length) {
+ byte[] tmp = new byte[length];
+ System.arraycopy(data, offset, tmp, 0, length);
+ setCentralDirectoryData(tmp);
+ if (localData == null) {
+ setLocalFileDataData(tmp);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnsupportedZipFeatureException.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnsupportedZipFeatureException.java
new file mode 100644
index 00000000..534bb165
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/UnsupportedZipFeatureException.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.tools.zip;
+
+import java.util.zip.ZipException;
+
+/**
+ * Exception thrown when attempting to read or write data for a zip
+ * entry that uses ZIP features not supported by this library.
+ * @since Ant 1.9.0
+ */
+public class UnsupportedZipFeatureException extends ZipException {
+
+ private final Feature reason;
+ private final ZipEntry entry;
+ private static final long serialVersionUID = 4430521921766595597L;
+
+ /**
+ * Creates an exception.
+ * @param reason the feature that is not supported
+ * @param entry the entry using the feature
+ */
+ public UnsupportedZipFeatureException(Feature reason,
+ ZipEntry entry) {
+ super("unsupported feature " + reason + " used in entry "
+ + entry.getName());
+ this.reason = reason;
+ this.entry = entry;
+ }
+
+ /**
+ * The unsupported feature that has been used.
+ */
+ public Feature getFeature() {
+ return reason;
+ }
+
+ /**
+ * The entry using the unsupported feature.
+ */
+ public ZipEntry getEntry() {
+ return entry;
+ }
+
+ /**
+ * ZIP Features that may or may not be supported.
+ */
+ public static class Feature {
+ /**
+ * The entry is encrypted.
+ */
+ public static final Feature ENCRYPTION = new Feature("encryption");
+ /**
+ * The entry used an unsupported compression method.
+ */
+ public static final Feature METHOD = new Feature("compression method");
+ /**
+ * The entry uses a data descriptor.
+ */
+ public static final Feature DATA_DESCRIPTOR = new Feature("data descriptor");
+
+ private final String name;
+
+ private Feature(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Zip64ExtendedInformationExtraField.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Zip64ExtendedInformationExtraField.java
new file mode 100644
index 00000000..16502ac6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Zip64ExtendedInformationExtraField.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.tools.zip;
+
+import static org.apache.tools.zip.ZipConstants.DWORD;
+import static org.apache.tools.zip.ZipConstants.WORD;
+
+import java.util.zip.ZipException;
+
+/**
+ * Holds size and other extended information for entries that use Zip64
+ * features.
+ *
+ * <p>See {@link
+ * "http://www.pkware.com/documents/casestudies/APPNOTE.TXT PKWARE's
+ * APPNOTE.TXT, section 4.5.3"}.</p>
+ *
+ * <p>Currently Ant doesn't support encrypting the
+ * central directory so the note about masking doesn't apply.</p>
+ *
+ * <p>The implementation relies on data being read from the local file
+ * header and assumes that both size values are always present.</p>
+ *
+ * @since Ant 1.9.0
+ */
+public class Zip64ExtendedInformationExtraField
+ implements CentralDirectoryParsingZipExtraField {
+
+ static final ZipShort HEADER_ID = new ZipShort(0x0001);
+
+ private static final String LFH_MUST_HAVE_BOTH_SIZES_MSG =
+ "Zip64 extended information must contain"
+ + " both size values in the local file header.";
+ private static final byte[] EMPTY = new byte[0];
+
+ private ZipEightByteInteger size, compressedSize, relativeHeaderOffset;
+ private ZipLong diskStart;
+
+ /**
+ * Stored in {@link #parseFromCentralDirectoryData
+ * parseFromCentralDirectoryData} so it can be reused when ZipFile
+ * calls {@link #reparseCentralDirectoryData
+ * reparseCentralDirectoryData}.
+ *
+ * <p>Not used for anything else</p>
+ */
+ private byte[] rawCentralDirectoryData;
+
+ /**
+ * This constructor should only be used by the code that reads
+ * archives inside of Ant.
+ */
+ public Zip64ExtendedInformationExtraField() { }
+
+ /**
+ * Creates an extra field based on the original and compressed size.
+ *
+ * @param size the entry's original size
+ * @param compressedSize the entry's compressed size
+ *
+ * @throws IllegalArgumentException if size or compressedSize is null
+ */
+ public Zip64ExtendedInformationExtraField(ZipEightByteInteger size,
+ ZipEightByteInteger compressedSize) {
+ this(size, compressedSize, null, null);
+ }
+
+ /**
+ * Creates an extra field based on all four possible values.
+ *
+ * @param size the entry's original size
+ * @param compressedSize the entry's compressed size
+ *
+ * @throws IllegalArgumentException if size or compressedSize is null
+ */
+ public Zip64ExtendedInformationExtraField(ZipEightByteInteger size,
+ ZipEightByteInteger compressedSize,
+ ZipEightByteInteger relativeHeaderOffset,
+ ZipLong diskStart) {
+ this.size = size;
+ this.compressedSize = compressedSize;
+ this.relativeHeaderOffset = relativeHeaderOffset;
+ this.diskStart = diskStart;
+ }
+
+ /** {@inheritDoc} */
+ public ZipShort getHeaderId() {
+ return HEADER_ID;
+ }
+
+ /** {@inheritDoc} */
+ public ZipShort getLocalFileDataLength() {
+ return new ZipShort(size != null ? 2 * DWORD : 0);
+ }
+
+ /** {@inheritDoc} */
+ public ZipShort getCentralDirectoryLength() {
+ return new ZipShort((size != null ? DWORD : 0)
+ + (compressedSize != null ? DWORD : 0)
+ + (relativeHeaderOffset != null ? DWORD : 0)
+ + (diskStart != null ? WORD : 0));
+ }
+
+ /** {@inheritDoc} */
+ public byte[] getLocalFileDataData() {
+ if (size != null || compressedSize != null) {
+ if (size == null || compressedSize == null) {
+ throw new IllegalArgumentException(LFH_MUST_HAVE_BOTH_SIZES_MSG);
+ }
+ byte[] data = new byte[2 * DWORD];
+ addSizes(data);
+ return data;
+ }
+ return EMPTY;
+ }
+
+ /** {@inheritDoc} */
+ public byte[] getCentralDirectoryData() {
+ byte[] data = new byte[getCentralDirectoryLength().getValue()];
+ int off = addSizes(data);
+ if (relativeHeaderOffset != null) {
+ System.arraycopy(relativeHeaderOffset.getBytes(), 0, data, off, DWORD);
+ off += DWORD;
+ }
+ if (diskStart != null) {
+ System.arraycopy(diskStart.getBytes(), 0, data, off, WORD);
+ off += WORD;
+ }
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ public void parseFromLocalFileData(byte[] buffer, int offset, int length)
+ throws ZipException {
+ if (length == 0) {
+ // no local file data at all, may happen if an archive
+ // only holds a ZIP64 extended information extra field
+ // inside the central directory but not inside the local
+ // file header
+ return;
+ }
+ if (length < 2 * DWORD) {
+ throw new ZipException(LFH_MUST_HAVE_BOTH_SIZES_MSG);
+ }
+ size = new ZipEightByteInteger(buffer, offset);
+ offset += DWORD;
+ compressedSize = new ZipEightByteInteger(buffer, offset);
+ offset += DWORD;
+ int remaining = length - 2 * DWORD;
+ if (remaining >= DWORD) {
+ relativeHeaderOffset = new ZipEightByteInteger(buffer, offset);
+ offset += DWORD;
+ remaining -= DWORD;
+ }
+ if (remaining >= WORD) {
+ diskStart = new ZipLong(buffer, offset);
+ offset += WORD;
+ remaining -= WORD;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void parseFromCentralDirectoryData(byte[] buffer, int offset,
+ int length)
+ throws ZipException {
+ // store for processing in reparseCentralDirectoryData
+ rawCentralDirectoryData = new byte[length];
+ System.arraycopy(buffer, offset, rawCentralDirectoryData, 0, length);
+
+ // if there is no size information in here, we are screwed and
+ // can only hope things will get resolved by LFH data later
+ // But there are some cases that can be detected
+ // * all data is there
+ // * length == 24 -> both sizes and offset
+ // * length % 8 == 4 -> at least we can identify the diskStart field
+ if (length >= 3 * DWORD + WORD) {
+ parseFromLocalFileData(buffer, offset, length);
+ } else if (length == 3 * DWORD) {
+ size = new ZipEightByteInteger(buffer, offset);
+ offset += DWORD;
+ compressedSize = new ZipEightByteInteger(buffer, offset);
+ offset += DWORD;
+ relativeHeaderOffset = new ZipEightByteInteger(buffer, offset);
+ } else if (length % DWORD == WORD) {
+ diskStart = new ZipLong(buffer, offset + length - WORD);
+ }
+ }
+
+ /**
+ * Parses the raw bytes read from the central directory extra
+ * field with knowledge which fields are expected to be there.
+ *
+ * <p>All four fields inside the zip64 extended information extra
+ * field are optional and must only be present if their corresponding
+ * entry inside the central directory contains the correct magic
+ * value.</p>
+ */
+ public void reparseCentralDirectoryData(boolean hasUncompressedSize,
+ boolean hasCompressedSize,
+ boolean hasRelativeHeaderOffset,
+ boolean hasDiskStart)
+ throws ZipException {
+ if (rawCentralDirectoryData != null) {
+ int expectedLength = (hasUncompressedSize ? DWORD : 0)
+ + (hasCompressedSize ? DWORD : 0)
+ + (hasRelativeHeaderOffset ? DWORD : 0)
+ + (hasDiskStart ? WORD : 0);
+ if (rawCentralDirectoryData.length < expectedLength) {
+ throw new ZipException("central directory zip64 extended"
+ + " information extra field's length"
+ + " doesn't match central directory"
+ + " data. Expected length "
+ + expectedLength + " but is "
+ + rawCentralDirectoryData.length);
+ }
+ int offset = 0;
+ if (hasUncompressedSize) {
+ size = new ZipEightByteInteger(rawCentralDirectoryData, offset);
+ offset += DWORD;
+ }
+ if (hasCompressedSize) {
+ compressedSize = new ZipEightByteInteger(rawCentralDirectoryData,
+ offset);
+ offset += DWORD;
+ }
+ if (hasRelativeHeaderOffset) {
+ relativeHeaderOffset =
+ new ZipEightByteInteger(rawCentralDirectoryData, offset);
+ offset += DWORD;
+ }
+ if (hasDiskStart) {
+ diskStart = new ZipLong(rawCentralDirectoryData, offset);
+ offset += WORD;
+ }
+ }
+ }
+
+ /**
+ * The uncompressed size stored in this extra field.
+ */
+ public ZipEightByteInteger getSize() {
+ return size;
+ }
+
+ /**
+ * The uncompressed size stored in this extra field.
+ */
+ public void setSize(ZipEightByteInteger size) {
+ this.size = size;
+ }
+
+ /**
+ * The compressed size stored in this extra field.
+ */
+ public ZipEightByteInteger getCompressedSize() {
+ return compressedSize;
+ }
+
+ /**
+ * The uncompressed size stored in this extra field.
+ */
+ public void setCompressedSize(ZipEightByteInteger compressedSize) {
+ this.compressedSize = compressedSize;
+ }
+
+ /**
+ * The relative header offset stored in this extra field.
+ */
+ public ZipEightByteInteger getRelativeHeaderOffset() {
+ return relativeHeaderOffset;
+ }
+
+ /**
+ * The relative header offset stored in this extra field.
+ */
+ public void setRelativeHeaderOffset(ZipEightByteInteger rho) {
+ relativeHeaderOffset = rho;
+ }
+
+ /**
+ * The disk start number stored in this extra field.
+ */
+ public ZipLong getDiskStartNumber() {
+ return diskStart;
+ }
+
+ /**
+ * The disk start number stored in this extra field.
+ */
+ public void setDiskStartNumber(ZipLong ds) {
+ diskStart = ds;
+ }
+
+ private int addSizes(byte[] data) {
+ int off = 0;
+ if (size != null) {
+ System.arraycopy(size.getBytes(), 0, data, 0, DWORD);
+ off += DWORD;
+ }
+ if (compressedSize != null) {
+ System.arraycopy(compressedSize.getBytes(), 0, data, off, DWORD);
+ off += DWORD;
+ }
+ return off;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Zip64Mode.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Zip64Mode.java
new file mode 100644
index 00000000..30d9f1cb
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Zip64Mode.java
@@ -0,0 +1,47 @@
+/*
+ * 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.tools.zip;
+
+/**
+ * The different modes {@link ZipOutputStream} can operate in.
+ *
+ * @see ZipOutputStream#setUseZip64
+ *
+ * @since Ant 1.9.0
+ */
+public enum Zip64Mode {
+ /**
+ * Use Zip64 extensions for all entries, even if it is clear it is
+ * not required.
+ */
+ Always,
+ /**
+ * Don't use Zip64 extensions for any entries.
+ *
+ * <p>This will cause a {@link Zip64RequiredException} to be
+ * thrown if {@link ZipOutputStream} detects it needs Zip64
+ * support.</p>
+ */
+ Never,
+ /**
+ * Use Zip64 extensions for all entries where they are required,
+ * don't use them for entries that clearly don't require them.
+ */
+ AsNeeded
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Zip64RequiredException.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Zip64RequiredException.java
new file mode 100644
index 00000000..f7fb7790
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/Zip64RequiredException.java
@@ -0,0 +1,49 @@
+/*
+ * 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.tools.zip;
+
+import java.util.zip.ZipException;
+
+/**
+ * Exception thrown when attempting to write data that requires Zip64
+ * support to an archive and {@link ZipOutputStream#setUseZip64
+ * UseZip64} has been set to {@link Zip64Mode#Never Never}.
+ * @since Ant 1.9.0
+ */
+public class Zip64RequiredException extends ZipException {
+
+ private static final long serialVersionUID = 20110809L;
+
+ /**
+ * Helper to format "entry too big" messages.
+ */
+ static String getEntryTooBigMessage(ZipEntry ze) {
+ return ze.getName() + "'s size exceeds the limit of 4GByte.";
+ }
+
+ static final String ARCHIVE_TOO_BIG_MESSAGE =
+ "archive's size exceeds the limit of 4GByte.";
+
+ static final String TOO_MANY_ENTRIES_MESSAGE =
+ "archive contains more than 65535 entries.";
+
+ public Zip64RequiredException(String reason) {
+ super(reason);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipConstants.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipConstants.java
new file mode 100644
index 00000000..83ae9569
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipConstants.java
@@ -0,0 +1,59 @@
+/*
+ * 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.tools.zip;
+
+/**
+ * Various constants used throughout the package.
+ */
+final class ZipConstants {
+ private ZipConstants() { }
+
+ /** Masks last eight bits */
+ static final int BYTE_MASK = 0xFF;
+
+ /** length of a ZipShort in bytes */
+ static final int SHORT = 2;
+
+ /** length of a ZipLong in bytes */
+ static final int WORD = 4;
+
+ /** length of a ZipEightByteInteger in bytes */
+ static final int DWORD = 8;
+
+ /** Initial ZIP specification version */
+ static final int INITIAL_VERSION = 10;
+
+ /** ZIP specification version that introduced data descriptor method */
+ static final int DATA_DESCRIPTOR_MIN_VERSION = 20;
+
+ /** ZIP specification version that introduced ZIP64 */
+ static final int ZIP64_MIN_VERSION = 45;
+
+ /**
+ * Value stored in two-byte size and similar fields if ZIP64
+ * extensions are used.
+ */
+ static final int ZIP64_MAGIC_SHORT = 0xFFFF;
+
+ /**
+ * Value stored in four-byte size and similar fields if ZIP64
+ * extensions are used.
+ */
+ static final long ZIP64_MAGIC = 0xFFFFFFFFL;
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEightByteInteger.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEightByteInteger.java
new file mode 100644
index 00000000..8d582dd8
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEightByteInteger.java
@@ -0,0 +1,229 @@
+/*
+ * 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.tools.zip;
+
+import static org.apache.tools.zip.ZipConstants.BYTE_MASK;
+
+import java.math.BigInteger;
+
+/**
+ * Utility class that represents an eight byte integer with conversion
+ * rules for the big endian byte order of ZIP files.
+ */
+public final class ZipEightByteInteger {
+
+ private static final int BYTE_1 = 1;
+ private static final int BYTE_1_MASK = 0xFF00;
+ private static final int BYTE_1_SHIFT = 8;
+
+ private static final int BYTE_2 = 2;
+ private static final int BYTE_2_MASK = 0xFF0000;
+ private static final int BYTE_2_SHIFT = 16;
+
+ private static final int BYTE_3 = 3;
+ private static final long BYTE_3_MASK = 0xFF000000L;
+ private static final int BYTE_3_SHIFT = 24;
+
+ private static final int BYTE_4 = 4;
+ private static final long BYTE_4_MASK = 0xFF00000000L;
+ private static final int BYTE_4_SHIFT = 32;
+
+ private static final int BYTE_5 = 5;
+ private static final long BYTE_5_MASK = 0xFF0000000000L;
+ private static final int BYTE_5_SHIFT = 40;
+
+ private static final int BYTE_6 = 6;
+ private static final long BYTE_6_MASK = 0xFF000000000000L;
+ private static final int BYTE_6_SHIFT = 48;
+
+ private static final int BYTE_7 = 7;
+ private static final long BYTE_7_MASK = 0x7F00000000000000L;
+ private static final int BYTE_7_SHIFT = 56;
+
+ private static final int LEFTMOST_BIT_SHIFT = 63;
+ private static final byte LEFTMOST_BIT = (byte) 0x80;
+
+ private final BigInteger value;
+
+ public static final ZipEightByteInteger ZERO = new ZipEightByteInteger(0);
+
+ /**
+ * Create instance from a number.
+ * @param value the long to store as a ZipEightByteInteger
+ */
+ public ZipEightByteInteger(long value) {
+ this(BigInteger.valueOf(value));
+ }
+
+ /**
+ * Create instance from a number.
+ * @param value the BigInteger to store as a ZipEightByteInteger
+ */
+ public ZipEightByteInteger(BigInteger value) {
+ this.value = value;
+ }
+
+ /**
+ * Create instance from bytes.
+ * @param bytes the bytes to store as a ZipEightByteInteger
+ */
+ public ZipEightByteInteger (byte[] bytes) {
+ this(bytes, 0);
+ }
+
+ /**
+ * Create instance from the eight bytes starting at offset.
+ * @param bytes the bytes to store as a ZipEightByteInteger
+ * @param offset the offset to start
+ */
+ public ZipEightByteInteger (byte[] bytes, int offset) {
+ value = ZipEightByteInteger.getValue(bytes, offset);
+ }
+
+ /**
+ * Get value as eight bytes in big endian byte order.
+ * @return value as eight bytes in big endian order
+ */
+ public byte[] getBytes() {
+ return ZipEightByteInteger.getBytes(value);
+ }
+
+ /**
+ * Get value as Java long.
+ * @return value as a long
+ */
+ public long getLongValue() {
+ return value.longValue();
+ }
+
+ /**
+ * Get value as Java long.
+ * @return value as a long
+ */
+ public BigInteger getValue() {
+ return value;
+ }
+
+ /**
+ * Get value as eight bytes in big endian byte order.
+ * @param value the value to convert
+ * @return value as eight bytes in big endian byte order
+ */
+ public static byte[] getBytes(long value) {
+ return getBytes(BigInteger.valueOf(value));
+ }
+
+ /**
+ * Get value as eight bytes in big endian byte order.
+ * @param value the value to convert
+ * @return value as eight bytes in big endian byte order
+ */
+ public static byte[] getBytes(BigInteger value) {
+ byte[] result = new byte[8];
+ long val = value.longValue();
+ result[0] = (byte) ((val & BYTE_MASK));
+ result[BYTE_1] = (byte) ((val & BYTE_1_MASK) >> BYTE_1_SHIFT);
+ result[BYTE_2] = (byte) ((val & BYTE_2_MASK) >> BYTE_2_SHIFT);
+ result[BYTE_3] = (byte) ((val & BYTE_3_MASK) >> BYTE_3_SHIFT);
+ result[BYTE_4] = (byte) ((val & BYTE_4_MASK) >> BYTE_4_SHIFT);
+ result[BYTE_5] = (byte) ((val & BYTE_5_MASK) >> BYTE_5_SHIFT);
+ result[BYTE_6] = (byte) ((val & BYTE_6_MASK) >> BYTE_6_SHIFT);
+ result[BYTE_7] = (byte) ((val & BYTE_7_MASK) >> BYTE_7_SHIFT);
+ if (value.testBit(LEFTMOST_BIT_SHIFT)) {
+ result[BYTE_7] |= LEFTMOST_BIT;
+ }
+ return result;
+ }
+
+ /**
+ * Helper method to get the value as a Java long from eight bytes
+ * starting at given array offset
+ * @param bytes the array of bytes
+ * @param offset the offset to start
+ * @return the corresponding Java long value
+ */
+ public static long getLongValue(byte[] bytes, int offset) {
+ return getValue(bytes, offset).longValue();
+ }
+
+ /**
+ * Helper method to get the value as a Java BigInteger from eight
+ * bytes starting at given array offset
+ * @param bytes the array of bytes
+ * @param offset the offset to start
+ * @return the corresponding Java BigInteger value
+ */
+ public static BigInteger getValue(byte[] bytes, int offset) {
+ long value = ((long) bytes[offset + BYTE_7] << BYTE_7_SHIFT) & BYTE_7_MASK;
+ value += ((long) bytes[offset + BYTE_6] << BYTE_6_SHIFT) & BYTE_6_MASK;
+ value += ((long) bytes[offset + BYTE_5] << BYTE_5_SHIFT) & BYTE_5_MASK;
+ value += ((long) bytes[offset + BYTE_4] << BYTE_4_SHIFT) & BYTE_4_MASK;
+ value += ((long) bytes[offset + BYTE_3] << BYTE_3_SHIFT) & BYTE_3_MASK;
+ value += ((long) bytes[offset + BYTE_2] << BYTE_2_SHIFT) & BYTE_2_MASK;
+ value += ((long) bytes[offset + BYTE_1] << BYTE_1_SHIFT) & BYTE_1_MASK;
+ value += ((long) bytes[offset] & BYTE_MASK);
+ BigInteger val = BigInteger.valueOf(value);
+ return (bytes[offset + BYTE_7] & LEFTMOST_BIT) == LEFTMOST_BIT
+ ? val.setBit(LEFTMOST_BIT_SHIFT) : val;
+ }
+
+ /**
+ * Helper method to get the value as a Java long from an eight-byte array
+ * @param bytes the array of bytes
+ * @return the corresponding Java long value
+ */
+ public static long getLongValue(byte[] bytes) {
+ return getLongValue(bytes, 0);
+ }
+
+ /**
+ * Helper method to get the value as a Java long from an eight-byte array
+ * @param bytes the array of bytes
+ * @return the corresponding Java BigInteger value
+ */
+ public static BigInteger getValue(byte[] bytes) {
+ return getValue(bytes, 0);
+ }
+
+ /**
+ * Override to make two instances with same value equal.
+ * @param o an object to compare
+ * @return true if the objects are equal
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof ZipEightByteInteger)) {
+ return false;
+ }
+ return value.equals(((ZipEightByteInteger) o).getValue());
+ }
+
+ /**
+ * Override to make two instances with same value equal.
+ * @return the hashCode of the value stored in the ZipEightByteInteger
+ */
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "ZipEightByteInteger value: " + value;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEncoding.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEncoding.java
new file mode 100644
index 00000000..72653834
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEncoding.java
@@ -0,0 +1,84 @@
+/*
+ * 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.tools.zip;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * An interface for encoders that do a pretty encoding of ZIP
+ * filenames.
+ *
+ * <p>There are mostly two implementations, one that uses java.nio
+ * {@link java.nio.charset.Charset Charset} and one implementation,
+ * which copes with simple 8 bit charsets, because java-1.4 did not
+ * support Cp437 in java.nio.</p>
+ *
+ * <p>The main reason for defining an own encoding layer comes from
+ * the problems with {@link java.lang.String#getBytes(String)
+ * String.getBytes}, which encodes unknown characters as ASCII
+ * quotation marks ('?'). Quotation marks are per definition an
+ * invalid filename on some operating systems like Windows, which
+ * leads to ignored ZIP entries.</p>
+ *
+ * <p>All implementations should implement this interface in a
+ * reentrant way.</p>
+ */
+public interface ZipEncoding {
+ /**
+ * Check, whether the given string may be losslessly encoded using this
+ * encoding.
+ *
+ * @param name A filename or ZIP comment.
+ * @return Whether the given name may be encoded with out any losses.
+ */
+ boolean canEncode(String name);
+
+ /**
+ * Encode a filename or a comment to a byte array suitable for
+ * storing it to a serialized zip entry.
+ *
+ * <p>Examples for CP 437 (in pseudo-notation, right hand side is
+ * C-style notation):</p>
+ * <pre>
+ * encode("\u20AC_for_Dollar.txt") = "%U20AC_for_Dollar.txt"
+ * encode("\u00D6lf\u00E4sser.txt") = "\231lf\204sser.txt"
+ * </pre>
+ *
+ * @param name A filename or ZIP comment.
+ * @return A byte buffer with a backing array containing the
+ * encoded name. Unmappable characters or malformed
+ * character sequences are mapped to a sequence of utf-16
+ * words encoded in the format <code>%Uxxxx</code>. It is
+ * assumed, that the byte buffer is positioned at the
+ * beginning of the encoded result, the byte buffer has a
+ * backing array and the limit of the byte buffer points
+ * to the end of the encoded result.
+ * @throws IOException
+ */
+ ByteBuffer encode(String name) throws IOException;
+
+ /**
+ * @param data The byte values to decode.
+ * @return The decoded string.
+ * @throws IOException
+ */
+ String decode(byte [] data) throws IOException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEncodingHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEncodingHelper.java
new file mode 100644
index 00000000..4d3dab95
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEncodingHelper.java
@@ -0,0 +1,252 @@
+/*
+ * 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.tools.zip;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Static helper functions for robustly encoding filenames in zip files.
+ */
+public abstract class ZipEncodingHelper {
+
+ /**
+ * A class, which holds the high characters of a simple encoding
+ * and lazily instantiates a Simple8BitZipEncoding instance in a
+ * thread-safe manner.
+ */
+ private static class SimpleEncodingHolder {
+
+ private final char [] highChars;
+ private Simple8BitZipEncoding encoding;
+
+ /**
+ * Instantiate a simple encoding holder.
+ *
+ * @param highChars The characters for byte codes 128 to 255.
+ *
+ * @see Simple8BitZipEncoding#Simple8BitZipEncoding(char[])
+ */
+ SimpleEncodingHolder(final char [] highChars) {
+ this.highChars = highChars;
+ }
+
+ /**
+ * @return The associated {@link Simple8BitZipEncoding}, which
+ * is instantiated if not done so far.
+ */
+ public synchronized Simple8BitZipEncoding getEncoding() {
+ if (this.encoding == null) {
+ this.encoding = new Simple8BitZipEncoding(this.highChars);
+ }
+ return this.encoding;
+ }
+ }
+
+ private static final Map<String, SimpleEncodingHolder> simpleEncodings;
+
+ static {
+ final Map<String, SimpleEncodingHolder> se =
+ new HashMap<String, SimpleEncodingHolder>();
+
+ final char[] cp437_high_chars =
+ new char[] {0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0,
+ 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef,
+ 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6,
+ 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
+ 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5,
+ 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa,
+ 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310,
+ 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561,
+ 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x255c, 0x255b, 0x2510, 0x2514, 0x2534,
+ 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
+ 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550,
+ 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559,
+ 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518,
+ 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
+ 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3,
+ 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4,
+ 0x221e, 0x03c6, 0x03b5, 0x2229, 0x2261, 0x00b1,
+ 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
+ 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2,
+ 0x25a0, 0x00a0};
+
+ final SimpleEncodingHolder cp437 = new SimpleEncodingHolder(cp437_high_chars);
+
+ se.put("CP437", cp437);
+ se.put("Cp437", cp437);
+ se.put("cp437", cp437);
+ se.put("IBM437", cp437);
+ se.put("ibm437", cp437);
+
+ final char[] cp850_high_chars =
+ new char[] {0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0,
+ 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef,
+ 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6,
+ 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
+ 0x00ff, 0x00d6, 0x00dc, 0x00f8, 0x00a3, 0x00d8,
+ 0x00d7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa,
+ 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x00ae,
+ 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1,
+ 0x00c2, 0x00c0, 0x00a9, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x00a2, 0x00a5, 0x2510, 0x2514, 0x2534,
+ 0x252c, 0x251c, 0x2500, 0x253c, 0x00e3, 0x00c3,
+ 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550,
+ 0x256c, 0x00a4, 0x00f0, 0x00d0, 0x00ca, 0x00cb,
+ 0x00c8, 0x0131, 0x00cd, 0x00ce, 0x00cf, 0x2518,
+ 0x250c, 0x2588, 0x2584, 0x00a6, 0x00cc, 0x2580,
+ 0x00d3, 0x00df, 0x00d4, 0x00d2, 0x00f5, 0x00d5,
+ 0x00b5, 0x00fe, 0x00de, 0x00da, 0x00db, 0x00d9,
+ 0x00fd, 0x00dd, 0x00af, 0x00b4, 0x00ad, 0x00b1,
+ 0x2017, 0x00be, 0x00b6, 0x00a7, 0x00f7, 0x00b8,
+ 0x00b0, 0x00a8, 0x00b7, 0x00b9, 0x00b3, 0x00b2,
+ 0x25a0, 0x00a0};
+
+ final SimpleEncodingHolder cp850 = new SimpleEncodingHolder(cp850_high_chars);
+
+ se.put("CP850", cp850);
+ se.put("Cp850", cp850);
+ se.put("cp850", cp850);
+ se.put("IBM850", cp850);
+ se.put("ibm850", cp850);
+ simpleEncodings = Collections.unmodifiableMap(se);
+ }
+
+ /**
+ * Grow a byte buffer, so it has a minimal capacity or at least
+ * the double capacity of the original buffer
+ *
+ * @param b The original buffer.
+ * @param newCapacity The minimal requested new capacity.
+ * @return A byte buffer <code>r</code> with
+ * <code>r.capacity() = max(b.capacity()*2,newCapacity)</code> and
+ * all the data contained in <code>b</code> copied to the beginning
+ * of <code>r</code>.
+ *
+ */
+ static ByteBuffer growBuffer(final ByteBuffer b, final int newCapacity) {
+ b.limit(b.position());
+ b.rewind();
+
+ final int c2 = b.capacity() * 2;
+ final ByteBuffer on = ByteBuffer.allocate(c2 < newCapacity ? newCapacity : c2);
+
+ on.put(b);
+ return on;
+ }
+
+
+ /**
+ * The hexadecimal digits <code>0,...,9,A,...,F</code> encoded as
+ * ASCII bytes.
+ */
+ private static final byte[] HEX_DIGITS =
+ new byte [] {
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41,
+ 0x42, 0x43, 0x44, 0x45, 0x46
+ };
+
+ /**
+ * Append <code>%Uxxxx</code> to the given byte buffer.
+ * The caller must assure, that <code>bb.remaining()&gt;=6</code>.
+ *
+ * @param bb The byte buffer to write to.
+ * @param c The character to write.
+ */
+ static void appendSurrogate(final ByteBuffer bb, final char c) {
+
+ bb.put((byte) '%');
+ bb.put((byte) 'U');
+
+ bb.put(HEX_DIGITS[(c >> 12)&0x0f]);
+ bb.put(HEX_DIGITS[(c >> 8)&0x0f]);
+ bb.put(HEX_DIGITS[(c >> 4)&0x0f]);
+ bb.put(HEX_DIGITS[c & 0x0f]);
+ }
+
+
+ /**
+ * name of the encoding UTF-8
+ */
+ static final String UTF8 = "UTF8";
+
+ /**
+ * variant name of the encoding UTF-8 used for comparisons.
+ */
+ private static final String UTF_DASH_8 = "utf-8";
+
+ /**
+ * name of the encoding UTF-8
+ */
+ static final ZipEncoding UTF8_ZIP_ENCODING = new FallbackZipEncoding(UTF8);
+
+ /**
+ * Instantiates a zip encoding.
+ *
+ * @param name The name of the zip encoding. Specify {@code null} for
+ * the platform's default encoding.
+ * @return A zip encoding for the given encoding name.
+ */
+ public static ZipEncoding getZipEncoding(final String name) {
+
+ // fallback encoding is good enough for utf-8.
+ if (isUTF8(name)) {
+ return UTF8_ZIP_ENCODING;
+ }
+
+ if (name == null) {
+ return new FallbackZipEncoding();
+ }
+
+ final SimpleEncodingHolder h = simpleEncodings.get(name);
+
+ if (h!=null) {
+ return h.getEncoding();
+ }
+
+ try {
+
+ final Charset cs = Charset.forName(name);
+ return new NioZipEncoding(cs);
+
+ } catch (final UnsupportedCharsetException e) {
+ return new FallbackZipEncoding(name);
+ }
+ }
+
+ /**
+ * Whether a given encoding - or the platform's default encoding
+ * if the parameter is null - is UTF-8.
+ */
+ static boolean isUTF8(String encoding) {
+ if (encoding == null) {
+ // check platform's default encoding
+ encoding = System.getProperty("file.encoding");
+ }
+ return UTF8.equalsIgnoreCase(encoding)
+ || UTF_DASH_8.equalsIgnoreCase(encoding);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEntry.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEntry.java
new file mode 100644
index 00000000..30a8155b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipEntry.java
@@ -0,0 +1,786 @@
+/*
+ * 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.tools.zip;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.zip.ZipException;
+
+/**
+ * Extension that adds better handling of extra fields and provides
+ * access to the internal and external file attributes.
+ *
+ * <p>The extra data is expected to follow the recommendation of
+ * {@link <a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">
+ * APPNOTE.txt</a>}:</p>
+ * <ul>
+ * <li>the extra byte array consists of a sequence of extra fields</li>
+ * <li>each extra fields starts by a two byte header id followed by
+ * a two byte sequence holding the length of the remainder of
+ * data.</li>
+ * </ul>
+ *
+ * <p>Any extra data that cannot be parsed by the rules above will be
+ * consumed as "unparseable" extra data and treated differently by the
+ * methods of this class. Older versions would have thrown an
+ * exception if any attempt was made to read or write extra data not
+ * conforming to the recommendation.</p>
+ *
+ */
+public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
+
+ public static final int PLATFORM_UNIX = 3;
+ public static final int PLATFORM_FAT = 0;
+ public static final int CRC_UNKNOWN = -1;
+ private static final int SHORT_MASK = 0xFFFF;
+ private static final int SHORT_SHIFT = 16;
+ private static final byte[] EMPTY = new byte[0];
+
+ /**
+ * The {@link java.util.zip.ZipEntry} base class only supports
+ * the compression methods STORED and DEFLATED. We override the
+ * field so that any compression methods can be used.
+ * <p>
+ * The default value -1 means that the method has not been specified.
+ */
+ private int method = -1;
+
+ /**
+ * The {@link java.util.zip.ZipEntry#setSize} method in the base
+ * class throws an IllegalArgumentException if the size is bigger
+ * than 2GB for Java versions < 7. Need to keep our own size
+ * information for Zip64 support.
+ */
+ private long size = -1;
+
+ private int internalAttributes = 0;
+ private int platform = PLATFORM_FAT;
+ private long externalAttributes = 0;
+ private ZipExtraField[] extraFields;
+ private UnparseableExtraFieldData unparseableExtra = null;
+ private String name = null;
+ private byte[] rawName = null;
+ private GeneralPurposeBit gpb = new GeneralPurposeBit();
+ private static final ZipExtraField[] noExtraFields = new ZipExtraField[0];
+
+ /**
+ * Creates a new zip entry with the specified name.
+ *
+ * <p>Assumes the entry represents a directory if and only if the
+ * name ends with a forward slash "/".</p>
+ *
+ * @param name the name of the entry
+ * @since 1.1
+ */
+ public ZipEntry(final String name) {
+ super(name);
+ setName(name);
+ }
+
+ /**
+ * Creates a new zip entry with fields taken from the specified zip entry.
+ *
+ * <p>Assumes the entry represents a directory if and only if the
+ * name ends with a forward slash "/".</p>
+ *
+ * @param entry the entry to get fields from
+ * @since 1.1
+ * @throws ZipException on error
+ */
+ public ZipEntry(final java.util.zip.ZipEntry entry) throws ZipException {
+ super(entry);
+ setName(entry.getName());
+ final byte[] extra = entry.getExtra();
+ if (extra != null) {
+ setExtraFields(ExtraFieldUtils.parse(extra, true,
+ ExtraFieldUtils
+ .UnparseableExtraField.READ));
+ } else {
+ // initializes extra data to an empty byte array
+ setExtra();
+ }
+ setMethod(entry.getMethod());
+ this.size = entry.getSize();
+ }
+
+ /**
+ * Creates a new zip entry with fields taken from the specified zip entry.
+ *
+ * <p>Assumes the entry represents a directory if and only if the
+ * name ends with a forward slash "/".</p>
+ *
+ * @param entry the entry to get fields from
+ * @throws ZipException on error
+ * @since 1.1
+ */
+ public ZipEntry(final ZipEntry entry) throws ZipException {
+ this((java.util.zip.ZipEntry) entry);
+ setInternalAttributes(entry.getInternalAttributes());
+ setExternalAttributes(entry.getExternalAttributes());
+ setExtraFields(getAllExtraFieldsNoCopy());
+ setPlatform(entry.getPlatform());
+ GeneralPurposeBit other = entry.getGeneralPurposeBit();
+ setGeneralPurposeBit(other == null ? null :
+ (GeneralPurposeBit) other.clone());
+ }
+
+ /**
+ * @since 1.9
+ */
+ protected ZipEntry() {
+ this("");
+ }
+
+ /**
+ * Creates a new zip entry taking some information from the given
+ * file and using the provided name.
+ *
+ * <p>The name will be adjusted to end with a forward slash "/" if
+ * the file is a directory. If the file is not a directory a
+ * potential trailing forward slash will be stripped from the
+ * entry name.</p>
+ */
+ public ZipEntry(final File inputFile, final String entryName) {
+ this(inputFile.isDirectory() && !entryName.endsWith("/") ?
+ entryName + "/" : entryName);
+ if (inputFile.isFile()){
+ setSize(inputFile.length());
+ }
+ setTime(inputFile.lastModified());
+ // TODO are there any other fields we can set here?
+ }
+
+ /**
+ * Overwrite clone.
+ * @return a cloned copy of this ZipEntry
+ * @since 1.1
+ */
+ @Override
+ public Object clone() {
+ final ZipEntry e = (ZipEntry) super.clone();
+
+ e.setInternalAttributes(getInternalAttributes());
+ e.setExternalAttributes(getExternalAttributes());
+ e.setExtraFields(getAllExtraFieldsNoCopy());
+ return e;
+ }
+
+ /**
+ * Returns the compression method of this entry, or -1 if the
+ * compression method has not been specified.
+ *
+ * @return compression method
+ */
+ @Override
+ public int getMethod() {
+ return method;
+ }
+
+ /**
+ * Sets the compression method of this entry.
+ *
+ * @param method compression method
+ */
+ @Override
+ public void setMethod(final int method) {
+ if (method < 0) {
+ throw new IllegalArgumentException(
+ "ZIP compression method can not be negative: " + method);
+ }
+ this.method = method;
+ }
+
+ /**
+ * Retrieves the internal file attributes.
+ *
+ * @return the internal file attributes
+ * @since 1.1
+ */
+ public int getInternalAttributes() {
+ return internalAttributes;
+ }
+
+ /**
+ * Sets the internal file attributes.
+ * @param value an <code>int</code> value
+ * @since 1.1
+ */
+ public void setInternalAttributes(final int value) {
+ internalAttributes = value;
+ }
+
+ /**
+ * Retrieves the external file attributes.
+ * @return the external file attributes
+ * @since 1.1
+ */
+ public long getExternalAttributes() {
+ return externalAttributes;
+ }
+
+ /**
+ * Sets the external file attributes.
+ * @param value an <code>long</code> value
+ * @since 1.1
+ */
+ public void setExternalAttributes(final long value) {
+ externalAttributes = value;
+ }
+
+ /**
+ * Sets Unix permissions in a way that is understood by Info-Zip's
+ * unzip command.
+ * @param mode an <code>int</code> value
+ * @since Ant 1.5.2
+ */
+ public void setUnixMode(final int mode) {
+ // CheckStyle:MagicNumberCheck OFF - no point
+ setExternalAttributes((mode << SHORT_SHIFT)
+ // MS-DOS read-only attribute
+ | ((mode & 0200) == 0 ? 1 : 0)
+ // MS-DOS directory flag
+ | (isDirectory() ? 0x10 : 0));
+ // CheckStyle:MagicNumberCheck ON
+ platform = PLATFORM_UNIX;
+ }
+
+ /**
+ * Unix permission.
+ * @return the unix permissions
+ * @since Ant 1.6
+ */
+ public int getUnixMode() {
+ return platform != PLATFORM_UNIX ? 0 :
+ (int) ((getExternalAttributes() >> SHORT_SHIFT) & SHORT_MASK);
+ }
+
+ /**
+ * Platform specification to put into the &quot;version made
+ * by&quot; part of the central file header.
+ *
+ * @return PLATFORM_FAT unless {@link #setUnixMode setUnixMode}
+ * has been called, in which case PLATFORM_UNIX will be returned.
+ *
+ * @since Ant 1.5.2
+ */
+ public int getPlatform() {
+ return platform;
+ }
+
+ /**
+ * Set the platform (UNIX or FAT).
+ * @param platform an <code>int</code> value - 0 is FAT, 3 is UNIX
+ * @since 1.9
+ */
+ protected void setPlatform(final int platform) {
+ this.platform = platform;
+ }
+
+ /**
+ * Replaces all currently attached extra fields with the new array.
+ * @param fields an array of extra fields
+ * @since 1.1
+ */
+ public void setExtraFields(final ZipExtraField[] fields) {
+ List<ZipExtraField> newFields = new ArrayList<ZipExtraField>();
+ for (ZipExtraField field : fields) {
+ if (field instanceof UnparseableExtraFieldData) {
+ unparseableExtra = (UnparseableExtraFieldData) field;
+ } else {
+ newFields.add( field);
+ }
+ }
+ extraFields = newFields.toArray(new ZipExtraField[newFields.size()]);
+ setExtra();
+ }
+
+ /**
+ * Retrieves all extra fields that have been parsed successfully.
+ * @return an array of the extra fields
+ */
+ public ZipExtraField[] getExtraFields() {
+ return getParseableExtraFields();
+ }
+
+ /**
+ * Retrieves extra fields.
+ * @param includeUnparseable whether to also return unparseable
+ * extra fields as {@link UnparseableExtraFieldData} if such data
+ * exists.
+ * @return an array of the extra fields
+ * @since 1.1
+ */
+ public ZipExtraField[] getExtraFields(final boolean includeUnparseable) {
+ return includeUnparseable ?
+ getAllExtraFields() :
+ getParseableExtraFields();
+ }
+
+ private ZipExtraField[] getParseableExtraFieldsNoCopy() {
+ if (extraFields == null) {
+ return noExtraFields;
+ }
+ return extraFields;
+ }
+
+ private ZipExtraField[] getParseableExtraFields() {
+ final ZipExtraField[] parseableExtraFields = getParseableExtraFieldsNoCopy();
+ return (parseableExtraFields == extraFields)
+ ? copyOf(parseableExtraFields) : parseableExtraFields;
+ }
+
+ private ZipExtraField[] copyOf(ZipExtraField[] src){
+ return copyOf(src, src.length);
+ }
+
+ private ZipExtraField[] copyOf(ZipExtraField[] src, int length){
+ ZipExtraField[] cpy = new ZipExtraField[length];
+ System.arraycopy(src, 0, cpy, 0, Math.min(src.length, length));
+ return cpy;
+ }
+
+ private ZipExtraField[] getMergedFields() {
+ final ZipExtraField[] zipExtraFields =
+ copyOf(extraFields, extraFields.length + 1);
+ zipExtraFields[extraFields.length] = unparseableExtra;
+ return zipExtraFields;
+ }
+
+ private ZipExtraField[] getUnparseableOnly() {
+ return unparseableExtra == null
+ ? noExtraFields : new ZipExtraField[] { unparseableExtra };
+ }
+
+ private ZipExtraField[] getAllExtraFields() {
+ final ZipExtraField[] allExtraFieldsNoCopy = getAllExtraFieldsNoCopy();
+ return (allExtraFieldsNoCopy == extraFields)
+ ? copyOf(allExtraFieldsNoCopy) : allExtraFieldsNoCopy;
+ }
+
+ /**
+ * Get all extra fields, including unparseable ones.
+ * @return An array of all extra fields. Not necessarily a copy of internal data structures, hence private method
+ */
+ private ZipExtraField[] getAllExtraFieldsNoCopy() {
+ if (extraFields == null) {
+ return getUnparseableOnly();
+ }
+ return unparseableExtra != null ? getMergedFields() : extraFields;
+ }
+
+ /**
+ * Adds an extra field - replacing an already present extra field
+ * of the same type.
+ *
+ * <p>If no extra field of the same type exists, the field will be
+ * added as last field.</p>
+ * @param ze an extra field
+ * @since 1.1
+ */
+ public void addExtraField(final ZipExtraField ze) {
+ if (ze instanceof UnparseableExtraFieldData) {
+ unparseableExtra = (UnparseableExtraFieldData) ze;
+ } else {
+ if (extraFields == null) {
+ extraFields = new ZipExtraField[] {ze};
+ } else {
+ if (getExtraField(ze.getHeaderId()) != null){
+ removeExtraField(ze.getHeaderId());
+ }
+ final ZipExtraField[] zipExtraFields =
+ copyOf(extraFields, extraFields.length + 1);
+ zipExtraFields[extraFields.length] = ze;
+ extraFields = zipExtraFields;
+ }
+ }
+ setExtra();
+ }
+
+ /**
+ * Adds an extra field - replacing an already present extra field
+ * of the same type.
+ *
+ * <p>The new extra field will be the first one.</p>
+ * @param ze an extra field
+ * @since 1.1
+ */
+ public void addAsFirstExtraField(final ZipExtraField ze) {
+ if (ze instanceof UnparseableExtraFieldData) {
+ unparseableExtra = (UnparseableExtraFieldData) ze;
+ } else {
+ if (getExtraField(ze.getHeaderId()) != null){
+ removeExtraField(ze.getHeaderId());
+ }
+ ZipExtraField[] copy = extraFields;
+ int newLen = extraFields != null ? extraFields.length + 1: 1;
+ extraFields = new ZipExtraField[newLen];
+ extraFields[0] = ze;
+ if (copy != null){
+ System.arraycopy(copy, 0, extraFields, 1, extraFields.length - 1);
+ }
+ }
+ setExtra();
+ }
+
+ /**
+ * Remove an extra field.
+ * @param type the type of extra field to remove
+ * @since 1.1
+ */
+ public void removeExtraField(final ZipShort type) {
+ if (extraFields == null) {
+ throw new java.util.NoSuchElementException();
+ }
+ List<ZipExtraField> newResult = new ArrayList<ZipExtraField>();
+ for (ZipExtraField extraField : extraFields) {
+ if (!type.equals(extraField.getHeaderId())){
+ newResult.add(extraField);
+ }
+ }
+ if (extraFields.length == newResult.size()) {
+ throw new java.util.NoSuchElementException();
+ }
+ extraFields = newResult.toArray(new ZipExtraField[newResult.size()]);
+ setExtra();
+ }
+
+ /**
+ * Removes unparseable extra field data.
+ */
+ public void removeUnparseableExtraFieldData() {
+ if (unparseableExtra == null) {
+ throw new java.util.NoSuchElementException();
+ }
+ unparseableExtra = null;
+ setExtra();
+ }
+
+ /**
+ * Looks up an extra field by its header id.
+ *
+ * @return null if no such field exists.
+ */
+ public ZipExtraField getExtraField(final ZipShort type) {
+ if (extraFields != null) {
+ for (ZipExtraField extraField : extraFields) {
+ if (type.equals(extraField.getHeaderId())) {
+ return extraField;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Looks up extra field data that couldn't be parsed correctly.
+ *
+ * @return null if no such field exists.
+ */
+ public UnparseableExtraFieldData getUnparseableExtraFieldData() {
+ return unparseableExtra;
+ }
+
+ /**
+ * Parses the given bytes as extra field data and consumes any
+ * unparseable data as an {@link UnparseableExtraFieldData}
+ * instance.
+ * @param extra an array of bytes to be parsed into extra fields
+ * @throws RuntimeException if the bytes cannot be parsed
+ * @since 1.1
+ * @throws RuntimeException on error
+ */
+ @Override
+ public void setExtra(final byte[] extra) throws RuntimeException {
+ try {
+ final ZipExtraField[] local =
+ ExtraFieldUtils.parse(extra, true,
+ ExtraFieldUtils.UnparseableExtraField.READ);
+ mergeExtraFields(local, true);
+ } catch (final ZipException e) {
+ // actually this is not be possible as of Ant 1.8.1
+ throw new RuntimeException("Error parsing extra fields for entry: "
+ + getName() + " - " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Unfortunately {@link java.util.zip.ZipOutputStream
+ * java.util.zip.ZipOutputStream} seems to access the extra data
+ * directly, so overriding getExtra doesn't help - we need to
+ * modify super's data directly.
+ *
+ * @since 1.1
+ */
+ protected void setExtra() {
+ super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields(true)));
+ }
+
+ /**
+ * Sets the central directory part of extra fields.
+ */
+ public void setCentralDirectoryExtra(final byte[] b) {
+ try {
+ final ZipExtraField[] central =
+ ExtraFieldUtils.parse(b, false,
+ ExtraFieldUtils.UnparseableExtraField.READ);
+ mergeExtraFields(central, false);
+ } catch (final ZipException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Retrieves the extra data for the local file data.
+ * @return the extra data for local file
+ * @since 1.1
+ */
+ public byte[] getLocalFileDataExtra() {
+ final byte[] extra = getExtra();
+ return extra != null ? extra : EMPTY;
+ }
+
+ /**
+ * Retrieves the extra data for the central directory.
+ * @return the central directory extra data
+ * @since 1.1
+ */
+ public byte[] getCentralDirectoryExtra() {
+ return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields(true));
+ }
+
+ /**
+ * Make this class work in JDK 1.1 like a 1.2 class.
+ *
+ * <p>This either stores the size for later usage or invokes
+ * setCompressedSize via reflection.</p>
+ * @param size the size to use
+ * @deprecated since 1.7.
+ * Use setCompressedSize directly.
+ * @since 1.2
+ */
+ @Deprecated
+ public void setComprSize(final long size) {
+ setCompressedSize(size);
+ }
+
+ /**
+ * Get the name of the entry.
+ * @return the entry name
+ * @since 1.9
+ */
+ @Override
+ public String getName() {
+ return name == null ? super.getName() : name;
+ }
+
+ /**
+ * Is this entry a directory?
+ * @return true if the entry is a directory
+ * @since 1.10
+ */
+ @Override
+ public boolean isDirectory() {
+ return getName().endsWith("/");
+ }
+
+ /**
+ * Set the name of the entry.
+ * @param name the name to use
+ */
+ protected void setName(String name) {
+ if (name != null && getPlatform() == PLATFORM_FAT
+ && name.indexOf("/") == -1) {
+ name = name.replace('\\', '/');
+ }
+ this.name = name;
+ }
+
+ /**
+ * Gets the uncompressed size of the entry data.
+ * @return the entry size
+ */
+ @Override
+ public long getSize() {
+ return size;
+ }
+
+ /**
+ * Sets the uncompressed size of the entry data.
+ * @param size the uncompressed size in bytes
+ * @exception IllegalArgumentException if the specified size is less
+ * than 0
+ */
+ @Override
+ public void setSize(final long size) {
+ if (size < 0) {
+ throw new IllegalArgumentException("invalid entry size");
+ }
+ this.size = size;
+ }
+
+ /**
+ * Sets the name using the raw bytes and the string created from
+ * it by guessing or using the configured encoding.
+ * @param name the name to use created from the raw bytes using
+ * the guessed or configured encoding
+ * @param rawName the bytes originally read as name from the
+ * archive
+ */
+ protected void setName(final String name, final byte[] rawName) {
+ setName(name);
+ this.rawName = rawName;
+ }
+
+ /**
+ * Returns the raw bytes that made up the name before it has been
+ * converted using the configured or guessed encoding.
+ *
+ * <p>This method will return null if this instance has not been
+ * read from an archive.</p>
+ */
+ public byte[] getRawName() {
+ if (rawName != null) {
+ final byte[] b = new byte[rawName.length];
+ System.arraycopy(rawName, 0, b, 0, rawName.length);
+ return b;
+ }
+ return null;
+ }
+
+ /**
+ * Get the hashCode of the entry.
+ * This uses the name as the hashcode.
+ * @return a hashcode.
+ * @since Ant 1.7
+ */
+ @Override
+ public int hashCode() {
+ // this method has severe consequences on performance. We cannot rely
+ // on the super.hashCode() method since super.getName() always return
+ // the empty string in the current implemention (there's no setter)
+ // so it is basically draining the performance of a hashmap lookup
+ return getName().hashCode();
+ }
+
+ /**
+ * The "general purpose bit" field.
+ */
+ public GeneralPurposeBit getGeneralPurposeBit() {
+ return gpb;
+ }
+
+ /**
+ * The "general purpose bit" field.
+ */
+ public void setGeneralPurposeBit(final GeneralPurposeBit b) {
+ gpb = b;
+ }
+
+ /**
+ * If there are no extra fields, use the given fields as new extra
+ * data - otherwise merge the fields assuming the existing fields
+ * and the new fields stem from different locations inside the
+ * archive.
+ * @param f the extra fields to merge
+ * @param local whether the new fields originate from local data
+ */
+ private void mergeExtraFields(final ZipExtraField[] f, final boolean local)
+ throws ZipException {
+ if (extraFields == null) {
+ setExtraFields(f);
+ } else {
+ for (final ZipExtraField element : f) {
+ ZipExtraField existing;
+ if (element instanceof UnparseableExtraFieldData) {
+ existing = unparseableExtra;
+ } else {
+ existing = getExtraField(element.getHeaderId());
+ }
+ if (existing == null) {
+ addExtraField(element);
+ } else {
+ if (local
+ || !(existing
+ instanceof CentralDirectoryParsingZipExtraField)) {
+ final byte[] b = element.getLocalFileDataData();
+ existing.parseFromLocalFileData(b, 0, b.length);
+ } else {
+ final byte[] b = element.getCentralDirectoryData();
+ ((CentralDirectoryParsingZipExtraField) existing)
+ .parseFromCentralDirectoryData(b, 0, b.length);
+ }
+ }
+ }
+ setExtra();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public Date getLastModifiedDate() {
+ return new Date(getTime());
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final ZipEntry other = (ZipEntry) obj;
+ final String myName = getName();
+ final String otherName = other.getName();
+ if (myName == null) {
+ if (otherName != null) {
+ return false;
+ }
+ } else if (!myName.equals(otherName)) {
+ return false;
+ }
+ String myComment = getComment();
+ String otherComment = other.getComment();
+ if (myComment == null) {
+ myComment = "";
+ }
+ if (otherComment == null) {
+ otherComment = "";
+ }
+ return getTime() == other.getTime()
+ && myComment.equals(otherComment)
+ && getInternalAttributes() == other.getInternalAttributes()
+ && getPlatform() == other.getPlatform()
+ && getExternalAttributes() == other.getExternalAttributes()
+ && getMethod() == other.getMethod()
+ && getSize() == other.getSize()
+ && getCrc() == other.getCrc()
+ && getCompressedSize() == other.getCompressedSize()
+ && Arrays.equals(getCentralDirectoryExtra(),
+ other.getCentralDirectoryExtra())
+ && Arrays.equals(getLocalFileDataExtra(),
+ other.getLocalFileDataExtra())
+ && gpb.equals(other.gpb);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipExtraField.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipExtraField.java
new file mode 100644
index 00000000..649fca00
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipExtraField.java
@@ -0,0 +1,85 @@
+/*
+ * 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.tools.zip;
+
+import java.util.zip.ZipException;
+
+/**
+ * General format of extra field data.
+ *
+ * <p>Extra fields usually appear twice per file, once in the local
+ * file data and once in the central directory. Usually they are the
+ * same, but they don't have to be. {@link
+ * java.util.zip.ZipOutputStream java.util.zip.ZipOutputStream} will
+ * only use the local file data in both places.</p>
+ *
+ */
+public interface ZipExtraField {
+
+ /**
+ * The Header-ID.
+ * @return the header id
+ * @since 1.1
+ */
+ ZipShort getHeaderId();
+
+ /**
+ * Length of the extra field in the local file data - without
+ * Header-ID or length specifier.
+ * @return the length of the field in the local file data
+ * @since 1.1
+ */
+ ZipShort getLocalFileDataLength();
+
+ /**
+ * Length of the extra field in the central directory - without
+ * Header-ID or length specifier.
+ * @return the length of the field in the central directory
+ * @since 1.1
+ */
+ ZipShort getCentralDirectoryLength();
+
+ /**
+ * The actual data to put into local file data - without Header-ID
+ * or length specifier.
+ * @return the data
+ * @since 1.1
+ */
+ byte[] getLocalFileDataData();
+
+ /**
+ * The actual data to put into central directory - without Header-ID or
+ * length specifier.
+ * @return the data
+ * @since 1.1
+ */
+ byte[] getCentralDirectoryData();
+
+ /**
+ * Populate data from this array as if it was in local file data.
+ * @param data an array of bytes
+ * @param offset the start offset
+ * @param length the number of bytes in the array from offset
+ *
+ * @since 1.1
+ * @throws ZipException on error
+ */
+ void parseFromLocalFileData(byte[] data, int offset, int length)
+ throws ZipException;
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipFile.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipFile.java
new file mode 100644
index 00000000..7a2c9926
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipFile.java
@@ -0,0 +1,1048 @@
+/*
+ * 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.tools.zip;
+
+import static org.apache.tools.zip.ZipConstants.DWORD;
+import static org.apache.tools.zip.ZipConstants.SHORT;
+import static org.apache.tools.zip.ZipConstants.WORD;
+import static org.apache.tools.zip.ZipConstants.ZIP64_MAGIC;
+import static org.apache.tools.zip.ZipConstants.ZIP64_MAGIC_SHORT;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+import java.util.zip.ZipException;
+
+/**
+ * Replacement for <code>java.util.ZipFile</code>.
+ *
+ * <p>This class adds support for file name encodings other than UTF-8
+ * (which is required to work on ZIP files created by native zip tools
+ * and is able to skip a preamble like the one found in self
+ * extracting archives. Furthermore it returns instances of
+ * <code>org.apache.tools.zip.ZipEntry</code> instead of
+ * <code>java.util.zip.ZipEntry</code>.</p>
+ *
+ * <p>It doesn't extend <code>java.util.zip.ZipFile</code> as it would
+ * have to reimplement all methods anyway. Like
+ * <code>java.util.ZipFile</code>, it uses RandomAccessFile under the
+ * covers and supports compressed and uncompressed entries. As of
+ * Apache Ant 1.9.0 it also transparently supports Zip64
+ * extensions and thus individual entries and archives larger than 4
+ * GB or with more than 65536 entries.</p>
+ *
+ * <p>The method signatures mimic the ones of
+ * <code>java.util.zip.ZipFile</code>, with a couple of exceptions:
+ *
+ * <ul>
+ * <li>There is no getName method.</li>
+ * <li>entries has been renamed to getEntries.</li>
+ * <li>getEntries and getEntry return
+ * <code>org.apache.tools.zip.ZipEntry</code> instances.</li>
+ * <li>close is allowed to throw IOException.</li>
+ * </ul>
+ *
+ */
+public class ZipFile {
+ private static final int HASH_SIZE = 509;
+ static final int NIBLET_MASK = 0x0f;
+ static final int BYTE_SHIFT = 8;
+ private static final int POS_0 = 0;
+ private static final int POS_1 = 1;
+ private static final int POS_2 = 2;
+ private static final int POS_3 = 3;
+
+ /**
+ * List of entries in the order they appear inside the central
+ * directory.
+ */
+ private final List<ZipEntry> entries = new LinkedList<ZipEntry>();
+
+ /**
+ * Maps String to list of ZipEntrys, name -> actual entries.
+ */
+ private final Map<String, LinkedList<ZipEntry>> nameMap =
+ new HashMap<String, LinkedList<ZipEntry>>(HASH_SIZE);
+
+ private static final class OffsetEntry {
+ private long headerOffset = -1;
+ private long dataOffset = -1;
+ }
+
+ /**
+ * The encoding to use for filenames and the file comment.
+ *
+ * <p>For a list of possible values see <a
+ * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>.
+ * Defaults to the platform's default character encoding.</p>
+ */
+ private final String encoding;
+
+ /**
+ * The zip encoding to use for filenames and the file comment.
+ */
+ private final ZipEncoding zipEncoding;
+
+ /**
+ * File name of actual source.
+ */
+ private final String archiveName;
+
+ /**
+ * The actual data source.
+ */
+ private final RandomAccessFile archive;
+
+ /**
+ * Whether to look for and use Unicode extra fields.
+ */
+ private final boolean useUnicodeExtraFields;
+
+ /**
+ * Whether the file is closed.
+ */
+ private volatile boolean closed;
+
+ // cached buffers
+ private final byte[] DWORD_BUF = new byte[DWORD];
+ private final byte[] WORD_BUF = new byte[WORD];
+ private final byte[] CFH_BUF = new byte[CFH_LEN];
+ private final byte[] SHORT_BUF = new byte[SHORT];
+
+ /**
+ * Opens the given file for reading, assuming the platform's
+ * native encoding for file names.
+ *
+ * @param f the archive.
+ *
+ * @throws IOException if an error occurs while reading the file.
+ */
+ public ZipFile(final File f) throws IOException {
+ this(f, null);
+ }
+
+ /**
+ * Opens the given file for reading, assuming the platform's
+ * native encoding for file names.
+ *
+ * @param name name of the archive.
+ *
+ * @throws IOException if an error occurs while reading the file.
+ */
+ public ZipFile(final String name) throws IOException {
+ this(new File(name), null);
+ }
+
+ /**
+ * Opens the given file for reading, assuming the specified
+ * encoding for file names, scanning unicode extra fields.
+ *
+ * @param name name of the archive.
+ * @param encoding the encoding to use for file names, use null
+ * for the platform's default encoding
+ *
+ * @throws IOException if an error occurs while reading the file.
+ */
+ public ZipFile(final String name, final String encoding) throws IOException {
+ this(new File(name), encoding, true);
+ }
+
+ /**
+ * Opens the given file for reading, assuming the specified
+ * encoding for file names and scanning for unicode extra fields.
+ *
+ * @param f the archive.
+ * @param encoding the encoding to use for file names, use null
+ * for the platform's default encoding
+ *
+ * @throws IOException if an error occurs while reading the file.
+ */
+ public ZipFile(final File f, final String encoding) throws IOException {
+ this(f, encoding, true);
+ }
+
+ /**
+ * Opens the given file for reading, assuming the specified
+ * encoding for file names.
+ *
+ * @param f the archive.
+ * @param encoding the encoding to use for file names, use null
+ * for the platform's default encoding
+ * @param useUnicodeExtraFields whether to use InfoZIP Unicode
+ * Extra Fields (if present) to set the file names.
+ *
+ * @throws IOException if an error occurs while reading the file.
+ */
+ public ZipFile(final File f, final String encoding, final boolean useUnicodeExtraFields)
+ throws IOException {
+ this.archiveName = f.getAbsolutePath();
+ this.encoding = encoding;
+ this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
+ this.useUnicodeExtraFields = useUnicodeExtraFields;
+ archive = new RandomAccessFile(f, "r");
+ boolean success = false;
+ try {
+ final Map<ZipEntry, NameAndComment> entriesWithoutUTF8Flag =
+ populateFromCentralDirectory();
+ resolveLocalFileHeaderData(entriesWithoutUTF8Flag);
+ success = true;
+ } finally {
+ closed = !success;
+ if (!success) {
+ try {
+ archive.close();
+ } catch (final IOException e2) {
+ // swallow, throw the original exception instead
+ }
+ }
+ }
+ }
+
+ /**
+ * The encoding to use for filenames and the file comment.
+ *
+ * @return null if using the platform's default character encoding.
+ */
+ public String getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * Closes the archive.
+ * @throws IOException if an error occurs closing the archive.
+ */
+ public void close() throws IOException {
+ // this flag is only written here and read in finalize() which
+ // can never be run in parallel.
+ // no synchronization needed.
+ closed = true;
+
+ archive.close();
+ }
+
+ /**
+ * close a zipfile quietly; throw no io fault, do nothing
+ * on a null parameter
+ * @param zipfile file to close, can be null
+ */
+ public static void closeQuietly(final ZipFile zipfile) {
+ if (zipfile != null) {
+ try {
+ zipfile.close();
+ } catch (final IOException e) {
+ //ignore
+ }
+ }
+ }
+
+ /**
+ * Returns all entries.
+ *
+ * <p>Entries will be returned in the same order they appear
+ * within the archive's central directory.</p>
+ *
+ * @return all entries as {@link ZipEntry} instances
+ */
+ public Enumeration<ZipEntry> getEntries() {
+ return Collections.enumeration(entries);
+ }
+
+ /**
+ * Returns all entries in physical order.
+ *
+ * <p>Entries will be returned in the same order their contents
+ * appear within the archive.</p>
+ *
+ * @return all entries as {@link ZipEntry} instances
+ *
+ * @since Ant 1.9.0
+ */
+ public Enumeration<ZipEntry> getEntriesInPhysicalOrder() {
+ final ZipEntry[] allEntries = entries.toArray(new ZipEntry[0]);
+ Arrays.sort(allEntries, OFFSET_COMPARATOR);
+ return Collections.enumeration(Arrays.asList(allEntries));
+ }
+
+ /**
+ * Returns a named entry - or {@code null} if no entry by
+ * that name exists.
+ *
+ * <p>If multiple entries with the same name exist the first entry
+ * in the archive's central directory by that name is
+ * returned.</p>
+ *
+ * @param name name of the entry.
+ * @return the ZipEntry corresponding to the given name - or
+ * {@code null} if not present.
+ */
+ public ZipEntry getEntry(final String name) {
+ final LinkedList<ZipEntry> entriesOfThatName = nameMap.get(name);
+ return entriesOfThatName != null ? entriesOfThatName.getFirst() : null;
+ }
+
+ /**
+ * Returns all named entries in the same order they appear within
+ * the archive's central directory.
+ *
+ * @param name name of the entry.
+ * @return the Iterable<ZipEntry> corresponding to the
+ * given name
+ * @since 1.9.2
+ */
+ public Iterable<ZipEntry> getEntries(final String name) {
+ final List<ZipEntry> entriesOfThatName = nameMap.get(name);
+ return entriesOfThatName != null ? entriesOfThatName
+ : Collections.<ZipEntry>emptyList();
+ }
+
+ /**
+ * Returns all named entries in the same order their contents
+ * appear within the archive.
+ *
+ * @param name name of the entry.
+ * @return the Iterable<ZipEntry> corresponding to the
+ * given name
+ * @since 1.9.2
+ */
+ public Iterable<ZipEntry> getEntriesInPhysicalOrder(final String name) {
+ ZipEntry[] entriesOfThatName = new ZipEntry[0];
+ if (nameMap.containsKey(name)) {
+ entriesOfThatName = nameMap.get(name).toArray(entriesOfThatName);
+ Arrays.sort(entriesOfThatName, OFFSET_COMPARATOR);
+ }
+ return Arrays.asList(entriesOfThatName);
+ }
+
+ /**
+ * Whether this class is able to read the given entry.
+ *
+ * <p>May return false if it is set up to use encryption or a
+ * compression method that hasn't been implemented yet.</p>
+ */
+ public boolean canReadEntryData(final ZipEntry ze) {
+ return ZipUtil.canHandleEntryData(ze);
+ }
+
+ /**
+ * Returns an InputStream for reading the contents of the given entry.
+ *
+ * @param ze the entry to get the stream for.
+ * @return a stream to read the entry from.
+ * @throws IOException if unable to create an input stream from the zipentry
+ * @throws ZipException if the zipentry uses an unsupported feature
+ */
+ public InputStream getInputStream(final ZipEntry ze)
+ throws IOException, ZipException {
+ if (!(ze instanceof Entry)) {
+ return null;
+ }
+ // cast valididty is checked just above
+ final OffsetEntry offsetEntry = ((Entry) ze).getOffsetEntry();
+ ZipUtil.checkRequestedFeatures(ze);
+ final long start = offsetEntry.dataOffset;
+ final BoundedInputStream bis =
+ new BoundedInputStream(start, ze.getCompressedSize());
+ switch (ze.getMethod()) {
+ case ZipEntry.STORED:
+ return bis;
+ case ZipEntry.DEFLATED:
+ bis.addDummy();
+ final Inflater inflater = new Inflater(true);
+ return new InflaterInputStream(bis, inflater) {
+ @Override
+ public void close() throws IOException {
+ super.close();
+ inflater.end();
+ }
+ };
+ default:
+ throw new ZipException("Found unsupported compression method "
+ + ze.getMethod());
+ }
+ }
+
+ /**
+ * Ensures that the close method of this zipfile is called when
+ * there are no more references to it.
+ * @see #close()
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (!closed) {
+ System.err.println("Cleaning up unclosed ZipFile for archive "
+ + archiveName);
+ close();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Length of a "central directory" entry structure without file
+ * name, extra fields or comment.
+ */
+ private static final int CFH_LEN =
+ /* version made by */ SHORT
+ /* version needed to extract */ + SHORT
+ /* general purpose bit flag */ + SHORT
+ /* compression method */ + SHORT
+ /* last mod file time */ + SHORT
+ /* last mod file date */ + SHORT
+ /* crc-32 */ + WORD
+ /* compressed size */ + WORD
+ /* uncompressed size */ + WORD
+ /* filename length */ + SHORT
+ /* extra field length */ + SHORT
+ /* file comment length */ + SHORT
+ /* disk number start */ + SHORT
+ /* internal file attributes */ + SHORT
+ /* external file attributes */ + WORD
+ /* relative offset of local header */ + WORD;
+
+ private static final long CFH_SIG =
+ ZipLong.getValue(ZipOutputStream.CFH_SIG);
+
+ /**
+ * Reads the central directory of the given archive and populates
+ * the internal tables with ZipEntry instances.
+ *
+ * <p>The ZipEntrys will know all data that can be obtained from
+ * the central directory alone, but not the data that requires the
+ * local file header or additional data to be read.</p>
+ *
+ * @return a map of zipentries that didn't have the language
+ * encoding flag set when read.
+ */
+ private Map<ZipEntry, NameAndComment> populateFromCentralDirectory()
+ throws IOException {
+ final HashMap<ZipEntry, NameAndComment> noUTF8Flag =
+ new HashMap<ZipEntry, NameAndComment>();
+
+ positionAtCentralDirectory();
+
+ archive.readFully(WORD_BUF);
+ long sig = ZipLong.getValue(WORD_BUF);
+
+ if (sig != CFH_SIG && startsWithLocalFileHeader()) {
+ throw new IOException("central directory is empty, can't expand"
+ + " corrupt archive.");
+ }
+
+ while (sig == CFH_SIG) {
+ readCentralDirectoryEntry(noUTF8Flag);
+ archive.readFully(WORD_BUF);
+ sig = ZipLong.getValue(WORD_BUF);
+ }
+ return noUTF8Flag;
+ }
+
+ /**
+ * Reads an individual entry of the central directory, creats an
+ * ZipEntry from it and adds it to the global maps.
+ *
+ * @param noUTF8Flag map used to collect entries that don't have
+ * their UTF-8 flag set and whose name will be set by data read
+ * from the local file header later. The current entry may be
+ * added to this map.
+ */
+ private void
+ readCentralDirectoryEntry(final Map<ZipEntry, NameAndComment> noUTF8Flag)
+ throws IOException {
+ archive.readFully(CFH_BUF);
+ int off = 0;
+ final OffsetEntry offset = new OffsetEntry();
+ final Entry ze = new Entry(offset);
+
+ final int versionMadeBy = ZipShort.getValue(CFH_BUF, off);
+ off += SHORT;
+ ze.setPlatform((versionMadeBy >> BYTE_SHIFT) & NIBLET_MASK);
+
+ off += SHORT; // skip version info
+
+ final GeneralPurposeBit gpFlag = GeneralPurposeBit.parse(CFH_BUF, off);
+ final boolean hasUTF8Flag = gpFlag.usesUTF8ForNames();
+ final ZipEncoding entryEncoding =
+ hasUTF8Flag ? ZipEncodingHelper.UTF8_ZIP_ENCODING : zipEncoding;
+ ze.setGeneralPurposeBit(gpFlag);
+
+ off += SHORT;
+
+ ze.setMethod(ZipShort.getValue(CFH_BUF, off));
+ off += SHORT;
+
+ final long time = ZipUtil.dosToJavaTime(ZipLong.getValue(CFH_BUF, off));
+ ze.setTime(time);
+ off += WORD;
+
+ ze.setCrc(ZipLong.getValue(CFH_BUF, off));
+ off += WORD;
+
+ ze.setCompressedSize(ZipLong.getValue(CFH_BUF, off));
+ off += WORD;
+
+ ze.setSize(ZipLong.getValue(CFH_BUF, off));
+ off += WORD;
+
+ final int fileNameLen = ZipShort.getValue(CFH_BUF, off);
+ off += SHORT;
+
+ final int extraLen = ZipShort.getValue(CFH_BUF, off);
+ off += SHORT;
+
+ final int commentLen = ZipShort.getValue(CFH_BUF, off);
+ off += SHORT;
+
+ final int diskStart = ZipShort.getValue(CFH_BUF, off);
+ off += SHORT;
+
+ ze.setInternalAttributes(ZipShort.getValue(CFH_BUF, off));
+ off += SHORT;
+
+ ze.setExternalAttributes(ZipLong.getValue(CFH_BUF, off));
+ off += WORD;
+
+ final byte[] fileName = new byte[fileNameLen];
+ archive.readFully(fileName);
+ ze.setName(entryEncoding.decode(fileName), fileName);
+
+ // LFH offset,
+ offset.headerOffset = ZipLong.getValue(CFH_BUF, off);
+ // data offset will be filled later
+ entries.add(ze);
+
+ final byte[] cdExtraData = new byte[extraLen];
+ archive.readFully(cdExtraData);
+ ze.setCentralDirectoryExtra(cdExtraData);
+
+ setSizesAndOffsetFromZip64Extra(ze, offset, diskStart);
+
+ final byte[] comment = new byte[commentLen];
+ archive.readFully(comment);
+ ze.setComment(entryEncoding.decode(comment));
+
+ if (!hasUTF8Flag && useUnicodeExtraFields) {
+ noUTF8Flag.put(ze, new NameAndComment(fileName, comment));
+ }
+ }
+
+ /**
+ * If the entry holds a Zip64 extended information extra field,
+ * read sizes from there if the entry's sizes are set to
+ * 0xFFFFFFFFF, do the same for the offset of the local file
+ * header.
+ *
+ * <p>Ensures the Zip64 extra either knows both compressed and
+ * uncompressed size or neither of both as the internal logic in
+ * ExtraFieldUtils forces the field to create local header data
+ * even if they are never used - and here a field with only one
+ * size would be invalid.</p>
+ */
+ private void setSizesAndOffsetFromZip64Extra(final ZipEntry ze,
+ final OffsetEntry offset,
+ final int diskStart)
+ throws IOException {
+ final Zip64ExtendedInformationExtraField z64 =
+ (Zip64ExtendedInformationExtraField)
+ ze.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
+ if (z64 != null) {
+ final boolean hasUncompressedSize = ze.getSize() == ZIP64_MAGIC;
+ final boolean hasCompressedSize = ze.getCompressedSize() == ZIP64_MAGIC;
+ final boolean hasRelativeHeaderOffset =
+ offset.headerOffset == ZIP64_MAGIC;
+ z64.reparseCentralDirectoryData(hasUncompressedSize,
+ hasCompressedSize,
+ hasRelativeHeaderOffset,
+ diskStart == ZIP64_MAGIC_SHORT);
+
+ if (hasUncompressedSize) {
+ ze.setSize(z64.getSize().getLongValue());
+ } else if (hasCompressedSize) {
+ z64.setSize(new ZipEightByteInteger(ze.getSize()));
+ }
+
+ if (hasCompressedSize) {
+ ze.setCompressedSize(z64.getCompressedSize().getLongValue());
+ } else if (hasUncompressedSize) {
+ z64.setCompressedSize(new ZipEightByteInteger(ze.getCompressedSize()));
+ }
+
+ if (hasRelativeHeaderOffset) {
+ offset.headerOffset =
+ z64.getRelativeHeaderOffset().getLongValue();
+ }
+ }
+ }
+
+ /**
+ * Length of the "End of central directory record" - which is
+ * supposed to be the last structure of the archive - without file
+ * comment.
+ */
+ private static final int MIN_EOCD_SIZE =
+ /* end of central dir signature */ WORD
+ /* number of this disk */ + SHORT
+ /* number of the disk with the */
+ /* start of the central directory */ + SHORT
+ /* total number of entries in */
+ /* the central dir on this disk */ + SHORT
+ /* total number of entries in */
+ /* the central dir */ + SHORT
+ /* size of the central directory */ + WORD
+ /* offset of start of central */
+ /* directory with respect to */
+ /* the starting disk number */ + WORD
+ /* zipfile comment length */ + SHORT;
+
+ /**
+ * Maximum length of the "End of central directory record" with a
+ * file comment.
+ */
+ private static final int MAX_EOCD_SIZE = MIN_EOCD_SIZE
+ /* maximum length of zipfile comment */ + ZIP64_MAGIC_SHORT;
+
+ /**
+ * Offset of the field that holds the location of the first
+ * central directory entry inside the "End of central directory
+ * record" relative to the start of the "End of central directory
+ * record".
+ */
+ private static final int CFD_LOCATOR_OFFSET =
+ /* end of central dir signature */ WORD
+ /* number of this disk */ + SHORT
+ /* number of the disk with the */
+ /* start of the central directory */ + SHORT
+ /* total number of entries in */
+ /* the central dir on this disk */ + SHORT
+ /* total number of entries in */
+ /* the central dir */ + SHORT
+ /* size of the central directory */ + WORD;
+
+ /**
+ * Length of the "Zip64 end of central directory locator" - which
+ * should be right in front of the "end of central directory
+ * record" if one is present at all.
+ */
+ private static final int ZIP64_EOCDL_LENGTH =
+ /* zip64 end of central dir locator sig */ WORD
+ /* number of the disk with the start */
+ /* start of the zip64 end of */
+ /* central directory */ + WORD
+ /* relative offset of the zip64 */
+ /* end of central directory record */ + DWORD
+ /* total number of disks */ + WORD;
+
+ /**
+ * Offset of the field that holds the location of the "Zip64 end
+ * of central directory record" inside the "Zip64 end of central
+ * directory locator" relative to the start of the "Zip64 end of
+ * central directory locator".
+ */
+ private static final int ZIP64_EOCDL_LOCATOR_OFFSET =
+ /* zip64 end of central dir locator sig */ WORD
+ /* number of the disk with the start */
+ /* start of the zip64 end of */
+ /* central directory */ + WORD;
+
+ /**
+ * Offset of the field that holds the location of the first
+ * central directory entry inside the "Zip64 end of central
+ * directory record" relative to the start of the "Zip64 end of
+ * central directory record".
+ */
+ private static final int ZIP64_EOCD_CFD_LOCATOR_OFFSET =
+ /* zip64 end of central dir */
+ /* signature */ WORD
+ /* size of zip64 end of central */
+ /* directory record */ + DWORD
+ /* version made by */ + SHORT
+ /* version needed to extract */ + SHORT
+ /* number of this disk */ + WORD
+ /* number of the disk with the */
+ /* start of the central directory */ + WORD
+ /* total number of entries in the */
+ /* central directory on this disk */ + DWORD
+ /* total number of entries in the */
+ /* central directory */ + DWORD
+ /* size of the central directory */ + DWORD;
+
+ /**
+ * Searches for either the &quot;Zip64 end of central directory
+ * locator&quot; or the &quot;End of central dir record&quot;, parses
+ * it and positions the stream at the first central directory
+ * record.
+ */
+ private void positionAtCentralDirectory()
+ throws IOException {
+ positionAtEndOfCentralDirectoryRecord();
+ boolean found = false;
+ final boolean searchedForZip64EOCD =
+ archive.getFilePointer() > ZIP64_EOCDL_LENGTH;
+ if (searchedForZip64EOCD) {
+ archive.seek(archive.getFilePointer() - ZIP64_EOCDL_LENGTH);
+ archive.readFully(WORD_BUF);
+ found = Arrays.equals(ZipOutputStream.ZIP64_EOCD_LOC_SIG, WORD_BUF);
+ }
+ if (!found) {
+ // not a ZIP64 archive
+ if (searchedForZip64EOCD) {
+ skipBytes(ZIP64_EOCDL_LENGTH - WORD);
+ }
+ positionAtCentralDirectory32();
+ } else {
+ positionAtCentralDirectory64();
+ }
+ }
+
+ /**
+ * Parses the &quot;Zip64 end of central directory locator&quot;,
+ * finds the &quot;Zip64 end of central directory record&quot; using the
+ * parsed information, parses that and positions the stream at the
+ * first central directory record.
+ */
+ private void positionAtCentralDirectory64()
+ throws IOException {
+ skipBytes(ZIP64_EOCDL_LOCATOR_OFFSET
+ - WORD /* signature has already been read */);
+ archive.readFully(DWORD_BUF);
+ archive.seek(ZipEightByteInteger.getLongValue(DWORD_BUF));
+ archive.readFully(WORD_BUF);
+ if (!Arrays.equals(WORD_BUF, ZipOutputStream.ZIP64_EOCD_SIG)) {
+ throw new ZipException("archive's ZIP64 end of central "
+ + "directory locator is corrupt.");
+ }
+ skipBytes(ZIP64_EOCD_CFD_LOCATOR_OFFSET
+ - WORD /* signature has already been read */);
+ archive.readFully(DWORD_BUF);
+ archive.seek(ZipEightByteInteger.getLongValue(DWORD_BUF));
+ }
+
+ /**
+ * Searches for the &quot;End of central dir record&quot;, parses
+ * it and positions the stream at the first central directory
+ * record.
+ */
+ private void positionAtCentralDirectory32()
+ throws IOException {
+ skipBytes(CFD_LOCATOR_OFFSET);
+ archive.readFully(WORD_BUF);
+ archive.seek(ZipLong.getValue(WORD_BUF));
+ }
+
+ /**
+ * Searches for the and positions the stream at the start of the
+ * &quot;End of central dir record&quot;.
+ */
+ private void positionAtEndOfCentralDirectoryRecord()
+ throws IOException {
+ final boolean found = tryToLocateSignature(MIN_EOCD_SIZE, MAX_EOCD_SIZE,
+ ZipOutputStream.EOCD_SIG);
+ if (!found) {
+ throw new ZipException("archive is not a ZIP archive");
+ }
+ }
+
+ /**
+ * Searches the archive backwards from minDistance to maxDistance
+ * for the given signature, positions the RandomaccessFile right
+ * at the signature if it has been found.
+ */
+ private boolean tryToLocateSignature(final long minDistanceFromEnd,
+ final long maxDistanceFromEnd,
+ final byte[] sig) throws IOException {
+ boolean found = false;
+ long off = archive.length() - minDistanceFromEnd;
+ final long stopSearching =
+ Math.max(0L, archive.length() - maxDistanceFromEnd);
+ if (off >= 0) {
+ for (; off >= stopSearching; off--) {
+ archive.seek(off);
+ int curr = archive.read();
+ if (curr == -1) {
+ break;
+ }
+ if (curr == sig[POS_0]) {
+ curr = archive.read();
+ if (curr == sig[POS_1]) {
+ curr = archive.read();
+ if (curr == sig[POS_2]) {
+ curr = archive.read();
+ if (curr == sig[POS_3]) {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (found) {
+ archive.seek(off);
+ }
+ return found;
+ }
+
+ /**
+ * Skips the given number of bytes or throws an EOFException if
+ * skipping failed.
+ */
+ private void skipBytes(final int count) throws IOException {
+ int totalSkipped = 0;
+ while (totalSkipped < count) {
+ final int skippedNow = archive.skipBytes(count - totalSkipped);
+ if (skippedNow <= 0) {
+ throw new EOFException();
+ }
+ totalSkipped += skippedNow;
+ }
+ }
+
+ /**
+ * Number of bytes in local file header up to the &quot;length of
+ * filename&quot; entry.
+ */
+ private static final long LFH_OFFSET_FOR_FILENAME_LENGTH =
+ /* local file header signature */ WORD
+ /* version needed to extract */ + SHORT
+ /* general purpose bit flag */ + SHORT
+ /* compression method */ + SHORT
+ /* last mod file time */ + SHORT
+ /* last mod file date */ + SHORT
+ /* crc-32 */ + WORD
+ /* compressed size */ + WORD
+ /* uncompressed size */ + WORD;
+
+ /**
+ * Walks through all recorded entries and adds the data available
+ * from the local file header.
+ *
+ * <p>Also records the offsets for the data to read from the
+ * entries.</p>
+ */
+ private void resolveLocalFileHeaderData(final Map<ZipEntry, NameAndComment>
+ entriesWithoutUTF8Flag)
+ throws IOException {
+ for (final Iterator<ZipEntry> it = entries.iterator(); it.hasNext();) {
+ // entries is filled in populateFromCentralDirectory and
+ // never modified
+ final Entry ze = (Entry) it.next();
+ final OffsetEntry offsetEntry = ze.getOffsetEntry();
+ final long offset = offsetEntry.headerOffset;
+ archive.seek(offset + LFH_OFFSET_FOR_FILENAME_LENGTH);
+ archive.readFully(SHORT_BUF);
+ final int fileNameLen = ZipShort.getValue(SHORT_BUF);
+ archive.readFully(SHORT_BUF);
+ final int extraFieldLen = ZipShort.getValue(SHORT_BUF);
+ int lenToSkip = fileNameLen;
+ while (lenToSkip > 0) {
+ final int skipped = archive.skipBytes(lenToSkip);
+ if (skipped <= 0) {
+ throw new IOException("failed to skip file name in"
+ + " local file header");
+ }
+ lenToSkip -= skipped;
+ }
+ final byte[] localExtraData = new byte[extraFieldLen];
+ archive.readFully(localExtraData);
+ ze.setExtra(localExtraData);
+ offsetEntry.dataOffset = offset + LFH_OFFSET_FOR_FILENAME_LENGTH
+ + SHORT + SHORT + fileNameLen + extraFieldLen;
+
+ if (entriesWithoutUTF8Flag.containsKey(ze)) {
+ final NameAndComment nc = entriesWithoutUTF8Flag.get(ze);
+ ZipUtil.setNameAndCommentFromExtraFields(ze, nc.name,
+ nc.comment);
+ }
+
+ final String name = ze.getName();
+ LinkedList<ZipEntry> entriesOfThatName = nameMap.get(name);
+ if (entriesOfThatName == null) {
+ entriesOfThatName = new LinkedList<ZipEntry>();
+ nameMap.put(name, entriesOfThatName);
+ }
+ entriesOfThatName.addLast(ze);
+ }
+ }
+
+ /**
+ * Checks whether the archive starts with a LFH. If it doesn't,
+ * it may be an empty archive.
+ */
+ private boolean startsWithLocalFileHeader() throws IOException {
+ archive.seek(0);
+ archive.readFully(WORD_BUF);
+ return Arrays.equals(WORD_BUF, ZipOutputStream.LFH_SIG);
+ }
+
+ /**
+ * InputStream that delegates requests to the underlying
+ * RandomAccessFile, making sure that only bytes from a certain
+ * range can be read.
+ */
+ private class BoundedInputStream extends InputStream {
+ private long remaining;
+ private long loc;
+ private boolean addDummyByte = false;
+
+ BoundedInputStream(final long start, final long remaining) {
+ this.remaining = remaining;
+ loc = start;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (remaining-- <= 0) {
+ if (addDummyByte) {
+ addDummyByte = false;
+ return 0;
+ }
+ return -1;
+ }
+ synchronized (archive) {
+ archive.seek(loc++);
+ return archive.read();
+ }
+ }
+
+ @Override
+ public int read(final byte[] b, final int off, int len) throws IOException {
+ if (remaining <= 0) {
+ if (addDummyByte) {
+ addDummyByte = false;
+ b[off] = 0;
+ return 1;
+ }
+ return -1;
+ }
+
+ if (len <= 0) {
+ return 0;
+ }
+
+ if (len > remaining) {
+ len = (int) remaining;
+ }
+ int ret = -1;
+ synchronized (archive) {
+ archive.seek(loc);
+ ret = archive.read(b, off, len);
+ }
+ if (ret > 0) {
+ loc += ret;
+ remaining -= ret;
+ }
+ return ret;
+ }
+
+ /**
+ * Inflater needs an extra dummy byte for nowrap - see
+ * Inflater's javadocs.
+ */
+ void addDummy() {
+ addDummyByte = true;
+ }
+ }
+
+ private static final class NameAndComment {
+ private final byte[] name;
+ private final byte[] comment;
+ private NameAndComment(final byte[] name, final byte[] comment) {
+ this.name = name;
+ this.comment = comment;
+ }
+ }
+
+ /**
+ * Compares two ZipEntries based on their offset within the archive.
+ *
+ * <p>Won't return any meaningful results if one of the entries
+ * isn't part of the archive at all.</p>
+ *
+ * @since Ant 1.9.0
+ */
+ private final Comparator<ZipEntry> OFFSET_COMPARATOR =
+ new Comparator<ZipEntry>() {
+ public int compare(final ZipEntry e1, final ZipEntry e2) {
+ if (e1 == e2) {
+ return 0;
+ }
+
+ final Entry ent1 = e1 instanceof Entry ? (Entry) e1 : null;
+ final Entry ent2 = e2 instanceof Entry ? (Entry) e2 : null;
+ if (ent1 == null) {
+ return 1;
+ }
+ if (ent2 == null) {
+ return -1;
+ }
+ final long val = (ent1.getOffsetEntry().headerOffset
+ - ent2.getOffsetEntry().headerOffset);
+ return val == 0 ? 0 : val < 0 ? -1 : +1;
+ }
+ };
+
+ /**
+ * Extends ZipEntry to store the offset within the archive.
+ */
+ private static class Entry extends ZipEntry {
+
+ private final OffsetEntry offsetEntry;
+
+ Entry(final OffsetEntry offset) {
+ this.offsetEntry = offset;
+ }
+
+ OffsetEntry getOffsetEntry() {
+ return offsetEntry;
+ }
+
+ @Override
+ public int hashCode() {
+ return 3 * super.hashCode()
+ + (int) (offsetEntry.headerOffset % Integer.MAX_VALUE);
+ }
+
+ @Override
+ public boolean equals(final Object other) {
+ if (super.equals(other)) {
+ // super.equals would return false if other were not an Entry
+ final Entry otherEntry = (Entry) other;
+ return offsetEntry.headerOffset
+ == otherEntry.offsetEntry.headerOffset
+ && offsetEntry.dataOffset
+ == otherEntry.offsetEntry.dataOffset;
+ }
+ return false;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipLong.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipLong.java
new file mode 100644
index 00000000..72af84db
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipLong.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.tools.zip;
+
+import static org.apache.tools.zip.ZipConstants.BYTE_MASK;
+import static org.apache.tools.zip.ZipConstants.WORD;
+
+/**
+ * Utility class that represents a four byte integer with conversion
+ * rules for the big endian byte order of ZIP files.
+ *
+ */
+public final class ZipLong implements Cloneable {
+
+ //private static final int BYTE_BIT_SIZE = 8;
+
+ private static final int BYTE_1 = 1;
+ private static final int BYTE_1_MASK = 0xFF00;
+ private static final int BYTE_1_SHIFT = 8;
+
+ private static final int BYTE_2 = 2;
+ private static final int BYTE_2_MASK = 0xFF0000;
+ private static final int BYTE_2_SHIFT = 16;
+
+ private static final int BYTE_3 = 3;
+ private static final long BYTE_3_MASK = 0xFF000000L;
+ private static final int BYTE_3_SHIFT = 24;
+
+ private final long value;
+
+ /** Central File Header Signature */
+ public static final ZipLong CFH_SIG = new ZipLong(0X02014B50L);
+
+ /** Local File Header Signature */
+ public static final ZipLong LFH_SIG = new ZipLong(0X04034B50L);
+
+ /**
+ * Data Descriptor signature
+ */
+ public static final ZipLong DD_SIG = new ZipLong(0X08074B50L);
+
+ /**
+ * Value stored in size and similar fields if ZIP64 extensions are
+ * used.
+ */
+ static final ZipLong ZIP64_MAGIC = new ZipLong(ZipConstants.ZIP64_MAGIC);
+
+ /**
+ * Create instance from a number.
+ * @param value the long to store as a ZipLong
+ * @since 1.1
+ */
+ public ZipLong(long value) {
+ this.value = value;
+ }
+
+ /**
+ * Create instance from bytes.
+ * @param bytes the bytes to store as a ZipLong
+ * @since 1.1
+ */
+ public ZipLong (byte[] bytes) {
+ this(bytes, 0);
+ }
+
+ /**
+ * Create instance from the four bytes starting at offset.
+ * @param bytes the bytes to store as a ZipLong
+ * @param offset the offset to start
+ * @since 1.1
+ */
+ public ZipLong (byte[] bytes, int offset) {
+ value = ZipLong.getValue(bytes, offset);
+ }
+
+ /**
+ * Get value as four bytes in big endian byte order.
+ * @since 1.1
+ * @return value as four bytes in big endian order
+ */
+ public byte[] getBytes() {
+ return ZipLong.getBytes(value);
+ }
+
+ /**
+ * Get value as Java long.
+ * @since 1.1
+ * @return value as a long
+ */
+ public long getValue() {
+ return value;
+ }
+
+ /**
+ * Get value as four bytes in big endian byte order.
+ * @param value the value to convert
+ * @return value as four bytes in big endian byte order
+ */
+ public static byte[] getBytes(long value) {
+ byte[] result = new byte[WORD];
+ putLong(value, result, 0);
+ return result;
+ }
+
+ /**
+ * put the value as four bytes in big endian byte order.
+ * @param value the Java long to convert to bytes
+ * @param buf the output buffer
+ * @param offset
+ * The offset within the output buffer of the first byte to be written.
+ * must be non-negative and no larger than <tt>buf.length-4</tt>
+ */
+ public static void putLong(long value, byte[] buf, int offset) {
+ buf[offset++] = (byte) ((value & BYTE_MASK));
+ buf[offset++] = (byte) ((value & BYTE_1_MASK) >> BYTE_1_SHIFT);
+ buf[offset++] = (byte) ((value & BYTE_2_MASK) >> BYTE_2_SHIFT);
+ buf[offset] = (byte) ((value & BYTE_3_MASK) >> BYTE_3_SHIFT);
+ }
+
+ public void putLong(byte[] buf, int offset) {
+ putLong(value, buf, offset);
+ }
+
+ /**
+ * Helper method to get the value as a Java long from four bytes starting at given array offset
+ * @param bytes the array of bytes
+ * @param offset the offset to start
+ * @return the corresponding Java long value
+ */
+ public static long getValue(byte[] bytes, int offset) {
+ long value = (bytes[offset + BYTE_3] << BYTE_3_SHIFT) & BYTE_3_MASK;
+ value += (bytes[offset + BYTE_2] << BYTE_2_SHIFT) & BYTE_2_MASK;
+ value += (bytes[offset + BYTE_1] << BYTE_1_SHIFT) & BYTE_1_MASK;
+ value += (bytes[offset] & BYTE_MASK);
+ return value;
+ }
+
+ /**
+ * Helper method to get the value as a Java long from a four-byte array
+ * @param bytes the array of bytes
+ * @return the corresponding Java long value
+ */
+ public static long getValue(byte[] bytes) {
+ return getValue(bytes, 0);
+ }
+
+ /**
+ * Override to make two instances with same value equal.
+ * @param o an object to compare
+ * @return true if the objects are equal
+ * @since 1.1
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof ZipLong)) {
+ return false;
+ }
+ return value == ((ZipLong) o).getValue();
+ }
+
+ /**
+ * Override to make two instances with same value equal.
+ * @return the value stored in the ZipLong
+ * @since 1.1
+ */
+ @Override
+ public int hashCode() {
+ return (int) value;
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException cnfe) {
+ // impossible
+ throw new RuntimeException(cnfe);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ZipLong value: " + value;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipOutputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipOutputStream.java
new file mode 100644
index 00000000..261c717e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipOutputStream.java
@@ -0,0 +1,1674 @@
+/*
+ * 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.tools.zip;
+
+import static org.apache.tools.zip.ZipConstants.DATA_DESCRIPTOR_MIN_VERSION;
+import static org.apache.tools.zip.ZipConstants.DWORD;
+import static org.apache.tools.zip.ZipConstants.INITIAL_VERSION;
+import static org.apache.tools.zip.ZipConstants.SHORT;
+import static org.apache.tools.zip.ZipConstants.WORD;
+import static org.apache.tools.zip.ZipConstants.ZIP64_MAGIC;
+import static org.apache.tools.zip.ZipConstants.ZIP64_MAGIC_SHORT;
+import static org.apache.tools.zip.ZipConstants.ZIP64_MIN_VERSION;
+import static org.apache.tools.zip.ZipLong.putLong;
+import static org.apache.tools.zip.ZipShort.putShort;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.ZipException;
+
+/**
+ * Reimplementation of {@link java.util.zip.ZipOutputStream
+ * java.util.zip.ZipOutputStream} that does handle the extended
+ * functionality of this package, especially internal/external file
+ * attributes and extra fields with different layouts for local file
+ * data and central directory entries.
+ *
+ * <p>This class will try to use {@link java.io.RandomAccessFile
+ * RandomAccessFile} when you know that the output is going to go to a
+ * file.</p>
+ *
+ * <p>If RandomAccessFile cannot be used, this implementation will use
+ * a Data Descriptor to store size and CRC information for {@link
+ * #DEFLATED DEFLATED} entries, this means, you don't need to
+ * calculate them yourself. Unfortunately this is not possible for
+ * the {@link #STORED STORED} method, here setting the CRC and
+ * uncompressed size information is required before {@link
+ * #putNextEntry putNextEntry} can be called.</p>
+ *
+ * <p>As of Apache Ant 1.9.0 it transparently supports Zip64
+ * extensions and thus individual entries and archives larger than 4
+ * GB or with more than 65536 entries in most cases but explicit
+ * control is provided via {@link #setUseZip64}. If the stream can not
+ * user RandomAccessFile and you try to write a ZipEntry of
+ * unknown size then Zip64 extensions will be disabled by default.</p>
+ */
+public class ZipOutputStream extends FilterOutputStream {
+
+ private static final int BUFFER_SIZE = 512;
+ private static final int LFH_SIG_OFFSET = 0;
+ private static final int LFH_VERSION_NEEDED_OFFSET = 4;
+ private static final int LFH_GPB_OFFSET = 6;
+ private static final int LFH_METHOD_OFFSET = 8;
+ private static final int LFH_TIME_OFFSET = 10;
+ private static final int LFH_CRC_OFFSET = 14;
+ private static final int LFH_COMPRESSED_SIZE_OFFSET = 18;
+ private static final int LFH_ORIGINAL_SIZE_OFFSET = 22;
+ private static final int LFH_FILENAME_LENGTH_OFFSET = 26;
+ private static final int LFH_EXTRA_LENGTH_OFFSET = 28;
+ private static final int LFH_FILENAME_OFFSET = 30;
+ private static final int CFH_SIG_OFFSET = 0;
+ private static final int CFH_VERSION_MADE_BY_OFFSET = 4;
+ private static final int CFH_VERSION_NEEDED_OFFSET = 6;
+ private static final int CFH_GPB_OFFSET = 8;
+ private static final int CFH_METHOD_OFFSET = 10;
+ private static final int CFH_TIME_OFFSET = 12;
+ private static final int CFH_CRC_OFFSET = 16;
+ private static final int CFH_COMPRESSED_SIZE_OFFSET = 20;
+ private static final int CFH_ORIGINAL_SIZE_OFFSET = 24;
+ private static final int CFH_FILENAME_LENGTH_OFFSET = 28;
+ private static final int CFH_EXTRA_LENGTH_OFFSET = 30;
+ private static final int CFH_COMMENT_LENGTH_OFFSET = 32;
+ private static final int CFH_DISK_NUMBER_OFFSET = 34;
+ private static final int CFH_INTERNAL_ATTRIBUTES_OFFSET = 36;
+ private static final int CFH_EXTERNAL_ATTRIBUTES_OFFSET = 38;
+ private static final int CFH_LFH_OFFSET = 42;
+ private static final int CFH_FILENAME_OFFSET = 46;
+
+ /**
+ * indicates if this archive is finished.
+ */
+ private boolean finished = false;
+
+ /*
+ * Apparently Deflater.setInput gets slowed down a lot on Sun JVMs
+ * when it gets handed a really big buffer. See
+ * https://issues.apache.org/bugzilla/show_bug.cgi?id=45396
+ *
+ * Using a buffer size of 8 kB proved to be a good compromise
+ */
+ private static final int DEFLATER_BLOCK_SIZE = 8192;
+
+ /**
+ * Compression method for deflated entries.
+ *
+ * @since 1.1
+ */
+ public static final int DEFLATED = java.util.zip.ZipEntry.DEFLATED;
+
+ /**
+ * Default compression level for deflated entries.
+ *
+ * @since Ant 1.7
+ */
+ public static final int DEFAULT_COMPRESSION = Deflater.DEFAULT_COMPRESSION;
+
+ /**
+ * Compression method for stored entries.
+ *
+ * @since 1.1
+ */
+ public static final int STORED = java.util.zip.ZipEntry.STORED;
+
+ /**
+ * default encoding for file names and comment.
+ */
+ static final String DEFAULT_ENCODING = null;
+
+ /**
+ * General purpose flag, which indicates that filenames are
+ * written in utf-8.
+ * @deprecated use {@link GeneralPurposeBit#UFT8_NAMES_FLAG} instead
+ */
+ @Deprecated
+ public static final int EFS_FLAG = GeneralPurposeBit.UFT8_NAMES_FLAG;
+
+ private static final byte[] EMPTY = new byte[0];
+
+ /**
+ * Current entry.
+ *
+ * @since 1.1
+ */
+ private CurrentEntry entry;
+
+ /**
+ * The file comment.
+ *
+ * @since 1.1
+ */
+ private String comment = "";
+
+ /**
+ * Compression level for next entry.
+ *
+ * @since 1.1
+ */
+ private int level = DEFAULT_COMPRESSION;
+
+ /**
+ * Has the compression level changed when compared to the last
+ * entry?
+ *
+ * @since 1.5
+ */
+ private boolean hasCompressionLevelChanged = false;
+
+ /**
+ * Default compression method for next entry.
+ *
+ * @since 1.1
+ */
+ private int method = java.util.zip.ZipEntry.DEFLATED;
+
+ /**
+ * List of ZipEntries written so far.
+ *
+ * @since 1.1
+ */
+ private final List<ZipEntry> entries = new LinkedList<ZipEntry>();
+
+ /**
+ * CRC instance to avoid parsing DEFLATED data twice.
+ *
+ * @since 1.1
+ */
+ private final CRC32 crc = new CRC32();
+
+ /**
+ * Count the bytes written to out.
+ *
+ * @since 1.1
+ */
+ private long written = 0;
+
+ /**
+ * Start of central directory.
+ *
+ * @since 1.1
+ */
+ private long cdOffset = 0;
+
+ /**
+ * Length of central directory.
+ *
+ * @since 1.1
+ */
+ private long cdLength = 0;
+
+ /**
+ * Helper, a 0 as ZipShort.
+ *
+ * @since 1.1
+ */
+ private static final byte[] ZERO = {0, 0};
+
+ /**
+ * Helper, a 0 as ZipLong.
+ *
+ * @since 1.1
+ */
+ private static final byte[] LZERO = {0, 0, 0, 0};
+
+ private static final byte[] ONE = ZipLong.getBytes(1L);
+
+ /**
+ * Holds the offsets of the LFH starts for each entry.
+ *
+ * @since 1.1
+ */
+ private final Map<ZipEntry, Long> offsets = new HashMap<ZipEntry, Long>();
+
+ /**
+ * The encoding to use for filenames and the file comment.
+ *
+ * <p>For a list of possible values see <a
+ * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>.
+ * Defaults to the platform's default character encoding.</p>
+ *
+ * @since 1.3
+ */
+ private String encoding = null;
+
+ /**
+ * The zip encoding to use for filenames and the file comment.
+ *
+ * This field is of internal use and will be set in {@link
+ * #setEncoding(String)}.
+ */
+ private ZipEncoding zipEncoding =
+ ZipEncodingHelper.getZipEncoding(DEFAULT_ENCODING);
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /**
+ * This Deflater object is used for output.
+ *
+ */
+ protected final Deflater def = new Deflater(level, true);
+
+ /**
+ * This buffer serves as a Deflater.
+ *
+ * <p>This attribute is only protected to provide a level of API
+ * backwards compatibility. This class used to extend {@link
+ * java.util.zip.DeflaterOutputStream DeflaterOutputStream} up to
+ * Revision 1.13.</p>
+ *
+ * @since 1.14
+ */
+ protected byte[] buf = new byte[BUFFER_SIZE];
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Optional random access output.
+ *
+ * @since 1.14
+ */
+ private final RandomAccessFile raf;
+
+ /**
+ * whether to use the general purpose bit flag when writing UTF-8
+ * filenames or not.
+ */
+ private boolean useUTF8Flag = true;
+
+ /**
+ * Whether to encode non-encodable file names as UTF-8.
+ */
+ private boolean fallbackToUTF8 = false;
+
+ /**
+ * whether to create UnicodePathExtraField-s for each entry.
+ */
+ private UnicodeExtraFieldPolicy createUnicodeExtraFields = UnicodeExtraFieldPolicy.NEVER;
+
+ /**
+ * Whether anything inside this archive has used a ZIP64 feature.
+ */
+ private boolean hasUsedZip64 = false;
+
+ private Zip64Mode zip64Mode = Zip64Mode.AsNeeded;
+
+ private final Calendar calendarInstance = Calendar.getInstance();
+
+ /**
+ * Creates a new ZIP OutputStream filtering the underlying stream.
+ * @param out the outputstream to zip
+ * @since 1.1
+ */
+ public ZipOutputStream(OutputStream out) {
+ super(out);
+ this.raf = null;
+ }
+
+ /**
+ * Creates a new ZIP OutputStream writing to a File. Will use
+ * random access if possible.
+ * @param file the file to zip to
+ * @since 1.14
+ * @throws IOException on error
+ */
+ public ZipOutputStream(File file) throws IOException {
+ super(null);
+ RandomAccessFile _raf = null;
+ try {
+ _raf = new RandomAccessFile(file, "rw");
+ _raf.setLength(0);
+ } catch (IOException e) {
+ if (_raf != null) {
+ try {
+ _raf.close();
+ } catch (IOException inner) { // NOPMD
+ // ignore
+ }
+ _raf = null;
+ }
+ out = new FileOutputStream(file);
+ }
+ raf = _raf;
+ }
+
+ /**
+ * This method indicates whether this archive is writing to a
+ * seekable stream (i.e., to a random access file).
+ *
+ * <p>For seekable streams, you don't need to calculate the CRC or
+ * uncompressed size for {@link #STORED} entries before
+ * invoking {@link #putNextEntry}.
+ * @return true if seekable
+ * @since 1.17
+ */
+ public boolean isSeekable() {
+ return raf != null;
+ }
+
+ /**
+ * The encoding to use for filenames and the file comment.
+ *
+ * <p>For a list of possible values see <a
+ * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>.
+ * Defaults to the platform's default character encoding.</p>
+ * @param encoding the encoding value
+ * @since 1.3
+ */
+ public void setEncoding(final String encoding) {
+ this.encoding = encoding;
+ this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
+ if (useUTF8Flag && !ZipEncodingHelper.isUTF8(encoding)) {
+ useUTF8Flag = false;
+ }
+ }
+
+ /**
+ * The encoding to use for filenames and the file comment.
+ *
+ * @return null if using the platform's default character encoding.
+ *
+ * @since 1.3
+ */
+ public String getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * Whether to set the language encoding flag if the file name
+ * encoding is UTF-8.
+ *
+ * <p>Defaults to true.</p>
+ */
+ public void setUseLanguageEncodingFlag(boolean b) {
+ useUTF8Flag = b && ZipEncodingHelper.isUTF8(encoding);
+ }
+
+ /**
+ * Whether to create Unicode Extra Fields.
+ *
+ * <p>Defaults to NEVER.</p>
+ */
+ public void setCreateUnicodeExtraFields(UnicodeExtraFieldPolicy b) {
+ createUnicodeExtraFields = b;
+ }
+
+ /**
+ * Whether to fall back to UTF and the language encoding flag if
+ * the file name cannot be encoded using the specified encoding.
+ *
+ * <p>Defaults to false.</p>
+ */
+ public void setFallbackToUTF8(boolean b) {
+ fallbackToUTF8 = b;
+ }
+
+ /**
+ * Whether Zip64 extensions will be used.
+ *
+ * <p>When setting the mode to {@link Zip64Mode#Never Never},
+ * {@link #putNextEntry}, {@link #closeEntry}, {@link
+ * #finish} or {@link #close} may throw a {@link
+ * Zip64RequiredException} if the entry's size or the total size
+ * of the archive exceeds 4GB or there are more than 65536 entries
+ * inside the archive. Any archive created in this mode will be
+ * readable by implementations that don't support Zip64.</p>
+ *
+ * <p>When setting the mode to {@link Zip64Mode#Always Always},
+ * Zip64 extensions will be used for all entries. Any archive
+ * created in this mode may be unreadable by implementations that
+ * don't support Zip64 even if all its contents would be.</p>
+ *
+ * <p>When setting the mode to {@link Zip64Mode#AsNeeded
+ * AsNeeded}, Zip64 extensions will transparently be used for
+ * those entries that require them. This mode can only be used if
+ * the uncompressed size of the {@link ZipEntry} is known
+ * when calling {@link #putNextEntry} or the archive is written
+ * to a seekable output (i.e. you have used the {@link
+ * #ZipOutputStream(java.io.File) File-arg constructor}) -
+ * this mode is not valid when the output stream is not seekable
+ * and the uncompressed size is unknown when {@link
+ * #putNextEntry} is called.</p>
+ *
+ * <p>If no entry inside the resulting archive requires Zip64
+ * extensions then {@link Zip64Mode#Never Never} will create the
+ * smallest archive. {@link Zip64Mode#AsNeeded AsNeeded} will
+ * create a slightly bigger archive if the uncompressed size of
+ * any entry has initially been unknown and create an archive
+ * identical to {@link Zip64Mode#Never Never} otherwise. {@link
+ * Zip64Mode#Always Always} will create an archive that is at
+ * least 24 bytes per entry bigger than the one {@link
+ * Zip64Mode#Never Never} would create.</p>
+ *
+ * <p>Defaults to {@link Zip64Mode#AsNeeded AsNeeded} unless
+ * {@link #putNextEntry} is called with an entry of unknown
+ * size and data is written to a non-seekable stream - in this
+ * case the default is {@link Zip64Mode#Never Never}.</p>
+ *
+ * @since 1.3
+ */
+ public void setUseZip64(Zip64Mode mode) {
+ zip64Mode = mode;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws Zip64RequiredException if the archive's size exceeds 4
+ * GByte or there are more than 65535 entries inside the archive
+ * and {@link #setUseZip64} is {@link Zip64Mode#Never}.
+ */
+ public void finish() throws IOException {
+ if (finished) {
+ throw new IOException("This archive has already been finished");
+ }
+
+ if (entry != null) {
+ closeEntry();
+ }
+
+ cdOffset = written;
+ writeCentralDirectoryInChunks();
+ cdLength = written - cdOffset;
+ writeZip64CentralDirectory();
+ writeCentralDirectoryEnd();
+ offsets.clear();
+ entries.clear();
+ def.end();
+ finished = true;
+ }
+
+ private void writeCentralDirectoryInChunks() throws IOException {
+ final int NUM_PER_WRITE = 1000;
+ final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(70 * NUM_PER_WRITE);
+ int count = 0;
+ for (ZipEntry ze : entries) {
+ byteArrayOutputStream.write(createCentralFileHeader(ze));
+ if (++count > NUM_PER_WRITE){
+ writeCounted(byteArrayOutputStream.toByteArray());
+ byteArrayOutputStream.reset();
+ count = 0;
+ }
+ }
+ writeCounted(byteArrayOutputStream.toByteArray());
+ }
+
+ /**
+ * Writes all necessary data for this entry.
+ *
+ * @since 1.1
+ * @throws IOException on error
+ * @throws Zip64RequiredException if the entry's uncompressed or
+ * compressed size exceeds 4 GByte and {@link #setUseZip64}
+ * is {@link Zip64Mode#Never}.
+ */
+ public void closeEntry() throws IOException {
+ preClose();
+
+ flushDeflater();
+
+ final Zip64Mode effectiveMode = getEffectiveZip64Mode(entry.entry);
+ long bytesWritten = written - entry.dataStart;
+ long realCrc = crc.getValue();
+ crc.reset();
+
+ final boolean actuallyNeedsZip64 =
+ handleSizesAndCrc(bytesWritten, realCrc, effectiveMode);
+
+ closeEntry(actuallyNeedsZip64);
+ }
+
+ private void closeEntry(boolean actuallyNeedsZip64) throws IOException {
+ if (raf != null) {
+ rewriteSizesAndCrc(actuallyNeedsZip64);
+ }
+
+ writeDataDescriptor(entry.entry);
+ entry = null;
+ }
+
+ private void preClose() throws IOException {
+ if (finished) {
+ throw new IOException("Stream has already been finished");
+ }
+
+ if (entry == null) {
+ throw new IOException("No current entry to close");
+ }
+
+ if (!entry.hasWritten) {
+ write(EMPTY, 0, 0);
+ }
+ }
+
+ /**
+ * Ensures all bytes sent to the deflater are written to the stream.
+ */
+ private void flushDeflater() throws IOException {
+ if (entry.entry.getMethod() == DEFLATED) {
+ def.finish();
+ while (!def.finished()) {
+ deflate();
+ }
+ }
+ }
+
+ /**
+ * Ensures the current entry's size and CRC information is set to
+ * the values just written, verifies it isn't too big in the
+ * Zip64Mode.Never case and returns whether the entry would
+ * require a Zip64 extra field.
+ */
+ private boolean handleSizesAndCrc(long bytesWritten, long crc,
+ Zip64Mode effectiveMode)
+ throws ZipException {
+ if (entry.entry.getMethod() == DEFLATED) {
+ /* It turns out def.getBytesRead() returns wrong values if
+ * the size exceeds 4 GB on Java < Java7
+ entry.entry.setSize(def.getBytesRead());
+ */
+ entry.entry.setSize(entry.bytesRead);
+ entry.entry.setCompressedSize(bytesWritten);
+ entry.entry.setCrc(crc);
+
+ def.reset();
+ } else if (raf == null) {
+ if (entry.entry.getCrc() != crc) {
+ throw new ZipException("bad CRC checksum for entry "
+ + entry.entry.getName() + ": "
+ + Long.toHexString(entry.entry.getCrc())
+ + " instead of "
+ + Long.toHexString(crc));
+ }
+
+ if (entry.entry.getSize() != bytesWritten) {
+ throw new ZipException("bad size for entry "
+ + entry.entry.getName() + ": "
+ + entry.entry.getSize()
+ + " instead of "
+ + bytesWritten);
+ }
+ } else { /* method is STORED and we used RandomAccessFile */
+ entry.entry.setSize(bytesWritten);
+ entry.entry.setCompressedSize(bytesWritten);
+ entry.entry.setCrc(crc);
+ }
+
+ return checkIfNeedsZip64(effectiveMode);
+ }
+
+ /**
+ * Ensures the current entry's size and CRC information is set to
+ * the values just written, verifies it isn't too big in the
+ * Zip64Mode.Never case and returns whether the entry would
+ * require a Zip64 extra field.
+ */
+ private boolean checkIfNeedsZip64(Zip64Mode effectiveMode)
+ throws ZipException {
+ final boolean actuallyNeedsZip64 = isZip64Required(entry.entry,
+ effectiveMode);
+ if (actuallyNeedsZip64 && effectiveMode == Zip64Mode.Never) {
+ throw new Zip64RequiredException(Zip64RequiredException
+ .getEntryTooBigMessage(entry.entry));
+ }
+ return actuallyNeedsZip64;
+ }
+
+ private boolean isZip64Required(ZipEntry entry1, Zip64Mode requestedMode) {
+ return requestedMode == Zip64Mode.Always || isTooLageForZip32(entry1);
+ }
+
+ private boolean isTooLageForZip32(ZipEntry zipArchiveEntry){
+ return zipArchiveEntry.getSize() >= ZIP64_MAGIC
+ || zipArchiveEntry.getCompressedSize() >= ZIP64_MAGIC;
+ }
+
+ /**
+ * When using random access output, write the local file header
+ * and potentiall the ZIP64 extra containing the correct CRC and
+ * compressed/uncompressed sizes.
+ */
+ private void rewriteSizesAndCrc(boolean actuallyNeedsZip64)
+ throws IOException {
+ long save = raf.getFilePointer();
+
+ raf.seek(entry.localDataStart);
+ writeOut(ZipLong.getBytes(entry.entry.getCrc()));
+ if (!hasZip64Extra(entry.entry) || !actuallyNeedsZip64) {
+ writeOut(ZipLong.getBytes(entry.entry.getCompressedSize()));
+ writeOut(ZipLong.getBytes(entry.entry.getSize()));
+ } else {
+ writeOut(ZipLong.ZIP64_MAGIC.getBytes());
+ writeOut(ZipLong.ZIP64_MAGIC.getBytes());
+ }
+
+ if (hasZip64Extra(entry.entry)) {
+ // seek to ZIP64 extra, skip header and size information
+ raf.seek(entry.localDataStart + 3 * WORD + 2 * SHORT
+ + getName(entry.entry).limit() + 2 * SHORT);
+ // inside the ZIP64 extra uncompressed size comes
+ // first, unlike the LFH, CD or data descriptor
+ writeOut(ZipEightByteInteger.getBytes(entry.entry.getSize()));
+ writeOut(ZipEightByteInteger.getBytes(entry.entry.getCompressedSize()));
+
+ if (!actuallyNeedsZip64) {
+ // do some cleanup:
+ // * rewrite version needed to extract
+ raf.seek(entry.localDataStart - 5 * SHORT);
+ writeOut(ZipShort.getBytes(INITIAL_VERSION));
+
+ // * remove ZIP64 extra so it doesn't get written
+ // to the central directory
+ entry.entry.removeExtraField(Zip64ExtendedInformationExtraField
+ .HEADER_ID);
+ entry.entry.setExtra();
+
+ // * reset hasUsedZip64 if it has been set because
+ // of this entry
+ if (entry.causedUseOfZip64) {
+ hasUsedZip64 = false;
+ }
+ }
+ }
+ raf.seek(save);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws Zip64RequiredException if the entry's uncompressed or
+ * compressed size is known to exceed 4 GByte and {@link #setUseZip64}
+ * is {@link Zip64Mode#Never}.
+ */
+ public void putNextEntry(ZipEntry archiveEntry) throws IOException {
+ if (finished) {
+ throw new IOException("Stream has already been finished");
+ }
+
+ if (entry != null) {
+ closeEntry();
+ }
+
+ entry = new CurrentEntry(archiveEntry);
+ entries.add(entry.entry);
+
+ setDefaults(entry.entry);
+
+ final Zip64Mode effectiveMode = getEffectiveZip64Mode(entry.entry);
+ validateSizeInformation(effectiveMode);
+
+ if (shouldAddZip64Extra(entry.entry, effectiveMode)) {
+
+ Zip64ExtendedInformationExtraField z64 = getZip64Extra(entry.entry);
+
+ // just a placeholder, real data will be in data
+ // descriptor or inserted later via RandomAccessFile
+ ZipEightByteInteger size = ZipEightByteInteger.ZERO;
+ ZipEightByteInteger compressedSize = ZipEightByteInteger.ZERO;
+ if (entry.entry.getMethod() == STORED
+ && entry.entry.getSize() != -1) {
+ // actually, we already know the sizes
+ size = new ZipEightByteInteger(entry.entry.getSize());
+ compressedSize = size;
+ }
+ z64.setSize(size);
+ z64.setCompressedSize(compressedSize);
+ entry.entry.setExtra();
+ }
+
+ if (entry.entry.getMethod() == DEFLATED && hasCompressionLevelChanged) {
+ def.setLevel(level);
+ hasCompressionLevelChanged = false;
+ }
+ writeLocalFileHeader(entry.entry);
+ }
+
+ /**
+ * Provides default values for compression method and last
+ * modification time.
+ */
+ private void setDefaults(ZipEntry entry) {
+ if (entry.getMethod() == -1) { // not specified
+ entry.setMethod(method);
+ }
+
+ if (entry.getTime() == -1) { // not specified
+ entry.setTime(System.currentTimeMillis());
+ }
+ }
+
+ /**
+ * Throws an exception if the size is unknown for a stored entry
+ * that is written to a non-seekable output or the entry is too
+ * big to be written without Zip64 extra but the mode has been set
+ * to Never.
+ */
+ private void validateSizeInformation(Zip64Mode effectiveMode)
+ throws ZipException {
+ // Size/CRC not required if RandomAccessFile is used
+ if (entry.entry.getMethod() == STORED && raf == null) {
+ if (entry.entry.getSize() == -1) {
+ throw new ZipException("uncompressed size is required for"
+ + " STORED method when not writing to a"
+ + " file");
+ }
+ if (entry.entry.getCrc() == -1) {
+ throw new ZipException("crc checksum is required for STORED"
+ + " method when not writing to a file");
+ }
+ entry.entry.setCompressedSize(entry.entry.getSize());
+ }
+
+ if ((entry.entry.getSize() >= ZIP64_MAGIC
+ || entry.entry.getCompressedSize() >= ZIP64_MAGIC)
+ && effectiveMode == Zip64Mode.Never) {
+ throw new Zip64RequiredException(Zip64RequiredException
+ .getEntryTooBigMessage(entry.entry));
+ }
+ }
+
+ /**
+ * Whether to addd a Zip64 extended information extra field to the
+ * local file header.
+ *
+ * <p>Returns true if</p>
+ *
+ * <ul>
+ * <li>mode is Always</li>
+ * <li>or we already know it is going to be needed</li>
+ * <li>or the size is unknown and we can ensure it won't hurt
+ * other implementations if we add it (i.e. we can erase its
+ * usage</li>
+ * </ul>
+ */
+ private boolean shouldAddZip64Extra(ZipEntry entry, Zip64Mode mode) {
+ return mode == Zip64Mode.Always
+ || entry.getSize() >= ZIP64_MAGIC
+ || entry.getCompressedSize() >= ZIP64_MAGIC
+ || (entry.getSize() == -1
+ && raf != null && mode != Zip64Mode.Never);
+ }
+
+ /**
+ * Set the file comment.
+ * @param comment the comment
+ */
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ /**
+ * Sets the compression level for subsequent entries.
+ *
+ * <p>Default is Deflater.DEFAULT_COMPRESSION.</p>
+ * @param level the compression level.
+ * @throws IllegalArgumentException if an invalid compression
+ * level is specified.
+ * @since 1.1
+ */
+ public void setLevel(int level) {
+ if (level < Deflater.DEFAULT_COMPRESSION
+ || level > Deflater.BEST_COMPRESSION) {
+ throw new IllegalArgumentException("Invalid compression level: "
+ + level);
+ }
+ hasCompressionLevelChanged = (this.level != level);
+ this.level = level;
+ }
+
+ /**
+ * Sets the default compression method for subsequent entries.
+ *
+ * <p>Default is DEFLATED.</p>
+ * @param method an <code>int</code> from java.util.zip.ZipEntry
+ * @since 1.1
+ */
+ public void setMethod(int method) {
+ this.method = method;
+ }
+
+ /**
+ * Whether this stream is able to write the given entry.
+ *
+ * <p>May return false if it is set up to use encryption or a
+ * compression method that hasn't been implemented yet.</p>
+ */
+ public boolean canWriteEntryData(ZipEntry ae) {
+ return ZipUtil.canHandleEntryData(ae);
+ }
+
+ /**
+ * Writes bytes to ZIP entry.
+ * @param b the byte array to write
+ * @param offset the start position to write from
+ * @param length the number of bytes to write
+ * @throws IOException on error
+ */
+ @Override
+ public void write(byte[] b, int offset, int length) throws IOException {
+ if (entry == null) {
+ throw new IllegalStateException("No current entry");
+ }
+ ZipUtil.checkRequestedFeatures(entry.entry);
+ entry.hasWritten = true;
+ if (entry.entry.getMethod() == DEFLATED) {
+ writeDeflated(b, offset, length);
+ } else {
+ writeCounted(b, offset, length);
+ }
+ crc.update(b, offset, length);
+ }
+
+ /**
+ * Write bytes to output or random access file.
+ * @param data the byte array to write
+ * @throws IOException on error
+ */
+ private void writeCounted(byte[] data) throws IOException {
+ writeCounted(data, 0, data.length);
+ }
+
+ private void writeCounted(byte[] data, int offset, int length) throws IOException {
+ writeOut(data, offset, length);
+ written += length;
+ }
+
+ /**
+ * write implementation for DEFLATED entries.
+ */
+ private void writeDeflated(byte[]b, int offset, int length)
+ throws IOException {
+ if (length > 0 && !def.finished()) {
+ entry.bytesRead += length;
+ if (length <= DEFLATER_BLOCK_SIZE) {
+ def.setInput(b, offset, length);
+ deflateUntilInputIsNeeded();
+ } else {
+ final int fullblocks = length / DEFLATER_BLOCK_SIZE;
+ for (int i = 0; i < fullblocks; i++) {
+ def.setInput(b, offset + i * DEFLATER_BLOCK_SIZE,
+ DEFLATER_BLOCK_SIZE);
+ deflateUntilInputIsNeeded();
+ }
+ final int done = fullblocks * DEFLATER_BLOCK_SIZE;
+ if (done < length) {
+ def.setInput(b, offset + done, length - done);
+ deflateUntilInputIsNeeded();
+ }
+ }
+ }
+ }
+
+ /**
+ * Closes this output stream and releases any system resources
+ * associated with the stream.
+ *
+ * @exception IOException if an I/O error occurs.
+ * @throws Zip64RequiredException if the archive's size exceeds 4
+ * GByte or there are more than 65535 entries inside the archive
+ * and {@link #setUseZip64} is {@link Zip64Mode#Never}.
+ */
+ @Override
+ public void close() throws IOException {
+ if (!finished) {
+ finish();
+ }
+ destroy();
+ }
+
+ /**
+ * Flushes this output stream and forces any buffered output bytes
+ * to be written out to the stream.
+ *
+ * @exception IOException if an I/O error occurs.
+ */
+ @Override
+ public void flush() throws IOException {
+ if (out != null) {
+ out.flush();
+ }
+ }
+
+ /*
+ * Various ZIP constants
+ */
+ /**
+ * local file header signature
+ *
+ * @since 1.1
+ */
+ protected static final byte[] LFH_SIG = ZipLong.LFH_SIG.getBytes();
+ /**
+ * data descriptor signature
+ *
+ * @since 1.1
+ */
+ protected static final byte[] DD_SIG = ZipLong.DD_SIG.getBytes();
+ /**
+ * central file header signature
+ *
+ * @since 1.1
+ */
+ protected static final byte[] CFH_SIG = ZipLong.CFH_SIG.getBytes();
+ /**
+ * end of central dir signature
+ *
+ * @since 1.1
+ */
+ protected static final byte[] EOCD_SIG = ZipLong.getBytes(0X06054B50L);
+ /**
+ * ZIP64 end of central dir signature
+ */
+ static final byte[] ZIP64_EOCD_SIG = ZipLong.getBytes(0X06064B50L);
+ /**
+ * ZIP64 end of central dir locator signature
+ */
+ static final byte[] ZIP64_EOCD_LOC_SIG = ZipLong.getBytes(0X07064B50L);
+
+ /**
+ * Writes next block of compressed data to the output stream.
+ * @throws IOException on error
+ *
+ * @since 1.14
+ */
+ protected final void deflate() throws IOException {
+ int len = def.deflate(buf, 0, buf.length);
+ if (len > 0) {
+ writeCounted(buf, 0, len);
+ }
+ }
+
+ /**
+ * Writes the local file header entry
+ * @param ze the entry to write
+ * @throws IOException on error
+ *
+ * @since 1.1
+ */
+ protected void writeLocalFileHeader(ZipEntry ze) throws IOException {
+
+ boolean encodable = zipEncoding.canEncode(ze.getName());
+ ByteBuffer name = getName(ze);
+
+ if (createUnicodeExtraFields != UnicodeExtraFieldPolicy.NEVER) {
+ addUnicodeExtraFields(ze, encodable, name);
+ }
+
+ final byte[] localHeader = createLocalFileHeader(ze, name, encodable);
+ final long localHeaderStart = written;
+ offsets.put(ze, localHeaderStart);
+ entry.localDataStart = localHeaderStart + LFH_CRC_OFFSET; // At crc offset
+ writeCounted(localHeader);
+ entry.dataStart = written;
+ }
+
+ private byte[] createLocalFileHeader(ZipEntry ze, ByteBuffer name, boolean encodable) {
+ byte[] extra = ze.getLocalFileDataExtra();
+ final int nameLen = name.limit() - name.position();
+ int len= LFH_FILENAME_OFFSET + nameLen + extra.length;
+ byte[] buf = new byte[len];
+
+ System.arraycopy(LFH_SIG, 0, buf, LFH_SIG_OFFSET, WORD);
+
+ //store method in local variable to prevent multiple method calls
+ final int zipMethod = ze.getMethod();
+
+ putShort(versionNeededToExtract(zipMethod, hasZip64Extra(ze)),
+ buf, LFH_VERSION_NEEDED_OFFSET);
+
+ GeneralPurposeBit generalPurposeBit =
+ getGeneralPurposeBits(zipMethod, !encodable && fallbackToUTF8);
+ generalPurposeBit.encode(buf, LFH_GPB_OFFSET);
+
+ // compression method
+ putShort(zipMethod, buf, LFH_METHOD_OFFSET);
+
+ ZipUtil.toDosTime(calendarInstance, ze.getTime(), buf, LFH_TIME_OFFSET);
+
+ // CRC
+ if (zipMethod == DEFLATED || raf != null) {
+ System.arraycopy(LZERO, 0, buf, LFH_CRC_OFFSET, WORD);
+ } else {
+ putLong(ze.getCrc(), buf, LFH_CRC_OFFSET);
+ }
+
+ // compressed length
+ // uncompressed length
+ if (hasZip64Extra(entry.entry)){
+ // point to ZIP64 extended information extra field for
+ // sizes, may get rewritten once sizes are known if
+ // stream is seekable
+ ZipLong.ZIP64_MAGIC.putLong(buf, LFH_COMPRESSED_SIZE_OFFSET);
+ ZipLong.ZIP64_MAGIC.putLong(buf, LFH_ORIGINAL_SIZE_OFFSET);
+ } else if (zipMethod == DEFLATED || raf != null) {
+ System.arraycopy(LZERO, 0, buf, LFH_COMPRESSED_SIZE_OFFSET, WORD);
+ System.arraycopy(LZERO, 0, buf, LFH_ORIGINAL_SIZE_OFFSET, WORD);
+ } else { // Stored
+ putLong(ze.getSize(), buf, LFH_COMPRESSED_SIZE_OFFSET);
+ putLong(ze.getSize(), buf, LFH_ORIGINAL_SIZE_OFFSET);
+ }
+ // file name length
+ putShort(nameLen, buf, LFH_FILENAME_LENGTH_OFFSET);
+
+ // extra field length
+ putShort(extra.length, buf, LFH_EXTRA_LENGTH_OFFSET);
+
+ // file name
+ System.arraycopy(name.array(), name.arrayOffset(), buf,
+ LFH_FILENAME_OFFSET, nameLen);
+
+ System.arraycopy(extra, 0, buf, LFH_FILENAME_OFFSET + nameLen, extra.length);
+ return buf;
+ }
+
+ /**
+ * Adds UnicodeExtra fields for name and file comment if mode is
+ * ALWAYS or the data cannot be encoded using the configured
+ * encoding.
+ */
+ private void addUnicodeExtraFields(ZipEntry ze, boolean encodable,
+ ByteBuffer name)
+ throws IOException {
+ if (createUnicodeExtraFields == UnicodeExtraFieldPolicy.ALWAYS
+ || !encodable) {
+ ze.addExtraField(new UnicodePathExtraField(ze.getName(),
+ name.array(),
+ name.arrayOffset(),
+ name.limit()
+ - name.position()));
+ }
+
+ String comm = ze.getComment();
+ if (comm != null && !"".equals(comm)) {
+
+ boolean commentEncodable = zipEncoding.canEncode(comm);
+
+ if (createUnicodeExtraFields == UnicodeExtraFieldPolicy.ALWAYS
+ || !commentEncodable) {
+ ByteBuffer commentB = getEntryEncoding(ze).encode(comm);
+ ze.addExtraField(new UnicodeCommentExtraField(comm,
+ commentB.array(),
+ commentB.arrayOffset(),
+ commentB.limit()
+ - commentB.position())
+ );
+ }
+ }
+ }
+
+ /**
+ * Writes the data descriptor entry.
+ * @param ze the entry to write
+ * @throws IOException on error
+ *
+ * @since 1.1
+ */
+ protected void writeDataDescriptor(ZipEntry ze) throws IOException {
+ if (ze.getMethod() != DEFLATED || raf != null) {
+ return;
+ }
+ writeCounted(DD_SIG);
+ writeCounted(ZipLong.getBytes(ze.getCrc()));
+ if (!hasZip64Extra(ze)) {
+ writeCounted(ZipLong.getBytes(ze.getCompressedSize()));
+ writeCounted(ZipLong.getBytes(ze.getSize()));
+ } else {
+ writeCounted(ZipEightByteInteger.getBytes(ze.getCompressedSize()));
+ writeCounted(ZipEightByteInteger.getBytes(ze.getSize()));
+ }
+ }
+
+ /**
+ * Writes the central file header entry.
+ * @param ze the entry to write
+ * @throws IOException on error
+ * @throws Zip64RequiredException if the archive's size exceeds 4
+ * GByte and {@link Zip64Mode #setUseZip64} is {@link
+ * Zip64Mode#Never}.
+ */
+ protected void writeCentralFileHeader(ZipEntry ze) throws IOException {
+ byte[] centralFileHeader = createCentralFileHeader(ze);
+ writeCounted(centralFileHeader);
+ }
+
+ private byte[] createCentralFileHeader(ZipEntry ze) throws IOException {
+ final long lfhOffset = offsets.get(ze);
+ final boolean needsZip64Extra = hasZip64Extra(ze)
+ || ze.getCompressedSize() >= ZIP64_MAGIC
+ || ze.getSize() >= ZIP64_MAGIC
+ || lfhOffset >= ZIP64_MAGIC;
+
+ if (needsZip64Extra && zip64Mode == Zip64Mode.Never) {
+ // must be the offset that is too big, otherwise an
+ // exception would have been throw in putArchiveEntry or
+ // closeArchiveEntry
+ throw new Zip64RequiredException(Zip64RequiredException
+ .ARCHIVE_TOO_BIG_MESSAGE);
+ }
+
+
+ handleZip64Extra(ze, lfhOffset, needsZip64Extra);
+
+ return createCentralFileHeader(ze, getName(ze), lfhOffset, needsZip64Extra);
+ }
+
+ /**
+ * Writes the central file header entry.
+ * @param ze the entry to write
+ * @param name The encoded name
+ * @param lfhOffset Local file header offset for this file
+ * @throws IOException on error
+ */
+ private byte[] createCentralFileHeader(ZipEntry ze, ByteBuffer name, long lfhOffset,
+ boolean needsZip64Extra) throws IOException {
+ byte[] extra = ze.getCentralDirectoryExtra();
+
+ // file comment length
+ String comm = ze.getComment();
+ if (comm == null) {
+ comm = "";
+ }
+
+ ByteBuffer commentB = getEntryEncoding(ze).encode(comm);
+ final int nameLen = name.limit() - name.position();
+ final int commentLen = commentB.limit() - commentB.position();
+ int len= CFH_FILENAME_OFFSET + nameLen + extra.length + commentLen;
+ byte[] buf = new byte[len];
+
+ System.arraycopy(CFH_SIG, 0, buf, CFH_SIG_OFFSET, WORD);
+
+ // version made by
+ // CheckStyle:MagicNumber OFF
+ putShort((ze.getPlatform() << 8) | (!hasUsedZip64 ? DATA_DESCRIPTOR_MIN_VERSION : ZIP64_MIN_VERSION),
+ buf, CFH_VERSION_MADE_BY_OFFSET);
+
+ final int zipMethod = ze.getMethod();
+ final boolean encodable = zipEncoding.canEncode(ze.getName());
+ putShort(versionNeededToExtract(zipMethod, needsZip64Extra), buf, CFH_VERSION_NEEDED_OFFSET);
+ getGeneralPurposeBits(zipMethod, !encodable && fallbackToUTF8).encode(buf, CFH_GPB_OFFSET);
+
+ // compression method
+ putShort(zipMethod, buf, CFH_METHOD_OFFSET);
+
+
+ // last mod. time and date
+ ZipUtil.toDosTime(calendarInstance, ze.getTime(), buf, CFH_TIME_OFFSET);
+
+ // CRC
+ // compressed length
+ // uncompressed length
+ putLong(ze.getCrc(), buf, CFH_CRC_OFFSET);
+ if (ze.getCompressedSize() >= ZIP64_MAGIC
+ || ze.getSize() >= ZIP64_MAGIC) {
+ ZipLong.ZIP64_MAGIC.putLong(buf, CFH_COMPRESSED_SIZE_OFFSET);
+ ZipLong.ZIP64_MAGIC.putLong(buf, CFH_ORIGINAL_SIZE_OFFSET);
+ } else {
+ putLong(ze.getCompressedSize(), buf, CFH_COMPRESSED_SIZE_OFFSET);
+ putLong(ze.getSize(), buf, CFH_ORIGINAL_SIZE_OFFSET);
+ }
+
+ putShort(nameLen, buf, CFH_FILENAME_LENGTH_OFFSET);
+
+ // extra field length
+ putShort(extra.length, buf, CFH_EXTRA_LENGTH_OFFSET);
+
+ putShort(commentLen, buf, CFH_COMMENT_LENGTH_OFFSET);
+
+ // disk number start
+ System.arraycopy(ZERO, 0, buf, CFH_DISK_NUMBER_OFFSET, SHORT);
+
+ // internal file attributes
+ putShort(ze.getInternalAttributes(), buf, CFH_INTERNAL_ATTRIBUTES_OFFSET);
+
+ // external file attributes
+ putLong(ze.getExternalAttributes(), buf, CFH_EXTERNAL_ATTRIBUTES_OFFSET);
+
+ // relative offset of LFH
+ putLong(Math.min(lfhOffset, ZIP64_MAGIC), buf, CFH_LFH_OFFSET);
+
+ // file name
+ System.arraycopy(name.array(), name.arrayOffset(), buf, CFH_FILENAME_OFFSET, nameLen);
+
+ int extraStart = CFH_FILENAME_OFFSET + nameLen;
+ System.arraycopy(extra, 0, buf, extraStart, extra.length);
+
+ int commentStart = extraStart + commentLen;
+
+ // file comment
+ System.arraycopy(commentB.array(), commentB.arrayOffset(), buf, commentStart, commentLen);
+ return buf;
+ }
+
+ /**
+ * If the entry needs Zip64 extra information inside the central
+ * directory then configure its data.
+ */
+ private void handleZip64Extra(ZipEntry ze, long lfhOffset,
+ boolean needsZip64Extra) {
+ if (needsZip64Extra) {
+ Zip64ExtendedInformationExtraField z64 = getZip64Extra(ze);
+ if (ze.getCompressedSize() >= ZIP64_MAGIC
+ || ze.getSize() >= ZIP64_MAGIC) {
+ z64.setCompressedSize(new ZipEightByteInteger(ze.getCompressedSize()));
+ z64.setSize(new ZipEightByteInteger(ze.getSize()));
+ } else {
+ // reset value that may have been set for LFH
+ z64.setCompressedSize(null);
+ z64.setSize(null);
+ }
+ if (lfhOffset >= ZIP64_MAGIC) {
+ z64.setRelativeHeaderOffset(new ZipEightByteInteger(lfhOffset));
+ }
+ ze.setExtra();
+ }
+ }
+
+ /**
+ * Writes the &quot;End of central dir record&quot;.
+ * @throws IOException on error
+ * @throws Zip64RequiredException if the archive's size exceeds 4
+ * GByte or there are more than 65535 entries inside the archive
+ * and {@link Zip64Mode #setUseZip64} is {@link Zip64Mode#Never}.
+ */
+ protected void writeCentralDirectoryEnd() throws IOException {
+ writeCounted(EOCD_SIG);
+
+ // disk numbers
+ writeCounted(ZERO);
+ writeCounted(ZERO);
+
+ // number of entries
+ int numberOfEntries = entries.size();
+ if (numberOfEntries > ZIP64_MAGIC_SHORT
+ && zip64Mode == Zip64Mode.Never) {
+ throw new Zip64RequiredException(Zip64RequiredException
+ .TOO_MANY_ENTRIES_MESSAGE);
+ }
+ if (cdOffset > ZIP64_MAGIC && zip64Mode == Zip64Mode.Never) {
+ throw new Zip64RequiredException(Zip64RequiredException
+ .ARCHIVE_TOO_BIG_MESSAGE);
+ }
+
+ byte[] num = ZipShort.getBytes(Math.min(numberOfEntries,
+ ZIP64_MAGIC_SHORT));
+ writeCounted(num);
+ writeCounted(num);
+
+ // length and location of CD
+ writeCounted(ZipLong.getBytes(Math.min(cdLength, ZIP64_MAGIC)));
+ writeCounted(ZipLong.getBytes(Math.min(cdOffset, ZIP64_MAGIC)));
+
+ // ZIP file comment
+ ByteBuffer data = this.zipEncoding.encode(comment);
+ int dataLen = data.limit() - data.position();
+ writeCounted(ZipShort.getBytes(dataLen));
+ writeCounted(data.array(), data.arrayOffset(), dataLen);
+ }
+
+ /**
+ * Convert a Date object to a DOS date/time field.
+ * @param time the <code>Date</code> to convert
+ * @return the date as a <code>ZipLong</code>
+ * @since 1.1
+ * @deprecated use ZipUtil#toDosTime
+ */
+ @Deprecated
+ protected static ZipLong toDosTime(Date time) {
+ return ZipUtil.toDosTime(time);
+ }
+
+ /**
+ * Convert a Date object to a DOS date/time field.
+ *
+ * <p>Stolen from InfoZip's <code>fileio.c</code></p>
+ * @param t number of milliseconds since the epoch
+ * @return the date as a byte array
+ * @since 1.26
+ * @deprecated use ZipUtil#toDosTime
+ */
+ @Deprecated
+ protected static byte[] toDosTime(long t) {
+ return ZipUtil.toDosTime(t);
+ }
+
+ /**
+ * Retrieve the bytes for the given String in the encoding set for
+ * this Stream.
+ * @param name the string to get bytes from
+ * @return the bytes as a byte array
+ * @throws ZipException on error
+ *
+ * @since 1.3
+ */
+ protected byte[] getBytes(String name) throws ZipException {
+ try {
+ ByteBuffer b =
+ ZipEncodingHelper.getZipEncoding(encoding).encode(name);
+ byte[] result = new byte[b.limit()];
+ System.arraycopy(b.array(), b.arrayOffset(), result, 0,
+ result.length);
+ return result;
+ } catch (IOException ex) {
+ throw new ZipException("Failed to encode name: " + ex.getMessage());
+ }
+ }
+
+ /**
+ * Writes the &quot;ZIP64 End of central dir record&quot; and
+ * &quot;ZIP64 End of central dir locator&quot;.
+ * @throws IOException on error
+ */
+ protected void writeZip64CentralDirectory() throws IOException {
+ if (zip64Mode == Zip64Mode.Never) {
+ return;
+ }
+
+ if (!hasUsedZip64
+ && (cdOffset >= ZIP64_MAGIC || cdLength >= ZIP64_MAGIC
+ || entries.size() >= ZIP64_MAGIC_SHORT)) {
+ // actually "will use"
+ hasUsedZip64 = true;
+ }
+
+ if (!hasUsedZip64) {
+ return;
+ }
+
+ long offset = written;
+
+ writeOut(ZIP64_EOCD_SIG);
+ // size, we don't have any variable length as we don't support
+ // the extensible data sector, yet
+ writeOut(ZipEightByteInteger
+ .getBytes(SHORT /* version made by */
+ + SHORT /* version needed to extract */
+ + WORD /* disk number */
+ + WORD /* disk with central directory */
+ + DWORD /* number of entries in CD on this disk */
+ + DWORD /* total number of entries */
+ + DWORD /* size of CD */
+ + DWORD /* offset of CD */
+ ));
+
+ // version made by and version needed to extract
+ writeOut(ZipShort.getBytes(ZIP64_MIN_VERSION));
+ writeOut(ZipShort.getBytes(ZIP64_MIN_VERSION));
+
+ // disk numbers - four bytes this time
+ writeOut(LZERO);
+ writeOut(LZERO);
+
+ // number of entries
+ byte[] num = ZipEightByteInteger.getBytes(entries.size());
+ writeOut(num);
+ writeOut(num);
+
+ // length and location of CD
+ writeOut(ZipEightByteInteger.getBytes(cdLength));
+ writeOut(ZipEightByteInteger.getBytes(cdOffset));
+
+ // no "zip64 extensible data sector" for now
+
+ // and now the "ZIP64 end of central directory locator"
+ writeOut(ZIP64_EOCD_LOC_SIG);
+
+ // disk number holding the ZIP64 EOCD record
+ writeOut(LZERO);
+ // relative offset of ZIP64 EOCD record
+ writeOut(ZipEightByteInteger.getBytes(offset));
+ // total number of disks
+ writeOut(ONE);
+ }
+
+ /**
+ * Write bytes to output or random access file.
+ * @param data the byte array to write
+ * @throws IOException on error
+ *
+ * @since 1.14
+ */
+ protected final void writeOut(byte[] data) throws IOException {
+ writeOut(data, 0, data.length);
+ }
+
+ /**
+ * Write bytes to output or random access file.
+ * @param data the byte array to write
+ * @param offset the start position to write from
+ * @param length the number of bytes to write
+ * @throws IOException on error
+ *
+ * @since 1.14
+ */
+ protected final void writeOut(byte[] data, int offset, int length)
+ throws IOException {
+ if (raf != null) {
+ raf.write(data, offset, length);
+ } else {
+ out.write(data, offset, length);
+ }
+ }
+
+ /**
+ * Assumes a negative integer really is a positive integer that
+ * has wrapped around and re-creates the original value.
+ * @param i the value to treat as unsigned int.
+ * @return the unsigned int as a long.
+ * @since 1.34
+ * @deprecated use ZipUtil#adjustToLong
+ */
+ @Deprecated
+ protected static long adjustToLong(int i) {
+ return ZipUtil.adjustToLong(i);
+ }
+
+ private void deflateUntilInputIsNeeded() throws IOException {
+ while (!def.needsInput()) {
+ deflate();
+ }
+ }
+
+ private GeneralPurposeBit getGeneralPurposeBits(final int zipMethod, final boolean utfFallback) {
+ GeneralPurposeBit b = new GeneralPurposeBit();
+ b.useUTF8ForNames(useUTF8Flag || utfFallback);
+ if (isDeflatedToOutputStream(zipMethod)) {
+ b.useDataDescriptor(true);
+ }
+ return b;
+ }
+
+ private int versionNeededToExtract(final int zipMethod, final boolean zip64) {
+ if (zip64) {
+ return ZIP64_MIN_VERSION;
+ }
+ // requires version 2 as we are going to store length info
+ // in the data descriptor
+ return (isDeflatedToOutputStream(zipMethod)) ?
+ DATA_DESCRIPTOR_MIN_VERSION :
+ INITIAL_VERSION;
+ }
+
+ private boolean isDeflatedToOutputStream(int zipMethod) {
+ return zipMethod == DEFLATED && raf == null;
+ }
+
+ /**
+ * Get the existing ZIP64 extended information extra field or
+ * create a new one and add it to the entry.
+ */
+ private Zip64ExtendedInformationExtraField getZip64Extra(ZipEntry ze) {
+ if (entry != null) {
+ entry.causedUseOfZip64 = !hasUsedZip64;
+ }
+ hasUsedZip64 = true;
+ Zip64ExtendedInformationExtraField z64 =
+ (Zip64ExtendedInformationExtraField)
+ ze.getExtraField(Zip64ExtendedInformationExtraField
+ .HEADER_ID);
+ if (z64 == null) {
+ /*
+ System.err.println("Adding z64 for " + ze.getName()
+ + ", method: " + ze.getMethod()
+ + " (" + (ze.getMethod() == STORED) + ")"
+ + ", raf: " + (raf != null));
+ */
+ z64 = new Zip64ExtendedInformationExtraField();
+ }
+
+ // even if the field is there already, make sure it is the first one
+ ze.addAsFirstExtraField(z64);
+
+ return z64;
+ }
+
+ /**
+ * Is there a ZIP64 extended information extra field for the
+ * entry?
+ */
+ private boolean hasZip64Extra(ZipEntry ze) {
+ return ze.getExtraField(Zip64ExtendedInformationExtraField
+ .HEADER_ID)
+ != null;
+ }
+
+ /**
+ * If the mode is AsNeeded and the entry is a compressed entry of
+ * unknown size that gets written to a non-seekable stream the
+ * change the default to Never.
+ */
+ private Zip64Mode getEffectiveZip64Mode(ZipEntry ze) {
+ if (zip64Mode != Zip64Mode.AsNeeded
+ || raf != null
+ || ze.getMethod() != DEFLATED
+ || ze.getSize() != -1) {
+ return zip64Mode;
+ }
+ return Zip64Mode.Never;
+ }
+
+ private ZipEncoding getEntryEncoding(ZipEntry ze) {
+ boolean encodable = zipEncoding.canEncode(ze.getName());
+ return !encodable && fallbackToUTF8
+ ? ZipEncodingHelper.UTF8_ZIP_ENCODING : zipEncoding;
+ }
+
+ private ByteBuffer getName(ZipEntry ze) throws IOException {
+ return getEntryEncoding(ze).encode(ze.getName());
+ }
+
+ /**
+ * Closes the underlying stream/file without finishing the
+ * archive, the result will likely be a corrupt archive.
+ *
+ * <p>This method only exists to support tests that generate
+ * corrupt archives so they can clean up any temporary files.</p>
+ */
+ void destroy() throws IOException {
+ if (raf != null) {
+ raf.close();
+ }
+ if (out != null) {
+ out.close();
+ }
+ }
+
+ /**
+ * enum that represents the possible policies for creating Unicode
+ * extra fields.
+ */
+ public static final class UnicodeExtraFieldPolicy {
+ /**
+ * Always create Unicode extra fields.
+ */
+ public static final UnicodeExtraFieldPolicy ALWAYS =
+ new UnicodeExtraFieldPolicy("always");
+ /**
+ * Never create Unicode extra fields.
+ */
+ public static final UnicodeExtraFieldPolicy NEVER =
+ new UnicodeExtraFieldPolicy("never");
+ /**
+ * Create Unicode extra fields for filenames that cannot be
+ * encoded using the specified encoding.
+ */
+ public static final UnicodeExtraFieldPolicy NOT_ENCODEABLE =
+ new UnicodeExtraFieldPolicy("not encodeable");
+
+ private final String name;
+ private UnicodeExtraFieldPolicy(String n) {
+ name = n;
+ }
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ /**
+ * Structure collecting information for the entry that is
+ * currently being written.
+ */
+ private static final class CurrentEntry {
+ private CurrentEntry(ZipEntry entry) {
+ this.entry = entry;
+ }
+ /**
+ * Current ZIP entry.
+ */
+ private final ZipEntry entry;
+ /**
+ * Offset for CRC entry in the local file header data for the
+ * current entry starts here.
+ */
+ private long localDataStart = 0;
+ /**
+ * Data for local header data
+ */
+ private long dataStart = 0;
+ /**
+ * Number of bytes read for the current entry (can't rely on
+ * Deflater#getBytesRead) when using DEFLATED.
+ */
+ private long bytesRead = 0;
+ /**
+ * Whether current entry was the first one using ZIP64 features.
+ */
+ private boolean causedUseOfZip64 = false;
+ /**
+ * Whether write() has been called at all.
+ *
+ * <p>In order to create a valid archive {@link
+ * #closeEntry closeEntry} will write an empty
+ * array to get the CRC right if nothing has been written to
+ * the stream at all.</p>
+ */
+ private boolean hasWritten;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipShort.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipShort.java
new file mode 100644
index 00000000..e52c570d
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipShort.java
@@ -0,0 +1,166 @@
+/*
+ * 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.tools.zip;
+
+import static org.apache.tools.zip.ZipConstants.BYTE_MASK;
+
+/**
+ * Utility class that represents a two byte integer with conversion
+ * rules for the big endian byte order of ZIP files.
+ *
+ */
+public final class ZipShort implements Cloneable {
+ private static final int BYTE_1_MASK = 0xFF00;
+ private static final int BYTE_1_SHIFT = 8;
+
+ private final int value;
+
+ /**
+ * Create instance from a number.
+ * @param value the int to store as a ZipShort
+ * @since 1.1
+ */
+ public ZipShort (int value) {
+ this.value = value;
+ }
+
+ /**
+ * Create instance from bytes.
+ * @param bytes the bytes to store as a ZipShort
+ * @since 1.1
+ */
+ public ZipShort (byte[] bytes) {
+ this(bytes, 0);
+ }
+
+ /**
+ * Create instance from the two bytes starting at offset.
+ * @param bytes the bytes to store as a ZipShort
+ * @param offset the offset to start
+ * @since 1.1
+ */
+ public ZipShort (byte[] bytes, int offset) {
+ value = ZipShort.getValue(bytes, offset);
+ }
+
+ /**
+ * Get value as two bytes in big endian byte order.
+ * @return the value as a a two byte array in big endian byte order
+ * @since 1.1
+ */
+ public byte[] getBytes() {
+ byte[] result = new byte[2];
+ putShort(value, result, 0);
+ return result;
+ }
+
+ /**
+ * put the value as two bytes in big endian byte order.
+ * @param value the Java int to convert to bytes
+ * @param buf the output buffer
+ * @param offset
+ * The offset within the output buffer of the first byte to be written.
+ * must be non-negative and no larger than <tt>buf.length-2</tt>
+ */
+ public static void putShort(int value, byte[] buf, int offset) {
+ buf[offset] = (byte) (value & BYTE_MASK);
+ buf[offset+1] = (byte) ((value & BYTE_1_MASK) >> BYTE_1_SHIFT);
+ }
+
+ /**
+ * Get value as Java int.
+ * @return value as a Java int
+ * @since 1.1
+ */
+ public int getValue() {
+ return value;
+ }
+
+ /**
+ * Get value as two bytes in big endian byte order.
+ * @param value the Java int to convert to bytes
+ * @return the converted int as a byte array in big endian byte order
+ */
+ public static byte[] getBytes(int value) {
+ byte[] result = new byte[2];
+ result[0] = (byte) (value & BYTE_MASK);
+ result[1] = (byte) ((value & BYTE_1_MASK) >> BYTE_1_SHIFT);
+ return result;
+ }
+
+ /**
+ * Helper method to get the value as a java int from two bytes starting at given array offset
+ * @param bytes the array of bytes
+ * @param offset the offset to start
+ * @return the corresponding java int value
+ */
+ public static int getValue(byte[] bytes, int offset) {
+ int value = (bytes[offset + 1] << BYTE_1_SHIFT) & BYTE_1_MASK;
+ value += (bytes[offset] & BYTE_MASK);
+ return value;
+ }
+
+ /**
+ * Helper method to get the value as a java int from a two-byte array
+ * @param bytes the array of bytes
+ * @return the corresponding java int value
+ */
+ public static int getValue(byte[] bytes) {
+ return getValue(bytes, 0);
+ }
+
+ /**
+ * Override to make two instances with same value equal.
+ * @param o an object to compare
+ * @return true if the objects are equal
+ * @since 1.1
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof ZipShort)) {
+ return false;
+ }
+ return value == ((ZipShort) o).getValue();
+ }
+
+ /**
+ * Override to make two instances with same value equal.
+ * @return the value stored in the ZipShort
+ * @since 1.1
+ */
+ @Override
+ public int hashCode() {
+ return value;
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException cnfe) {
+ // impossible
+ throw new RuntimeException(cnfe);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ZipShort value: " + value;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipUtil.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipUtil.java
new file mode 100644
index 00000000..c25b8c7f
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipUtil.java
@@ -0,0 +1,252 @@
+/*
+ * 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.tools.zip;
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.zip.CRC32;
+
+/**
+ * Utility class for handling DOS and Java time conversions.
+ * @since Ant 1.8.1
+ */
+public abstract class ZipUtil {
+ /**
+ * Smallest date/time ZIP can handle.
+ */
+ private static final byte[] DOS_TIME_MIN = ZipLong.getBytes(0x00002100L);
+
+ /**
+ * Convert a Date object to a DOS date/time field.
+ * @param time the <code>Date</code> to convert
+ * @return the date as a <code>ZipLong</code>
+ */
+ public static ZipLong toDosTime(Date time) {
+ return new ZipLong(toDosTime(time.getTime()));
+ }
+
+ /**
+ * Convert a Date object to a DOS date/time field.
+ *
+ * <p>Stolen from InfoZip's <code>fileio.c</code></p>
+ * @param t number of milliseconds since the epoch
+ * @return the date as a byte array
+ */
+ public static byte[] toDosTime(long t) {
+ byte[] result = new byte[4];
+ toDosTime(t, result, 0);
+ return result;
+ }
+
+ /**
+ * Convert a Date object to a DOS date/time field.
+ *
+ * <p>Stolen from InfoZip's <code>fileio.c</code></p>
+ * @param t number of milliseconds since the epoch
+ * @param buf the output buffer
+ * @param offset
+ * The offset within the output buffer of the first byte to be written.
+ * must be non-negative and no larger than <tt>buf.length-4</tt>
+ */
+ public static void toDosTime(long t, byte[] buf, int offset) {
+ toDosTime(Calendar.getInstance(), t, buf, offset);
+ }
+
+ static void toDosTime(Calendar c, long t, byte[] buf, int offset) {
+ c.setTimeInMillis(t);
+
+ int year = c.get(Calendar.YEAR);
+ if (year < 1980) {
+ System.arraycopy(DOS_TIME_MIN, 0, buf, offset, DOS_TIME_MIN.length);// stop callers from changing the array
+ return;
+ }
+ int month = c.get(Calendar.MONTH) + 1;
+ long value = ((year - 1980) << 25)
+ | (month << 21)
+ | (c.get(Calendar.DAY_OF_MONTH) << 16)
+ | (c.get(Calendar.HOUR_OF_DAY) << 11)
+ | (c.get(Calendar.MINUTE) << 5)
+ | (c.get(Calendar.SECOND) >> 1);
+ ZipLong.putLong(value, buf, offset);
+ }
+
+ /**
+ * Assumes a negative integer really is a positive integer that
+ * has wrapped around and re-creates the original value.
+ *
+ * <p>This methods is no longer used as of Apache Ant 1.9.0</p>
+ *
+ * @param i the value to treat as unsigned int.
+ * @return the unsigned int as a long.
+ */
+ public static long adjustToLong(int i) {
+ if (i < 0) {
+ return 2 * ((long) Integer.MAX_VALUE) + 2 + i;
+ } else {
+ return i;
+ }
+ }
+
+ /**
+ * Convert a DOS date/time field to a Date object.
+ *
+ * @param zipDosTime contains the stored DOS time.
+ * @return a Date instance corresponding to the given time.
+ */
+ public static Date fromDosTime(ZipLong zipDosTime) {
+ long dosTime = zipDosTime.getValue();
+ return new Date(dosToJavaTime(dosTime));
+ }
+
+ /**
+ * Converts DOS time to Java time (number of milliseconds since
+ * epoch).
+ */
+ public static long dosToJavaTime(long dosTime) {
+ Calendar cal = Calendar.getInstance();
+ // CheckStyle:MagicNumberCheck OFF - no point
+ cal.set(Calendar.YEAR, (int) ((dosTime >> 25) & 0x7f) + 1980);
+ cal.set(Calendar.MONTH, (int) ((dosTime >> 21) & 0x0f) - 1);
+ cal.set(Calendar.DATE, (int) (dosTime >> 16) & 0x1f);
+ cal.set(Calendar.HOUR_OF_DAY, (int) (dosTime >> 11) & 0x1f);
+ cal.set(Calendar.MINUTE, (int) (dosTime >> 5) & 0x3f);
+ cal.set(Calendar.SECOND, (int) (dosTime << 1) & 0x3e);
+ cal.set(Calendar.MILLISECOND, 0);
+ // CheckStyle:MagicNumberCheck ON
+ return cal.getTime().getTime();
+ }
+
+ /**
+ * If the entry has Unicode*ExtraFields and the CRCs of the
+ * names/comments match those of the extra fields, transfer the
+ * known Unicode values from the extra field.
+ */
+ static void setNameAndCommentFromExtraFields(ZipEntry ze,
+ byte[] originalNameBytes,
+ byte[] commentBytes) {
+ UnicodePathExtraField name = (UnicodePathExtraField)
+ ze.getExtraField(UnicodePathExtraField.UPATH_ID);
+ String originalName = ze.getName();
+ String newName = getUnicodeStringIfOriginalMatches(name,
+ originalNameBytes);
+ if (newName != null && !originalName.equals(newName)) {
+ ze.setName(newName);
+ }
+
+ if (commentBytes != null && commentBytes.length > 0) {
+ UnicodeCommentExtraField cmt = (UnicodeCommentExtraField)
+ ze.getExtraField(UnicodeCommentExtraField.UCOM_ID);
+ String newComment =
+ getUnicodeStringIfOriginalMatches(cmt, commentBytes);
+ if (newComment != null) {
+ ze.setComment(newComment);
+ }
+ }
+ }
+
+ /**
+ * If the stored CRC matches the one of the given name, return the
+ * Unicode name of the given field.
+ *
+ * <p>If the field is null or the CRCs don't match, return null
+ * instead.</p>
+ */
+ private static
+ String getUnicodeStringIfOriginalMatches(AbstractUnicodeExtraField f,
+ byte[] orig) {
+ if (f != null) {
+ CRC32 crc32 = new CRC32();
+ crc32.update(orig);
+ long origCRC32 = crc32.getValue();
+
+ if (origCRC32 == f.getNameCRC32()) {
+ try {
+ return ZipEncodingHelper
+ .UTF8_ZIP_ENCODING.decode(f.getUnicodeName());
+ } catch (IOException ex) {
+ // UTF-8 unsupported? should be impossible the
+ // Unicode*ExtraField must contain some bad bytes
+
+ // TODO log this anywhere?
+ return null;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Create a copy of the given array - or return null if the
+ * argument is null.
+ */
+ static byte[] copy(byte[] from) {
+ if (from != null) {
+ byte[] to = new byte[from.length];
+ System.arraycopy(from, 0, to, 0, to.length);
+ return to;
+ }
+ return null;
+ }
+
+ /**
+ * Whether this library is able to read or write the given entry.
+ */
+ static boolean canHandleEntryData(ZipEntry entry) {
+ return supportsEncryptionOf(entry) && supportsMethodOf(entry);
+ }
+
+ /**
+ * Whether this library supports the encryption used by the given
+ * entry.
+ *
+ * @return true if the entry isn't encrypted at all
+ */
+ private static boolean supportsEncryptionOf(ZipEntry entry) {
+ return !entry.getGeneralPurposeBit().usesEncryption();
+ }
+
+ /**
+ * Whether this library supports the compression method used by
+ * the given entry.
+ *
+ * @return true if the compression method is STORED or DEFLATED
+ */
+ private static boolean supportsMethodOf(ZipEntry entry) {
+ return entry.getMethod() == ZipEntry.STORED
+ || entry.getMethod() == ZipEntry.DEFLATED;
+ }
+
+ /**
+ * Checks whether the entry requires features not (yet) supported
+ * by the library and throws an exception if it does.
+ */
+ static void checkRequestedFeatures(ZipEntry ze)
+ throws UnsupportedZipFeatureException {
+ if (!supportsEncryptionOf(ze)) {
+ throw
+ new UnsupportedZipFeatureException(UnsupportedZipFeatureException
+ .Feature.ENCRYPTION, ze);
+ }
+ if (!supportsMethodOf(ze)) {
+ throw
+ new UnsupportedZipFeatureException(UnsupportedZipFeatureException
+ .Feature.METHOD, ze);
+ }
+ }
+}