From cfd546c6b3240bd9d1ef34a9f6201b360fac3261 Mon Sep 17 00:00:00 2001 From: ctrlaltca <> Date: Sun, 18 Nov 2012 17:18:54 +0000 Subject: update ping. Part 2: added ping 2.4.12, fixed caller script --- buildscripts/phing/classes/phing/BuildEvent.php | 198 +++ .../phing/classes/phing/BuildException.php | 124 ++ buildscripts/phing/classes/phing/BuildListener.php | 91 ++ buildscripts/phing/classes/phing/BuildLogger.php | 70 + .../phing/classes/phing/ConfigurationException.php | 85 ++ .../phing/classes/phing/IntrospectionHelper.php | 575 ++++++++ buildscripts/phing/classes/phing/Phing.php | 1414 ++++++++++++++++++++ buildscripts/phing/classes/phing/Project.php | 1050 +++++++++++++++ .../phing/classes/phing/ProjectComponent.php | 70 + .../phing/classes/phing/RuntimeConfigurable.php | 116 ++ buildscripts/phing/classes/phing/Target.php | 375 ++++++ buildscripts/phing/classes/phing/Task.php | 272 ++++ buildscripts/phing/classes/phing/TaskAdapter.php | 85 ++ buildscripts/phing/classes/phing/TaskContainer.php | 44 + .../phing/classes/phing/UnknownElement.php | 215 +++ .../phing/contrib/DocBlox/Parallel/Manager.php | 304 +++++ .../phing/contrib/DocBlox/Parallel/README.md | 106 ++ .../phing/contrib/DocBlox/Parallel/Worker.php | 203 +++ .../phing/contrib/DocBlox/Parallel/WorkerPipe.php | 127 ++ .../phing/contrib/DocBlox/Parallel/example.php | 57 + .../classes/phing/filters/BaseFilterReader.php | 157 +++ .../phing/filters/BaseParamFilterReader.php | 69 + .../classes/phing/filters/ChainableReader.php | 43 + .../classes/phing/filters/ExpandProperties.php | 99 ++ .../phing/classes/phing/filters/HeadFilter.php | 161 +++ .../phing/classes/phing/filters/IconvFilter.php | 155 +++ .../phing/classes/phing/filters/LineContains.php | 260 ++++ .../classes/phing/filters/LineContainsRegexp.php | 179 +++ .../phing/classes/phing/filters/PrefixLines.php | 142 ++ .../phing/classes/phing/filters/ReplaceRegexp.php | 129 ++ .../phing/classes/phing/filters/ReplaceTokens.php | 435 ++++++ .../phing/filters/ReplaceTokensWithFile.php | 361 +++++ .../classes/phing/filters/StripLineBreaks.php | 148 ++ .../classes/phing/filters/StripLineComments.php | 207 +++ .../classes/phing/filters/StripPhpComments.php | 188 +++ .../classes/phing/filters/StripWhitespace.php | 95 ++ .../phing/classes/phing/filters/TabToSpaces.php | 144 ++ .../phing/classes/phing/filters/TailFilter.php | 157 +++ .../phing/classes/phing/filters/TidyFilter.php | 162 +++ .../classes/phing/filters/TranslateGettext.php | 285 ++++ .../phing/classes/phing/filters/XincludeFilter.php | 176 +++ .../phing/classes/phing/filters/XsltFilter.php | 408 ++++++ .../phing/filters/util/ChainReaderHelper.php | 183 +++ .../phing/filters/util/IniFileTokenReader.php | 97 ++ .../classes/phing/input/DefaultInputHandler.php | 85 ++ .../phing/classes/phing/input/InputHandler.php | 45 + .../phing/classes/phing/input/InputRequest.php | 107 ++ .../phing/input/MultipleChoiceInputRequest.php | 58 + .../classes/phing/input/YesNoInputRequest.php | 47 + buildscripts/phing/classes/phing/lib/Capsule.php | 267 ++++ .../classes/phing/listener/AnsiColorLogger.php | 234 ++++ .../phing/classes/phing/listener/DefaultLogger.php | 279 ++++ .../classes/phing/listener/HtmlColorLogger.php | 175 +++ .../phing/classes/phing/listener/MailLogger.php | 105 ++ .../classes/phing/listener/NoBannerLogger.php | 59 + .../classes/phing/listener/PearLogListener.php | 197 +++ .../phing/listener/StreamRequiredBuildLogger.php | 39 + .../phing/classes/phing/listener/XmlLogger.php | 354 +++++ .../classes/phing/listener/defaults.properties | 43 + .../phing/classes/phing/mappers/FileNameMapper.php | 59 + .../phing/classes/phing/mappers/FlattenMapper.php | 55 + .../phing/classes/phing/mappers/GlobMapper.php | 113 ++ .../phing/classes/phing/mappers/IdentityMapper.php | 54 + .../phing/classes/phing/mappers/MergeMapper.php | 69 + .../phing/classes/phing/mappers/RegexpMapper.php | 97 ++ .../phing/classes/phing/parser/AbstractHandler.php | 98 ++ .../classes/phing/parser/AbstractSAXParser.php | 116 ++ .../phing/classes/phing/parser/DataTypeHandler.php | 144 ++ .../classes/phing/parser/ExpatParseException.php | 31 + .../phing/classes/phing/parser/ExpatParser.php | 140 ++ .../phing/classes/phing/parser/Location.php | 76 ++ .../classes/phing/parser/NestedElementHandler.php | 186 +++ .../phing/classes/phing/parser/PhingXMLContext.php | 81 ++ .../classes/phing/parser/ProjectConfigurator.php | 387 ++++++ .../phing/classes/phing/parser/ProjectHandler.php | 176 +++ .../phing/classes/phing/parser/RootHandler.php | 82 ++ .../phing/classes/phing/parser/TargetHandler.php | 196 +++ .../phing/classes/phing/parser/TaskHandler.php | 234 ++++ .../classes/phing/system/io/BufferedReader.php | 168 +++ .../classes/phing/system/io/BufferedWriter.php | 71 + .../classes/phing/system/io/ConsoleReader.php | 84 ++ .../classes/phing/system/io/FileInputStream.php | 79 ++ .../classes/phing/system/io/FileOutputStream.php | 71 + .../phing/classes/phing/system/io/FileReader.php | 41 + .../phing/classes/phing/system/io/FileSystem.php | 840 ++++++++++++ .../phing/classes/phing/system/io/FileWriter.php | 42 + .../phing/classes/phing/system/io/FilterReader.php | 68 + .../phing/classes/phing/system/io/IOException.php | 27 + .../phing/classes/phing/system/io/InputStream.php | 178 +++ .../classes/phing/system/io/InputStreamReader.php | 127 ++ .../phing/classes/phing/system/io/OutputStream.php | 108 ++ .../classes/phing/system/io/OutputStreamWriter.php | 84 ++ .../phing/classes/phing/system/io/PhingFile.php | 996 ++++++++++++++ .../phing/classes/phing/system/io/Reader.php | 91 ++ .../phing/classes/phing/system/io/StringReader.php | 84 ++ .../classes/phing/system/io/UnixFileSystem.php | 302 +++++ .../classes/phing/system/io/Win32FileSystem.php | 477 +++++++ .../classes/phing/system/io/WinNTFileSystem.php | 34 + .../phing/classes/phing/system/io/Writer.php | 53 + .../phing/classes/phing/system/lang/Character.php | 49 + .../classes/phing/system/lang/EventObject.php | 52 + .../phing/system/lang/FileNotFoundException.php | 26 + .../phing/system/lang/NullPointerException.php | 26 + .../phing/system/lang/SecurityException.php | 26 + .../phing/classes/phing/system/util/Properties.php | 314 +++++ .../phing/classes/phing/system/util/Register.php | 149 +++ .../phing/classes/phing/system/util/Timer.php | 96 ++ .../phing/classes/phing/tasks/defaults.properties | 145 ++ .../phing/classes/phing/tasks/ext/CapsuleTask.php | 480 +++++++ .../phing/tasks/ext/ExportPropertiesTask.php | 141 ++ .../classes/phing/tasks/ext/ExtractBaseTask.php | 199 +++ .../phing/classes/phing/tasks/ext/FileHashTask.php | 147 ++ .../phing/classes/phing/tasks/ext/FileSizeTask.php | 120 ++ .../classes/phing/tasks/ext/FtpDeployTask.php | 233 ++++ .../phing/classes/phing/tasks/ext/HttpGetTask.php | 170 +++ .../classes/phing/tasks/ext/HttpRequestTask.php | 286 ++++ .../phing/classes/phing/tasks/ext/JslLintTask.php | 284 ++++ .../phing/classes/phing/tasks/ext/MailTask.php | 159 +++ .../phing/classes/phing/tasks/ext/ManifestTask.php | 343 +++++ .../classes/phing/tasks/ext/PackageAsPathTask.php | 65 + .../phing/classes/phing/tasks/ext/ParallelTask.php | 83 ++ .../phing/classes/phing/tasks/ext/PatchTask.php | 301 +++++ .../classes/phing/tasks/ext/PearPackage2Task.php | 279 ++++ .../classes/phing/tasks/ext/PearPackageTask.php | 504 +++++++ .../classes/phing/tasks/ext/PhpCodeSnifferTask.php | 648 +++++++++ .../phing/classes/phing/tasks/ext/PhpLintTask.php | 278 ++++ .../classes/phing/tasks/ext/ReplaceRegexpTask.php | 204 +++ .../phing/classes/phing/tasks/ext/ScpTask.php | 380 ++++++ .../classes/phing/tasks/ext/Service/Amazon.php | 120 ++ .../classes/phing/tasks/ext/Service/Amazon/S3.php | 188 +++ .../tasks/ext/Service/Amazon/S3/S3GetTask.php | 108 ++ .../tasks/ext/Service/Amazon/S3/S3PutTask.php | 243 ++++ .../phing/classes/phing/tasks/ext/SmartyTask.php | 610 +++++++++ .../phing/classes/phing/tasks/ext/SshTask.php | 224 ++++ .../classes/phing/tasks/ext/SymfonyConsole/Arg.php | 96 ++ .../ext/SymfonyConsole/SymfonyConsoleTask.php | 113 ++ .../phing/classes/phing/tasks/ext/SymlinkTask.php | 309 +++++ .../phing/classes/phing/tasks/ext/TarTask.php | 445 ++++++ .../phing/classes/phing/tasks/ext/UntarTask.php | 89 ++ .../phing/classes/phing/tasks/ext/UnzipTask.php | 77 ++ .../phing/classes/phing/tasks/ext/VersionTask.php | 217 +++ .../phing/classes/phing/tasks/ext/XmlLintTask.php | 179 +++ .../classes/phing/tasks/ext/XmlPropertyTask.php | 273 ++++ .../phing/tasks/ext/ZendCodeAnalyzerTask.php | 207 +++ .../phing/classes/phing/tasks/ext/ZipTask.php | 301 +++++ .../classes/phing/tasks/ext/apigen/ApiGenTask.php | 439 ++++++ .../phing/tasks/ext/coverage/CoverageMerger.php | 154 +++ .../tasks/ext/coverage/CoverageMergerTask.php | 92 ++ .../tasks/ext/coverage/CoverageReportTask.php | 564 ++++++++ .../ext/coverage/CoverageReportTransformer.php | 176 +++ .../phing/tasks/ext/coverage/CoverageSetupTask.php | 164 +++ .../tasks/ext/coverage/CoverageThresholdTask.php | 458 +++++++ .../phing/tasks/ext/creole/CreoleSQLExecTask.php | 592 ++++++++ .../classes/phing/tasks/ext/creole/CreoleTask.php | 242 ++++ .../phing/tasks/ext/dbdeploy/DbDeployTask.php | 436 ++++++ .../phing/tasks/ext/dbdeploy/DbmsSyntax.php | 34 + .../phing/tasks/ext/dbdeploy/DbmsSyntaxFactory.php | 67 + .../phing/tasks/ext/dbdeploy/DbmsSyntaxMsSql.php | 37 + .../phing/tasks/ext/dbdeploy/DbmsSyntaxMysql.php | 37 + .../phing/tasks/ext/dbdeploy/DbmsSyntaxOracle.php | 37 + .../phing/tasks/ext/dbdeploy/DbmsSyntaxPgSQL.php | 36 + .../phing/tasks/ext/dbdeploy/DbmsSyntaxSQLite.php | 37 + .../phing/tasks/ext/docblox/DocBloxTask.php | 221 +++ .../classes/phing/tasks/ext/git/GitBaseTask.php | 134 ++ .../classes/phing/tasks/ext/git/GitBranchTask.php | 296 ++++ .../phing/tasks/ext/git/GitCheckoutTask.php | 256 ++++ .../classes/phing/tasks/ext/git/GitCloneTask.php | 128 ++ .../classes/phing/tasks/ext/git/GitCommitTask.php | 179 +++ .../classes/phing/tasks/ext/git/GitFetchTask.php | 284 ++++ .../classes/phing/tasks/ext/git/GitGcTask.php | 158 +++ .../classes/phing/tasks/ext/git/GitInitTask.php | 81 ++ .../classes/phing/tasks/ext/git/GitLogTask.php | 270 ++++ .../classes/phing/tasks/ext/git/GitMergeTask.php | 258 ++++ .../classes/phing/tasks/ext/git/GitPullTask.php | 373 ++++++ .../classes/phing/tasks/ext/git/GitPushTask.php | 255 ++++ .../classes/phing/tasks/ext/git/GitTagTask.php | 406 ++++++ .../phing/tasks/ext/ioncube/IoncubeComment.php | 43 + .../phing/tasks/ext/ioncube/IoncubeEncoderTask.php | 642 +++++++++ .../phing/tasks/ext/ioncube/IoncubeLicenseTask.php | 208 +++ .../phing/classes/phing/tasks/ext/jsmin/JsMin.php | 292 ++++ .../classes/phing/tasks/ext/jsmin/JsMinTask.php | 145 ++ .../tasks/ext/liquibase/AbstractLiquibaseTask.php | 184 +++ .../tasks/ext/liquibase/LiquibaseChangeLogTask.php | 39 + .../tasks/ext/liquibase/LiquibaseDbDocTask.php | 86 ++ .../tasks/ext/liquibase/LiquibaseDiffTask.php | 137 ++ .../tasks/ext/liquibase/LiquibaseRollbackTask.php | 72 + .../phing/tasks/ext/liquibase/LiquibaseTagTask.php | 75 ++ .../tasks/ext/liquibase/LiquibaseUpdateTask.php | 39 + .../tasks/ext/pdepend/PhpDependAnalyzerElement.php | 109 ++ .../tasks/ext/pdepend/PhpDependLoggerElement.php | 105 ++ .../phing/tasks/ext/pdepend/PhpDependTask.php | 506 +++++++ .../tasks/ext/pdo/DefaultPDOQuerySplitter.php | 151 +++ .../phing/tasks/ext/pdo/PDOQuerySplitter.php | 63 + .../phing/tasks/ext/pdo/PDOResultFormatter.php | 84 ++ .../tasks/ext/pdo/PDOSQLExecFormatterElement.php | 313 +++++ .../classes/phing/tasks/ext/pdo/PDOSQLExecTask.php | 606 +++++++++ .../phing/classes/phing/tasks/ext/pdo/PDOTask.php | 215 +++ .../phing/tasks/ext/pdo/PgsqlPDOQuerySplitter.php | 291 ++++ .../tasks/ext/pdo/PlainPDOResultFormatter.php | 130 ++ .../phing/tasks/ext/pdo/XMLPDOResultFormatter.php | 141 ++ .../phing/tasks/ext/pearpackage/Fileset.php | 228 ++++ .../classes/phing/tasks/ext/phar/PharMetadata.php | 55 + .../phing/tasks/ext/phar/PharMetadataElement.php | 80 ++ .../phing/tasks/ext/phar/PharPackageTask.php | 362 +++++ .../classes/phing/tasks/ext/phk/PhkPackageTask.php | 248 ++++ .../phing/tasks/ext/phk/PhkPackageWebAccess.php | 57 + .../tasks/ext/phk/PhkPackageWebAccessPath.php | 46 + .../tasks/ext/phpcpd/PHPCPDFormatterElement.php | 177 +++ .../classes/phing/tasks/ext/phpcpd/PHPCPDTask.php | 312 +++++ .../formatter/DefaultPHPCPDResultFormatter.php | 58 + .../ext/phpcpd/formatter/PHPCPDResultFormatter.php | 40 + .../phpcpd/formatter/PMDPHPCPDResultFormatter.php | 51 + .../ext/phpdoc/PhingPhpDocumentorErrorTracker.php | 98 ++ .../tasks/ext/phpdoc/PhingPhpDocumentorSetup.php | 230 ++++ .../phing/tasks/ext/phpdoc/PhpDocumentor2Task.php | 224 ++++ .../tasks/ext/phpdoc/PhpDocumentorExternalTask.php | 265 ++++ .../phing/tasks/ext/phpdoc/PhpDocumentorTask.php | 480 +++++++ .../tasks/ext/phpmd/PHPMDFormatterElement.php | 181 +++ .../classes/phing/tasks/ext/phpmd/PHPMDTask.php | 284 ++++ .../classes/phing/tasks/ext/phpunit/BatchTest.php | 230 ++++ .../phing/tasks/ext/phpunit/FormatterElement.php | 178 +++ .../phing/tasks/ext/phpunit/PHPUnitReportTask.php | 248 ++++ .../phing/tasks/ext/phpunit/PHPUnitTask.php | 378 ++++++ .../phing/tasks/ext/phpunit/PHPUnitTestRunner.php | 312 +++++ .../phing/tasks/ext/phpunit/PHPUnitUtil.php | 141 ++ .../formatter/CloverPHPUnitResultFormatter.php | 87 ++ .../phpunit/formatter/PHPUnitResultFormatter.php | 203 +++ .../formatter/PlainPHPUnitResultFormatter.php | 128 ++ .../formatter/SummaryPHPUnitResultFormatter.php | 62 + .../formatter/XMLPHPUnitResultFormatter.php | 120 ++ .../phing/classes/phing/tasks/ext/rSTTask.php | 476 +++++++ .../simpletest/SimpleTestCountResultFormatter.php | 51 + .../simpletest/SimpleTestDebugResultFormatter.php | 119 ++ .../ext/simpletest/SimpleTestFormatterElement.php | 68 + .../simpletest/SimpleTestPlainResultFormatter.php | 95 ++ .../ext/simpletest/SimpleTestResultFormatter.php | 161 +++ .../SimpleTestSummaryResultFormatter.php | 53 + .../phing/tasks/ext/simpletest/SimpleTestTask.php | 264 ++++ .../simpletest/SimpleTestXmlResultFormatter.php | 178 +++ .../classes/phing/tasks/ext/svn/SvnBaseTask.php | 347 +++++ .../phing/tasks/ext/svn/SvnCheckoutTask.php | 67 + .../classes/phing/tasks/ext/svn/SvnCommitTask.php | 113 ++ .../classes/phing/tasks/ext/svn/SvnCopyTask.php | 70 + .../classes/phing/tasks/ext/svn/SvnExportTask.php | 70 + .../classes/phing/tasks/ext/svn/SvnInfoTask.php | 112 ++ .../phing/tasks/ext/svn/SvnLastRevisionTask.php | 120 ++ .../classes/phing/tasks/ext/svn/SvnListTask.php | 128 ++ .../classes/phing/tasks/ext/svn/SvnLogTask.php | 108 ++ .../classes/phing/tasks/ext/svn/SvnSwitchTask.php | 73 + .../classes/phing/tasks/ext/svn/SvnUpdateTask.php | 67 + .../tasks/ext/zendguard/ZendGuardEncodeTask.php | 510 +++++++ .../tasks/ext/zendguard/ZendGuardLicenseTask.php | 524 ++++++++ .../phing/classes/phing/tasks/system/AdhocTask.php | 88 ++ .../phing/tasks/system/AdhocTaskdefTask.php | 103 ++ .../phing/tasks/system/AdhocTypedefTask.php | 71 + .../classes/phing/tasks/system/AppendTask.php | 240 ++++ .../classes/phing/tasks/system/AvailableTask.php | 172 +++ .../phing/classes/phing/tasks/system/ChmodTask.php | 203 +++ .../phing/classes/phing/tasks/system/ChownTask.php | 216 +++ .../classes/phing/tasks/system/ConditionTask.php | 74 + .../phing/classes/phing/tasks/system/CopyTask.php | 480 +++++++ .../classes/phing/tasks/system/CvsPassTask.php | 173 +++ .../phing/classes/phing/tasks/system/CvsTask.php | 540 ++++++++ .../classes/phing/tasks/system/DeleteTask.php | 276 ++++ .../phing/classes/phing/tasks/system/EchoTask.php | 152 +++ .../phing/classes/phing/tasks/system/ExecTask.php | 516 +++++++ .../phing/classes/phing/tasks/system/FailTask.php | 118 ++ .../classes/phing/tasks/system/ForeachTask.php | 320 +++++ .../phing/classes/phing/tasks/system/IfTask.php | 227 ++++ .../classes/phing/tasks/system/ImportTask.php | 136 ++ .../classes/phing/tasks/system/IncludePathTask.php | 115 ++ .../phing/classes/phing/tasks/system/InputTask.php | 150 +++ .../classes/phing/tasks/system/LoadFileTask.php | 119 ++ .../classes/phing/tasks/system/MatchingTask.php | 361 +++++ .../phing/classes/phing/tasks/system/MkdirTask.php | 79 ++ .../phing/classes/phing/tasks/system/MoveTask.php | 202 +++ .../classes/phing/tasks/system/PhingCallTask.php | 161 +++ .../phing/classes/phing/tasks/system/PhingTask.php | 627 +++++++++ .../classes/phing/tasks/system/PhpEvalTask.php | 192 +++ .../phing/tasks/system/PropertyPromptTask.php | 234 ++++ .../classes/phing/tasks/system/PropertyTask.php | 438 ++++++ .../classes/phing/tasks/system/ReflexiveTask.php | 155 +++ .../classes/phing/tasks/system/ResolvePathTask.php | 159 +++ .../classes/phing/tasks/system/SequentialTask.php | 58 + .../classes/phing/tasks/system/TaskdefTask.php | 165 +++ .../phing/classes/phing/tasks/system/TouchTask.php | 170 +++ .../classes/phing/tasks/system/TryCatchTask.php | 123 ++ .../classes/phing/tasks/system/TstampTask.php | 171 +++ .../classes/phing/tasks/system/TypedefTask.php | 127 ++ .../classes/phing/tasks/system/UpToDateTask.php | 257 ++++ .../classes/phing/tasks/system/WaitForTask.php | 188 +++ .../phing/classes/phing/tasks/system/WarnTask.php | 35 + .../phing/classes/phing/tasks/system/XsltTask.php | 103 ++ .../phing/tasks/system/condition/AndCondition.php | 46 + .../phing/tasks/system/condition/Condition.php | 38 + .../phing/tasks/system/condition/ConditionBase.php | 197 +++ .../tasks/system/condition/ContainsCondition.php | 76 ++ .../tasks/system/condition/EqualsCondition.php | 78 ++ .../tasks/system/condition/IsFalseCondition.php | 60 + .../tasks/system/condition/IsSetCondition.php | 53 + .../tasks/system/condition/IsTrueCondition.php | 59 + .../phing/tasks/system/condition/NotCondition.php | 48 + .../phing/tasks/system/condition/OrCondition.php | 46 + .../phing/tasks/system/condition/OsCondition.php | 63 + .../system/condition/ReferenceExistsCondition.php | 52 + .../phing/classes/phing/types/AbstractFileSet.php | 587 ++++++++ .../phing/classes/phing/types/Commandline.php | 475 +++++++ .../phing/classes/phing/types/DataType.php | 182 +++ .../phing/classes/phing/types/Description.php | 53 + buildscripts/phing/classes/phing/types/DirSet.php | 49 + .../phing/classes/phing/types/Excludes.php | 208 +++ .../classes/phing/types/ExcludesNameEntry.php | 81 ++ .../phing/classes/phing/types/FileList.php | 224 ++++ buildscripts/phing/classes/phing/types/FileSet.php | 56 + .../phing/classes/phing/types/FilterChain.php | 191 +++ .../phing/classes/phing/types/IterableFileSet.php | 56 + buildscripts/phing/classes/phing/types/Mapper.php | 207 +++ .../phing/classes/phing/types/Parameter.php | 99 ++ .../phing/classes/phing/types/Parameterizable.php | 32 + buildscripts/phing/classes/phing/types/Path.php | 452 +++++++ .../phing/classes/phing/types/PatternSet.php | 493 +++++++ .../classes/phing/types/PearPackageFileSet.php | 179 +++ .../classes/phing/types/PhingFilterReader.php | 136 ++ .../phing/classes/phing/types/Reference.php | 56 + .../classes/phing/types/RegularExpression.php | 128 ++ .../phing/classes/phing/types/TokenReader.php | 66 + .../phing/classes/phing/types/TokenSource.php | 157 +++ .../phing/classes/phing/types/defaults.properties | 13 + .../classes/phing/types/selectors/AndSelector.php | 67 + .../phing/types/selectors/BaseExtendSelector.php | 62 + .../classes/phing/types/selectors/BaseSelector.php | 84 ++ .../types/selectors/BaseSelectorContainer.php | 270 ++++ .../types/selectors/ContainsRegexpSelector.php | 164 +++ .../phing/types/selectors/ContainsSelector.php | 151 +++ .../classes/phing/types/selectors/DateSelector.php | 214 +++ .../phing/types/selectors/DependSelector.php | 151 +++ .../phing/types/selectors/DepthSelector.php | 158 +++ .../phing/types/selectors/ExtendFileSelector.php | 43 + .../phing/types/selectors/ExtendSelector.php | 124 ++ .../classes/phing/types/selectors/FileSelector.php | 47 + .../phing/types/selectors/FilenameSelector.php | 157 +++ .../phing/types/selectors/MajoritySelector.php | 92 ++ .../classes/phing/types/selectors/NoneSelector.php | 71 + .../classes/phing/types/selectors/NotSelector.php | 59 + .../classes/phing/types/selectors/OrSelector.php | 72 + .../phing/types/selectors/PresentSelector.php | 154 +++ .../phing/types/selectors/SelectSelector.php | 124 ++ .../phing/types/selectors/SelectorContainer.php | 141 ++ .../phing/types/selectors/SelectorScanner.php | 55 + .../phing/types/selectors/SelectorUtils.php | 200 +++ .../classes/phing/types/selectors/SizeSelector.php | 228 ++++ .../classes/phing/types/selectors/TypeSelector.php | 120 ++ .../phing/classes/phing/util/DataStore.php | 151 +++ .../phing/classes/phing/util/DirectoryScanner.php | 755 +++++++++++ .../classes/phing/util/ExtendedFileStream.php | 129 ++ .../phing/classes/phing/util/FileUtils.php | 298 +++++ .../phing/classes/phing/util/LogWriter.php | 95 ++ .../phing/classes/phing/util/PathTokenizer.php | 245 ++++ .../classes/phing/util/PearPackageScanner.php | 170 +++ .../phing/classes/phing/util/SourceFileScanner.php | 159 +++ .../phing/classes/phing/util/StringHelper.php | 208 +++ .../phing/classes/phing/util/regexp/PregEngine.php | 167 +++ .../phing/classes/phing/util/regexp/Regexp.php | 203 +++ .../classes/phing/util/regexp/RegexpEngine.php | 73 + 364 files changed, 68585 insertions(+) create mode 100755 buildscripts/phing/classes/phing/BuildEvent.php create mode 100755 buildscripts/phing/classes/phing/BuildException.php create mode 100755 buildscripts/phing/classes/phing/BuildListener.php create mode 100755 buildscripts/phing/classes/phing/BuildLogger.php create mode 100755 buildscripts/phing/classes/phing/ConfigurationException.php create mode 100755 buildscripts/phing/classes/phing/IntrospectionHelper.php create mode 100755 buildscripts/phing/classes/phing/Phing.php create mode 100755 buildscripts/phing/classes/phing/Project.php create mode 100755 buildscripts/phing/classes/phing/ProjectComponent.php create mode 100755 buildscripts/phing/classes/phing/RuntimeConfigurable.php create mode 100755 buildscripts/phing/classes/phing/Target.php create mode 100755 buildscripts/phing/classes/phing/Task.php create mode 100755 buildscripts/phing/classes/phing/TaskAdapter.php create mode 100755 buildscripts/phing/classes/phing/TaskContainer.php create mode 100755 buildscripts/phing/classes/phing/UnknownElement.php create mode 100644 buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Manager.php create mode 100644 buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/README.md create mode 100644 buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Worker.php create mode 100644 buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/WorkerPipe.php create mode 100644 buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/example.php create mode 100755 buildscripts/phing/classes/phing/filters/BaseFilterReader.php create mode 100755 buildscripts/phing/classes/phing/filters/BaseParamFilterReader.php create mode 100644 buildscripts/phing/classes/phing/filters/ChainableReader.php create mode 100755 buildscripts/phing/classes/phing/filters/ExpandProperties.php create mode 100755 buildscripts/phing/classes/phing/filters/HeadFilter.php create mode 100755 buildscripts/phing/classes/phing/filters/IconvFilter.php create mode 100755 buildscripts/phing/classes/phing/filters/LineContains.php create mode 100755 buildscripts/phing/classes/phing/filters/LineContainsRegexp.php create mode 100755 buildscripts/phing/classes/phing/filters/PrefixLines.php create mode 100755 buildscripts/phing/classes/phing/filters/ReplaceRegexp.php create mode 100755 buildscripts/phing/classes/phing/filters/ReplaceTokens.php create mode 100644 buildscripts/phing/classes/phing/filters/ReplaceTokensWithFile.php create mode 100755 buildscripts/phing/classes/phing/filters/StripLineBreaks.php create mode 100755 buildscripts/phing/classes/phing/filters/StripLineComments.php create mode 100755 buildscripts/phing/classes/phing/filters/StripPhpComments.php create mode 100755 buildscripts/phing/classes/phing/filters/StripWhitespace.php create mode 100755 buildscripts/phing/classes/phing/filters/TabToSpaces.php create mode 100755 buildscripts/phing/classes/phing/filters/TailFilter.php create mode 100755 buildscripts/phing/classes/phing/filters/TidyFilter.php create mode 100755 buildscripts/phing/classes/phing/filters/TranslateGettext.php create mode 100644 buildscripts/phing/classes/phing/filters/XincludeFilter.php create mode 100644 buildscripts/phing/classes/phing/filters/XsltFilter.php create mode 100755 buildscripts/phing/classes/phing/filters/util/ChainReaderHelper.php create mode 100755 buildscripts/phing/classes/phing/filters/util/IniFileTokenReader.php create mode 100755 buildscripts/phing/classes/phing/input/DefaultInputHandler.php create mode 100755 buildscripts/phing/classes/phing/input/InputHandler.php create mode 100755 buildscripts/phing/classes/phing/input/InputRequest.php create mode 100755 buildscripts/phing/classes/phing/input/MultipleChoiceInputRequest.php create mode 100755 buildscripts/phing/classes/phing/input/YesNoInputRequest.php create mode 100755 buildscripts/phing/classes/phing/lib/Capsule.php create mode 100755 buildscripts/phing/classes/phing/listener/AnsiColorLogger.php create mode 100755 buildscripts/phing/classes/phing/listener/DefaultLogger.php create mode 100755 buildscripts/phing/classes/phing/listener/HtmlColorLogger.php create mode 100755 buildscripts/phing/classes/phing/listener/MailLogger.php create mode 100755 buildscripts/phing/classes/phing/listener/NoBannerLogger.php create mode 100755 buildscripts/phing/classes/phing/listener/PearLogListener.php create mode 100755 buildscripts/phing/classes/phing/listener/StreamRequiredBuildLogger.php create mode 100755 buildscripts/phing/classes/phing/listener/XmlLogger.php create mode 100644 buildscripts/phing/classes/phing/listener/defaults.properties create mode 100755 buildscripts/phing/classes/phing/mappers/FileNameMapper.php create mode 100755 buildscripts/phing/classes/phing/mappers/FlattenMapper.php create mode 100755 buildscripts/phing/classes/phing/mappers/GlobMapper.php create mode 100755 buildscripts/phing/classes/phing/mappers/IdentityMapper.php create mode 100755 buildscripts/phing/classes/phing/mappers/MergeMapper.php create mode 100755 buildscripts/phing/classes/phing/mappers/RegexpMapper.php create mode 100755 buildscripts/phing/classes/phing/parser/AbstractHandler.php create mode 100755 buildscripts/phing/classes/phing/parser/AbstractSAXParser.php create mode 100755 buildscripts/phing/classes/phing/parser/DataTypeHandler.php create mode 100755 buildscripts/phing/classes/phing/parser/ExpatParseException.php create mode 100755 buildscripts/phing/classes/phing/parser/ExpatParser.php create mode 100755 buildscripts/phing/classes/phing/parser/Location.php create mode 100755 buildscripts/phing/classes/phing/parser/NestedElementHandler.php create mode 100755 buildscripts/phing/classes/phing/parser/PhingXMLContext.php create mode 100755 buildscripts/phing/classes/phing/parser/ProjectConfigurator.php create mode 100755 buildscripts/phing/classes/phing/parser/ProjectHandler.php create mode 100755 buildscripts/phing/classes/phing/parser/RootHandler.php create mode 100755 buildscripts/phing/classes/phing/parser/TargetHandler.php create mode 100755 buildscripts/phing/classes/phing/parser/TaskHandler.php create mode 100755 buildscripts/phing/classes/phing/system/io/BufferedReader.php create mode 100755 buildscripts/phing/classes/phing/system/io/BufferedWriter.php create mode 100755 buildscripts/phing/classes/phing/system/io/ConsoleReader.php create mode 100644 buildscripts/phing/classes/phing/system/io/FileInputStream.php create mode 100644 buildscripts/phing/classes/phing/system/io/FileOutputStream.php create mode 100644 buildscripts/phing/classes/phing/system/io/FileReader.php create mode 100755 buildscripts/phing/classes/phing/system/io/FileSystem.php create mode 100644 buildscripts/phing/classes/phing/system/io/FileWriter.php create mode 100644 buildscripts/phing/classes/phing/system/io/FilterReader.php create mode 100644 buildscripts/phing/classes/phing/system/io/IOException.php create mode 100644 buildscripts/phing/classes/phing/system/io/InputStream.php create mode 100644 buildscripts/phing/classes/phing/system/io/InputStreamReader.php create mode 100644 buildscripts/phing/classes/phing/system/io/OutputStream.php create mode 100644 buildscripts/phing/classes/phing/system/io/OutputStreamWriter.php create mode 100755 buildscripts/phing/classes/phing/system/io/PhingFile.php create mode 100755 buildscripts/phing/classes/phing/system/io/Reader.php create mode 100644 buildscripts/phing/classes/phing/system/io/StringReader.php create mode 100755 buildscripts/phing/classes/phing/system/io/UnixFileSystem.php create mode 100644 buildscripts/phing/classes/phing/system/io/Win32FileSystem.php create mode 100644 buildscripts/phing/classes/phing/system/io/WinNTFileSystem.php create mode 100644 buildscripts/phing/classes/phing/system/io/Writer.php create mode 100644 buildscripts/phing/classes/phing/system/lang/Character.php create mode 100644 buildscripts/phing/classes/phing/system/lang/EventObject.php create mode 100644 buildscripts/phing/classes/phing/system/lang/FileNotFoundException.php create mode 100644 buildscripts/phing/classes/phing/system/lang/NullPointerException.php create mode 100644 buildscripts/phing/classes/phing/system/lang/SecurityException.php create mode 100755 buildscripts/phing/classes/phing/system/util/Properties.php create mode 100755 buildscripts/phing/classes/phing/system/util/Register.php create mode 100755 buildscripts/phing/classes/phing/system/util/Timer.php create mode 100755 buildscripts/phing/classes/phing/tasks/defaults.properties create mode 100644 buildscripts/phing/classes/phing/tasks/ext/CapsuleTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/ExportPropertiesTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/ExtractBaseTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/FileHashTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/FileSizeTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/FtpDeployTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/HttpGetTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/HttpRequestTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/JslLintTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/MailTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/ManifestTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/PackageAsPathTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/ParallelTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/PatchTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/PearPackage2Task.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/PearPackageTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/PhpCodeSnifferTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/PhpLintTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/ReplaceRegexpTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/ScpTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/Service/Amazon.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/Service/Amazon/S3.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3GetTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3PutTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/SmartyTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/SshTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/SymfonyConsole/Arg.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/SymfonyConsole/SymfonyConsoleTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/SymlinkTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/TarTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/UntarTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/UnzipTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/VersionTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/XmlLintTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/XmlPropertyTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/ZendCodeAnalyzerTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/ZipTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/apigen/ApiGenTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageMerger.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageMergerTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageReportTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageReportTransformer.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageSetupTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageThresholdTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/creole/CreoleSQLExecTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/creole/CreoleTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbDeployTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntax.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxFactory.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMsSql.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMysql.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxOracle.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxPgSQL.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxSQLite.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/docblox/DocBloxTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/git/GitBaseTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/git/GitBranchTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/git/GitCheckoutTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/git/GitCloneTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/git/GitCommitTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/git/GitFetchTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/git/GitGcTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/git/GitInitTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/git/GitLogTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/git/GitMergeTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/git/GitPullTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/git/GitPushTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/git/GitTagTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/ioncube/IoncubeComment.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/ioncube/IoncubeEncoderTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/ioncube/IoncubeLicenseTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/jsmin/JsMin.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/jsmin/JsMinTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/liquibase/AbstractLiquibaseTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseChangeLogTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseDbDocTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseDiffTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseRollbackTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseTagTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseUpdateTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/pdepend/PhpDependAnalyzerElement.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/pdepend/PhpDependLoggerElement.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/pdepend/PhpDependTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/pdo/DefaultPDOQuerySplitter.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/pdo/PDOQuerySplitter.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/pdo/PDOResultFormatter.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/pdo/PDOSQLExecFormatterElement.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/pdo/PDOSQLExecTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/pdo/PDOTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/pdo/PgsqlPDOQuerySplitter.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/pdo/PlainPDOResultFormatter.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/pdo/XMLPDOResultFormatter.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/pearpackage/Fileset.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phar/PharMetadata.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phar/PharMetadataElement.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phar/PharPackageTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phk/PhkPackageTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccess.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccessPath.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phpcpd/PHPCPDFormatterElement.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phpcpd/PHPCPDTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phpcpd/formatter/DefaultPHPCPDResultFormatter.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phpcpd/formatter/PHPCPDResultFormatter.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phpcpd/formatter/PMDPHPCPDResultFormatter.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorErrorTracker.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorSetup.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentor2Task.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorExternalTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phpmd/PHPMDFormatterElement.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/phpmd/PHPMDTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpunit/BatchTest.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpunit/FormatterElement.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitReportTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitTestRunner.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitUtil.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/CloverPHPUnitResultFormatter.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/PlainPHPUnitResultFormatter.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/SummaryPHPUnitResultFormatter.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/XMLPHPUnitResultFormatter.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/rSTTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestCountResultFormatter.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestDebugResultFormatter.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestFormatterElement.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestPlainResultFormatter.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestResultFormatter.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestSummaryResultFormatter.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestXmlResultFormatter.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/svn/SvnBaseTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/svn/SvnCheckoutTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/svn/SvnCommitTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/svn/SvnCopyTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/svn/SvnExportTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/svn/SvnInfoTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/svn/SvnLastRevisionTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/svn/SvnListTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/ext/svn/SvnLogTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/svn/SvnSwitchTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/svn/SvnUpdateTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/zendguard/ZendGuardEncodeTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/ext/zendguard/ZendGuardLicenseTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/AdhocTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/AdhocTaskdefTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/AdhocTypedefTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/AppendTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/AvailableTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/ChmodTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/ChownTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/ConditionTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/CopyTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/CvsPassTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/CvsTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/DeleteTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/EchoTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/ExecTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/FailTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/ForeachTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/system/IfTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/ImportTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/IncludePathTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/InputTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/system/LoadFileTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/MatchingTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/MkdirTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/MoveTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/PhingCallTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/PhingTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/PhpEvalTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/PropertyPromptTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/PropertyTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/ReflexiveTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/ResolvePathTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/system/SequentialTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/TaskdefTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/TouchTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/system/TryCatchTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/TstampTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/TypedefTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/UpToDateTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/WaitForTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/WarnTask.php create mode 100644 buildscripts/phing/classes/phing/tasks/system/XsltTask.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/condition/AndCondition.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/condition/Condition.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/condition/ConditionBase.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/condition/ContainsCondition.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/condition/EqualsCondition.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/condition/IsFalseCondition.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/condition/IsSetCondition.php create mode 100644 buildscripts/phing/classes/phing/tasks/system/condition/IsTrueCondition.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/condition/NotCondition.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/condition/OrCondition.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/condition/OsCondition.php create mode 100755 buildscripts/phing/classes/phing/tasks/system/condition/ReferenceExistsCondition.php create mode 100755 buildscripts/phing/classes/phing/types/AbstractFileSet.php create mode 100755 buildscripts/phing/classes/phing/types/Commandline.php create mode 100644 buildscripts/phing/classes/phing/types/DataType.php create mode 100644 buildscripts/phing/classes/phing/types/Description.php create mode 100644 buildscripts/phing/classes/phing/types/DirSet.php create mode 100644 buildscripts/phing/classes/phing/types/Excludes.php create mode 100644 buildscripts/phing/classes/phing/types/ExcludesNameEntry.php create mode 100755 buildscripts/phing/classes/phing/types/FileList.php create mode 100644 buildscripts/phing/classes/phing/types/FileSet.php create mode 100755 buildscripts/phing/classes/phing/types/FilterChain.php create mode 100644 buildscripts/phing/classes/phing/types/IterableFileSet.php create mode 100644 buildscripts/phing/classes/phing/types/Mapper.php create mode 100644 buildscripts/phing/classes/phing/types/Parameter.php create mode 100644 buildscripts/phing/classes/phing/types/Parameterizable.php create mode 100644 buildscripts/phing/classes/phing/types/Path.php create mode 100755 buildscripts/phing/classes/phing/types/PatternSet.php create mode 100644 buildscripts/phing/classes/phing/types/PearPackageFileSet.php create mode 100755 buildscripts/phing/classes/phing/types/PhingFilterReader.php create mode 100644 buildscripts/phing/classes/phing/types/Reference.php create mode 100755 buildscripts/phing/classes/phing/types/RegularExpression.php create mode 100755 buildscripts/phing/classes/phing/types/TokenReader.php create mode 100644 buildscripts/phing/classes/phing/types/TokenSource.php create mode 100644 buildscripts/phing/classes/phing/types/defaults.properties create mode 100644 buildscripts/phing/classes/phing/types/selectors/AndSelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/BaseExtendSelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/BaseSelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/BaseSelectorContainer.php create mode 100755 buildscripts/phing/classes/phing/types/selectors/ContainsRegexpSelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/ContainsSelector.php create mode 100755 buildscripts/phing/classes/phing/types/selectors/DateSelector.php create mode 100755 buildscripts/phing/classes/phing/types/selectors/DependSelector.php create mode 100755 buildscripts/phing/classes/phing/types/selectors/DepthSelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/ExtendFileSelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/ExtendSelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/FileSelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/FilenameSelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/MajoritySelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/NoneSelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/NotSelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/OrSelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/PresentSelector.php create mode 100755 buildscripts/phing/classes/phing/types/selectors/SelectSelector.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/SelectorContainer.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/SelectorScanner.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/SelectorUtils.php create mode 100644 buildscripts/phing/classes/phing/types/selectors/SizeSelector.php create mode 100755 buildscripts/phing/classes/phing/types/selectors/TypeSelector.php create mode 100755 buildscripts/phing/classes/phing/util/DataStore.php create mode 100755 buildscripts/phing/classes/phing/util/DirectoryScanner.php create mode 100755 buildscripts/phing/classes/phing/util/ExtendedFileStream.php create mode 100755 buildscripts/phing/classes/phing/util/FileUtils.php create mode 100755 buildscripts/phing/classes/phing/util/LogWriter.php create mode 100644 buildscripts/phing/classes/phing/util/PathTokenizer.php create mode 100644 buildscripts/phing/classes/phing/util/PearPackageScanner.php create mode 100644 buildscripts/phing/classes/phing/util/SourceFileScanner.php create mode 100644 buildscripts/phing/classes/phing/util/StringHelper.php create mode 100644 buildscripts/phing/classes/phing/util/regexp/PregEngine.php create mode 100755 buildscripts/phing/classes/phing/util/regexp/Regexp.php create mode 100755 buildscripts/phing/classes/phing/util/regexp/RegexpEngine.php (limited to 'buildscripts/phing/classes') diff --git a/buildscripts/phing/classes/phing/BuildEvent.php b/buildscripts/phing/classes/phing/BuildEvent.php new file mode 100755 index 00000000..581f7cf4 --- /dev/null +++ b/buildscripts/phing/classes/phing/BuildEvent.php @@ -0,0 +1,198 @@ +. + */ + +require_once 'phing/system/lang/EventObject.php'; + +/** + * Encapsulates a build specific event. + * + *

We have three sources of events all handled by this class: + * + *

+ * + *

Events are all fired from the project class by creating an event object + * using this class and passing it to the listeners. + * + * @author Andreas Aderhold + * @author Hans Lellelid + * @version $Id$ + * @package phing + */ +class BuildEvent extends EventObject { + + /** + * A reference to the project + * @var Project + */ + protected $project; + + /** + * A reference to the target + * @var Target + */ + protected $target; + + /** + * A reference to the task + * + * @var Task + */ + protected $task; + + /** + * The message of this event, if the event is a message + * @var string + */ + protected $message = null; + + /** + * The priority of the message + * + * @var string + * @see $message + */ + protected $priority = Project::MSG_VERBOSE; + + /** + * The execption that caused the event, if any + * + * @var object + */ + protected $exception = null; + + /** + * Construct a BuildEvent for a project, task or target source event + * + * @param object project the project that emitted the event. + */ + public function __construct($source) { + parent::__construct($source); + if ($source instanceof Project) { + $this->project = $source; + $this->target = null; + $this->task = null; + } elseif ($source instanceof Target) { + $this->project = $source->getProject(); + $this->target = $source; + $this->task = null; + } elseif ($source instanceof Task) { + $this->project = $source->getProject(); + $this->target = $source->getOwningTarget(); + $this->task = $source; + } else { + throw new Exception("Can not construct BuildEvent, unknown source given."); + } + } + + /** + * Sets the message with details and the message priority for this event. + * + * @param string The string message of the event + * @param integer The priority this message should have + */ + public function setMessage($message, $priority) { + $this->message = (string) $message; + $this->priority = (int) $priority; + } + + /** + * Set the exception that was the cause of this event. + * + * @param Exception The exception that caused the event + */ + public function setException($exception) { + $this->exception = $exception; + } + + /** + * Returns the project instance that fired this event. + * + * The reference to the project instance is set by the constructor if this + * event was fired from the project class. + * + * @return Project The project instance that fired this event + */ + public function getProject() { + return $this->project; + } + + /** + * Returns the target instance that fired this event. + * + * The reference to the target instance is set by the constructor if this + * event was fired from the target class. + * + * @return Target The target that fired this event + */ + public function getTarget() { + return $this->target; + } + + /** + * Returns the target instance that fired this event. + * + * The reference to the task instance is set by the constructor if this + * event was fired within a task. + * + * @return Task The task that fired this event + */ + public function getTask() { + return $this->task; + } + + /** + * Returns the logging message. This field will only be set for + * "messageLogged" events. + * + * @return string The log message + */ + function getMessage() { + return $this->message; + } + + /** + * Returns the priority of the logging message. This field will only + * be set for "messageLogged" events. + * + * @return integer The message priority + */ + function getPriority() { + return $this->priority; + } + + /** + * Returns the exception that was thrown, if any. + * This field will only be set for "taskFinished", "targetFinished", and + * "buildFinished" events. + * + * @see BuildListener::taskFinished() + * @see BuildListener::targetFinished() + * @see BuildListener::buildFinished() + * @return Exception + */ + public function getException() { + return $this->exception; + } +} diff --git a/buildscripts/phing/classes/phing/BuildException.php b/buildscripts/phing/classes/phing/BuildException.php new file mode 100755 index 00000000..dda1a05a --- /dev/null +++ b/buildscripts/phing/classes/phing/BuildException.php @@ -0,0 +1,124 @@ +. + */ + +/** + * BuildException is for when things go wrong in a build execution. + * + * @author Andreas Aderhold + * @version $Id$ + * @package phing + */ +class BuildException extends Exception { + + /** + * Location in the xml file. + * @var Location + */ + protected $location; + + /** + * The nested "cause" exception. + * @var Exception + */ + protected $cause; + + /** + * Construct a BuildException. + * Supported signatures: + * throw new BuildException($causeExc); + * throw new BuildException($msg); + * throw new Buildexception($causeExc, $loc); + * throw new BuildException($msg, $causeExc); + * throw new BuildException($msg, $loc); + * throw new BuildException($msg, $causeExc, $loc); + * @param Exception|string $p1 + * @param Location|Exception|null $p2 + * @param Location|null $p3 + */ + public function __construct($p1, $p2 = null, $p3 = null) { + + $cause = null; + $loc = null; + $msg = ""; + + if ($p3 !== null) { + $cause = $p2; + $loc = $p3; + $msg = $p1; + } elseif ($p2 !== null) { + if ($p2 instanceof Exception) { + $cause = $p2; + $msg = $p1; + } elseif ($p2 instanceof Location) { + $loc = $p2; + if ($p1 instanceof Exception) { + $cause = $p1; + } else { + $msg = $p1; + } + } + } elseif ($p1 instanceof Exception) { + $cause = $p1; + } else { + $msg = $p1; + } + + parent::__construct($msg); + + if ($cause !== null) { + $this->cause = $cause; + $this->message .= " [wrapped: " . $cause->getMessage() ."]"; + } + + if ($loc !== null) { + $this->setLocation($loc); + } + } + + /** + * Gets the cause exception. + * + * @return Exception + */ + public function getCause() { + return $this->cause; + } + + /** + * Gets the location of error in XML file. + * + * @return Location + */ + public function getLocation() { + return $this->location; + } + + /** + * Sets the location of error in XML file. + * + * @param Location $loc + */ + public function setLocation(Location $loc) { + $this->location = $loc; + $this->message = $loc->toString() . ': ' . $this->message; + } + +} diff --git a/buildscripts/phing/classes/phing/BuildListener.php b/buildscripts/phing/classes/phing/BuildListener.php new file mode 100755 index 00000000..a4086aa3 --- /dev/null +++ b/buildscripts/phing/classes/phing/BuildListener.php @@ -0,0 +1,91 @@ +. + */ + +/** + * Interface for build listeners. + * + * Classes that implement a listener must extend this class and (faux)implement + * all methods that are decleard as dummies below. + * + * @author Andreas Aderhold + * @author Hans Lellelid + * @version $Id$ + * @see BuildEvent + * @see Project::addBuildListener() + * @package phing + */ +interface BuildListener { + + /** + * Fired before any targets are started. + * + * @param BuildEvent $event The BuildEvent + */ + public function buildStarted(BuildEvent $event); + + /** + * Fired after the last target has finished. + * + * @param BuildEvent $event The BuildEvent + * @see BuildEvent::getException() + */ + public function buildFinished(BuildEvent $event); + + /** + * Fired when a target is started. + * + * @param BuildEvent $event The BuildEvent + * @see BuildEvent::getTarget() + */ + public function targetStarted(BuildEvent $event); + + /** + * Fired when a target has finished. + * + * @param BuildEvent $event The BuildEvent + * @see BuildEvent#getException() + */ + public function targetFinished(BuildEvent $event); + + /** + * Fired when a task is started. + * + * @param BuildEvent $event The BuildEvent + * @see BuildEvent::getTask() + */ + public function taskStarted(BuildEvent $event); + + /** + * Fired when a task has finished. + * + * @param BuildEvent $event The BuildEvent + * @see BuildEvent::getException() + */ + public function taskFinished(BuildEvent $event); + + /** + * Fired whenever a message is logged. + * + * @param BuildEvent $event The BuildEvent + * @see BuildEvent::getMessage() + */ + public function messageLogged(BuildEvent $event); +} diff --git a/buildscripts/phing/classes/phing/BuildLogger.php b/buildscripts/phing/classes/phing/BuildLogger.php new file mode 100755 index 00000000..03458159 --- /dev/null +++ b/buildscripts/phing/classes/phing/BuildLogger.php @@ -0,0 +1,70 @@ +. + */ + +require_once 'phing/BuildListener.php'; + +/** + * Interface for build loggers. + * + * Build loggers are build listeners but with some additional functionality: + * - They can be configured with a log level (below which they will ignore messages) + * - They have error and output streams + * + * Classes that implement a listener must implement this interface. + * + * @author Hans Lellelid + * @version $Id$ + * @see BuildEvent + * @see Project::addBuildListener() + * @package phing + */ +interface BuildLogger extends BuildListener { + + /** + * Sets the min log level that this logger should respect. + * + * Messages below this level are ignored. + * + * Constants for the message levels are in Project.php. The order of + * the levels, from least to most verbose, is: + * - Project::MSG_ERR + * - Project::MSG_WARN + * - Project::MSG_INFO + * - Project::MSG_VERBOSE + * - Project::MSG_DEBUG + * + * @param int $level The log level integer (e.g. Project::MSG_VERBOSE, etc.). + */ + public function setMessageOutputLevel($level); + + /** + * Sets the standard output stream to use. + * @param OutputStream $output Configured output stream (e.g. STDOUT) for standard output. + */ + public function setOutputStream(OutputStream $output); + + /** + * Sets the output stream to use for errors. + * @param OutputStream $err Configured output stream (e.g. STDERR) for errors. + */ + public function setErrorStream(OutputStream $err); + +} diff --git a/buildscripts/phing/classes/phing/ConfigurationException.php b/buildscripts/phing/classes/phing/ConfigurationException.php new file mode 100755 index 00000000..81735e1b --- /dev/null +++ b/buildscripts/phing/classes/phing/ConfigurationException.php @@ -0,0 +1,85 @@ +. + */ + +/** + * ConfigurationException is thrown by Phing during the configuration and setup phase of the project. + * + * @author Hans Lellelid + * @version $Id$ + * @package phing + */ +class ConfigurationException extends Exception { + + /** + * Location in the xml file. + * @var Location + */ + protected $location; + + /** + * The nested "cause" exception. + * @var Exception + */ + protected $cause; + + /** + * Construct a ConfigurationException. + * Supported signatures: + * throw new BuildException($causeExc); + * throw new BuildException($msg); + * throw new BuildException($msg, $causeExc); + * @param Exception|string $p1 + * @param Exception|null $p2 + */ + public function __construct($p1, $p2 = null) { + + $cause = null; + $msg = ""; + + if ($p2 !== null) { + if ($p2 instanceof Exception) { + $cause = $p2; + $msg = $p1; + } + } elseif ($p1 instanceof Exception) { + $cause = $p1; + } else { + $msg = $p1; + } + + parent::__construct($msg); + + if ($cause !== null) { + $this->cause = $cause; + $this->message .= " [wrapped: " . $cause->getMessage() ."]"; + } + } + + /** + * Gets the cause exception. + * + * @return Exception + */ + public function getCause() { + return $this->cause; + } + +} diff --git a/buildscripts/phing/classes/phing/IntrospectionHelper.php b/buildscripts/phing/classes/phing/IntrospectionHelper.php new file mode 100755 index 00000000..8bcf2456 --- /dev/null +++ b/buildscripts/phing/classes/phing/IntrospectionHelper.php @@ -0,0 +1,575 @@ +. + */ + +include_once 'phing/types/Reference.php'; +include_once 'phing/types/Path.php'; +include_once 'phing/util/StringHelper.php'; + +/** + * Helper class that collects the methods that a task or nested element + * holds to set attributes, create nested elements or hold PCDATA + * elements. + * + *

+ * + * @author Andreas Aderhold + * @author Hans Lellelid + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id: a5f2dbd71bafd2c363026d911132c3b47bf44239 $ + * @package phing + */ +class IntrospectionHelper { + + + + /** + * Holds the attribute setter methods. + * + * @var array string[] + */ + private $attributeSetters = array(); + + /** + * Holds methods to create nested elements. + * + * @var array string[] + */ + private $nestedCreators = array(); + + /** + * Holds methods to store configured nested elements. + * + * @var array string[] + */ + private $nestedStorers = array(); + + /** + * Map from attribute names to nested types. + */ + private $nestedTypes = array(); + + /** + * New idea in phing: any class can register certain + * keys -- e.g. "task.current_file" -- which can be used in + * task attributes, if supported. In the build XML these + * are referred to like this: + * + * In the type/task a listener method must be defined: + * function setListeningReplace($slot) {} + * @var array string[] + */ + private $slotListeners = array(); + + /** + * The method to add PCDATA stuff. + * + * @var string Method name of the addText (redundant?) method, if class supports it :) + */ + private $methodAddText = null; + + /** + * The Class that's been introspected. + * + * @var object + * @access private + */ + private $bean; + + /** + * The cache of IntrospectionHelper classes instantiated by getHelper(). + * @var array IntrospectionHelpers[] + */ + private static $helpers = array(); + + /** + * Factory method for helper objects. + * + * @param string $class The class to create a Helper for + */ + public static function getHelper($class) { + if (!isset(self::$helpers[$class])) { + self::$helpers[$class] = new IntrospectionHelper($class); + } + return self::$helpers[$class]; + } + + /** + * This function constructs a new introspection helper for a specific class. + * + * This method loads all methods for the specified class and categorizes them + * as setters, creators, slot listeners, etc. This way, the setAttribue() doesn't + * need to perform any introspection -- either the requested attribute setter/creator + * exists or it does not & a BuildException is thrown. + * + * @param string $class The classname for this IH. + */ + public function __construct($class) { + + $this->bean = new ReflectionClass($class); + + //$methods = get_class_methods($bean); + foreach($this->bean->getMethods() as $method) { + + if ($method->isPublic()) { + + // We're going to keep case-insensitive method names + // for as long as we're allowed :) It makes it much + // easier to map XML attributes to PHP class method names. + $name = strtolower($method->getName()); + + // There are a few "reserved" names that might look like attribute setters + // but should actually just be skipped. (Note: this means you can't ever + // have an attribute named "location" or "tasktype" or a nested element named "task".) + if ($name === "setlocation" || $name === "settasktype" || $name === "addtask") { + continue; + } + + if ($name === "addtext") { + + $this->methodAddText = $method; + + } elseif (strpos($name, "setlistening") === 0) { + + // Phing supports something unique called "RegisterSlots" + // These are dynamic values that use a basic slot system so that + // classes can register to listen to specific slots, and the value + // will always be grabbed from the slot (and never set in the project + // component). This is useful for things like tracking the current + // file being processed by a filter (e.g. AppendTask sets an append.current_file + // slot, which can be ready by the XSLTParam type.) + + if (count($method->getParameters()) !== 1) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take exactly one parameter."); + } + + $this->slotListeners[$name] = $method; + + } elseif (strpos($name, "set") === 0) { + + // A standard attribute setter. + + if (count($method->getParameters()) !== 1) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take exactly one parameter."); + } + + $this->attributeSetters[$name] = $method; + + } elseif (strpos($name, "create") === 0) { + + if ($method->getNumberOfRequiredParameters() > 0) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() may not take any parameters."); + } + + // Because PHP doesn't support return types, we are going to do + // two things here to guess return type: + // 1) parse comments for an explicit value + // 2) if that fails, assume that the part of the method after "create" + // is the name of the return type (in many cases it is not) + + // This isn't super important -- i.e. we're not instantaiting classes + // based on this information. It's more just so that IntrospectionHelper + // can keep track of all the nested types -- and provide more helpful + // exception messages, etc. + + preg_match('/@return[\s]+([\w]+)/', $method->getDocComment(), $matches); + if (!empty($matches[1]) && class_exists($matches[1], false)) { + $this->nestedTypes[$name] = $matches[1]; + } else { + // assume that method createEquals() creates object of type "Equals" + // (that example would be false, of course) + $this->nestedTypes[$name] = $this->getPropertyName($name, "create"); + } + + $this->nestedCreators[$name] = $method; + + } elseif (strpos($name, "addconfigured") === 0) { + + // *must* use class hints if using addConfigured ... + + // 1 param only + $params = $method->getParameters(); + + if (count($params) < 1) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take at least one parameter."); + } + + if (count($params) > 1) { + $this->warn($method->getDeclaringClass()->getName()."::".$method->getName()."() takes more than one parameter. (IH only uses the first)"); + } + + $classname = null; + + if (($hint = $params[0]->getClass()) !== null) { + $classname = $hint->getName(); + } + + if ($classname === null) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() method MUST use a class hint to indicate the class type of parameter."); + } + + $this->nestedTypes[$name] = $classname; + + $this->nestedStorers[$name] = $method; + + } elseif (strpos($name, "add") === 0) { + + // *must* use class hints if using add ... + + // 1 param only + $params = $method->getParameters(); + if (count($params) < 1) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take at least one parameter."); + } + + if (count($params) > 1) { + $this->warn($method->getDeclaringClass()->getName()."::".$method->getName()."() takes more than one parameter. (IH only uses the first)"); + } + + $classname = null; + + if (($hint = $params[0]->getClass()) !== null) { + $classname = $hint->getName(); + } + + // we don't use the classname here, but we need to make sure it exists before + // we later try to instantiate a non-existant class + if ($classname === null) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() method MUST use a class hint to indicate the class type of parameter."); + } + + $this->nestedCreators[$name] = $method; + } + } // if $method->isPublic() + } // foreach + } + + + /** + * Sets the named attribute. + * @param Project $project + * @param string $element + * @param string $attributeName + * @param mixed $value + */ + public function setAttribute(Project $project, $element, $attributeName, &$value) { + + // we want to check whether the value we are setting looks like + // a slot-listener variable: %{task.current_file} + // + // slot-listener variables are not like properties, in that they cannot be mixed with + // other text values. The reason for this disparity is that properties are only + // set when first constructing objects from XML, whereas slot-listeners are always dynamic. + // + // This is made possible by PHP5 (objects automatically passed by reference) and PHP's loose + // typing. + + if (StringHelper::isSlotVar($value)) { + + $as = "setlistening" . strtolower($attributeName); + + if (!isset($this->slotListeners[$as])) { + $msg = $this->getElementName($project, $element) . " doesn't support a slot-listening '$attributeName' attribute."; + throw new BuildException($msg); + } + + $method = $this->slotListeners[$as]; + + $key = StringHelper::slotVar($value); + $value = Register::getSlot($key); // returns a RegisterSlot object which will hold current value of that register (accessible using getValue()) + + } else { + + // Traditional value options + + $as = "set".strtolower($attributeName); + + if (!isset($this->attributeSetters[$as])) { + $msg = $this->getElementName($project, $element) . " doesn't support the '$attributeName' attribute."; + throw new BuildException($msg); + } + + $method = $this->attributeSetters[$as]; + + if ($as == "setrefid") { + $value = new Reference($value); + } else { + // value is a string representation of a boolean type, + // convert it to primitive + if (StringHelper::isBoolean($value)) { + + $value = StringHelper::booleanValue($value); + } + + // does method expect a PhingFile object? if so, then + // pass a project-relative file. + $params = $method->getParameters(); + + $classname = null; + + if (($hint = $params[0]->getClass()) !== null) { + $classname = $hint->getName(); + } + + // there should only be one param; we'll just assume .... + if ($classname !== null) { + switch(strtolower($classname)) { + case "phingfile": + $value = $project->resolveFile($value); + break; + case "path": + $value = new Path($project, $value); + break; + case "reference": + $value = new Reference($value); + break; + // any other object params we want to support should go here ... + } + + } // if hint !== null + + } // if not setrefid + + } // if is slot-listener + + try { + $project->log(" -calling setter ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", Project::MSG_DEBUG); + $method->invoke($element, $value); + } catch(Exception $exc) { + throw new BuildException($exc); + } + + } + + /** + * Adds PCDATA areas. + * + * @param Project $project + * @param string $element + * @param string $text + */ + public function addText(Project $project, $element, $text) { + if ($this->methodAddText === null) { + $msg = $this->getElementName($project, $element)." doesn't support nested text data."; + throw new BuildException($msg); + } + try { + $method = $this->methodAddText; + $method->invoke($element, $text); + } catch (Exception $exc) { + throw new BuildException($exc); + } + } + + /** + * Creates a named nested element. + * + * Valid creators can be in the form createFoo() or addFoo(Bar). + * + * @param Project $project + * @param string $element + * @param string $elementName + * @return object Returns the nested element. + * @throws BuildException + */ + public function createElement(Project $project, $element, $elementName) { + + $addMethod = "add".strtolower($elementName); + $createMethod = "create".strtolower($elementName); + $nestedElement = null; + + if (isset($this->nestedCreators[$createMethod])) { + + $method = $this->nestedCreators[$createMethod]; + try { // try to invoke the creator method on object + $project->log(" -calling creator ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", Project::MSG_DEBUG); + $nestedElement = $method->invoke($element); + } catch (Exception $exc) { + throw new BuildException($exc); + } + + } elseif (isset($this->nestedCreators[$addMethod])) { + + $method = $this->nestedCreators[$addMethod]; + + // project components must use class hints to support the add methods + + try { // try to invoke the adder method on object + + $project->log(" -calling adder ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", Project::MSG_DEBUG); + // we've already assured that correct num of params + // exist and that method is using class hints + $params = $method->getParameters(); + + $classname = null; + + if (($hint = $params[0]->getClass()) !== null) { + $classname = $hint->getName(); + } + + // create a new instance of the object and add it via $addMethod + $nestedElement = new $classname(); + + $method->invoke($element, $nestedElement); + + } catch (Exception $exc) { + throw new BuildException($exc); + } + } else { + $msg = $this->getElementName($project, $element) . " doesn't support the '$elementName' creator/adder."; + throw new BuildException($msg); + } + + if ($nestedElement instanceof ProjectComponent) { + $nestedElement->setProject($project); + } + + return $nestedElement; + } + + /** + * Creates a named nested element. + * + * @param Project $project + * @param string $element + * @param string $child + * @param string|null $elementName + * @return void + * @throws BuildException + */ + public function storeElement($project, $element, $child, $elementName = null) { + + if ($elementName === null) { + return; + } + + $storer = "addconfigured".strtolower($elementName); + + if (isset($this->nestedStorers[$storer])) { + + $method = $this->nestedStorers[$storer]; + + try { + $project->log(" -calling storer ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", Project::MSG_DEBUG); + $method->invoke($element, $child); + } catch (Exception $exc) { + throw new BuildException($exc); + } + } + + } + + /** + * Does the introspected class support PCDATA? + * @return boolean + */ + public function supportsCharacters() { + return ($this->methodAddText !== null); + } + + /** + * Return all attribues supported by the introspected class. + * @return string[] + */ + public function getAttributes() { + $attribs = array(); + foreach (array_keys($this->attributeSetters) as $setter) { + $attribs[] =$this->getPropertyName($setter, "set"); + } + return $attribs; + } + + /** + * Return all nested elements supported by the introspected class. + * @return string[] + */ + public function getNestedElements() { + return $this->nestedTypes; + } + + /** + * Get the the name for an element. + * When possible the full classnam (phing.tasks.system.PropertyTask) will + * be returned. If not available (loaded in taskdefs or typedefs) then the + * XML element name will be returned. + * + * @param Project $project + * @param object $element The Task or type element. + * @return string Fully qualified class name of element when possible. + */ + public function getElementName(Project $project, $element) { + + $taskdefs = $project->getTaskDefinitions(); + $typedefs = $project->getDataTypeDefinitions(); + + // check if class of element is registered with project (tasks & types) + // most element types don't have a getTag() method + $elClass = get_class($element); + + if (!in_array('getTag', get_class_methods($elClass))) { + // loop through taskdefs and typesdefs and see if the class name + // matches (case-insensitive) any of the classes in there + foreach(array_merge($taskdefs, $typedefs) as $elName => $class) { + if (0 === strcasecmp($elClass, StringHelper::unqualify($class))) { + return $class; + } + } + return "$elClass (unknown)"; + } else { + // ->getTag() method does exist, so use it + $elName = $element->getTag(); + if (isset($taskdefs[$elName])) { + return $taskdefs[$elName]; + } elseif (isset($typedefs[$elName])) { + + return $typedefs[$elName]; + } else { + return "$elName (unknown)"; + } + } + } + + /** + * Extract the name of a property from a method name - subtracting a given prefix. + * + * @param string $methodName + * @param string $prefix + * @return string + */ + public function getPropertyName($methodName, $prefix) { + $start = strlen($prefix); + return strtolower(substr($methodName, $start)); + } + + /** + * Prints warning message to screen if -debug was used. + * @param string $msg + */ + public function warn($msg) { + if (Phing::getMsgOutputLevel() === Project::MSG_DEBUG) { + print("[IntrospectionHelper] " . $msg . "\n"); + } + } + +} diff --git a/buildscripts/phing/classes/phing/Phing.php b/buildscripts/phing/classes/phing/Phing.php new file mode 100755 index 00000000..d663ee42 --- /dev/null +++ b/buildscripts/phing/classes/phing/Phing.php @@ -0,0 +1,1414 @@ +. + */ + +require_once 'phing/Project.php'; +require_once 'phing/ProjectComponent.php'; +require_once 'phing/Target.php'; +require_once 'phing/Task.php'; + +include_once 'phing/BuildException.php'; +include_once 'phing/ConfigurationException.php'; +include_once 'phing/BuildEvent.php'; + +include_once 'phing/parser/Location.php'; +include_once 'phing/parser/ExpatParser.php'; +include_once 'phing/parser/AbstractHandler.php'; +include_once 'phing/parser/ProjectConfigurator.php'; +include_once 'phing/parser/RootHandler.php'; +include_once 'phing/parser/ProjectHandler.php'; +include_once 'phing/parser/TaskHandler.php'; +include_once 'phing/parser/TargetHandler.php'; +include_once 'phing/parser/DataTypeHandler.php'; +include_once 'phing/parser/NestedElementHandler.php'; + +include_once 'phing/system/util/Properties.php'; +include_once 'phing/util/StringHelper.php'; +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/system/io/OutputStream.php'; +include_once 'phing/system/io/FileOutputStream.php'; +include_once 'phing/system/io/FileReader.php'; +include_once 'phing/system/util/Register.php'; + +/** + * Entry point into Phing. This class handles the full lifecycle of a build -- from + * parsing & handling commandline arguments to assembling the project to shutting down + * and cleaning up in the end. + * + * If you are invoking Phing from an external application, this is still + * the class to use. Your applicaiton can invoke the start() method, passing + * any commandline arguments or additional properties. + * + * @author Andreas Aderhold + * @author Hans Lellelid + * @version $Id: 1ad418f51ac07c9afaadf1c1f4befd5535f5b390 $ + * @package phing + */ +class Phing { + + /** The default build file name */ + const DEFAULT_BUILD_FILENAME = "build.xml"; + + /** Our current message output status. Follows Project::MSG_XXX */ + private static $msgOutputLevel = Project::MSG_INFO; + + /** PhingFile that we are using for configuration */ + private $buildFile = null; + + /** The build targets */ + private $targets = array(); + + /** + * Set of properties that are passed in from commandline or invoking code. + * @var Properties + */ + private static $definedProps; + + /** Names of classes to add as listeners to project */ + private $listeners = array(); + + private $loggerClassname = null; + + /** The class to handle input (can be only one). */ + private $inputHandlerClassname; + + /** Indicates if this phing should be run */ + private $readyToRun = false; + + /** Indicates we should only parse and display the project help information */ + private $projectHelp = false; + + /** Used by utility function getResourcePath() */ + private static $importPaths; + + /** System-wide static properties (moved from System) */ + private static $properties = array(); + + /** Static system timer. */ + private static $timer; + + /** The current Project */ + private static $currentProject; + + /** Whether to capture PHP errors to buffer. */ + private static $phpErrorCapture = false; + + /** Array of captured PHP errors */ + private static $capturedPhpErrors = array(); + + /** + * @var OUtputStream Stream for standard output. + */ + private static $out; + + /** + * @var OutputStream Stream for error output. + */ + private static $err; + + /** + * @var boolean Whether we are using a logfile. + */ + private static $isLogFileUsed = false; + + /** + * Array to hold original ini settings that Phing changes (and needs + * to restore in restoreIni() method). + * + * @var array Struct of array(setting-name => setting-value) + * @see restoreIni() + */ + private static $origIniSettings = array(); + + /** + * Entry point allowing for more options from other front ends. + * + * This method encapsulates the complete build lifecycle. + * + * @param array $args The commandline args passed to phing shell script. + * @param array $additionalUserProperties Any additional properties to be passed to Phing (alternative front-end might implement this). + * These additional properties will be available using the getDefinedProperty() method and will + * be added to the project's "user" properties + * @see execute() + * @see runBuild() + * @throws Exception - if there is an error during build + */ + public static function start($args, array $additionalUserProperties = null) { + + try { + $m = new Phing(); + $m->execute($args); + } catch (Exception $exc) { + self::handleLogfile(); + throw $exc; + } + + if ($additionalUserProperties !== null) { + foreach($additionalUserProperties as $key => $value) { + $m->setDefinedProperty($key, $value); + } + } + + try { + $m->runBuild(); + } catch(Exception $exc) { + self::handleLogfile(); + throw $exc; + } + + // everything fine, shutdown + self::handleLogfile(); + } + + /** + * Prints the message of the Exception if it's not null. + * @param Exception $t + */ + public static function printMessage(Exception $t) { + if (self::$err === null) { // Make sure our error output is initialized + self::initializeOutputStreams(); + } + if (self::getMsgOutputLevel() >= Project::MSG_VERBOSE) { + self::$err->write($t->__toString() . PHP_EOL); + } else { + self::$err->write($t->getMessage() . PHP_EOL); + } + } + + /** + * Sets the stdout and stderr streams if they are not already set. + */ + private static function initializeOutputStreams() { + if (self::$out === null) { + self::$out = new OutputStream(fopen("php://stdout", "w")); + } + if (self::$err === null) { + self::$err = new OutputStream(fopen("php://stderr", "w")); + } + } + + /** + * Sets the stream to use for standard (non-error) output. + * @param OutputStream $stream The stream to use for standard output. + */ + public static function setOutputStream(OutputStream $stream) { + self::$out = $stream; + } + + /** + * Gets the stream to use for standard (non-error) output. + * @return OutputStream + */ + public static function getOutputStream() { + return self::$out; + } + + /** + * Sets the stream to use for error output. + * @param OutputStream $stream The stream to use for error output. + */ + public static function setErrorStream(OutputStream $stream) { + self::$err = $stream; + } + + /** + * Gets the stream to use for error output. + * @return OutputStream + */ + public static function getErrorStream() { + return self::$err; + } + + /** + * Close logfiles, if we have been writing to them. + * + * @since Phing 2.3.0 + */ + private static function handleLogfile() { + if (self::$isLogFileUsed) { + self::$err->close(); + self::$out->close(); + } + } + + /** + * Making output level a static property so that this property + * can be accessed by other parts of the system, enabling + * us to display more information -- e.g. backtraces -- for "debug" level. + * @return int + */ + public static function getMsgOutputLevel() { + return self::$msgOutputLevel; + } + + /** + * 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 array $args Command line args. + * @return void + */ + public static function fire($args) { + self::start($args, null); + } + + /** + * Setup/initialize Phing environment from commandline args. + * @param array $args commandline args passed to phing shell. + * @return void + */ + public function execute($args) { + + self::$definedProps = new Properties(); + $this->searchForThis = null; + + // 1) First handle any options which should always + // Note: The order in which these are executed is important (if multiple of these options are specified) + + if (in_array('-help', $args) || in_array('-h', $args)) { + $this->printUsage(); + return; + } + + if (in_array('-version', $args) || in_array('-v', $args)) { + $this->printVersion(); + return; + } + + // 2) Next pull out stand-alone args. + // Note: The order in which these are executed is important (if multiple of these options are specified) + + if (false !== ($key = array_search('-quiet', $args, true)) || false !== ($key = array_search('-q', $args, true))) { + self::$msgOutputLevel = Project::MSG_WARN; + unset($args[$key]); + } + + if (false !== ($key = array_search('-verbose', $args, true))) { + self::$msgOutputLevel = Project::MSG_VERBOSE; + unset($args[$key]); + } + + if (false !== ($key = array_search('-debug', $args, true))) { + self::$msgOutputLevel = Project::MSG_DEBUG; + unset($args[$key]); + } + + // 3) Finally, cycle through to parse remaining args + // + $keys = array_keys($args); // Use keys and iterate to max(keys) since there may be some gaps + $max = $keys ? max($keys) : -1; + for($i=0; $i <= $max; $i++) { + + if (!array_key_exists($i, $args)) { + // skip this argument, since it must have been removed above. + continue; + } + + $arg = $args[$i]; + + if ($arg == "-logfile") { + try { + // see: http://phing.info/trac/ticket/65 + if (!isset($args[$i+1])) { + $msg = "You must specify a log file when using the -logfile argument\n"; + throw new ConfigurationException($msg); + } else { + $logFile = new PhingFile($args[++$i]); + $out = new FileOutputStream($logFile); // overwrite + self::setOutputStream($out); + self::setErrorStream($out); + self::$isLogFileUsed = true; + } + } catch (IOException $ioe) { + $msg = "Cannot write on the specified log file. Make sure the path exists and you have write permissions."; + throw new ConfigurationException($msg, $ioe); + } + } elseif ($arg == "-buildfile" || $arg == "-file" || $arg == "-f") { + if (!isset($args[$i+1])) { + $msg = "You must specify a buildfile when using the -buildfile argument."; + throw new ConfigurationException($msg); + } else { + $this->buildFile = new PhingFile($args[++$i]); + } + } elseif ($arg == "-listener") { + if (!isset($args[$i+1])) { + $msg = "You must specify a listener class when using the -listener argument"; + throw new ConfigurationException($msg); + } else { + $this->listeners[] = $args[++$i]; + } + } elseif (StringHelper::startsWith("-D", $arg)) { + $name = substr($arg, 2); + $value = null; + $posEq = strpos($name, "="); + if ($posEq !== false) { + $value = substr($name, $posEq+1); + $name = substr($name, 0, $posEq); + } elseif ($i < count($args)-1 && !StringHelper::startsWith("-D", $args[$i + 1])) { + $value = $args[++$i]; + } + self::$definedProps->setProperty($name, $value); + } elseif ($arg == "-logger") { + if (!isset($args[$i+1])) { + $msg = "You must specify a classname when using the -logger argument"; + throw new ConfigurationException($msg); + } else { + $this->loggerClassname = $args[++$i]; + } + } elseif ($arg == "-inputhandler") { + if ($this->inputHandlerClassname !== null) { + throw new ConfigurationException("Only one input handler class may be specified."); + } + if (!isset($args[$i+1])) { + $msg = "You must specify a classname when using the -inputhandler argument"; + throw new ConfigurationException($msg); + } else { + $this->inputHandlerClassname = $args[++$i]; + } + } elseif ($arg == "-propertyfile") { + if (!isset($args[$i+1])) { + $msg = "You must specify a filename when using the -propertyfile argument"; + throw new ConfigurationException($msg); + } else { + $p = new Properties(); + $p->load(new PhingFile($args[++$i])); + foreach ($p->getProperties() as $prop => $value) { + $this->setProperty($prop, $value); + } + } + } elseif ($arg == "-longtargets") { + self::$definedProps->setProperty('phing.showlongtargets', 1); + } elseif ($arg == "-projecthelp" || $arg == "-targets" || $arg == "-list" || $arg == "-l" || $arg == "-p") { + // set the flag to display the targets and quit + $this->projectHelp = true; + } elseif ($arg == "-find") { + // eat up next arg if present, default to build.xml + if ($i < count($args)-1) { + $this->searchForThis = $args[++$i]; + } else { + $this->searchForThis = self::DEFAULT_BUILD_FILENAME; + } + } elseif (substr($arg,0,1) == "-") { + // we don't have any more args + self::$err->write("Unknown argument: $arg" . PHP_EOL); + self::printUsage(); + return; + } else { + // if it's no other arg, it may be the target + array_push($this->targets, $arg); + } + } + + // if buildFile was not specified on the command line, + if ($this->buildFile === null) { + // but -find then search for it + if ($this->searchForThis !== null) { + $this->buildFile = $this->_findBuildFile(self::getProperty("user.dir"), $this->searchForThis); + } else { + $this->buildFile = new PhingFile(self::DEFAULT_BUILD_FILENAME); + } + } + // make sure buildfile exists + if (!$this->buildFile->exists()) { + throw new ConfigurationException("Buildfile: " . $this->buildFile->__toString() . " does not exist!"); + } + + // make sure it's not a directory + if ($this->buildFile->isDirectory()) { + throw new ConfigurationException("Buildfile: " . $this->buildFile->__toString() . " is a dir!"); + } + + $this->readyToRun = true; + } + + /** + * Helper to get the parent file for a given file. + * + * @param PhingFile $file + * @return PhingFile Parent file or null if none + */ + private function _getParentFile(PhingFile $file) { + $filename = $file->getAbsolutePath(); + $file = new PhingFile($filename); + $filename = $file->getParent(); + return ($filename === null) ? null : new PhingFile($filename); + } + + /** + * Search parent directories for the build file. + * + * 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 an exception + * is thrown. + * + * @param string $start Start file path. + * @param string $suffix Suffix filename to look for in parents. + * @return PhingFile A handle to the build file + * + * @throws BuildException Failed to locate a build file + */ + private function _findBuildFile($start, $suffix) { + $startf = new PhingFile($start); + $parent = new PhingFile($startf->getAbsolutePath()); + $file = new PhingFile($parent, $suffix); + + // check if the target file exists in the current directory + while (!$file->exists()) { + // change to parent directory + $parent = $this->_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) { + throw new ConfigurationException("Could not locate a build file!"); + } + // refresh our file handle + $file = new PhingFile($parent, $suffix); + } + return $file; + } + + /** + * Executes the build. + * @return void + */ + function runBuild() { + + if (!$this->readyToRun) { + return; + } + + $project = new Project(); + + self::setCurrentProject($project); + set_error_handler(array('Phing', 'handlePhpError')); + + $error = null; + + $this->addBuildListeners($project); + $this->addInputHandler($project); + + // set this right away, so that it can be used in logging. + $project->setUserProperty("phing.file", $this->buildFile->getAbsolutePath()); + + try { + $project->fireBuildStarted(); + $project->init(); + } catch (Exception $exc) { + $project->fireBuildFinished($exc); + throw $exc; + } + + $project->setUserProperty("phing.version", $this->getPhingVersion()); + + $e = self::$definedProps->keys(); + while (count($e)) { + $arg = (string) array_shift($e); + $value = (string) self::$definedProps->getProperty($arg); + $project->setUserProperty($arg, $value); + } + unset($e); + + $project->setUserProperty("phing.file", $this->buildFile->getAbsolutePath()); + + // first use the Configurator to create the project object + // from the given build file. + + try { + ProjectConfigurator::configureProject($project, $this->buildFile); + } catch (Exception $exc) { + $project->fireBuildFinished($exc); + restore_error_handler(); + self::unsetCurrentProject(); + throw $exc; + } + + // make sure that we have a target to execute + if (count($this->targets) === 0) { + $this->targets[] = $project->getDefaultTarget(); + } + + // make sure that minimum required phing version is satisfied + try { + $this->comparePhingVersion($project->getPhingVersion()); + } catch(Exception $exc) { + $project->fireBuildFinished($exc); + restore_error_handler(); + self::unsetCurrentProject(); + throw $exc; + } + + // execute targets if help param was not given + if (!$this->projectHelp) { + + try { + $project->executeTargets($this->targets); + } catch (Exception $exc) { + $project->fireBuildFinished($exc); + restore_error_handler(); + self::unsetCurrentProject(); + throw $exc; + } + } + // if help is requested print it + if ($this->projectHelp) { + try { + $this->printDescription($project); + $this->printTargets($project); + } catch (Exception $exc) { + $project->fireBuildFinished($exc); + restore_error_handler(); + self::unsetCurrentProject(); + throw $exc; + } + } + + // finally { + if (!$this->projectHelp) { + $project->fireBuildFinished(null); + } + + restore_error_handler(); + self::unsetCurrentProject(); + } + + private function comparePhingVersion($version) { + $current = strtolower(self::getPhingVersion()); + $current = trim(str_replace('phing', '', $current)); + + // make sure that version checks are not applied to trunk + if('dev' === $current) { + return 1; + } + + if(-1 == version_compare($current, $version)) { + throw new BuildException( + sprintf('Incompatible Phing version (%s). Version "%s" required.', $current, $version)); + } + } + + /** + * Bind any registered build listeners to this project. + * + * This means adding the logger and any build listeners that were specified + * with -listener arg. + * + * @param Project $project + * @return void + */ + private function addBuildListeners(Project $project) { + // Add the default listener + $project->addBuildListener($this->createLogger()); + + foreach($this->listeners as $listenerClassname) { + try { + $clz = Phing::import($listenerClassname); + } catch (Exception $x) { + $msg = "Unable to instantiate specified listener " + . "class " . $listenerClassname . " : " + . $e->getMessage(); + throw new ConfigurationException($msg); + } + + $listener = new $clz(); + + if ($listener instanceof StreamRequiredBuildLogger) { + throw new ConfigurationException("Unable to add " . $listenerClassname . " as a listener, since it requires explicit error/output streams. (You can specify it as a -logger.)"); + } + $project->addBuildListener($listener); + } + } + + /** + * Creates the InputHandler and adds it to the project. + * + * @param Project $project the project instance. + * + * @throws BuildException if a specified InputHandler + * class could not be loaded. + */ + private function addInputHandler(Project $project) { + if ($this->inputHandlerClassname === null) { + $handler = new DefaultInputHandler(); + } else { + try { + $clz = Phing::import($this->inputHandlerClassname); + $handler = new $clz(); + if ($project !== null && method_exists($handler, 'setProject')) { + $handler->setProject($project); + } + } catch (Exception $e) { + $msg = "Unable to instantiate specified input handler " + . "class " . $this->inputHandlerClassname . " : " + . $e->getMessage(); + throw new ConfigurationException($msg); + } + } + $project->setInputHandler($handler); + } + + /** + * Creates the default build logger for sending build events to the log. + * @return BuildLogger The created Logger + */ + private function createLogger() { + if ($this->loggerClassname !== null) { + self::import($this->loggerClassname); + // get class name part + $classname = self::import($this->loggerClassname); + $logger = new $classname; + if (!($logger instanceof BuildLogger)) { + throw new BuildException($classname . ' does not implement the BuildLogger interface.'); + } + } else { + require_once 'phing/listener/DefaultLogger.php'; + $logger = new DefaultLogger(); + } + $logger->setMessageOutputLevel(self::$msgOutputLevel); + $logger->setOutputStream(self::$out); + $logger->setErrorStream(self::$err); + return $logger; + } + + /** + * Sets the current Project + * @param Project $p + */ + public static function setCurrentProject($p) { + self::$currentProject = $p; + } + + /** + * Unsets the current Project + */ + public static function unsetCurrentProject() { + self::$currentProject = null; + } + + /** + * Gets the current Project. + * @return Project Current Project or NULL if none is set yet/still. + */ + public static function getCurrentProject() { + return self::$currentProject; + } + + /** + * A static convenience method to send a log to the current (last-setup) Project. + * If there is no currently-configured Project, then this will do nothing. + * @param string $message + * @param int $priority Project::MSG_INFO, etc. + */ + public static function log($message, $priority = Project::MSG_INFO) { + $p = self::getCurrentProject(); + if ($p) { + $p->log($message, $priority); + } + } + + /** + * Error handler for PHP errors encountered during the build. + * This uses the logging for the currently configured project. + */ + public static function handlePhpError($level, $message, $file, $line) { + + // don't want to print supressed errors + if (error_reporting() > 0) { + + if (self::$phpErrorCapture) { + + self::$capturedPhpErrors[] = array('message' => $message, 'level' => $level, 'line' => $line, 'file' => $file); + + } else { + + $message = '[PHP Error] ' . $message; + $message .= ' [line ' . $line . ' of ' . $file . ']'; + + switch ($level) { + case 16384: // E_USER_DEPRECATED + case 8192: // E_DEPRECATED + case E_STRICT: + case E_NOTICE: + case E_USER_NOTICE: + self::log($message, Project::MSG_VERBOSE); + break; + case E_WARNING: + case E_USER_WARNING: + self::log($message, Project::MSG_WARN); + break; + case E_ERROR: + case E_USER_ERROR: + default: + self::log($message, Project::MSG_ERR); + + } // switch + + } // if phpErrorCapture + + } // if not @ + + } + + /** + * Begins capturing PHP errors to a buffer. + * While errors are being captured, they are not logged. + */ + public static function startPhpErrorCapture() { + self::$phpErrorCapture = true; + self::$capturedPhpErrors = array(); + } + + /** + * Stops capturing PHP errors to a buffer. + * The errors will once again be logged after calling this method. + */ + public static function stopPhpErrorCapture() { + self::$phpErrorCapture = false; + } + + /** + * Clears the captured errors without affecting the starting/stopping of the capture. + */ + public static function clearCapturedPhpErrors() { + self::$capturedPhpErrors = array(); + } + + /** + * Gets any PHP errors that were captured to buffer. + * @return array array('message' => message, 'line' => line number, 'file' => file name, 'level' => error level) + */ + public static function getCapturedPhpErrors() { + return self::$capturedPhpErrors; + } + + /** Prints the usage of how to use this class */ + public static function printUsage() { + + $msg = ""; + $msg .= "phing [options] [target [target2 [target3] ...]]" . PHP_EOL; + $msg .= "Options: " . PHP_EOL; + $msg .= " -h -help print this message" . PHP_EOL; + $msg .= " -l -list list available targets in this project" . PHP_EOL; + $msg .= " -v -version print the version information and exit" . PHP_EOL; + $msg .= " -q -quiet be extra quiet" . PHP_EOL; + $msg .= " -verbose be extra verbose" . PHP_EOL; + $msg .= " -debug print debugging information" . PHP_EOL; + $msg .= " -longtargets show target descriptions during build" . PHP_EOL; + $msg .= " -logfile use given file for log" . PHP_EOL; + $msg .= " -logger the class which is to perform logging" . PHP_EOL; + $msg .= " -f -buildfile use given buildfile" . PHP_EOL; + $msg .= " -D= use value for given property" . PHP_EOL; + $msg .= " -propertyfile load all properties from file" . PHP_EOL; + $msg .= " -find search for buildfile towards the root of the" . PHP_EOL; + $msg .= " filesystem and use it" . PHP_EOL; + $msg .= " -inputhandler the class to use to handle user input" . PHP_EOL; + //$msg .= " -recursive search for buildfile downwards and use it" . PHP_EOL; + $msg .= PHP_EOL; + $msg .= "Report bugs to ".PHP_EOL; + self::$err->write($msg); + } + + /** + * Prints the current Phing version. + */ + public static function printVersion() { + self::$out->write(self::getPhingVersion().PHP_EOL); + } + + /** + * Gets the current Phing version based on VERSION.TXT file. + * @return string + * @throws BuildException - if unable to find version file. + */ + public static function getPhingVersion() { + $versionPath = self::getResourcePath("phing/etc/VERSION.TXT"); + if ($versionPath === null) { + $versionPath = self::getResourcePath("etc/VERSION.TXT"); + } + if ($versionPath === null) { + throw new ConfigurationException("No VERSION.TXT file found; try setting phing.home environment variable."); + } + try { // try to read file + $buffer = null; + $file = new PhingFile($versionPath); + $reader = new FileReader($file); + $reader->readInto($buffer); + $buffer = trim($buffer); + //$buffer = "PHING version 1.0, Released 2002-??-??"; + $phingVersion = $buffer; + } catch (IOException $iox) { + throw new ConfigurationException("Can't read version information file"); + } + return $phingVersion; + } + + /** + * Print the project description, if any + */ + public static function printDescription(Project $project) { + if ($project->getDescription() !== null) { + self::$out->write($project->getDescription() . PHP_EOL); + } + } + + /** Print out a list of all targets in the current buildfile */ + function printTargets($project) { + // find the target with the longest name + $maxLength = 0; + $targets = $project->getTargets(); + $targetNames = array_keys($targets); + $targetName = null; + $targetDescription = null; + $currentTarget = null; + + // split the targets in top-level and sub-targets depending + // on the presence of a description + + $subNames = array(); + $topNameDescMap = array(); + + foreach($targets as $currentTarget) { + $targetName = $currentTarget->getName(); + $targetDescription = $currentTarget->getDescription(); + if ($currentTarget->isHidden()) { + continue; + } + + // subtargets are targets w/o descriptions + if ($targetDescription === null) { + $subNames[] = $targetName; + } else { + // topNames and topDescriptions are handled later + // here we store in hash map (for sorting purposes) + $topNameDescMap[$targetName] = $targetDescription; + if (strlen($targetName) > $maxLength) { + $maxLength = strlen($targetName); + } + } + } + + // Sort the arrays + sort($subNames); // sort array values, resetting keys (which are numeric) + ksort($topNameDescMap); // sort the keys (targetName) keeping key=>val associations + + $topNames = array_keys($topNameDescMap); + $topDescriptions = array_values($topNameDescMap); + + $defaultTarget = $project->getDefaultTarget(); + + if ($defaultTarget !== null && $defaultTarget !== "") { + $defaultName = array(); + $defaultDesc = array(); + $defaultName[] = $defaultTarget; + + $indexOfDefDesc = array_search($defaultTarget, $topNames, true); + if ($indexOfDefDesc !== false && $indexOfDefDesc >= 0) { + $defaultDesc = array(); + $defaultDesc[] = $topDescriptions[$indexOfDefDesc]; + } + + $this->_printTargets($defaultName, $defaultDesc, "Default target:", $maxLength); + + } + $this->_printTargets($topNames, $topDescriptions, "Main targets:", $maxLength); + $this->_printTargets($subNames, null, "Subtargets:", 0); + } + + /** + * Writes a formatted list of target names with an optional description. + * + * @param array $names The names to be printed. + * Must not be null. + * @param array $descriptions The associated target descriptions. + * May be null, in which case + * no descriptions are displayed. + * If non-null, this should have + * as many elements as names. + * @param string $heading The heading to display. + * Should not be null. + * @param int $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 + * are shorter than this). + */ + private function _printTargets($names, $descriptions, $heading, $maxlen) { + + $spaces = ' '; + while (strlen($spaces) < $maxlen) { + $spaces .= $spaces; + } + $msg = ""; + $msg .= $heading . PHP_EOL; + $msg .= str_repeat("-",79) . PHP_EOL; + + $total = count($names); + for($i=0; $i < $total; $i++) { + $msg .= " "; + $msg .= $names[$i]; + if (!empty($descriptions)) { + $msg .= substr($spaces, 0, $maxlen - strlen($names[$i]) + 2); + $msg .= $descriptions[$i]; + } + $msg .= PHP_EOL; + } + if ($total > 0) { + self::$out->write($msg . PHP_EOL); + } + } + + /** + * Import a dot-path notation class path. + * @param string $dotPath + * @param mixed $classpath String or object supporting __toString() + * @return string The unqualified classname (which can be instantiated). + * @throws BuildException - if cannot find the specified file + */ + public static function import($dotPath, $classpath = null) { + + /// check if this is a PEAR-style path (@link http://pear.php.net/manual/en/standards.naming.php) + if (strpos($dotPath, '.') === false && strpos($dotPath, '_') !== false) { + $classname = $dotPath; + $dotPath = str_replace('_', '.', $dotPath); + } else { + $classname = StringHelper::unqualify($dotPath); + } + + // first check to see that the class specified hasn't already been included. + // (this also handles case where this method is called w/ a classname rather than dotpath) + if (class_exists($classname)) { + return $classname; + } + + $dotClassname = basename($dotPath); + $dotClassnamePos = strlen($dotPath) - strlen($dotClassname); + + // 1- temporarily replace escaped '.' with another illegal char (#) + $tmp = str_replace('\.', '##', $dotClassname); + // 2- swap out the remaining '.' with DIR_SEP + $tmp = strtr($tmp, '.', DIRECTORY_SEPARATOR); + // 3- swap back the escaped '.' + $tmp = str_replace('##', '.', $tmp); + + $classFile = $tmp . ".php"; + + $path = substr_replace($dotPath, $classFile, $dotClassnamePos); + + Phing::__import($path, $classpath); + + return $classname; + } + + /** + * Import a PHP file + * @param string $path Path to the PHP file + * @param mixed $classpath String or object supporting __toString() + * @throws BuildException - if cannot find the specified file + */ + public static function __import($path, $classpath = null) { + + if ($classpath) { + + // Apparently casting to (string) no longer invokes __toString() automatically. + if (is_object($classpath)) { + $classpath = $classpath->__toString(); + } + + // classpaths are currently additive, but we also don't want to just + // indiscriminantly prepand/append stuff to the include_path. This means + // we need to parse current incldue_path, and prepend any + // specified classpath locations that are not already in the include_path. + // + // NOTE: the reason why we do it this way instead of just changing include_path + // and then changing it back, is that in many cases applications (e.g. Propel) will + // include/require class files from within method calls. This means that not all + // necessary files will be included in this import() call, and hence we can't + // change the include_path back without breaking those apps. While this method could + // be more expensive than switching & switching back (not sure, but maybe), it makes it + // possible to write far less expensive run-time applications (e.g. using Propel), which is + // really where speed matters more. + + $curr_parts = explode(PATH_SEPARATOR, get_include_path()); + $add_parts = explode(PATH_SEPARATOR, $classpath); + $new_parts = array_diff($add_parts, $curr_parts); + if ($new_parts) { + set_include_path(implode(PATH_SEPARATOR, array_merge($new_parts, $curr_parts))); + } + } + + $ret = include_once($path); + + if ($ret === false) { + $msg = "Error importing $path"; + if (self::getMsgOutputLevel() >= Project::MSG_DEBUG) { + $x = new Exception("for-path-trace-only"); + $msg .= $x->getTraceAsString(); + } + throw new ConfigurationException($msg); + } + } + + /** + * Looks on include path for specified file. + * @return string File found (null if no file found). + */ + public static function getResourcePath($path) { + + if (self::$importPaths === null) { + $paths = get_include_path(); + self::$importPaths = explode(PATH_SEPARATOR, ini_get("include_path")); + } + + $path = str_replace('\\', DIRECTORY_SEPARATOR, $path); + $path = str_replace('/', DIRECTORY_SEPARATOR, $path); + + foreach (self::$importPaths as $prefix) { + $testPath = $prefix . DIRECTORY_SEPARATOR . $path; + if (file_exists($testPath)) { + return $testPath; + } + } + + // Check for the property phing.home + $homeDir = self::getProperty('phing.home'); + if ($homeDir) { + $testPath = $homeDir . DIRECTORY_SEPARATOR . $path; + if (file_exists($testPath)) { + return $testPath; + } + } + + // If we are using this via PEAR then check for the file in the data dir + // This is a bit of a hack, but works better than previous solution of assuming + // data_dir is on the include_path. + $dataDir = '@DATA-DIR@'; + if ($dataDir{0} != '@') { // if we're using PEAR then the @ DATA-DIR @ token will have been substituted. + if (!file_exists($dataDir)) { + self::log("The PEAR data_dir setting is incorrect: {$dataDir}.", Project::MSG_ERR); + self::log("Please edit using 'pear config-set data_dir ...' and re-install Phing.", Project::MSG_ERR); + return null; + } + + $testPath = $dataDir . DIRECTORY_SEPARATOR . $path; + if (file_exists($testPath)) { + return $testPath; + } + } else { + // We're not using PEAR, so do one additional check based on path of + // current file (Phing.php) + $maybeHomeDir = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..'); + $testPath = $maybeHomeDir . DIRECTORY_SEPARATOR . $path; + if (file_exists($testPath)) { + return $testPath; + } + } + + return null; + } + + // ------------------------------------------------------------------------------------------- + // System-wide methods (moved from System class, which had namespace conflicts w/ PEAR System) + // ------------------------------------------------------------------------------------------- + + /** + * Set System constants which can be retrieved by calling Phing::getProperty($propName). + * @return void + */ + private static function setSystemConstants() { + + /* + * PHP_OS returns on + * WindowsNT4.0sp6 => WINNT + * Windows2000 => WINNT + * Windows ME => WIN32 + * Windows 98SE => WIN32 + * FreeBSD 4.5p7 => FreeBSD + * Redhat Linux => Linux + * Mac OS X => Darwin + */ + self::setProperty('host.os', PHP_OS); + + // this is used by some tasks too + self::setProperty('os.name', PHP_OS); + + // it's still possible this won't be defined, + // e.g. if Phing is being included in another app w/o + // using the phing.php script. + if (!defined('PHP_CLASSPATH')) { + define('PHP_CLASSPATH', get_include_path()); + } + + self::setProperty('php.classpath', PHP_CLASSPATH); + + // try to determine the host filesystem and set system property + // used by Fileself::getFileSystem to instantiate the correct + // abstraction layer + + switch (strtoupper(PHP_OS)) { + case 'WINNT': + self::setProperty('host.fstype', 'WINNT'); + break; + case 'WIN32': + self::setProperty('host.fstype', 'WIN32'); + break; + default: + self::setProperty('host.fstype', 'UNIX'); + break; + } + + self::setProperty('php.interpreter', getenv('PHP_COMMAND')); + self::setProperty('line.separator', PHP_EOL); + self::setProperty('php.version', PHP_VERSION); + self::setProperty('user.home', getenv('HOME')); + self::setProperty('application.startdir', getcwd()); + self::setProperty('phing.startTime', gmdate('D, d M Y H:i:s', time()) . ' GMT'); + + // try to detect machine dependent information + $sysInfo = array(); + if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' && function_exists("posix_uname")) { + $sysInfo = posix_uname(); + } else { + $sysInfo['nodename'] = php_uname('n'); + $sysInfo['machine']= php_uname('m') ; + //this is a not so ideal substition, but maybe better than nothing + $sysInfo['domain'] = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : "unknown"; + $sysInfo['release'] = php_uname('r'); + $sysInfo['version'] = php_uname('v'); + } + + + self::setProperty("host.name", isset($sysInfo['nodename']) ? $sysInfo['nodename'] : "unknown"); + self::setProperty("host.arch", isset($sysInfo['machine']) ? $sysInfo['machine'] : "unknown"); + self::setProperty("host.domain",isset($sysInfo['domain']) ? $sysInfo['domain'] : "unknown"); + self::setProperty("host.os.release", isset($sysInfo['release']) ? $sysInfo['release'] : "unknown"); + self::setProperty("host.os.version", isset($sysInfo['version']) ? $sysInfo['version'] : "unknown"); + unset($sysInfo); + } + + /** + * This gets a property that was set via command line or otherwise passed into Phing. + * "Defined" in this case means "externally defined". The reason this method exists is to + * provide a public means of accessing commandline properties for (e.g.) logger or listener + * scripts. E.g. to specify which logfile to use, PearLogger needs to be able to access + * the pear.log.name property. + * + * @param string $name + * @return string value of found property (or null, if none found). + */ + public static function getDefinedProperty($name) { + return self::$definedProps->getProperty($name); + } + + /** + * This sets a property that was set via command line or otherwise passed into Phing. + * + * @param string $name + * @return string value of found property (or null, if none found). + */ + public static function setDefinedProperty($name, $value) { + return self::$definedProps->setProperty($name, $value); + } + + /** + * Returns property value for a System property. + * System properties are "global" properties like application.startdir, + * and user.dir. Many of these correspond to similar properties in Java + * or Ant. + * + * @param string $paramName + * @return string Value of found property (or null, if none found). + */ + public static function getProperty($propName) { + + // some properties are detemined on each access + // some are cached, see below + + // default is the cached value: + $val = isset(self::$properties[$propName]) ? self::$properties[$propName] : null; + + // special exceptions + switch($propName) { + case 'user.dir': + $val = getcwd(); + break; + } + + return $val; + } + + /** Retuns reference to all properties*/ + public static function &getProperties() { + return self::$properties; + } + + public static function setProperty($propName, $propValue) { + $propName = (string) $propName; + $oldValue = self::getProperty($propName); + self::$properties[$propName] = $propValue; + return $oldValue; + } + + public static function currentTimeMillis() { + list($usec, $sec) = explode(" ",microtime()); + return ((float)$usec + (float)$sec); + } + + /** + * Sets the include path to PHP_CLASSPATH constant (if this has been defined). + * @return void + * @throws ConfigurationException - if the include_path could not be set (for some bizarre reason) + */ + private static function setIncludePaths() { + if (defined('PHP_CLASSPATH')) { + $result = set_include_path(PHP_CLASSPATH); + if ($result === false) { + throw new ConfigurationException("Could not set PHP include_path."); + } + self::$origIniSettings['include_path'] = $result; // save original value for setting back later + } + } + + /** + * Converts shorthand notation values as returned by ini_get() + * @see http://www.php.net/ini_get + * @param string $val + */ + private static function convertShorthand($val) + { + $val = trim($val); + $last = strtolower($val[strlen($val) - 1]); + + switch($last) { + // The 'G' modifier is available since PHP 5.1.0 + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + } + + return $val; + } + + /** + * Sets PHP INI values that Phing needs. + * @return void + */ + private static function setIni() { + + self::$origIniSettings['error_reporting'] = error_reporting(E_ALL); + + // We won't bother storing original max_execution_time, since 1) the value in + // php.ini may be wrong (and there's no way to get the current value) and + // 2) it would mean something very strange to set it to a value less than time script + // has already been running, which would be the likely change. + + set_time_limit(0); + + self::$origIniSettings['magic_quotes_gpc'] = ini_set('magic_quotes_gpc', 'off'); + self::$origIniSettings['short_open_tag'] = ini_set('short_open_tag', 'off'); + self::$origIniSettings['default_charset'] = ini_set('default_charset', 'iso-8859-1'); + self::$origIniSettings['register_globals'] = ini_set('register_globals', 'off'); + self::$origIniSettings['allow_call_time_pass_reference'] = ini_set('allow_call_time_pass_reference', 'on'); + self::$origIniSettings['track_errors'] = ini_set('track_errors', 1); + + $mem_limit = (int) self::convertShorthand(ini_get('memory_limit')); + if ($mem_limit < (32 * 1024 * 1024) && $mem_limit > -1) { + // We do *not* need to save the original value here, since we don't plan to restore + // this after shutdown (we don't trust the effectiveness of PHP's garbage collection). + ini_set('memory_limit', '32M'); // nore: this may need to be higher for many projects + } + } + + /** + * Restores [most] PHP INI values to their pre-Phing state. + * + * Currently the following settings are not restored: + * - max_execution_time (because getting current time limit is not possible) + * - memory_limit (which may have been increased by Phing) + * + * @return void + */ + private static function restoreIni() + { + foreach(self::$origIniSettings as $settingName => $settingValue) { + switch($settingName) { + case 'error_reporting': + error_reporting($settingValue); + break; + default: + ini_set($settingName, $settingValue); + } + } + } + + /** + * Returns reference to Timer object. + * @return Timer + */ + public static function getTimer() { + if (self::$timer === null) { + include_once 'phing/system/util/Timer.php'; + self::$timer= new Timer(); + } + return self::$timer; + } + + /** + * Start up Phing. + * Sets up the Phing environment but does not initiate the build process. + * @return void + * @throws Exception - If the Phing environment cannot be initialized. + */ + public static function startup() { + + // setup STDOUT and STDERR defaults + self::initializeOutputStreams(); + + // some init stuff + self::getTimer()->start(); + + self::setSystemConstants(); + self::setIncludePaths(); + self::setIni(); + } + + /** + * Halts the system. + * @deprecated This method is deprecated and is no longer called by Phing internally. Any + * normal shutdown routines are handled by the shutdown() method. + * @see shutdown() + */ + public static function halt() { + self::shutdown(); + } + + /** + * Performs any shutdown routines, such as stopping timers. + * @return void + */ + public static function shutdown() { + self::restoreIni(); + self::getTimer()->stop(); + } + +} diff --git a/buildscripts/phing/classes/phing/Project.php b/buildscripts/phing/classes/phing/Project.php new file mode 100755 index 00000000..8e662543 --- /dev/null +++ b/buildscripts/phing/classes/phing/Project.php @@ -0,0 +1,1050 @@ +. + */ + +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/util/FileUtils.php'; +include_once 'phing/TaskAdapter.php'; +include_once 'phing/util/StringHelper.php'; +include_once 'phing/BuildEvent.php'; +include_once 'phing/input/DefaultInputHandler.php'; + +/** + * The Phing project class. Represents a completely configured Phing project. + * The class defines the project and all tasks/targets. It also contains + * methods to start a build as well as some properties and FileSystem + * abstraction. + * + * @author Andreas Aderhold + * @author Hans Lellelid + * @version $Id: 7e67218e8e616860f9c746f9ab600f523089ea2e $ + * @package phing + */ +class Project { + + // Logging level constants. + const MSG_DEBUG = 4; + const MSG_VERBOSE = 3; + const MSG_INFO = 2; + const MSG_WARN = 1; + const MSG_ERR = 0; + + /** contains the targets */ + private $targets = array(); + /** global filterset (future use) */ + private $globalFilterSet = array(); + /** all globals filters (future use) */ + private $globalFilters = array(); + + /** Project properties map (usually String to String). */ + private $properties = array(); + + /** + * 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. + * Mapping is String to String. + */ + private $userProperties = array(); + + /** + * 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. + * Mapping is String to String. + */ + private $inheritedProperties = array(); + + /** task definitions for this project*/ + private $taskdefs = array(); + + /** type definitions for this project */ + private $typedefs = array(); + + /** holds ref names and a reference to the referred object*/ + private $references = array(); + + /** The InputHandler being used by this project. */ + private $inputHandler; + + /* -- properties that come in via xml attributes -- */ + + /** basedir (PhingFile object) */ + private $basedir; + + /** the default target name */ + private $defaultTarget = 'all'; + + /** project name (required) */ + private $name; + + /** project description */ + private $description; + + /** require phing version */ + private $phingVersion; + + /** a FileUtils object */ + private $fileUtils; + + /** Build listeneers */ + private $listeners = array(); + + /** + * Constructor, sets any default vars. + */ + public function __construct() { + $this->fileUtils = new FileUtils(); + $this->inputHandler = new DefaultInputHandler(); + } + + /** + * Sets the input handler + * @param InputHandler $handler + */ + public function setInputHandler(InputHandler $handler) { + $this->inputHandler = $handler; + } + + /** + * Retrieves the current input handler. + * @return InputHandler + */ + public function getInputHandler() { + return $this->inputHandler; + } + + /** inits the project, called from main app */ + public function init() { + // set builtin properties + $this->setSystemProperties(); + + // load default tasks + $taskdefs = Phing::getResourcePath("phing/tasks/defaults.properties"); + + try { // try to load taskdefs + $props = new Properties(); + $in = new PhingFile((string)$taskdefs); + + if ($in === null) { + throw new BuildException("Can't load default task list"); + } + $props->load($in); + + $enum = $props->propertyNames(); + foreach($enum as $key) { + $value = $props->getProperty($key); + $this->addTaskDefinition($key, $value); + } + } catch (IOException $ioe) { + throw new BuildException("Can't load default task list"); + } + + // load default tasks + $typedefs = Phing::getResourcePath("phing/types/defaults.properties"); + + try { // try to load typedefs + $props = new Properties(); + $in = new PhingFile((string)$typedefs); + if ($in === null) { + throw new BuildException("Can't load default datatype list"); + } + $props->load($in); + + $enum = $props->propertyNames(); + foreach($enum as $key) { + $value = $props->getProperty($key); + $this->addDataTypeDefinition($key, $value); + } + } catch(IOException $ioe) { + throw new BuildException("Can't load default datatype list"); + } + } + + /** returns the global filterset (future use) */ + public function getGlobalFilterSet() { + return $this->globalFilterSet; + } + + // --------------------------------------------------------- + // Property methods + // --------------------------------------------------------- + + /** + * Sets a property. Any existing property of the same name + * is overwritten, unless it is a user property. + * @param string $name The name of property to set. + * Must not be null. + * @param string $value The new value of the property. + * Must not be null. + * @return void + */ + public function setProperty($name, $value) { + + // command line properties take precedence + if (isset($this->userProperties[$name])) { + $this->log("Override ignored for user property " . $name, Project::MSG_VERBOSE); + return; + } + + if (isset($this->properties[$name])) { + $this->log("Overriding previous definition of property " . $name, Project::MSG_VERBOSE); + } + + $this->log("Setting project property: " . $name . " -> " . $value, Project::MSG_DEBUG); + $this->properties[$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 string $name The name of property to set. + * Must not be null. + * @param string $value The new value of the property. + * Must not be null. + * @since 2.0 + */ + public function setNewProperty($name, $value) { + if (isset($this->properties[$name])) { + $this->log("Override ignored for property " . $name, Project::MSG_DEBUG); + return; + } + $this->log("Setting project property: " . $name . " -> " . $value, Project::MSG_DEBUG); + $this->properties[$name] = $value; + } + + /** + * Sets a user property, which cannot be overwritten by + * set/unset property calls. Any previous value is overwritten. + * @param string $name The name of property to set. + * Must not be null. + * @param string $value The new value of the property. + * Must not be null. + * @see #setProperty() + */ + public function setUserProperty($name, $value) { + $this->log("Setting ro project property: " . $name . " -> " . $value, Project::MSG_DEBUG); + $this->userProperties[$name] = $value; + $this->properties[$name] = $value; + } + + /** + * Sets 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 string $name The name of property to set. + * Must not be null. + * @param string $value The new value of the property. + * Must not be null. + * @see #setProperty() + */ + public function setInheritedProperty($name, $value) { + $this->inheritedProperties[$name] = $value; + $this->setUserProperty($name, $value); + } + + /** + * Sets 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 null. + * @param value The property value. Must not be null. + */ + private function setPropertyInternal($name, $value) { + if (isset($this->userProperties[$name])) { + $this->log("Override ignored for user property " . $name, Project::MSG_VERBOSE); + return; + } + $this->properties[$name] = $value; + } + + /** + * Returns the value of a property, if it is set. + * + * @param string $name The name of the property. + * May be null, in which case + * the return value is also null. + * @return string The property value, or null for no match + * or if a null name is provided. + */ + public function getProperty($name) { + if (!isset($this->properties[$name])) { + return null; + } + $found = $this->properties[$name]; + // check to see if there are unresolved property references + if (false !== strpos($found, '${')) { + // attempt to resolve properties + $found = $this->replaceProperties($found); + // save resolved value + $this->properties[$name] = $found; + } + return $found; + } + + /** + * Replaces ${} 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 null. + * + * @return the given string with embedded property names replaced + * by values, or null if the given string is + * null. + * + * @exception BuildException if the given value has an unclosed + * property name, e.g. ${xxx + */ + public function replaceProperties($value) { + return ProjectConfigurator::replaceProperties($this, $value, $this->properties); + } + + /** + * Returns the value of a user property, if it is set. + * + * @param string $name The name of the property. + * May be null, in which case + * the return value is also null. + * @return string The property value, or null for no match + * or if a null name is provided. + */ + public function getUserProperty($name) { + if (!isset($this->userProperties[$name])) { + return null; + } + return $this->userProperties[$name]; + } + + /** + * Returns a copy of the properties table. + * @return array A hashtable containing all properties + * (including user properties). + */ + public function getProperties() { + return $this->properties; + } + + /** + * Returns a copy of the user property hashtable + * @return a hashtable containing just the user properties + */ + public function getUserProperties() { + return $this->userProperties; + } + + /** + * 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. + * + *

To copy all "user" properties, you will also have to call + * {@link #copyInheritedProperties copyInheritedProperties}.

+ * + * @param Project $other the project to copy the properties to. Must not be null. + * @return void + * @since phing 2.0 + */ + public function copyUserProperties(Project $other) { + foreach($this->userProperties as $arg => $value) { + if (isset($this->inheritedProperties[$arg])) { + continue; + } + $other->setUserProperty($arg, $value); + } + } + + /** + * 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. + * + *

To copy all "user" properties, you will also have to call + * {@link #copyUserProperties copyUserProperties}.

+ * + * @param Project $other the project to copy the properties to. Must not be null. + * + * @since phing 2.0 + */ + public function copyInheritedProperties(Project $other) { + foreach($this->userProperties as $arg => $value) { + if ($other->getUserProperty($arg) !== null) { + continue; + } + $other->setInheritedProperty($arg, $value); + } + } + + // --------------------------------------------------------- + // END Properties methods + // --------------------------------------------------------- + + + /** + * Sets default target + * @param string $targetName + */ + public function setDefaultTarget($targetName) { + $this->defaultTarget = (string) trim($targetName); + } + + /** + * Returns default target + * @return string + */ + public function getDefaultTarget() { + return (string) $this->defaultTarget; + } + + /** + * Sets the name of the current project + * + * @param string $name name of project + * @return void + * @access public + * @author Andreas Aderhold, andi@binarycloud.com + */ + public function setName($name) { + $this->name = (string) trim($name); + $this->setProperty("phing.project.name", $this->name); + } + + /** + * Returns the name of this project + * + * @return string projectname + * @access public + * @author Andreas Aderhold, andi@binarycloud.com + */ + public function getName() { + return (string) $this->name; + } + + /** + * Set the projects description + * @param string $description + */ + public function setDescription($description) { + $this->description = (string) trim($description); + } + + /** + * return the description, null otherwise + * @return string|null + */ + public function getDescription() { + return $this->description; + } + + /** + * Set the minimum required phing version + * @param string $version + */ + public function setPhingVersion($version) { + $version = str_replace('phing', '', strtolower($version)); + $this->phingVersion = (string)trim($version); + } + + /** + * Get the minimum required phing version + * @return string + */ + public function getPhingVersion() { + if($this->phingVersion === null) { + $this->setPhingVersion(Phing::getPhingVersion()); + } + return $this->phingVersion; + } + + /** + * Set basedir object from xm + * @param PhingFile|string $dir + */ + public function setBasedir($dir) { + if ($dir instanceof PhingFile) { + $dir = $dir->getAbsolutePath(); + } + + $dir = $this->fileUtils->normalize($dir); + + $dir = new PhingFile((string) $dir); + if (!$dir->exists()) { + throw new BuildException("Basedir ".$dir->getAbsolutePath()." does not exist"); + } + if (!$dir->isDirectory()) { + throw new BuildException("Basedir ".$dir->getAbsolutePath()." is not a directory"); + } + $this->basedir = $dir; + $this->setPropertyInternal("project.basedir", $this->basedir->getAbsolutePath()); + $this->log("Project base dir set to: " . $this->basedir->getPath(), Project::MSG_VERBOSE); + + // [HL] added this so that ./ files resolve correctly. This may be a mistake ... or may be in wrong place. + chdir($dir->getAbsolutePath()); + } + + /** + * Returns the basedir of this project + * + * @return PhingFile Basedir PhingFile object + * @access public + * @throws BuildException + * @author Andreas Aderhold, andi@binarycloud.com + */ + public function getBasedir() { + if ($this->basedir === null) { + try { // try to set it + $this->setBasedir("."); + } catch (BuildException $exc) { + throw new BuildException("Can not set default basedir. ".$exc->getMessage()); + } + } + return $this->basedir; + } + + /** + * Sets system properties and the environment variables for this project. + * + * @return void + */ + public function setSystemProperties() { + + // first get system properties + $systemP = array_merge( self::getProperties(), Phing::getProperties() ); + foreach($systemP as $name => $value) { + $this->setPropertyInternal($name, $value); + } + + // and now the env vars + foreach($_SERVER as $name => $value) { + // skip arrays + if (is_array($value)) { + continue; + } + $this->setPropertyInternal('env.' . $name, $value); + } + return true; + } + + + /** + * Adds a task definition. + * @param string $name Name of tag. + * @param string $class The class path to use. + * @param string $classpath The classpat to use. + */ + public function addTaskDefinition($name, $class, $classpath = null) { + $name = $name; + $class = $class; + if ($class === "") { + $this->log("Task $name has no class defined.", Project::MSG_ERR); + } elseif (!isset($this->taskdefs[$name])) { + Phing::import($class, $classpath); + $this->taskdefs[$name] = $class; + $this->log(" +Task definiton: $name ($class)", Project::MSG_DEBUG); + } else { + $this->log("Task $name ($class) already registerd, skipping", Project::MSG_VERBOSE); + } + } + + /** + * Returns the task definitions + * @return array + */ + public function getTaskDefinitions() { + return $this->taskdefs; + } + + /** + * Adds a data type definition. + * @param string $typeName Name of the type. + * @param string $typeClass The class to use. + * @param string $classpath The classpath to use. + */ + public function addDataTypeDefinition($typeName, $typeClass, $classpath = null) { + if (!isset($this->typedefs[$typeName])) { + Phing::import($typeClass, $classpath); + $this->typedefs[$typeName] = $typeClass; + $this->log(" +User datatype: $typeName ($typeClass)", Project::MSG_DEBUG); + } else { + $this->log("Type $typeName ($typeClass) already registerd, skipping", Project::MSG_VERBOSE); + } + } + + /** + * Returns the data type definitions + * @return array + */ + public function getDataTypeDefinitions() { + return $this->typedefs; + } + + /** + * Add a new target to the project + * @param string $targetName + * @param Target $target + */ + public function addTarget($targetName, &$target) { + if (isset($this->targets[$targetName])) { + throw new BuildException("Duplicate target: $targetName"); + } + $this->addOrReplaceTarget($targetName, $target); + } + + /** + * Adds or replaces a target in the project + * @param string $targetName + * @param Target $target + */ + public function addOrReplaceTarget($targetName, &$target) { + $this->log(" +Target: $targetName", Project::MSG_DEBUG); + $target->setProject($this); + $this->targets[$targetName] = $target; + + $ctx = $this->getReference("phing.parsing.context"); + $current = $ctx->getConfigurator()->getCurrentTargets(); + $current[$targetName] = $target; + } + + /** + * Returns the available targets + * @return array + */ + public function getTargets() { + return $this->targets; + } + + /** + * Create a new task instance and return reference to it. This method is + * sorta factory like. A _local_ instance is created and a reference returned to + * that instance. Usually PHP destroys local variables when the function call + * ends. But not if you return a reference to that variable. + * This is kinda error prone, because if no reference exists to the variable + * it is destroyed just like leaving the local scope with primitive vars. There's no + * central place where the instance is stored as in other OOP like languages. + * + * [HL] Well, ZE2 is here now, and this is still working. We'll leave this alone + * unless there's any good reason not to. + * + * @param string $taskType Task name + * @return Task A task object + * @throws BuildException + * Exception + */ + public function createTask($taskType) { + try { + $classname = ""; + $tasklwr = strtolower($taskType); + foreach ($this->taskdefs as $name => $class) { + if (strtolower($name) === $tasklwr) { + $classname = $class; + break; + } + } + + if ($classname === "") { + return null; + } + + $cls = Phing::import($classname); + + if (!class_exists($cls)) { + throw new BuildException("Could not instantiate class $cls, even though a class was specified. (Make sure that the specified class file contains a class with the correct name.)"); + } + + $o = new $cls(); + + if ($o instanceof Task) { + $task = $o; + } else { + $this->log (" (Using TaskAdapter for: $taskType)", Project::MSG_DEBUG); + // not a real task, try adapter + $taskA = new TaskAdapter(); + $taskA->setProxy($o); + $task = $taskA; + } + $task->setProject($this); + $task->setTaskType($taskType); + // set default value, can be changed by the user + $task->setTaskName($taskType); + $this->log (" +Task: " . $taskType, Project::MSG_DEBUG); + } catch (Exception $t) { + throw new BuildException("Could not create task of type: " . $taskType, $t); + } + // everything fine return reference + return $task; + } + + /** + * Create a datatype instance and return reference to it + * See createTask() for explanation how this works + * + * @param string $typeName Type name + * @return object A datatype object + * @throws BuildException + * Exception + */ + public function createDataType($typeName) { + try { + $cls = ""; + $typelwr = strtolower($typeName); + foreach ($this->typedefs as $name => $class) { + if (strtolower($name) === $typelwr) { + $cls = StringHelper::unqualify($class); + break; + } + } + + if ($cls === "") { + return null; + } + + if (!class_exists($cls)) { + throw new BuildException("Could not instantiate class $cls, even though a class was specified. (Make sure that the specified class file contains a class with the correct name.)"); + } + + $type = new $cls(); + $this->log(" +Type: $typeName", Project::MSG_DEBUG); + if (!($type instanceof DataType)) { + throw new Exception("$class is not an instance of phing.types.DataType"); + } + if ($type instanceof ProjectComponent) { + $type->setProject($this); + } + } catch (Exception $t) { + throw new BuildException("Could not create type: $typeName", $t); + } + // everything fine return reference + return $type; + } + + /** + * Executes a list of targets + * + * @param array $targetNames List of target names to execute + * @return void + * @throws BuildException + */ + public function executeTargets($targetNames) { + foreach($targetNames as $tname) { + $this->executeTarget($tname); + } + } + + /** + * Executes a target + * + * @param string $targetName Name of Target to execute + * @return void + * @throws BuildException + */ + public function executeTarget($targetName) { + + // complain about executing void + if ($targetName === null) { + throw new BuildException("No target specified"); + } + + // invoke topological sort of the target tree and run all targets + // until targetName occurs. + $sortedTargets = $this->_topoSort($targetName, $this->targets); + + $curIndex = (int) 0; + $curTarget = null; + do { + try { + $curTarget = $sortedTargets[$curIndex++]; + $curTarget->performTasks(); + } catch (BuildException $exc) { + $this->log("Execution of target \"".$curTarget->getName()."\" failed for the following reason: ".$exc->getMessage(), Project::MSG_ERR); + throw $exc; + } + } while ($curTarget->getName() !== $targetName); + } + + /** + * Helper function + */ + public function resolveFile($fileName, $rootDir = null) { + if ($rootDir === null) { + return $this->fileUtils->resolveFile($this->basedir, $fileName); + } else { + return $this->fileUtils->resolveFile($rootDir, $fileName); + } + } + + /** + * Topologically sort a set of Targets. + * @param string $root is the (String) name of the root Target. The sort is + * created in such a way that the sequence of Targets until the root + * target is the minimum possible such sequence. + * @param array $targets is a array representing a "name to Target" mapping + * @return An array of Strings with the names of the targets in + * sorted order. + */ + public function _topoSort($root, &$targets) { + + $root = (string) $root; + $ret = array(); + $state = array(); + $visiting = array(); + + // We first run a DFS based sort using the root as the starting node. + // This creates the minimum sequence of Targets to the root node. + // 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. + + $this->_tsort($root, $targets, $state, $visiting, $ret); + + $retHuman = ""; + for ($i=0, $_i=count($ret); $i < $_i; $i++) { + $retHuman .= $ret[$i]->toString()." "; + } + $this->log("Build sequence for target '$root' is: $retHuman", Project::MSG_VERBOSE); + + $keys = array_keys($targets); + while($keys) { + $curTargetName = (string) array_shift($keys); + if (!isset($state[$curTargetName])) { + $st = null; + } else { + $st = (string) $state[$curTargetName]; + } + + if ($st === null) { + $this->_tsort($curTargetName, $targets, $state, $visiting, $ret); + } elseif ($st === "VISITING") { + throw new Exception("Unexpected node in visiting state: $curTargetName"); + } + } + + $retHuman = ""; + for ($i=0,$_i=count($ret); $i < $_i; $i++) { + $retHuman .= $ret[$i]->toString()." "; + } + $this->log("Complete build sequence is: $retHuman", Project::MSG_VERBOSE); + + return $ret; + } + + // one step in a recursive DFS traversal of the target dependency tree. + // - The array "state" contains the state (VISITED or VISITING or null) + // of all the target names. + // - The stack "visiting" contains a stack of target names that are + // currently on the DFS stack. (NB: the target names in "visiting" are + // exactly the target names in "state" that are in the VISITING state.) + // 1. Set the current target to the VISITING state, and push it onto + // the "visiting" stack. + // 2. Throw a BuildException if any child of the current node is + // in the VISITING state (implies there is a cycle.) It uses the + // "visiting" Stack to construct the cycle. + // 3. If any children have not been VISITED, tsort() the child. + // 4. Add the current target to the Vector "ret" after the children + // have been visited. Move the current target to the VISITED state. + // "ret" now contains the sorted sequence of Targets upto the current + // Target. + + public function _tsort($root, &$targets, &$state, &$visiting, &$ret) { + $state[$root] = "VISITING"; + $visiting[] = $root; + + if (!isset($targets[$root]) || !($targets[$root] instanceof Target)) { + $target = null; + } else { + $target = $targets[$root]; + } + + // make sure we exist + if ($target === null) { + $sb = "Target '$root' does not exist in this project."; + array_pop($visiting); + if (!empty($visiting)) { + $parent = (string) $visiting[count($visiting)-1]; + $sb .= " It is a dependency of target '$parent'."; + } + throw new BuildException($sb); + } + + $deps = $target->getDependencies(); + + while($deps) { + $cur = (string) array_shift($deps); + if (!isset($state[$cur])) { + $m = null; + } else { + $m = (string) $state[$cur]; + } + if ($m === null) { + // not been visited + $this->_tsort($cur, $targets, $state, $visiting, $ret); + } elseif ($m == "VISITING") { + // currently visiting this node, so have a cycle + throw $this->_makeCircularException($cur, $visiting); + } + } + + $p = (string) array_pop($visiting); + if ($root !== $p) { + throw new Exception("Unexpected internal error: expected to pop $root but got $p"); + } + + $state[$root] = "VISITED"; + $ret[] = $target; + } + + public function _makeCircularException($end, $stk) { + $sb = "Circular dependency: $end"; + do { + $c = (string) array_pop($stk); + $sb .= " <- ".$c; + } while($c != $end); + return new BuildException($sb); + } + + /** + * Adds a reference to an object. This method is called when the parser + * detects a id="foo" attribute. It passes the id as $name and a reference + * to the object assigned to this id as $value + * @param string $name + * @param object $object + */ + public function addReference($name, $object) { + if (isset($this->references[$name])) { + $this->log("Overriding previous definition of reference to $name", Project::MSG_WARN); + } + $this->log("Adding reference: $name -> ".get_class($object), Project::MSG_DEBUG); + $this->references[$name] = $object; + } + + /** + * Returns the references array. + * @return array + */ + public function getReferences() { + return $this->references; + } + + /** + * Returns a specific reference. + * @param string $key The reference id/key. + * @return object Reference or null if not defined + */ + public function getReference($key) + { + if (isset($this->references[$key])) { + return $this->references[$key]; + } + return null; // just to be explicit + } + + /** + * Abstracting and simplifyling Logger calls for project messages + * @param string $msg + * @param int $level + */ + public function log($msg, $level = Project::MSG_INFO) { + $this->logObject($this, $msg, $level); + } + + function logObject($obj, $msg, $level) { + $this->fireMessageLogged($obj, $msg, $level); + } + + function addBuildListener(BuildListener $listener) { + $this->listeners[] = $listener; + } + + function removeBuildListener(BuildListener $listener) { + $newarray = array(); + for ($i=0, $size=count($this->listeners); $i < $size; $i++) { + if ($this->listeners[$i] !== $listener) { + $newarray[] = $this->listeners[$i]; + } + } + $this->listeners = $newarray; + } + + function getBuildListeners() { + return $this->listeners; + } + + function fireBuildStarted() { + $event = new BuildEvent($this); + foreach($this->listeners as $listener) { + $listener->buildStarted($event); + } + } + + function fireBuildFinished($exception) { + $event = new BuildEvent($this); + $event->setException($exception); + foreach($this->listeners as $listener) { + $listener->buildFinished($event); + } + } + + function fireTargetStarted($target) { + $event = new BuildEvent($target); + foreach($this->listeners as $listener) { + $listener->targetStarted($event); + } + } + + function fireTargetFinished($target, $exception) { + $event = new BuildEvent($target); + $event->setException($exception); + foreach($this->listeners as $listener) { + $listener->targetFinished($event); + } + } + + function fireTaskStarted($task) { + $event = new BuildEvent($task); + foreach($this->listeners as $listener) { + $listener->taskStarted($event); + } + } + + function fireTaskFinished($task, $exception) { + $event = new BuildEvent($task); + $event->setException($exception); + foreach($this->listeners as $listener) { + $listener->taskFinished($event); + } + } + + function fireMessageLoggedEvent($event, $message, $priority) { + $event->setMessage($message, $priority); + foreach($this->listeners as $listener) { + $listener->messageLogged($event); + } + } + + function fireMessageLogged($object, $message, $priority) { + $this->fireMessageLoggedEvent(new BuildEvent($object), $message, $priority); + } +} diff --git a/buildscripts/phing/classes/phing/ProjectComponent.php b/buildscripts/phing/classes/phing/ProjectComponent.php new file mode 100755 index 00000000..343e3a6c --- /dev/null +++ b/buildscripts/phing/classes/phing/ProjectComponent.php @@ -0,0 +1,70 @@ +. + */ + +/** + * Abstract class providing properties and methods common to all + * the project components + * + * @author Andreas Aderhold + * @author Hans Lellelid + * @version $Id$ + * @package phing + */ +abstract class ProjectComponent { + + /** + * Holds a reference to the project that a project component + * (a task, a target, etc.) belongs to + * + * @var Project A reference to the current project instance + */ + protected $project = null; + + /** + * References the project to the current component. + * + * @param Project $project The reference to the current project + */ + public function setProject($project) { + $this->project = $project; + } + + /** + * Returns a reference to current project + * + * @return Project Reference to current porject object + */ + public function getProject() { + return $this->project; + } + + /** + * Logs a message with the given priority. + * + * @param string $msg The message to be logged. + * @param integer $level The message's priority at this message should have + */ + public function log($msg, $level = Project::MSG_INFO) { + if ($this->project !== null) { + $this->project->log($msg, $level); + } + } +} diff --git a/buildscripts/phing/classes/phing/RuntimeConfigurable.php b/buildscripts/phing/classes/phing/RuntimeConfigurable.php new file mode 100755 index 00000000..305a35f3 --- /dev/null +++ b/buildscripts/phing/classes/phing/RuntimeConfigurable.php @@ -0,0 +1,116 @@ +. + */ + +/** + * Wrapper class that holds the attributes of a Task (or elements + * nested below that level) and takes care of configuring that element + * at runtime. + * + * SMART-UP INLINE DOCS + * + * @author Andreas Aderhold + * @author Hans Lellelid + * @version $Id$ + * @package phing + */ +class RuntimeConfigurable { + + private $elementTag = null; + private $children = array(); + private $wrappedObject = null; + private $attributes = array(); + private $characters = ""; + + + /** @param proxy The element to wrap. */ + function __construct($proxy, $elementTag) { + $this->wrappedObject = $proxy; + $this->elementTag = $elementTag; + } + + function setProxy($proxy) { + $this->wrappedObject = $proxy; + } + + /** Set's the attributes for the wrapped element. */ + function setAttributes($attributes) { + $this->attributes = $attributes; + } + + /** Returns the AttributeList of the wrapped element. */ + function getAttributes() { + return $this->attributes; + } + + /** Adds child elements to the wrapped element. */ + function addChild(RuntimeConfigurable $child) { + $this->children[] = $child; + } + + /** Returns the child with index */ + function getChild($index) { + return $this->children[(int)$index]; + } + + /** Add characters from #PCDATA areas to the wrapped element. */ + function addText($data) { + $this->characters .= (string) $data; + } + + function getElementTag() { + return $this->elementTag; + } + + + /** Configure the wrapped element and all children. */ + function maybeConfigure(Project $project) { + $id = null; + + // DataType configured in ProjectConfigurator + // if ( is_a($this->wrappedObject, "DataType") ) + // return; + + if ($this->attributes || $this->characters) { + ProjectConfigurator::configure($this->wrappedObject, $this->attributes, $project); + + if (isset($this->attributes["id"])) { + $id = $this->attributes["id"]; + } + + if ($this->characters) { + ProjectConfigurator::addText($project, $this->wrappedObject, (string) $this->characters); + $this->characters=""; + } + if ($id !== null) { + $project->addReference($id, $this->wrappedObject); + } + } + + if ( is_array($this->children) && !empty($this->children) ) { + // Configure all child of this object ... + foreach($this->children as $child) { + $child->maybeConfigure($project); + ProjectConfigurator::storeChild($project, $this->wrappedObject, $child->wrappedObject, strtolower($child->getElementTag())); + } + } + } +} + diff --git a/buildscripts/phing/classes/phing/Target.php b/buildscripts/phing/classes/phing/Target.php new file mode 100755 index 00000000..342d11cb --- /dev/null +++ b/buildscripts/phing/classes/phing/Target.php @@ -0,0 +1,375 @@ +. + */ + +include_once 'phing/TaskContainer.php'; + +/** + * The Target component. Carries all required target data. Implements the + * abstract class {@link TaskContainer} + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id: b6779ff7860ec7a7a84d74a40ffcc9efa75255f7 $ + * @access public + * @see TaskContainer + * @package phing + */ + +class Target implements TaskContainer { + + /** + * Name of target + * @var string + */ + private $name; + + /** + * Dependencies + * @var array + */ + private $dependencies = array(); + + /** + * Holds objects of children of this target + * @var array + */ + private $children = array(); + + /** + * The if condition from xml + * @var string + */ + private $ifCondition = ""; + + /** + * The unless condition from xml + * @var string + */ + private $unlessCondition = ""; + + /** + * Description of this target + * @var string + */ + private $description; + + /** + * Whether to hide target in targets list (-list -p switches) + * @var boolean + */ + private $hidden = false; + + /** + * Rreference to project + * @var Project + */ + private $project; + + /** + * References the project to the current component. + * + * @param Project $project The reference to the current project + */ + public function setProject(Project $project) { + $this->project = $project; + } + + /** + * Returns reference to current project + * + * @return Project Reference to current porject object + */ + public function getProject() { + return $this->project; + } + + /** + * Sets the target dependencies from xml + * + * @param string $depends Comma separated list of targetnames that depend on + * this target + * @throws BuildException + */ + public function setDepends($depends) { + // explode should be faster than strtok + $deps = explode(',', $depends); + for ($i=0, $size=count($deps); $i < $size; $i++) { + $trimmed = trim($deps[$i]); + if ($trimmed === "") { + throw new BuildException("Syntax Error: Depend attribute for target ".$this->getName()." is malformed."); + } + $this->addDependency($trimmed); + } + } + + /** + * Adds a singular dependent target name to the list + * + * @param string $dependency The dependency target to add + * @access public + */ + public function addDependency($dependency) { + $this->dependencies[] = (string) $dependency; + } + + /** + * Returns reference to indexed array of the dependencies this target has. + * + * @return array Referece to target dependencoes + */ + public function getDependencies() { + return $this->dependencies; + } + + /** + * Sets the name of the target + * + * @param string $name Name of this target + */ + public function setName($name) { + $this->name = (string) $name; + } + + /** + * Returns name of this target. + * + * @return string The name of the target + * @access public + */ + public function getName() { + return (string) $this->name; + } + + /** + * Set target status. If true, target does not come in phing -list + * + * @param boolean $flag + * @return Target + */ + public function setHidden($flag) + { + $this->hidden = (boolean) $flag; + return $this; + } + + /** + * Get target status. If true, target does not come in phing -list + * + * @return boolean + */ + public function getHidden() + { + return $this->hidden; + } + + /** + * Alias for getHidden() + * + * @return boolean + */ + public function isHidden() + { + return $this->getHidden(); + } + + /** + * Adds a task element to the list of this targets child elements + * + * @param Task $task The task object to add + * @access public + */ + public function addTask(Task $task) { + $this->children[] = $task; + } + + /** + * Adds a runtime configurable element to the list of this targets child + * elements. + * + * @param RuntimeConfigurable $rtc The RuntimeConfigurable object + * @access public + */ + public function addDataType($rtc) { + $this->children[] = $rtc; + } + + /** + * Returns an array of all tasks this target has as childrens. + * + * The task objects are copied here. Don't use this method to modify + * task objects. + * + * @return array Task[] + */ + public function getTasks() { + $tasks = array(); + for ($i=0,$size=count($this->children); $i < $size; $i++) { + $tsk = $this->children[$i]; + if ($tsk instanceof Task) { + // note: we're copying objects here! + $tasks[] = clone $tsk; + } + } + return $tasks; + } + + /** + * Set the if-condition from the XML tag, if any. The property name given + * as parameter must be present so the if condition evaluates to true + * + * @param string $property The property name that has to be present + * @access public + */ + public function setIf($property) { + $this->ifCondition = ($property === null) ? "" : $property; + } + + /** + * Set the unless-condition from the XML tag, if any. The property name + * given as parameter must be present so the unless condition evaluates + * to true + * + * @param string $property The property name that has to be present + * @access public + */ + public function setUnless($property) { + $this->unlessCondition = ($property === null) ? "" : $property; + } + + /** + * Sets a textual description of this target. + * + * @param string $description The description text + */ + public function setDescription($description) { + if ($description !== null && strcmp($description, "") !== 0) { + $this->description = (string) $description; + } else { + $this->description = null; + } + } + + /** + * Returns the description of this target. + * + * @return string The description text of this target + */ + public function getDescription() { + return $this->description; + } + + /** + * Returns a string representation of this target. In our case it + * simply returns the target name field + * + * @return string The string representation of this target + */ + public function toString() { + return (string) $this->name; + } + + /** + * The entry point for this class. Does some checking, then processes and + * performs the tasks for this target. + */ + public function main() { + if ($this->testIfCondition() && $this->testUnlessCondition()) { + foreach($this->children as $o) { + if ($o instanceof Task) { + // child is a task + $o->perform(); + } else { + // child is a RuntimeConfigurable + $o->maybeConfigure($this->project); + } + } + } elseif (!$this->testIfCondition()) { + $this->project->log("Skipped target '".$this->name."' because property '".$this->ifCondition."' not set.", Project::MSG_VERBOSE); + } else { + $this->project->log("Skipped target '".$this->name."' because property '".$this->unlessCondition."' set.", Project::MSG_VERBOSE); + } + } + + /** + * Performs the tasks by calling the main method of this target that + * actually executes the tasks. + * + * This method is for ZE2 and used for proper exception handling of + * task exceptions. + */ + public function performTasks() { + try {// try to execute this target + $this->project->fireTargetStarted($this); + $this->main(); + $this->project->fireTargetFinished($this, $null=null); + } catch (BuildException $exc) { + // log here and rethrow + $this->project->fireTargetFinished($this, $exc); + throw $exc; + } + } + + /** + * Tests if the property set in ifConfiditon exists. + * + * @return boolean true if the property specified + * in $this->ifCondition exists; + * false otherwise + */ + private function testIfCondition() { + if ($this->ifCondition === "") { + return true; + } + + $properties = explode(",", $this->ifCondition); + + $result = true; + foreach ($properties as $property) { + $test = ProjectConfigurator::replaceProperties($this->getProject(), $property, $this->project->getProperties()); + $result = $result && ($this->project->getProperty($test) !== null); + } + + return $result; + } + + /** + * Tests if the property set in unlessCondition exists. + * + * @return boolean true if the property specified + * in $this->unlessCondition exists; + * false otherwise + */ + private function testUnlessCondition() { + if ($this->unlessCondition === "") { + return true; + } + + $properties = explode(",", $this->unlessCondition); + + $result = true; + foreach ($properties as $property) { + $test = ProjectConfigurator::replaceProperties($this->getProject(), $property, $this->project->getProperties()); + $result = $result && ($this->project->getProperty($test) === null); + } + return $result; + } + +} diff --git a/buildscripts/phing/classes/phing/Task.php b/buildscripts/phing/classes/phing/Task.php new file mode 100755 index 00000000..d2490a90 --- /dev/null +++ b/buildscripts/phing/classes/phing/Task.php @@ -0,0 +1,272 @@ +. + */ + +require_once 'phing/ProjectComponent.php'; +include_once 'phing/RuntimeConfigurable.php'; + +/** + * The base class for all Tasks. + * + * Use {@link Project#createTask} to register a new Task. + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @see Project#createTask() + * @package phing + */ +abstract class Task extends ProjectComponent { + + /** + * Owning Target object + * @var Target + */ + protected $target; + + /** + * Description of the task + * @var string + */ + protected $description; + + /** + * Internal taskname (req) + * @var string + */ + protected $taskType; + + /** + * Taskname for logger + * @var string + */ + protected $taskName; + + /** + * Stored buildfile location + * @var Location + */ + protected $location; + + /** + * Wrapper of the task + * @var RuntimeConfigurable + */ + protected $wrapper; + + /** + * Sets the owning target this task belongs to. + * + * @param Target Reference to owning target + */ + public function setOwningTarget(Target $target) { + $this->target = $target; + } + + /** + * Returns the owning target of this task. + * + * @return Target The target object that owns this task + */ + public function getOwningTarget() { + return $this->target; + } + + /** + * Returns the name of task, used only for log messages + * + * @return string Name of this task + */ + public function getTaskName() { + if ($this->taskName === null) { + // if no task name is set, then it's possible + // this task was created from within another task. We don't + // therefore know the XML tag name for this task, so we'll just + // use the class name stripped of "task" suffix. This is only + // for log messages, so we don't have to worry much about accuracy. + return preg_replace('/task$/i', '', get_class($this)); + } + return $this->taskName; + } + + /** + * Sets the name of this task for log messages + * + * @param string $name + * @return string A string representing the name of this task for log + */ + public function setTaskName($name) { + $this->taskName = (string) $name; + } + + /** + * Returns the name of the task under which it was invoked, + * usually the XML tagname + * + * @return string The type of this task (XML Tag) + */ + public function getTaskType() { + return $this->taskType; + } + + /** + * Sets the type of the task. Usually this is the name of the XML tag + * + * @param string The type of this task (XML Tag) + */ + public function setTaskType($name) { + $this->taskType = (string) $name; + } + + /** + * Returns a name + * @param string $slotName + */ + protected function getRegisterSlot($slotName) { + return Register::getSlot('task.' . $this->getTaskName() . '.' . $slotName); + } + + /** + * Provides a project level log event to the task. + * + * @param string The message to log + * @param integer The priority of the message + * @see BuildEvent + * @see BuildListener + */ + function log($msg, $level = Project::MSG_INFO) { + $this->project->logObject($this, $msg, $level); + } + + /** + * Sets a textual description of the task + * + * @param string $desc The text describing the task + */ + public function setDescription($desc) { + $this->description = $desc; + } + + /** + * Returns the textual description of the task + * + * @return string The text description of the task + */ + public function getDescription() { + return $this->description; + } + + /** + * Called by the parser to let the task initialize properly. + * Should throw a BuildException if something goes wrong with the build + * + * This is abstract here, but may not be overloaded by subclasses. + * + * @throws BuildException + */ + public function init() { + } + + /** + * Called by the project to let the task do it's 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 + * phing target1 target2 will run all tasks in target3 twice. + * + * Should throw a BuildException if someting goes wrong with the build + * + * This is abstract here. Must be overloaded by real tasks. + */ + abstract public function main(); + + /** + * Returns the location within the buildfile this task occurs. Used + * by {@link BuildException} to give detailed error messages. + * + * @return Location The location object describing the position of this + * task within the buildfile. + */ + function getLocation() { + return $this->location; + } + + /** + * Sets the location within the buildfile this task occurs. Called by + * the parser to set location information. + * + * @param Location $location The location object describing the position of this + * task within the buildfile. + */ + function setLocation(Location $location) { + $this->location = $location; + } + + /** + * Returns the wrapper object for runtime configuration + * + * @return RuntimeConfigurable The wrapper object used by this task + */ + function getRuntimeConfigurableWrapper() { + if ($this->wrapper === null) { + $this->wrapper = new RuntimeConfigurable($this, $this->getTaskName()); + } + return $this->wrapper; + } + + /** + * Sets the wrapper object this task should use for runtime + * configurable elements. + * + * @param RuntimeConfigurable $wrapper The wrapper object this task should use + */ + function setRuntimeConfigurableWrapper(RuntimeConfigurable $wrapper) { + $this->wrapper = $wrapper; + } + + /** + * Configure this task if it hasn't been done already. + */ + public function maybeConfigure() { + if ($this->wrapper !== null) { + $this->wrapper->maybeConfigure($this->project); + } + } + + /** + * Perfrom this task + */ + public function perform() { + + try { // try executing task + $this->project->fireTaskStarted($this); + $this->maybeConfigure(); + $this->main(); + $this->project->fireTaskFinished($this, $null=null); + } catch (Exception $exc) { + if ($exc instanceof BuildException) { + if ($exc->getLocation() === null) { + $exc->setLocation($this->getLocation()); + } + } + $this->project->fireTaskFinished($this, $exc); + throw $exc; + } + } +} diff --git a/buildscripts/phing/classes/phing/TaskAdapter.php b/buildscripts/phing/classes/phing/TaskAdapter.php new file mode 100755 index 00000000..ba323c56 --- /dev/null +++ b/buildscripts/phing/classes/phing/TaskAdapter.php @@ -0,0 +1,85 @@ +. + */ + +require_once 'phing/Task.php'; + +/** + * Use introspection to "adapt" an arbitrary ( not extending Task, but with + * similar patterns). + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @package phing + */ +class TaskAdapter extends Task { + + /** target object */ + private $proxy; + + /** + * Main entry point. + * @return void + */ + function main() { + + if (method_exists($this->proxy, "setProject")) { + try { // try to set project + $this->proxy->setProject($this->project); + } catch (Exception $ex) { + $this->log("Error setting project in " . get_class($this->proxy) . Project::MSG_ERR); + throw new BuildException($ex); + } + } else { + throw new Exception("Error setting project in class " . get_class($this->proxy)); + } + + if (method_exists($this->proxy, "main")) { + try { //try to call main + $this->proxy->main($this->project); + } catch (Exception $ex) { + $this->log("Error in " . get_class($this->proxy), Project::MSG_ERR); + $this->log($ex->getTraceAsString(), Project::MSG_DEBUG); + throw new BuildException($ex->getMessage()); + } + } else { + throw new BuildException("Your task-like class '" . get_class($this->proxy) ."' does not have a main() method"); + } + } + + /** + * Set the target object. + * @param object $o + * @return void + */ + function setProxy($o) { + $this->proxy = $o; + } + + /** + * Gets the target object. + * @return object + */ + function getProxy() { + return $this->proxy; + } + +} diff --git a/buildscripts/phing/classes/phing/TaskContainer.php b/buildscripts/phing/classes/phing/TaskContainer.php new file mode 100755 index 00000000..4be2ef41 --- /dev/null +++ b/buildscripts/phing/classes/phing/TaskContainer.php @@ -0,0 +1,44 @@ +. + * + * @package phing + */ + +/** + * Abstract interface for objects which can contain tasks (targets) + * Used to check if a class can contain tasks (via instanceof) + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing + */ +interface TaskContainer { + + /** + * Adds a task to this task container. Must be implemented + * by derived class + * + * @param object The task to be added to the container + * @access public + */ + function addTask(Task $task); +} diff --git a/buildscripts/phing/classes/phing/UnknownElement.php b/buildscripts/phing/classes/phing/UnknownElement.php new file mode 100755 index 00000000..b04365e7 --- /dev/null +++ b/buildscripts/phing/classes/phing/UnknownElement.php @@ -0,0 +1,215 @@ +. + */ + +require_once 'phing/Task.php'; + +/** + * Wrapper class that holds all information necessary to create a task + * that did not exist when Phing started. + * + * This has something to do with phing encountering an task XML element + * it is not aware of at start time. This is a situation where special steps + * need to be taken so that the element is then known. + * + * @author Andreas Aderhold + * @author Hans Lellelid + * @version $Id$ + * @package phing + */ +class UnknownElement extends Task { + + private $elementName; + private $realThing; + private $children = array(); + + /** + * Constructs a UnknownElement object + * + * @param string The XML element name that is unknown + * @access public + */ + function __construct($elementName) { + $this->elementName = (string) $elementName; + } + + /** + * Return the XML element name that this UnnownElement + * handles. + * + * @return string The XML element name that is unknown + */ + public function getTag() { + return (string) $this->elementName; + } + + /** + * Tries to configure the unknown element + * + * @throws BuildException if the element can not be configured + */ + public function maybeConfigure() { + + $this->realThing = $this->makeObject($this, $this->wrapper); + $this->wrapper->setProxy($this->realThing); + if ($this->realThing instanceof Task) { + $this->realThing->setRuntimeConfigurableWrapper($this->wrapper); + } + + $this->handleChildren($this->realThing, $this->wrapper); + $this->wrapper->maybeConfigure($this->getProject()); + + } + + /** + * Called when the real task has been configured for the first time. + * + * @throws BuildException if the task can not be created + */ + public function main() { + + if ($this->realThing === null) { + // plain impossible to get here, maybeConfigure should + // have thrown an exception. + throw new BuildException("Should not be executing UnknownElement::main() -- task/type: {$this->elementName}"); + } + + if ($this->realThing instanceof Task) { + $this->realThing->main(); + } + + } + + /** + * Add a child element to the unknown element + * + * @param object The object representing the child element + */ + public function addChild(UnknownElement $child) { + $this->children[] = $child; + } + + /** + * Handle child elemets of the unknown element, if any. + * + * @param ProjectComponent The parent object the unkown element belongs to + * @param object The parent wrapper object + */ + function handleChildren(ProjectComponent $parent, $parentWrapper) { + + if ($parent instanceof TaskAdapter) { + $parent = $parent->getProxy(); + } + + $parentClass = get_class($parent); + $ih = IntrospectionHelper::getHelper($parentClass); + + for ($i=0, $childrenCount=count($this->children); $i < $childrenCount; $i++) { + + $childWrapper = $parentWrapper->getChild($i); + $child = $this->children[$i]; + $realChild = null; + if ($parent instanceof TaskContainer) { + $realChild = $this->makeTask($child, $childWrapper, false); + $parent->addTask($realChild); + } else { + $project = $this->project === null ? $parent->project : $this->project; + $realChild = $ih->createElement($project, $parent, $child->getTag()); + } + + $childWrapper->setProxy($realChild); + if ($realChild instanceof Task) { + $realChild->setRuntimeConfigurableWrapper($childWrapper); + } + + if ($realChild instanceof ProjectComponent) { + $child->handleChildren($realChild, $childWrapper); + } + + if ($realChild instanceof Task) { + $realChild->maybeConfigure(); + } + } + } + + /** + * Creates a named task or data type. If the real object is a task, + * it is configured up to the init() stage. + * + * @param UnknownElement $ue The unknown element to create the real object for. + * Must not be null. + * @param RuntimeConfigurable $w Ignored in this implementation. + * @return object The Task or DataType represented by the given unknown element. + */ + protected function makeObject(UnknownElement $ue, RuntimeConfigurable $w) { + $o = $this->makeTask($ue, $w, true); + if ($o === null) { + $o = $this->project->createDataType($ue->getTag()); + } + if ($o === null) { + throw new BuildException("Could not create task/type: '".$ue->getTag()."'. Make sure that this class has been declared using taskdef / typedef."); + } + return $o; + } + + /** + * Create a named task and configure it up to the init() stage. + * + * @param UnknownElement $ue The unknwon element to create a task from + * @param RuntimeConfigurable $w The wrapper object + * @param boolean $onTopLevel Whether to treat this task as if it is top-level. + * @return Task The freshly created task + */ + protected function makeTask(UnknownElement $ue, RuntimeConfigurable $w, $onTopLevel = false) { + + $task = $this->project->createTask($ue->getTag()); + + if ($task === null) { + if (!$onTopLevel) { + throw new BuildException("Could not create task of type: '".$this->elementName."'. Make sure that this class has been declared using taskdef."); + } + return null; + } + + // used to set the location within the xmlfile so that exceptions can + // give detailed messages + + $task->setLocation($this->getLocation()); + $attrs = $w->getAttributes(); + if (isset($attrs['id'])) { + $this->project->addReference($attrs['id'], $task); + } + + // UnknownElement always has an associated target + $task->setOwningTarget($this->target); + + $task->init(); + return $task; + } + + /** + * Get the name of the task to use in logging messages. + * + * @return string The task's name + */ + function getTaskName() { + return $this->realThing === null ? parent::getTaskName() : $this->realThing->getTaskName(); + } +} diff --git a/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Manager.php b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Manager.php new file mode 100644 index 00000000..6fe71107 --- /dev/null +++ b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Manager.php @@ -0,0 +1,304 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ + +/** + * Manager class for Parallel processes. + * + * This class will manage the workers and make sure all processes are executed + * in parallel and not too many at the same time. + * + * @category DocBlox + * @package Parallel + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ +class DocBlox_Parallel_Manager extends ArrayObject +{ + /** @var int The maximum number of processes to run simultaneously */ + protected $process_limit = 2; + + /** @var boolean Tracks whether this manager is currently executing */ + protected $is_running = false; + + /** + * Tries to autodetect the optimal number of process by counting the number + * of processors. + * + * @param array $input Input for the array object. + * @param int $flags flags for the array object. + * @param string $iterator_class Iterator class for this array object. + */ + public function __construct( + $input = array(), $flags = 0, $iterator_class = "ArrayIterator" + ) { + parent::__construct($input, $flags, $iterator_class); + + if (is_readable('/proc/cpuinfo')) { + $processors = 0; + exec("cat /proc/cpuinfo | grep processor | wc -l", $processors); + $this->setProcessLimit(reset($processors)); + } + } + + /** + * Adds a worker to to the queue. + * + * This method will prepare a worker to be executed in parallel once the + * execute method is invoked. + * A fluent interface is provided so that you can chain multiple workers + * in one call. + * + * Example: + * + * $cb1 = function() { var_dump('a'); sleep(1); }; + * $cb2 = function() { var_dump('b'); sleep(1); }; + * + * $mgr = new DocBlox_Parallel_Manager(); + * $mgr->setProcessLimit(2) + * ->addWorker(new DocBlox_Parallel_Worker($cb1)) + * ->addWorker(new DocBlox_Parallel_Worker($cb2)) + * ->execute(); + * + * @param int $index The key for this worker. + * @param DocBlox_Parallel_Worker $newval The worker to add onto the queue. + * + * @see DocBlox_Parallel_Manager::execute() + * + * @throws RuntimeException if this method is invoked while the + * manager is busy executing tasks. + * @throws InvalidArgumentException if the provided element is not of type + * DocBlox_Parallel_Worker. + * + * @return void + */ + public function offsetSet($index, $newval) + { + if (!$newval instanceof DocBlox_Parallel_Worker) { + throw new InvalidArgumentException( + 'Provided element must be of type DocBlox_Parallel_Worker' + ); + } + if ($this->isRunning()) { + throw new RuntimeException( + 'Workers may not be added during execution of the manager' + ); + } + + parent::offsetSet($index, $newval); + } + + /** + * Convenience method to make the addition of workers explicit and allow a + * fluent interface. + * + * @param DocBlox_Parallel_Worker $worker The worker to add onto the queue. + * + * @return self + */ + public function addWorker(DocBlox_Parallel_Worker $worker) + { + $this[] = $worker; + + return $this; + } + + /** + * Sets how many processes at most to execute at the same time. + * + * A fluent interface is provided so that you can chain multiple workers + * in one call. + * + * @param int $process_limit The limit, minimum of 1 + * + * @see DocBlox_Parallel_Manager::addWorker() for an example + * + * @return self + */ + public function setProcessLimit($process_limit) + { + if ($process_limit < 1) { + throw new InvalidArgumentException( + 'Number of simultaneous processes may not be less than 1' + ); + } + + $this->process_limit = $process_limit; + + return $this; + } + + /** + * Returns the current limit on the amount of processes that can be + * executed at the same time. + * + * @return int + */ + public function getProcessLimit() + { + return $this->process_limit; + } + + /** + * Returns whether the manager is executing the workers. + * + * @return boolean + */ + public function isRunning() + { + return $this->is_running; + } + + /** + * Executes each worker. + * + * This method loops through the list of workers and tries to fork as + * many times as the ProcessLimit dictates at the same time. + * + * @return void + */ + public function execute() + { + /** @var int[] $processes */ + $processes = $this->startExecution(); + + /** @var DocBlox_Parallel_Worker $worker */ + foreach ($this as $worker) { + + // if requirements are not met, execute workers in series. + if (!$this->checkRequirements()) { + $worker->execute(); + continue; + } + + $this->forkAndRun($worker, $processes); + } + + $this->stopExecution($processes); + } + + /** + * Notifies manager that execution has started, checks requirements and + * returns array for child processes. + * + * If forking is not available because library requirements are not met + * than the list of workers is processed in series and a E_USER_NOTICE is + * triggered. + * + * @return int[] + */ + protected function startExecution() + { + $this->is_running = true; + + // throw a E_USER_NOTICE if the requirements are not met. + if (!$this->checkRequirements()) { + trigger_error( + 'The PCNTL extension is not available, running workers in series ' + . 'instead of parallel', + E_USER_NOTICE + ); + } + + return array(); + } + + /** + * Waits for all processes to have finished and notifies the manager that + * execution has stopped. + * + * @param int[] &$processes List of running processes. + * + * @return void + */ + protected function stopExecution(array &$processes) + { + // starting of processes has ended but some processes might still be + // running wait for them to finish + while (!empty($processes)) { + pcntl_waitpid(array_shift($processes), $status); + } + + /** @var DocBlox_Parallel_Worker $worker */ + foreach ($this as $worker) { + $worker->pipe->push(); + } + + $this->is_running = false; + } + + /** + * Forks the current process and calls the Worker's execute method OR + * handles the parent process' execution. + * + * This is the really tricky part of the forking mechanism. Here we invoke + * {@link http://www.php.net/manual/en/function.pcntl-fork.php pcntl_fork} + * and either execute the forked process or deal with the parent's process + * based on in which process we are. + * + * To fully understand what is going on here it is recommended to read the + * PHP manual page on + * {@link http://www.php.net/manual/en/function.pcntl-fork.php pcntl_fork} + * and associated articles. + * + * If there are more workers than may be ran simultaneously then this method + * will wait until a slot becomes available and then starts the next worker. + * + * @param DocBlox_Parallel_Worker $worker The worker to process. + * @param int[] &$processes The list of running processes. + * + * @throws RuntimeException if we are unable to fork. + * + * @return void + */ + protected function forkAndRun( + DocBlox_Parallel_Worker $worker, array &$processes + ) { + $worker->pipe = new DocBlox_Parallel_WorkerPipe($worker); + + // fork the process and register the PID + $pid = pcntl_fork(); + + switch ($pid) { + case -1: + throw new RuntimeException('Unable to establish a fork'); + case 0: // Child process + $worker->execute(); + + $worker->pipe->pull(); + + // Kill -9 this process to prevent closing of shared file handlers. + // Not doing this causes, for example, MySQL connections to be cleaned. + posix_kill(getmypid(), SIGKILL); + default: // Parent process + // Keep track if the worker children + $processes[] = $pid; + + if (count($processes) >= $this->getProcessLimit()) { + pcntl_waitpid(array_shift($processes), $status); + } + break; + } + } + + /** + * Returns true when all requirements are met. + * + * @return bool + */ + protected function checkRequirements() + { + return (bool)(extension_loaded('pcntl')); + } +} \ No newline at end of file diff --git a/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/README.md b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/README.md new file mode 100644 index 00000000..272d7191 --- /dev/null +++ b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/README.md @@ -0,0 +1,106 @@ +Parallel +======== + +This is a library for introducing Parallelization into your project. +See the `example.php` file for an example how to use this library. + +Theory of Operation +------------------- + +This library will enable the developer to execute a given amount of tasks +(workers) in parallel. This is achieved by adding workers onto a manager, +optionally defining how many processes to run simultaneously and then execute +the manager. + +Under Linux this library will try to detect the number of processors and allow +a maximum number of processes to run equal to the number of processors. If this +cannot be determined or the user is running Windows then a default of 2 is used. + +Requirements and graceful degradation +------------------------------------- + +Parallelization has several requirements. But to allow distribution, without +adding several requirements to your application, will this library execute the +given tasks in serie if the requirements are not met. And throw a E_USER_NOTICE +php error that explains to the user that dependencies are missing. + +The requirements for this library are: + +* A *NIX compatible operating system +* Scripts must not run from an apache module +* the PCNTL PHP extension (http://php.net/manual/en/book.pcntl.php) + +Workers +------- + +Workers are basically wrappers around callback functions or methods. As such you +can use anything in your existing project and parallelize it. + +Do note that each parallel process is a duplicate of the original. This means +that, for example, if you pass an object (or other reference) and change that, +that the changes that you have made do not carry over to the caller. + +The return value of the given callback is stored as result on the worker and +can be read using the `getResult()` method. + +Any exception that is thrown will result in an error, where the `getReturnCode()` +method will return the exception code (be warned: this may be 0!) and the +`getError()` method will return the exception message. + +Errors and exceptions +--------------------- + +if a task throws an exception it is caught and registered as an error. The +exception's code is used as error number, where the message is used as error +message. + +By using this, instead of dying, you can continue execution of the other parallel +processes and handle errors yourself after all processes have been executed. + +Examples +-------- + +### Fluent interface + + $mgr = new DocBlox_Parallel_Manager(); + $mgr + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'a'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'b'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'c'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'd'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'e'; })) + ->execute(); + + /** @var DocBlox_Parallel_Worker $worker */ + foreach ($mgr as $worker) { + var_dump($worker->getResult()); + } + +### Array interface + + $mgr = new DocBlox_Parallel_Manager(); + $mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'f'; }); + $mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'g'; }); + $mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'h'; }); + $mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'i'; }); + $mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'j'; }); + $mgr->execute(); + + /** @var DocBlox_Parallel_Worker $worker */ + foreach ($mgr as $worker) { + var_dump($worker->getResult()); + } + +TODO +---- + +* Improve docs +* More intelligent process slots; currently only the oldest in a 'set' of slots + is waited on but if this runs for a longer time then the other slots than + those will not be filled as long as the first slot is occupied. +* Last parts of IPC (Inter-Process Communication), to be able to return + information from Workers to the Manager. + + * STDOUT + * STDERR + diff --git a/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Worker.php b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Worker.php new file mode 100644 index 00000000..338f4b25 --- /dev/null +++ b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Worker.php @@ -0,0 +1,203 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ + +/** + * Class that represents the execution of a single task within a parallelized + * frame. + * + * @category DocBlox + * @package Parallel + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ +class DocBlox_Parallel_Worker +{ + /** @var callback the task to execute for this worker */ + protected $task = null; + + /** @var mixed[] A list of argument to pass to the task */ + protected $arguments = array(); + + /** @var int The return code to tell the parent process how it went */ + protected $return_code = -1; + + /** @var mixed The result of the given task */ + protected $result = ''; + + /** @var string The error message, if an error occurred */ + protected $error = ''; + + /** + * Creates the worker and sets the task to execute optionally including + * the arguments that need to be passed to the task. + * + * @param callback $task The task to invoke upon execution. + * @param mixed[] $arguments The arguments to provide to the task. + */ + function __construct($task, array $arguments = array()) + { + $this->setTask($task); + $this->arguments = $arguments; + } + + /** + * Returns the list of arguments as provided int he constructor. + * + * @see DocBlox_Parallel_Worker::__construct() + * + * @return mixed[] + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns the task as provided in the constructor. + * + * @see DocBlox_Parallel_Worker::__construct() + * + * @return callback + */ + public function getTask() + { + return $this->task; + } + + /** + * Returns the available return code. + * + * This method may return -1 if no return code is available yet. + * + * @return int + */ + public function getReturnCode() + { + return $this->return_code; + } + + /** + * Sets the return code for this worker. + * + * Recommended is to use the same codes as are used with + * {@link http://www.gnu.org/software/bash/manual/html_node/Exit-Status.html + * exit codes}. + * + * In short: 0 means that the task succeeded and a any other positive value + * indicates an error condition. + * + * @param int $return_code Recommended to be a positive number + * + * @throw InvalidArgumentException if the code is not a number or negative + * + * @return void + */ + public function setReturnCode($return_code) + { + if (!is_numeric($return_code) || ($return_code < 0)) { + throw new InvalidArgumentException( + 'Expected the return code to be a positive number' + ); + } + + $this->return_code = $return_code; + } + + /** + * Returns the error message associated with the return code. + * + * @return string + */ + public function getError() + { + return $this->error; + } + + /** + * Sets the error message. + * + * @param string $error The error message. + * + * @return void + */ + public function setError($error) + { + $this->error = $error; + } + + /** + * Returns the result for this task run. + * + * @return null|mixed + */ + public function getResult() + { + return $this->result; + } + + /** + * Sets the result for this task run. + * + * @param mixed $result The value that is returned by the task; can be anything. + * + * @return void + */ + public function setResult($result) + { + $this->result = $result; + } + + /** + * Invokes the task with the given arguments and processes the output. + * + * @return void. + */ + public function execute() + { + $this->setReturnCode(0); + try { + $this->setResult( + call_user_func_array($this->getTask(), $this->getArguments()) + ); + } catch (Exception $e) { + $this->setError($e->getMessage()); + $this->setReturnCode($e->getCode()); + } + } + + /** + * Sets the task for this worker and validates whether it is callable. + * + * @param callback $task The task to execute when the execute method + * is invoked. + * + * @throws InvalidArgumentException if the given argument is not a callback. + * + * @see DocBlox_Parallel_Worker::__construct() + * @see DocBlox_Parallel_Worker::execute() + * + * @return void + */ + protected function setTask($task) + { + if (!is_callable($task)) { + throw new InvalidArgumentException( + 'Worker task is not a callable object' + ); + } + + $this->task = $task; + } +} diff --git a/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/WorkerPipe.php b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/WorkerPipe.php new file mode 100644 index 00000000..3b7eb7fe --- /dev/null +++ b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/WorkerPipe.php @@ -0,0 +1,127 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ + +/** + * Class that represents a named pipe for a Worker. + * + * This class manages the named pipe for a worker and is able to push and pull + * specific data to facilitate IPC (interprocess communication). + * + * @category DocBlox + * @package Parallel + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ +class DocBlox_Parallel_WorkerPipe +{ + /** @var DocBlox_Parallel_Worker worker class that is associated */ + protected $worker; + + /** @var string Path to the pipe */ + protected $path; + + /** + * Initializes the named pipe. + * + * @param DocBlox_Parallel_Worker $worker Associated worker. + */ + public function __construct(DocBlox_Parallel_Worker $worker) + { + $this->worker = $worker; + + $this->path = tempnam(sys_get_temp_dir(), 'dpm_'); + posix_mkfifo($this->path, 0750); + } + + /** + * If the named pipe was not cleaned up, do so now. + */ + public function __destruct() + { + if (file_exists($this->path)) { + $this->release(); + } + } + + /** + * Pull the worker data into the named pipe. + * + * @return void + */ + public function pull() + { + $this->writePipeContents(); + } + + /** + * Push the worker data back onto the worker and release the pipe. + * + * @return void + */ + public function push() + { + list($result, $error, $return_code) = $this->readPipeContents(); + $this->release(); + + $this->worker->setResult($result); + $this->worker->setError($error); + $this->worker->setReturnCode($return_code); + } + + /** + * Convenience method to show relation to readPipeContents. + * + * @return void + */ + protected function writePipeContents() + { + // push the gathered data onto a name pipe + $pipe = fopen($this->path, 'w'); + fwrite( + $pipe, serialize( + array( + $this->worker->getResult(), + $this->worker->getError(), + $this->worker->getReturnCode() + ) + ) + ); + fclose($pipe); + } + + /** + * Returns the unserialized contents of the pipe. + * + * @return array + */ + protected function readPipeContents() + { + $pipe = fopen($this->path, 'r+'); + $result = unserialize(fread($pipe, filesize($this->path))); + fclose($pipe); + + return $result; + } + + /** + * Releases the pipe. + * + * @return void + */ + protected function release() + { + unlink($this->path); + } +} \ No newline at end of file diff --git a/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/example.php b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/example.php new file mode 100644 index 00000000..3bdcd408 --- /dev/null +++ b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/example.php @@ -0,0 +1,57 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ + +/** Include the manager as we do not autoload */ +require_once 'Manager.php'; + +/** Include the worker as we do not autoload */ +require_once 'Worker.php'; + +/** Include the worker's pipe as we do not autoload */ +require_once 'WorkerPipe.php'; + +// ----------------------------------------------------------------------------- +// method 1: using a fluent interface and the addWorker helper. +// ----------------------------------------------------------------------------- + +$mgr = new DocBlox_Parallel_Manager(); +$mgr + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'a'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'b'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'c'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'd'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'e'; })) + ->execute(); + +/** @var DocBlox_Parallel_Worker $worker */ +foreach ($mgr as $worker) { + var_dump($worker->getResult()); +} + +// ----------------------------------------------------------------------------- +// method 2: using the manager as worker array +// ----------------------------------------------------------------------------- + +$mgr = new DocBlox_Parallel_Manager(); +$mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'f'; }); +$mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'g'; }); +$mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'h'; }); +$mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'i'; }); +$mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'j'; }); +$mgr->execute(); + +/** @var DocBlox_Parallel_Worker $worker */ +foreach ($mgr as $worker) { + var_dump($worker->getResult()); +} diff --git a/buildscripts/phing/classes/phing/filters/BaseFilterReader.php b/buildscripts/phing/classes/phing/filters/BaseFilterReader.php new file mode 100755 index 00000000..4489b16e --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/BaseFilterReader.php @@ -0,0 +1,157 @@ +. +*/ + +include_once 'phing/system/io/FilterReader.php'; +include_once 'phing/system/io/StringReader.php'; + + +/** + * Base class for core filter readers. + * + * @author Yannick Lecaillez + * @version $Id$ + * @access public + * @see FilterReader + * @package phing.filters + */ +class BaseFilterReader extends FilterReader { + + /** Have the parameters passed been interpreted? */ + protected $initialized = false; + + /** The Phing project this filter is part of. */ + protected $project = null; + + /** + * Constructor used by Phing'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. + * + * @param Reader $in + */ + function __construct($in = null) { + if ($in === null) { + $dummy = ""; + $in = new StringReader($dummy); + } + parent::__construct($in); + } + + /** + * Returns the initialized status. + * + * @return boolean whether or not the filter is initialized + */ + function getInitialized() { + return $this->initialized; + } + + /** + * Sets the initialized status. + * + * @param boolean $initialized Whether or not the filter is initialized. + */ + function setInitialized($initialized) { + $this->initialized = (boolean) $initialized; + } + + /** + * Sets the project to work with. + * + * @param object $project The project this filter is part of. + * Should not be null. + */ + function setProject(Project $project) { + // type check, error must never occur, bad code of it does + $this->project = $project; + } + + /** + * Returns the project this filter is part of. + * + * @return object The project this filter is part of + */ + function getProject() { + return $this->project; + } + + /** + * Reads characters. + * + * @param off Offset at which to start storing characters. + * @param len Maximum number of characters to read. + * + * @return Characters read, or -1 if the end of the stream + * has been reached + * + * @throws IOException If an I/O error occurs + */ + function read($len = null) { + return $this->in->read($len); + } + + /** + * 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 null if the end of the + stream has already been reached + * + * @throws IOException if the underlying reader throws one during + * reading + */ + function readLine() { + $line = null; + + while ( ($ch = $this->in->read(1)) !== -1 ) { + $line .= $ch; + if ( $ch === "\n" ) + break; + } + + return $line; + } + + /** + * Returns whether the end of file has been reached with input stream. + * @return boolean + */ + function eof() { + return $this->in->eof(); + } + + /** + * Convenience method to support logging in filters. + * @param string $msg Message to log. + * @param int $level Priority level. + */ + function log($msg, $level = Project::MSG_INFO) { + if ($this->project !== null) { + $this->project->log("[filter:".get_class($this)."] ".$msg, $level); + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/BaseParamFilterReader.php b/buildscripts/phing/classes/phing/filters/BaseParamFilterReader.php new file mode 100755 index 00000000..8d3b0810 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/BaseParamFilterReader.php @@ -0,0 +1,69 @@ +. +*/ + +include_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/types/Parameterizable.php'; +include_once 'phing/types/Parameter.php'; + +/** + * Base class for core filter readers. + * + * @author Yannick Lecaillez + * @copyright � 2003 seasonfive. All rights reserved + * @version $Id$ + * @access public + * @see FilterReader + * @package phing.filters + */ +class BaseParamFilterReader extends BaseFilterReader implements Parameterizable { + + /** The passed in parameter array. */ + protected $_parameters = array(); + + /* + * Sets the parameters used by this filter, and sets + * the filter to an uninitialized status. + * + * @param array Array of parameters to be used by this filter. + * Should not be null. + */ + function setParameters($parameters) { + // type check, error must never occur, bad code of it does + if ( !is_array($parameters) ) { + throw new Exception("Expected parameters array got something else"); + } + + $this->_parameters = $parameters; + $this->setInitialized(false); + } + + /* + * Returns the parameters to be used by this filter. + * + * @return the parameters to be used by this filter + */ + function &getParameters() { + return $this->_parameters; + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/ChainableReader.php b/buildscripts/phing/classes/phing/filters/ChainableReader.php new file mode 100644 index 00000000..2b773dbe --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/ChainableReader.php @@ -0,0 +1,43 @@ +. +*/ + +/** + * Interface indicating that a reader may be chained to another one. + * + * @author Magesh Umasankar + * @package phing.filters + */ +interface ChainableReader { + + /** + * Returns a reader with the same configuration as this one, + * but filtering input from the specified reader. + * + * @param Reader $rdr the reader which the returned reader should be filtering + * + * @return Reader A reader with the same configuration as this one, but + * filtering input from the specified reader + */ + public function chain(Reader $rdr); +} + + diff --git a/buildscripts/phing/classes/phing/filters/ExpandProperties.php b/buildscripts/phing/classes/phing/filters/ExpandProperties.php new file mode 100755 index 00000000..2517783f --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/ExpandProperties.php @@ -0,0 +1,99 @@ +. +*/ + +require_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Expands Phing Properties, if any, in the data. + *

+ * Example:
+ *

+ * Or: + *
logLevel = Project::MSG_ERR; break;
+            case "warning": $this->logLevel = Project::MSG_WARN; break;
+            case "info": $this->logLevel = Project::MSG_INFO; break;
+            case "verbose": $this->logLevel = Project::MSG_VERBOSE; break;
+            case "debug": $this->logLevel = Project::MSG_DEBUG; break;
+        }
+    }
+    
+    /**
+     * Returns the filtered stream. 
+     * The original stream is first read in fully, and the Phing properties are expanded.
+     * 
+     * @return mixed     the filtered stream, or -1 if the end of the resulting stream has been reached.
+     * 
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+    function read($len = null) {
+                
+        $buffer = $this->in->read($len);
+        
+        if($buffer === -1) {
+            return -1;
+        }
+        
+        $project = $this->getProject();
+        $buffer = ProjectConfigurator::replaceProperties($project, $buffer, $project->getProperties(), $this->logLevel);
+        
+        return $buffer;
+    }
+        
+    /**
+     * Creates a new ExpandProperties filter using the passed in
+     * Reader for instantiation.
+     * 
+     * @param object A Reader object providing the underlying stream.
+     *               Must not be null.
+     * 
+     * @return object A new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    function chain(Reader $reader) {
+        $newFilter = new ExpandProperties($reader);
+        $newFilter->setProject($this->getProject());
+        return $newFilter;
+    }
+}
+
+
diff --git a/buildscripts/phing/classes/phing/filters/HeadFilter.php b/buildscripts/phing/classes/phing/filters/HeadFilter.php
new file mode 100755
index 00000000..3cbcb51b
--- /dev/null
+++ b/buildscripts/phing/classes/phing/filters/HeadFilter.php
@@ -0,0 +1,161 @@
+.
+*/
+
+include_once 'phing/filters/BaseParamFilterReader.php';
+include_once 'phing/filters/ChainableReader.php';
+
+/**
+ * Reads the first n lines of a stream.
+ * (Default is first 10 lines.)
+ * 

+ * Example: + *

+ * Or: + *

+ *    
+ * 
+ * + * @author Yannick Lecaillez + * @author hans lellelid, hans@velum.net + * @version $Id$ + * @access public + * @see FilterReader + * @package phing.filters + */ +class HeadFilter extends BaseParamFilterReader implements ChainableReader { + + /** + * Parameter name for the number of lines to be returned. + */ + const LINES_KEY = "lines"; + + /** + * Number of lines currently read in. + * @var integer + */ + private $_linesRead = 0; + + /** + * Number of lines to be returned in the filtered stream. + * @var integer + */ + private $_lines = 10; + + /** + * Returns first n lines of stream. + * @return 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 + */ + function read($len = null) { + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + // note, if buffer contains fewer lines than + // $this->_lines this code will not work. + + if($this->_linesRead < $this->_lines) { + + $buffer = $this->in->read($len); + + if($buffer === -1) { + return -1; + } + + // now grab first X lines from buffer + + $lines = explode("\n", $buffer); + + $linesCount = count($lines); + + // must account for possibility that the num lines requested could + // involve more than one buffer read. + $len = ($linesCount > $this->_lines ? $this->_lines - $this->_linesRead : $linesCount); + $filtered_buffer = implode("\n", array_slice($lines, 0, $len) ); + $this->_linesRead += $len; + + return $filtered_buffer; + + } + + return -1; // EOF, since the file is "finished" as far as subsequent filters are concerned. + } + + /** + * Sets the number of lines to be returned in the filtered stream. + * + * @param integer $lines the number of lines to be returned in the filtered stream. + */ + function setLines($lines) { + $this->_lines = (int) $lines; + } + + /** + * Returns the number of lines to be returned in the filtered stream. + * + * @return integer The number of lines to be returned in the filtered stream. + */ + function getLines() { + return $this->_lines; + } + + /** + * Creates a new HeadFilter using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be null. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader. + */ + function chain(Reader $reader) { + $newFilter = new HeadFilter($reader); + $newFilter->setLines($this->getLines()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + 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. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0, $_i=count($params) ; $i < $_i; $i++) { + if ( self::LINES_KEY == $params[$i]->getName() ) { + $this->_lines = (int) $params[$i]->getValue(); + break; + } + } + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/IconvFilter.php b/buildscripts/phing/classes/phing/filters/IconvFilter.php new file mode 100755 index 00000000..c9883b17 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/IconvFilter.php @@ -0,0 +1,155 @@ +. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Encode data from in encoding to out encoding. + * + * Example: + *
+ * 
+ * 
+ * Or: + *
+ * 
+ *    
+ *    
+ * 
+ * 
+ * + * @author Alexey Shockov, + * @version $Id$ + * @package phing.filters + */ +class IconvFilter + extends BaseParamFilterReader + implements ChainableReader { + + private $_inputEncoding; + + private $_outputEncoding; + + /** + * Returns first n lines of stream. + * @return 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 + */ + function read($len = null) { + $this->_initialize(); + + // Process whole text at once. + $text = null; + while (($data = $this->in->read($len)) !== -1) { + $text .= $data; + } + + // At the end. + if (null === $text) { + return -1; + } + + $this->log( + "Encoding " . $this->in->getResource() . " from " . $this->getInputEncoding() . " to " . $this->getOutputEncoding(), + Project::MSG_VERBOSE + ); + + return iconv($this->_inputEncoding, $this->_outputEncoding, $text); + } + + /** + * + * @param string $encoding Input encoding. + */ + public function setInputEncoding($encoding) { + $this->_inputEncoding = $encoding; + } + + /** + * + * @return string + */ + public function getInputEncoding() { + return $this->_inputEncoding; + } + + /** + * + * @param string $encoding Output encoding. + */ + public function setOutputEncoding($encoding) { + $this->_outputEncoding = $encoding; + } + + /** + * + * @return string + */ + public function getOutputEncoding() { + return $this->_outputEncoding; + } + + /** + * Creates a new IconvFilter using the passed in Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. Must not be null. + * + * @return object A new filter based on this configuration, but filtering the specified reader. + */ + function chain(Reader $reader) { + $filter = new self($reader); + + $filter->setInputEncoding($this->getInputEncoding()); + $filter->setOutputEncoding($this->getOutputEncoding()); + + $filter->setInitialized(true); + $filter->setProject($this->getProject()); + + return $filter; + } + + /** + * Configuring object from the parameters list. + */ + private function _initialize() { + if ($this->getInitialized()) { + return; + } + + $params = $this->getParameters(); + if ($params !== null) { + foreach ($params as $param) { + if ('in' == $param->getName()) { + $this->setInputEncoding($param->getValue()); + } else if ('out' == $param->getName()) { + $this->setOutputEncoding($param->getValue()); + } + } + } + + $this->setInitialized(true); + } +} diff --git a/buildscripts/phing/classes/phing/filters/LineContains.php b/buildscripts/phing/classes/phing/filters/LineContains.php new file mode 100755 index 00000000..b84b62c1 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/LineContains.php @@ -0,0 +1,260 @@ +. + */ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Filter which includes only those lines that contain all the user-specified + * strings. + * + * Example: + * + *

+ *   
+ *   
+ * 
+ * + * Or: + * + *

+ *    
+ *    
+ * 
+ * + * This will include only those lines that contain foo and + * bar. + * + * @author Yannick Lecaillez + * @author Hans Lellelid + * @version $Id$ + * @see PhingFilterReader + * @package phing.filters +*/ +class LineContains extends BaseParamFilterReader implements ChainableReader { + + /** + * The parameter name for the string to match on. + * @var string + */ + const CONTAINS_KEY = "contains"; + + /** + * Array of Contains objects. + * @var array + */ + private $_contains = array(); + + /** + * [Deprecated] + * @var string + */ + private $_line = null; + + /** + * Returns all lines in a buffer that contain specified strings. + * @return mixed buffer, -1 on EOF + */ + function read($len = null) { + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + + if ($buffer === -1) { + return -1; + } + + $lines = explode("\n", $buffer); + $matched = array(); + $containsSize = count($this->_contains); + + foreach($lines as $line) { + for($i = 0 ; $i < $containsSize ; $i++) { + $containsStr = $this->_contains[$i]->getValue(); + if ( strstr($line, $containsStr) === false ) { + $line = null; + break; + } + } + if($line !== null) { + $matched[] = $line; + } + } + $filtered_buffer = implode("\n", $matched); + return $filtered_buffer; + } + + /** + * [Deprecated. For reference only, used to be read() method.] + * 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 + */ + function readChar() { + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $ch = -1; + + if ( $this->_line !== null ) { + $ch = substr($this->_line, 0, 1); + if ( strlen($this->_line) === 1 ) + $this->_line = null; + else + $this->_line = substr($this->_line, 1); + } else { + $this->_line = $this->readLine(); + if ( $this->_line === null ) { + $ch = -1; + } else { + $containsSize = count($this->_contains); + for($i = 0 ; $i < $containsSize ; $i++) { + $containsStr = $this->_contains[$i]->getValue(); + if ( strstr($this->_line, $containsStr) === false ) { + $this->_line = null; + break; + } + } + return $this->readChar(); + } + } + + return $ch; + } + + /** + * Adds a contains nested element. + * + * @return Contains The contains element added. + * Must not be null. + */ + function createContains() { + $num = array_push($this->_contains, new Contains()); + return $this->_contains[$num-1]; + } + + /** + * Sets the array of words which must be contained within a line read + * from the original stream in order for it to match this filter. + * + * @param array $contains An array of words which must be contained + * within a line in order for it to match in this filter. + * Must not be null. + */ + function setContains($contains) { + // type check, error must never occur, bad code of it does + if ( !is_array($contains) ) { + throw new Exception("Excpected array got something else"); + } + + $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 array The array 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. + */ + function getContains() { + return $this->_contains; + } + + /** + * Creates a new LineContains using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be null. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new LineContains($reader); + $newFilter->setContains($this->getContains()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Parses the parameters to add user-defined contains strings. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + foreach($params as $param) { + if ( self::CONTAINS_KEY == $param->getType() ) { + $cont = new Contains(); + $cont->setValue($param->getValue()); + array_push($this->_contains, $cont); + break; // because we only support a single contains + } + } + } + } +} + +/** + * Holds a contains element. + * + * @package phing.filters + */ +class Contains { + + /** + * @var string + */ + private $_value; + + /** + * Set 'contains' value. + * @param string $contains + */ + function setValue($contains) { + $this->_value = (string) $contains; + } + + /** + * Returns 'contains' value. + * @return string + */ + function getValue() { + return $this->_value; + } +} + diff --git a/buildscripts/phing/classes/phing/filters/LineContainsRegexp.php b/buildscripts/phing/classes/phing/filters/LineContainsRegexp.php new file mode 100755 index 00000000..c603978f --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/LineContainsRegexp.php @@ -0,0 +1,179 @@ +. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/types/RegularExpression.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Filter which includes only those lines that contain the user-specified + * regular expression matching strings. + * + * Example: + *

+ *   
+ * 
+ * + * Or: + * + *

+ *    
+ * 
+ * + * This will fetch all those lines that contain the pattern foo + * + * @author Yannick Lecaillez + * @author Hans Lellelid + * @version $Id$ + * @see FilterReader + * @package phing.filters + */ +class LineContainsRegexp extends BaseParamFilterReader implements ChainableReader { + + /** + * Parameter name for regular expression. + * @var string + */ + const REGEXP_KEY = "regexp"; + + /** + * Regular expressions that are applied against lines. + * @var array + */ + private $_regexps = array(); + + /** + * Returns all lines in a buffer that contain specified strings. + * @return mixed buffer, -1 on EOF + */ + function read($len = null) { + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + + if ($buffer === -1) { + return -1; + } + + $lines = explode("\n", $buffer); + $matched = array(); + + $regexpsSize = count($this->_regexps); + foreach($lines as $line) { + for($i = 0 ; $i<$regexpsSize ; $i++) { + $regexp = $this->_regexps[$i]; + $re = $regexp->getRegexp($this->getProject()); + $matches = $re->matches($line); + if ( !$matches ) { + $line = null; + break; + } + } + if($line !== null) { + $matched[] = $line; + } + } + $filtered_buffer = implode("\n", $matched); + return $filtered_buffer; + } + + /** + * Adds a regexp element. + * + * @return object regExp The regexp element added. + */ + function createRegexp() { + $num = array_push($this->_regexps, new RegularExpression()); + return $this->_regexps[$num-1]; + } + + /** + * 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 An array of regular expressions which must be contained + * within a line in order for it to match in this filter. Must not be + * null. + */ + function setRegexps($regexps) { + // type check, error must never occur, bad code of it does + if ( !is_array($regexps) ) { + throw new Exception("Excpected an 'array', got something else"); + } + $this->_regexps = $regexps; + } + + /** + * Returns the array of regular expressions which must be contained within + * a line read from the original stream in order for it to match this + * filter. + * + * @return array The array 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. + */ + function getRegexps() { + return $this->_regexps; + } + + /** + * Creates a new LineContainsRegExp using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be null. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new LineContainsRegExp($reader); + $newFilter->setRegexps($this->getRegexps()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Parses parameters to add user defined regular expressions. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0 ; $igetType() ) { + $pattern = $params[$i]->getValue(); + $regexp = new RegularExpression(); + $regexp->setPattern($pattern); + array_push($this->_regexps, $regexp); + } + } + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/PrefixLines.php b/buildscripts/phing/classes/phing/filters/PrefixLines.php new file mode 100755 index 00000000..9edcc02a --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/PrefixLines.php @@ -0,0 +1,142 @@ +. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Attaches a prefix to every line. + * + * Example: + *
+ * + * Or: + * + *

+ *  
+ * 
+ * + * @author Yannick Lecaillez + * @author hans lellelid, hans@velum.net + * @version $Id$ + * @access public + * @see FilterReader + * @package phing.filters +*/ +class PrefixLines extends BaseParamFilterReader implements ChainableReader { + + /** + * Parameter name for the prefix. + * @var string + */ + const PREFIX_KEY = "lines"; + + /** + * The prefix to be used. + * @var string + */ + private $_prefix = null; + + /** + * Adds a prefix to each line of input stream and returns resulting stream. + * + * @return mixed buffer, -1 on EOF + */ + function read($len = null) { + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + + if ($buffer === -1) { + return -1; + } + + $lines = explode("\n", $buffer); + $filtered = array(); + + foreach($lines as $line) { + $line = $this->_prefix . $line; + $filtered[] = $line; + } + + $filtered_buffer = implode("\n", $filtered); + return $filtered_buffer; + } + + /** + * Sets the prefix to add at the start of each input line. + * + * @param string $prefix The prefix to add at the start of each input line. + * May be null, in which case no prefix + * is added. + */ + function setPrefix($prefix) { + $this->_prefix = (string) $prefix; + } + + /** + * Returns the prefix which will be added at the start of each input line. + * + * @return string The prefix which will be added at the start of each input line + */ + function getPrefix() { + return $this->_prefix; + } + + /** + * Creates a new PrefixLines filter using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be null. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new PrefixLines($reader); + $newFilter->setPrefix($this->getPrefix()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Initializes the prefix if it is available from the parameters. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0, $_i=count($params) ; $i < $_i ; $i++) { + if ( self::PREFIX_KEY == $params[$i]->getName() ) { + $this->_prefix = (string) $params[$i]->getValue(); + break; + } + } + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/ReplaceRegexp.php b/buildscripts/phing/classes/phing/filters/ReplaceRegexp.php new file mode 100755 index 00000000..70e8940f --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/ReplaceRegexp.php @@ -0,0 +1,129 @@ +. +*/ + +require_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; +include_once 'phing/types/RegularExpression.php'; + +/** + * Performs a regexp find/replace on stream. + *

+ * Example:
+ *

+ * 
+ *    
+ *    
+ * 
+ * 
+ * + * @author Hans Lellelid + * @version $Id$ + * @package phing.filters + */ +class ReplaceRegexp extends BaseFilterReader implements ChainableReader { + + /** + * @var array RegularExpression[] + */ + private $regexps = array(); + + /** + * Creator method handles nested tags. + * @return RegularExpression + */ + function createRegexp() { + $num = array_push($this->regexps, new RegularExpression()); + return $this->regexps[$num-1]; + } + + /** + * Sets the current regexps. + * (Used when, e.g., cloning/chaining the method.) + * @param array RegularExpression[] + */ + function setRegexps($regexps) { + $this->regexps = $regexps; + } + + /** + * Gets the current regexps. + * (Used when, e.g., cloning/chaining the method.) + * @return array RegularExpression[] + */ + function getRegexps() { + return $this->regexps; + } + + /** + * Returns the filtered stream. + * The original stream is first read in fully, and the regex replace is performed. + * + * @param int $len Required $len for Reader compliance. + * + * @return mixed The filtered stream, or -1 if the end of the resulting stream has been reached. + * + * @exception IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + + $buffer = $this->in->read($len); + + if($buffer === -1) { + return -1; + } + + // perform regex replace here ... + foreach($this->regexps as $exptype) { + $regexp = $exptype->getRegexp($this->project); + try { + $buffer = $regexp->replace($buffer); + $this->log("Performing regexp replace: /".$regexp->getPattern()."/".$regexp->getReplace()."/g".$regexp->getModifiers(), Project::MSG_VERBOSE); + } catch (Exception $e) { + // perhaps mismatch in params (e.g. no replace or pattern specified) + $this->log("Error performing regexp replace: " . $e->getMessage(), Project::MSG_WARN); + } + } + + return $buffer; + } + + /** + * Creates a new ReplaceRegExp filter using the passed in + * Reader for instantiation. + * + * @param Reader $reader A Reader object providing the underlying stream. + * Must not be null. + * + * @return ReplaceRegExp A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new ReplaceRegExp($reader); + $newFilter->setProject($this->getProject()); + $newFilter->setRegexps($this->getRegexps()); + return $newFilter; + } + +} + + diff --git a/buildscripts/phing/classes/phing/filters/ReplaceTokens.php b/buildscripts/phing/classes/phing/filters/ReplaceTokens.php new file mode 100755 index 00000000..a5cd7521 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/ReplaceTokens.php @@ -0,0 +1,435 @@ +. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/types/TokenSource.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Replaces tokens in the original input with user-supplied values. + * + * Example: + * + *
;
+ *   
+ * 
+ * + * Or: + * + *

+ *   
+ *   
+ *   
+ * 
+ * + * @author Yannick Lecaillez + * @author hans lellelid, hans@velum.net + * @version $Id: 6c5d97f2254de3c08ac34baaabf6119c54a49a7d $ + * @access public + * @see BaseParamFilterReader + * @package phing.filters + */ +class ReplaceTokens extends BaseParamFilterReader implements ChainableReader { + + /** + * Default "begin token" character. + * @var string + */ + const DEFAULT_BEGIN_TOKEN = "@"; + + /** + * Default "end token" character. + * @var string + */ + const DEFAULT_END_TOKEN = "@"; + + /** + * [Deprecated] Data that must be read from, if not null. + * @var string + */ + private $_queuedData = null; + + /** + * Array to hold the replacee-replacer pairs (String to String). + * @var array + */ + private $_tokens = array(); + + /** + * Array to hold the token sources that make tokens from + * different sources available + * @var array + */ + private $_tokensources = array(); + + /** + * Array holding all tokens given directly to the Filter and + * those passed via a TokenSource. + * @var array + */ + private $_alltokens = null; + + /** + * Character marking the beginning of a token. + * @var string + */ + private $_beginToken = "@"; // self::DEFAULT_BEGIN_TOKEN; + + /** + * Character marking the end of a token. + * @var string + */ + private $_endToken = "@"; //self::DEFAULT_END_TOKEN; + + /** + * Performs lookup on key and returns appropriate replacement string. + * @param array $matches Array of 1 el containing key to search for. + * @return string Text with which to replace key or value of key if none is found. + * @access private + */ + private function replaceTokenCallback($matches) { + + $key = $matches[1]; + + /* Get tokens from tokensource and merge them with the + * tokens given directly via build file. This should be + * done a bit more elegantly + */ + if ($this->_alltokens === null) { + $this->_alltokens = array(); + + $count = count($this->_tokensources); + for ($i = 0; $i < $count; $i++) { + $source = $this->_tokensources[$i]; + $this->_alltokens = array_merge($this->_alltokens, $source->getTokens()); + } + + + $this->_alltokens = array_merge($this->_tokens, $this->_alltokens); + } + + $tokens = $this->_alltokens; + + $replaceWith = null; + $count = count($tokens); + + for ($i = 0; $i < $count; $i++) { + if ($tokens[$i]->getKey() === $key) { + $replaceWith = $tokens[$i]->getValue(); + } + } + + if ($replaceWith === null) { + $replaceWith = $this->_beginToken . $key . $this->_endToken; + $this->log("No token defined for key \"".$this->_beginToken . $key . $this->_endToken."\""); + } else { + $this->log("Replaced \"".$this->_beginToken . $key . $this->_endToken ."\" with \"".$replaceWith."\"", Project::MSG_VERBOSE); + } + + return $replaceWith; + } + + /** + * Returns stream with tokens having been replaced with appropriate values. + * If a replacement value is not found for a token, the token is left in the stream. + * + * @return mixed filtered stream, -1 on EOF. + */ + function read($len = null) { + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + // read from next filter up the chain + $buffer = $this->in->read($len); + + if($buffer === -1) { + return -1; + } + + // filter buffer + $buffer = preg_replace_callback( + "/".preg_quote($this->_beginToken, '/')."([\w\.\-:]+?)".preg_quote($this->_endToken, '/')."/", + array($this, 'replaceTokenCallback'), $buffer); + + return $buffer; + } + + /** + * Sets the "begin token" character. + * + * @param string $beginToken the character used to denote the beginning of a token. + */ + function setBeginToken($beginToken) { + $this->_beginToken = (string) $beginToken; + } + + /** + * Returns the "begin token" character. + * + * @return string The character used to denote the beginning of a token. + */ + function getBeginToken() { + return $this->_beginToken; + } + + /** + * Sets the "end token" character. + * + * @param string $endToken the character used to denote the end of a token + */ + function setEndToken($endToken) { + $this->_endToken = (string) $endToken; + } + + /** + * Returns the "end token" character. + * + * @return the character used to denote the beginning of a token + */ + function getEndToken() { + return $this->_endToken; + } + + /** + * Adds a token element to the map of tokens to replace. + * + * @return object The token added to the map of replacements. + * Must not be null. + */ + function createToken() { + $num = array_push($this->_tokens, new Token()); + return $this->_tokens[$num-1]; + } + + /** + * Adds a token source to the sources of this filter. + * + * @return object A Reference to the source just added. + */ + function createTokensource() { + $num = array_push($this->_tokensources, new TokenSource()); + return $this->_tokensources[$num-1]; + } + + /** + * Sets the map of tokens to replace. + * ; used by ReplaceTokens::chain() + * + * @param array A map (String->String) of token keys to replacement + * values. Must not be null. + */ + function setTokens($tokens) { + // type check, error must never occur, bad code of it does + if ( !is_array($tokens) ) { + throw new Exception("Excpected 'array', got something else"); + } + + $this->_tokens = $tokens; + } + + /** + * Returns the map of tokens which will be replaced. + * ; used by ReplaceTokens::chain() + * + * @return array A map (String->String) of token keys to replacement values. + */ + function getTokens() { + return $this->_tokens; + } + + /** + * Sets the tokensources to use; used by ReplaceTokens::chain() + * + * @param array An array of token sources. + */ + function setTokensources($sources) { + // type check + if ( !is_array($sources)) { + throw new Exception("Exspected 'array', got something else"); + } + $this->_tokensources = $sources; + } + + /** + * Returns the token sources used by this filter; used by ReplaceTokens::chain() + * + * @return array + */ + function getTokensources() { + return $this->_tokensources; + } + + /** + * Creates a new ReplaceTokens using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be null. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new ReplaceTokens($reader); + $newFilter->setProject($this->getProject()); + $newFilter->setBeginToken($this->getBeginToken()); + $newFilter->setEndToken($this->getEndToken()); + $newFilter->setTokens($this->getTokens()); + $newFilter->setTokensources($this->getTokensources()); + $newFilter->setInitialized(true); + return $newFilter; + } + + /** + * Initializes tokens and loads the replacee-replacer hashtable. + * This method is only called when this filter is used through + * a tag in build file. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0 ; $igetType(); + if ( $type === "tokenchar" ) { + $name = $params[$i]->getName(); + if ( $name === "begintoken" ) { + $this->_beginToken = substr($params[$i]->getValue(), 0, 1); + } else if ( $name === "endtoken" ) { + $this->_endToken = substr($params[$i]->getValue(), 0, 1); + } + } else if ( $type === "token" ) { + $name = $params[$i]->getName(); + $value = $params[$i]->getValue(); + + $tok = new Token(); + $tok->setKey($name); + $tok->setValue($value); + + array_push($this->_tokens, $tok); + } else if ( $type === "tokensource" ) { + // Store data from nested tags in local array + $arr = array(); $subparams = $params[$i]->getParams(); + $count = count($subparams); + for ($i = 0; $i < $count; $i++) { + $arr[$subparams[$i]->getName()] = $subparams[$i]->getValue(); + } + + // Create TokenSource + $tokensource = new TokenSource(); + if (isset($arr["classname"])) + $tokensource->setClassname($arr["classname"]); + + // Copy other parameters 1:1 to freshly created TokenSource + foreach ($arr as $key => $value) { + if (strtolower($key) === "classname") + continue; + $param = $tokensource->createParam(); + $param->setName($key); + $param->setValue($value); + } + + $this->_tokensources[] = $tokensource; + } + } + } + } + } +} + +/** + * Holds a token. + * + * @package phing.filters + */ +class Token { + + /** + * Token key. + * @var string + */ + private $_key; + + /** + * Token value. + * @var string + */ + private $_value; + + /** + * Sets the token key. + * + * @param string $key The key for this token. Must not be null. + */ + function setKey($key) { + $this->_key = (string) $key; + } + + /** + * Sets the token value. + * + * @param string $value The value for this token. Must not be null. + */ + function setValue($value) { + // special case for boolean values + if (is_bool($value)) { + if ($value) { + $this->_value = "true"; + } else { + $this->_value = "false"; + } + } else { + $this->_value = (string) $value; + } + } + + /** + * Returns the key for this token. + * + * @return string The key for this token. + */ + function getKey() { + return $this->_key; + } + + /** + * Returns the value for this token. + * + * @return string The value for this token. + */ + function getValue() { + return $this->_value; + } + + /** + * Sets the token value from text. + * + * @param string $value The value for this token. Must not be null. + */ + function addText($value) { + $this->setValue($value); + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/ReplaceTokensWithFile.php b/buildscripts/phing/classes/phing/filters/ReplaceTokensWithFile.php new file mode 100644 index 00000000..580b8d84 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/ReplaceTokensWithFile.php @@ -0,0 +1,361 @@ +. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Replaces tokens in the original input with the contents of a file. + * The file to be used is controlled by the name of the token which + * corresponds to the basename of the file to be used together with + * the optional pre and postfix strings that is possible to set. + * + * By default all HTML entities in the file is replaced by the + * corresponding HTML entities. This behaviour can be controlled by + * the "translatehtml" parameter. + * + * Supported parameters are: + *
+ *  prefix         string Text to be prefixed to token before using as filename
+ *  postfix        string Text to be prefixed to token before using as filename
+ *  dir            string The directory where the files should be read from
+ *  translatehtml  bool   If we should translate all HTML entities in the file.
+ * 
+ * Example: + * + *

+ *   
+ *   
+ * 
+ * + * @author johan persson, johanp@aditus.nu + * @version $Id: 164a2d9eeba3673653086b32e9fa2045168c992c $ + * @access public + * @see ReplaceTokensWithFile + * @package phing.filters + */ +class ReplaceTokensWithFile extends BaseParamFilterReader implements ChainableReader { + + /** + * Default "begin token" character. + * @var string + */ + const DEFAULT_BEGIN_TOKEN = "#@#"; + + /** + * Default "end token" character. + * @var string + */ + const DEFAULT_END_TOKEN = "#@#"; + + /** + * Array to hold the token sources that make tokens from + * different sources available + * @var array + */ + private $_tokensources = array(); + + /** + * Character marking the beginning of a token. + * @var string + */ + private $_beginToken = ReplaceTokensWithFile::DEFAULT_BEGIN_TOKEN; + + /** + * Character marking the end of a token. + * @var string + */ + private $_endToken = ReplaceTokensWithFile::DEFAULT_END_TOKEN; + + /** + * File prefix to be inserted in front of the token to create the + * file name to be used. + * @var string + */ + private $_prefix = ''; + + /** + * File postfix to be inserted in front of the token to create the + * file name to be used. + * @var string + */ + private $_postfix = ''; + + /** + * Directory where to look for the files. The default is to look in the + * current file. + * + * @var string + */ + private $_dir = './'; + + /** + * Translate all HTML entities in the file to the corresponding HTML + * entities before it is used as replacements. For example all '<' + * will be translated to < before the content is inserted. + * + * @var boolean + */ + private $_translatehtml = true; + + + /** + * Sets the drectory where to look for the files to use for token replacement + * + * @param string $dir + */ + function setTranslateHTML($translate) { + $this->_translatehtml = (bool) $translate; + } + + /** + * Returns the drectory where to look for the files to use for token replacement + */ + function getTranslateHTML() { + return $this->_translatehtml; + } + + /** + * Sets the drectory where to look for the files to use for token replacement + * + * @param string $dir + */ + function setDir($dir) { + $this->_dir = (string) $dir; + } + + /** + * Returns the drectory where to look for the files to use for token replacement + */ + function getDir() { + return $this->_dir; + } + + /** + * Sets the prefix that is prepended to the token in order to create the file + * name. For example if the token is 01 and the prefix is "example" then + * the filename to look for will be "example01" + * + * @param string $prefix + */ + function setPrefix($prefix) { + $this->_prefix = (string) $prefix; + } + + /* + * Returns the prefix that is prepended to the token in order to create the file + * name. For example if the token is 01 and the prefix is "example" then + * the filename to look for will be "example01" + */ + function getPrefix() { + return $this->_prefix; + } + + /** + * Sets the postfix that is added to the token in order to create the file + * name. For example if the token is 01 and the postfix is ".php" then + * the filename to look for will be "01.php" + * + * @param string $postfix + */ + function setPostfix($postfix) { + $this->_postfix = (string) $postfix; + } + + /** + * Returns the postfix that is added to the token in order to create the file + * name. For example if the token is 01 and the postfix is ".php" then + * the filename to look for will be "01.php" + */ + function getPostfix() { + return $this->_postfix; + } + + /** + * Sets the "begin token" character. + * + * @param string $beginToken the character used to denote the beginning of a token. + */ + function setBeginToken($beginToken) { + $this->_beginToken = (string) $beginToken; + } + + /** + * Returns the "begin token" character. + * + * @return string The character used to denote the beginning of a token. + */ + function getBeginToken() { + return $this->_beginToken; + } + + /** + * Sets the "end token" character. + * + * @param string $endToken the character used to denote the end of a token + */ + function setEndToken($endToken) { + $this->_endToken = (string) $endToken; + } + + /** + * Returns the "end token" character. + * + * @return the character used to denote the beginning of a token + */ + function getEndToken() { + return $this->_endToken; + } + + /** + * Replace the token found with the appropriate file contents + * @param array $matches Array of 1 el containing key to search for. + * @return string Text with which to replace key or value of key if none is found. + * @access private + */ + private function replaceTokenCallback($matches) { + + $filetoken = $matches[1]; + + // We look in all specified directories for the named file and use + // the first directory which has the file. + $dirs = explode(';',$this->_dir); + + $ndirs = count($dirs); + $n = 0; + $file = $dirs[$n] . $this->_prefix . $filetoken . $this->_postfix; + + while ( $n < $ndirs && ! is_readable($file) ) { + ++$n; + } + + if( ! is_readable($file) || $n >= $ndirs ) { + $this->log("Can not read or find file \"$file\". Searched in directories: {$this->_dir}", Project::MSG_WARN); + //return $this->_beginToken . $filetoken . $this->_endToken; + return "[Phing::Filters::ReplaceTokensWithFile: Can not find file " . '"' . $filetoken . $this->_postfix . '"' . "]"; + } + + $buffer = file_get_contents($file); + if( $this->_translatehtml ) { + $buffer = htmlentities($buffer); + } + + if ($buffer === null) { + $buffer = $this->_beginToken . $filetoken . $this->_endToken; + $this->log("No corresponding file found for key \"$buffer\"", Project::MSG_WARN); + } else { + $this->log("Replaced \"".$this->_beginToken . $filetoken . $this->_endToken."\" with content from file \"$file\""); + } + + return $buffer; + } + + /** + * Returns stream with tokens having been replaced with appropriate values. + * If a replacement value is not found for a token, the token is left in the stream. + * + * @return mixed filtered stream, -1 on EOF. + */ + function read($len = null) { + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + // read from next filter up the chain + $buffer = $this->in->read($len); + + if($buffer === -1) { + return -1; + } + + // filter buffer + $buffer = preg_replace_callback( + "/".preg_quote($this->_beginToken)."([\w\.\-:\/]+?)".preg_quote($this->_endToken)."/", + array($this, 'replaceTokenCallback'), $buffer); + + return $buffer; + } + + /** + * Creates a new ReplaceTokensWithFile using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be null. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new ReplaceTokensWithFile($reader); + $newFilter->setProject($this->getProject()); + $newFilter->setTranslateHTML($this->getTranslateHTML()); + $newFilter->setDir($this->getDir()); + $newFilter->setPrefix($this->getPrefix()); + $newFilter->setPostfix($this->getPostfix()); + $newFilter->setBeginToken($this->getBeginToken()); + $newFilter->setEndToken($this->getEndToken()); + $newFilter->setInitialized(true); + return $newFilter; + } + + /** + * Initializes parameters + * This method is only called when this filter is used through + * a tag in build file. + */ + private function _initialize() { + $params = $this->getParameters(); + $n = count($params); + + if ( $params !== null ) { + for($i = 0 ; $i < $n ; $i++) { + if ( $params[$i] !== null ) { + $name = $params[$i]->getName(); + switch( $name ) { + case 'begintoken' : + $this->_beginToken = $params[$i]->getValue(); + break; + case 'endtoken' : + $this->_endToken = $params[$i]->getValue(); + break; + case 'dir': + $this->_dir = $params[$i]->getValue(); + break; + case 'prefix': + $this->_prefix = $params[$i]->getValue(); + break; + case 'postfix': + $this->_postfix = $params[$i]->getValue(); + break; + case 'translatehtml': + $this->_translatehtml = $params[$i]->getValue(); + break; + } + } + } + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/StripLineBreaks.php b/buildscripts/phing/classes/phing/filters/StripLineBreaks.php new file mode 100755 index 00000000..08d1aa6b --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/StripLineBreaks.php @@ -0,0 +1,148 @@ +. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Filter to flatten the stream to a single line. + * + * Example: + * + *
+ * + * Or: + * + *
+ * + * @author Yannick Lecaillez + * @author hans lellelid, hans@velum.net + * @version $Id$ + * @access public + * @see BaseParamFilterReader + * @package phing.filters + */ +class StripLineBreaks extends BaseParamFilterReader implements ChainableReader { + + /** + * Default line-breaking characters. + * @var string + */ + const DEFAULT_LINE_BREAKS = "\r\n"; + + /** + * Parameter name for the line-breaking characters parameter. + * @var string + */ + const LINES_BREAKS_KEY = "linebreaks"; + + /** + * The characters that are recognized as line breaks. + * @var string + */ + private $_lineBreaks = "\r\n"; // self::DEFAULT_LINE_BREAKS; + + /** + * Returns the filtered stream, only including + * characters not in the set of line-breaking characters. + * + * @return mixed 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 + */ + function read($len = null) { + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + if($buffer === -1) { + return -1; + } + + $buffer = preg_replace("/[".$this->_lineBreaks."]/", '', $buffer); + + return $buffer; + } + + /** + * Sets the line-breaking characters. + * + * @param string $lineBreaks A String containing all the characters to be + * considered as line-breaking. + */ + function setLineBreaks($lineBreaks) { + $this->_lineBreaks = (string) $lineBreaks; + } + + /** + * Gets the line-breaking characters. + * + * @return string A String containing all the characters that are considered as line-breaking. + */ + function getLineBreaks() { + return $this->_lineBreaks; + } + + /** + * Creates a new StripLineBreaks using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be null. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new StripLineBreaks($reader); + $newFilter->setLineBreaks($this->getLineBreaks()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Parses the parameters to set the line-breaking characters. + */ + private function _initialize() { + $userDefinedLineBreaks = null; + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0 ; $igetName() ) { + $userDefinedLineBreaks = $params[$i]->getValue(); + break; + } + } + } + + if ( $userDefinedLineBreaks !== null ) { + $this->_lineBreaks = $userDefinedLineBreaks; + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/StripLineComments.php b/buildscripts/phing/classes/phing/filters/StripLineComments.php new file mode 100755 index 00000000..2a19ca25 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/StripLineComments.php @@ -0,0 +1,207 @@ +. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * This filter strips line comments. + * + * Example: + * + *

+ *   
+ *   
+ *   
+ *   
+ *   
+ * 
+ * + * Or: + * + *

+ *   
+ *   
+ *   
+ *   
+ *   
+ * 
+ * + * @author Yannick Lecaillez + * @author hans lellelid, hans@velum.net + * @version $Id$ + * @access public + * @see BaseParamFilterReader + * @package phing.filters + */ +class StripLineComments extends BaseParamFilterReader implements ChainableReader { + + /** Parameter name for the comment prefix. */ + const COMMENTS_KEY = "comment"; + + /** Array that holds the comment prefixes. */ + private $_comments = array(); + + /** + * Returns stream only including + * lines from the original stream which don't start with any of the + * specified comment prefixes. + * + * @return mixed the resulting stream, or -1 + * if the end of the resulting stream has been reached. + * + * @throws IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + + if ($buffer === -1) { + return -1; + } + + $lines = explode("\n", $buffer); + $filtered = array(); + + $commentsSize = count($this->_comments); + + foreach($lines as $line) { + for($i = 0; $i < $commentsSize; $i++) { + $comment = $this->_comments[$i]->getValue(); + if ( StringHelper::startsWith($comment, ltrim($line)) ) { + $line = null; + break; + } + } + if ($line !== null) { + $filtered[] = $line; + } + } + + $filtered_buffer = implode("\n", $filtered); + return $filtered_buffer; + } + + /* + * Adds a comment element to the list of prefixes. + * + * @return comment The comment element added to the + * list of comment prefixes to strip. + */ + function createComment() { + $num = array_push($this->_comments, new Comment()); + return $this->_comments[$num-1]; + } + + /* + * 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 null. + */ + function setComments($lineBreaks) { + if (!is_array($lineBreaks)) { + throw new Exception("Excpected 'array', got something else"); + } + $this->_comments = $lineBreaks; + } + + /* + * Returns the list of comment prefixes to strip. + * + * @return array The list of comment prefixes to strip. + */ + function getComments() { + return $this->_comments; + } + + /* + * Creates a new StripLineComments using the passed in + * Reader for instantiation. + * + * @param reader A Reader object providing the underlying stream. + * Must not be null. + * + * @return a new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new StripLineComments($reader); + $newFilter->setComments($this->getComments()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /* + * Parses the parameters to set the comment prefixes. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0 ; $igetType() ) { + $comment = new Comment(); + $comment->setValue($params[$i]->getValue()); + array_push($this->_comments, $comment); + } + } + } + } +} + +/** + * The class that holds a comment representation. + * + * @package phing.filters + */ +class Comment { + + /** The prefix for a line comment. */ + private $_value; + + /* + * Sets the prefix for this type of line comment. + * + * @param string $value The prefix for a line comment of this type. + * Must not be null. + */ + function setValue($value) { + $this->_value = (string) $value; + } + + /* + * Returns the prefix for this type of line comment. + * + * @return string The prefix for this type of line comment. + */ + function getValue() { + return $this->_value; + } +} + diff --git a/buildscripts/phing/classes/phing/filters/StripPhpComments.php b/buildscripts/phing/classes/phing/filters/StripPhpComments.php new file mode 100755 index 00000000..0abb8a67 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/StripPhpComments.php @@ -0,0 +1,188 @@ +. +*/ + +include_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * This is a Php comment and string stripper reader that filters + * those lexical tokens out for purposes of simple Php parsing. + * (if you have more complex Php parsing needs, use a real lexer). + * Since this class heavily relies on the single char read function, + * you are reccomended to make it work on top of a buffered reader. + * + * @author Yannick Lecaillez + * @author hans lellelid, hans@velum.net + * @version $Id$ + * @access public + * @see FilterReader + * @package phing.filters + */ +class StripPhpComments extends BaseFilterReader implements ChainableReader { + /** + * The read-ahead character, used for effectively pushing a single + * character back. -1 indicates that no character is in the buffer. + */ + private $_readAheadCh = -1; + + /** + * Whether or not the parser is currently in the middle of a string + * literal. + * @var boolean + */ + private $_inString = false; + + /** + * Returns the stream without Php comments. + * + * @return the resulting stream, or -1 + * if the end of the resulting stream has been reached + * + * @throws IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + + $buffer = $this->in->read($len); + if($buffer === -1) { + return -1; + } + + // This regex replace /* */ and // style comments + $buffer = preg_replace('/\/\*[^*]*\*+([^\/*][^*]*\*+)*\/|\/\/[^\n]*|("(\\\\.|[^"\\\\])*"|\'(\\\\.|[^\'\\\\])*\'|.[^\/"\'\\\\]*)/s', "$2", $buffer); + + // The regex above is not identical to, but is based on the expression below: + // + // created by Jeffrey Friedl + // and later modified by Fred Curtis. + // s{ + // /\* ## Start of /* ... */ comment + // [^*]*\*+ ## Non-* followed by 1-or-more *'s + // ( + // [^/*][^*]*\*+ + // )* ## 0-or-more things which don't start with / + // ## but do end with '*' + // / ## End of /* ... */ comment + // + // | ## OR various things which aren't comments: + // + // ( + // " ## Start of " ... " string + // ( + // \\. ## Escaped char + // | ## OR + // [^"\\] ## Non "\ + // )* + // " ## End of " ... " string + // + // | ## OR + // + // ' ## Start of ' ... ' string + // ( + // \\. ## Escaped char + // | ## OR + // [^'\\] ## Non '\ + // )* + // ' ## End of ' ... ' string + // + // | ## OR + // + // . ## Anything other char + // [^/"'\\]* ## Chars which doesn't start a comment, string or escape + // ) + // }{$2}gxs; + + return $buffer; + } + + + /* + * Returns the next character in the filtered stream, not including + * Php comments. + * + * @return the next character in the resulting stream, or -1 + * if the end of the resulting stream has been reached + * + * @throws IOException if the underlying stream throws an IOException + * during reading + * @deprecated + */ + function readChar() { + $ch = -1; + + if ( $this->_readAheadCh !== -1 ) { + $ch = $this->_readAheadCh; + $this->_readAheadCh = -1; + } else { + $ch = $this->in->readChar(); + if ( $ch === "\"" ) { + $this->_inString = !$this->_inString; + } else { + if ( !$this->_inString ) { + if ( $ch === "/" ) { + $ch = $this->in->readChar(); + if ( $ch === "/" ) { + while ( $ch !== "\n" && $ch !== -1 ) { + $ch = $this->in->readChar(); + } + } else if ( $ch === "*" ) { + while ( $ch !== -1 ) { + $ch = $this->in->readChar(); + while ( $ch === "*" && $ch !== -1 ) { + $ch = $this->in->readChar(); + } + + if ( $ch === "/" ) { + $ch = $this->readChar(); + echo "$ch\n"; + break; + } + } + } else { + $this->_readAheadCh = $ch; + $ch = "/"; + } + } + } + } + } + + return $ch; + } + + /** + * Creates a new StripPhpComments using the passed in + * Reader for instantiation. + * + * @param reader A Reader object providing the underlying stream. + * Must not be null. + * + * @return a new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new StripPhpComments($reader); + $newFilter->setProject($this->getProject()); + return $newFilter; + } +} + diff --git a/buildscripts/phing/classes/phing/filters/StripWhitespace.php b/buildscripts/phing/classes/phing/filters/StripWhitespace.php new file mode 100755 index 00000000..d7b6113f --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/StripWhitespace.php @@ -0,0 +1,95 @@ +. +*/ + +include_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Strips whitespace from [php] files using PHP stripwhitespace() method. + * + * @author Hans Lellelid, hans@velum.net + * @version $Id$ + * @see FilterReader + * @package phing.filters + * @todo -c use new PHP functions to perform this instead of regex. + */ +class StripWhitespace extends BaseFilterReader implements ChainableReader { + + private $processed = false; + + /** + * Returns the stream without Php comments and whitespace. + * + * @return the resulting stream, or -1 + * if the end of the resulting stream has been reached + * + * @throws IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + + if ($this->processed === true) { + return -1; // EOF + } + + // Read XML + $php = null; + while ( ($buffer = $this->in->read($len)) !== -1 ) { + $php .= $buffer; + } + + if ($php === null ) { // EOF? + return -1; + } + + if(empty($php)) { + $this->log("PHP file is empty!", Project::MSG_WARN); + return ''; // return empty string, don't attempt to strip whitespace + } + + // write buffer to a temporary file, since php_strip_whitespace() needs a filename + $file = new PhingFile(tempnam(PhingFile::getTempDir(), 'stripwhitespace')); + file_put_contents($file->getAbsolutePath(), $php); + $output = php_strip_whitespace($file->getAbsolutePath()); + unlink($file->getAbsolutePath()); + + $this->processed = true; + + return $output; + } + + /** + * Creates a new StripWhitespace using the passed in + * Reader for instantiation. + * + * @param reader A Reader object providing the underlying stream. + * Must not be null. + * + * @return a new filter based on this configuration, but filtering + * the specified reader + */ + public function chain(Reader $reader) { + $newFilter = new StripWhitespace($reader); + $newFilter->setProject($this->getProject()); + return $newFilter; + } +} diff --git a/buildscripts/phing/classes/phing/filters/TabToSpaces.php b/buildscripts/phing/classes/phing/filters/TabToSpaces.php new file mode 100755 index 00000000..80d3c215 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/TabToSpaces.php @@ -0,0 +1,144 @@ +. +*/ + +require_once 'phing/filters/BaseParamFilterReader.php'; +require_once 'phing/filters/ChainableReader.php'; + +/** + * Converts tabs to spaces. + * + * Example: + * + *
+ * + * Or: + * + *

+ *   
+ * 
+ * + * @author Yannick Lecaillez + * @author Hans Lellelid + * @version $Id$ + * @see BaseParamFilterReader + * @package phing.filters + */ +class TabToSpaces extends BaseParamFilterReader implements ChainableReader { + + /** + * The default tab length. + * @var int + */ + const DEFAULT_TAB_LENGTH = 8; + + /** + * Parameter name for the length of a tab. + * @var string + */ + const TAB_LENGTH_KEY = "tablength"; + + /** + * Tab length in this filter. + * @var int + */ + private $tabLength = 8; //self::DEFAULT_TAB_LENGTH; + + /** + * Returns stream after converting tabs to the specified number of spaces. + * + * @return 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 + */ + function read($len = null) { + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + + if($buffer === -1) { + return -1; + } + + $buffer = str_replace("\t", str_repeat(' ', $this->tabLength), $buffer); + + return $buffer; + } + + /** + * Sets the tab length. + * + * @param int $tabLength The number of spaces to be used when converting a tab. + */ + function setTablength($tabLength) { + $this->tabLength = (int) $tabLength; + } + + /** + * Returns the tab length. + * + * @return int The number of spaces used when converting a tab + */ + function getTablength() { + return $this->tabLength; + } + + /** + * Creates a new TabsToSpaces using the passed in + * Reader for instantiation. + * + * @param Reader $reader A Reader object providing the underlying stream. + * Must not be null. + * + * @return Reader A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new TabToSpaces($reader); + $newFilter->setTablength($this->getTablength()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Parses the parameters to set the tab length. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0 ; $igetName()) { + $this->tabLength = $params[$i]->getValue(); + break; + } + } + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/TailFilter.php b/buildscripts/phing/classes/phing/filters/TailFilter.php new file mode 100755 index 00000000..95a9c6f5 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/TailFilter.php @@ -0,0 +1,157 @@ +. +*/ + +require_once 'phing/filters/BaseParamFilterReader.php'; + +/** + * Reads the last n lines of a stream. (Default is last10 lines.) + * + * Example: + * + *
+ * + * Or: + * + *

+ *   
+ * 
+ * + * @author Yannick Lecaillez + * @author hans lellelid, hans@velum.net + * @copyright � 2003 seasonfive. All rights reserved + * @version $Id$ + * @see BaseParamFilterReader + * @package phing.filters + */ +class TailFilter extends BaseParamFilterReader implements ChainableReader { + + /** + * Parameter name for the number of lines to be returned. + * @var string + */ + const LINES_KEY = "lines"; + + + /** + * Number of lines to be returned in the filtered stream. + * @var integer + */ + private $_lines = 10; + + /** + * Array to hold lines. + * @var array + */ + private $_lineBuffer = array(); + + /** + * Returns the last n lines of a file. + * @param int $len Num chars to read. + * @return mixed The filtered buffer or -1 if EOF. + */ + function read($len = null) { + + while ( ($buffer = $this->in->read($len)) !== -1 ) { + // Remove the last "\n" from buffer for + // prevent explode to add an empty cell at + // the end of array + $buffer= trim($buffer, "\n"); + + $lines = explode("\n", $buffer); + + if ( count($lines) >= $this->_lines ) { + // Buffer have more (or same) number of lines than needed. + // Fill lineBuffer with the last "$this->_lines" lasts ones. + $off = count($lines)-$this->_lines; + $this->_lineBuffer = array_slice($lines, $off); + } else { + // Some new lines ... + // Prepare space for insert these new ones + $this->_lineBuffer = array_slice($this->_lineBuffer, count($lines)-1); + $this->_lineBuffer = array_merge($this->_lineBuffer, $lines); + } + } + + if ( empty($this->_lineBuffer) ) + $ret = -1; + else { + $ret = implode("\n", $this->_lineBuffer); + $this->_lineBuffer = array(); + } + + return $ret; + } + + /** + * Sets the number of lines to be returned in the filtered stream. + * + * @param integer $lines the number of lines to be returned in the filtered stream. + */ + function setLines($lines) { + $this->_lines = (int) $lines; + } + + /** + * Returns the number of lines to be returned in the filtered stream. + * + * @return integer The number of lines to be returned in the filtered stream. + */ + function getLines() { + return $this->_lines; + } + + /** + * Creates a new TailFilter using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be null. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader. + */ + function chain(Reader $reader) { + $newFilter = new TailFilter($reader); + $newFilter->setLines($this->getLines()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + 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. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i=0, $_i=count($params); $i < $_i; $i++) { + if ( self::LINES_KEY == $params[$i]->getName() ) { + $this->_lines = (int) $params[$i]->getValue(); + break; + } + } + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/TidyFilter.php b/buildscripts/phing/classes/phing/filters/TidyFilter.php new file mode 100755 index 00000000..22abcee5 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/TidyFilter.php @@ -0,0 +1,162 @@ +. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * This filter uses the bundled-with-PHP Tidy extension to filter input. + * + *

+ * Example:
+ *

+ * 
+ *   
+ *   
+ * 
+ * 
+ * + * @author Hans Lellelid + * @version $Id$ + * @package phing.filters + */ +class TidyFilter extends BaseParamFilterReader implements ChainableReader { + + /** @var string Encoding of resulting document. */ + private $encoding = 'utf8'; + + /** @var array Parameter[] */ + private $configParameters = array(); + + /** + * Set the encoding for resulting (X)HTML document. + * @param string $v + */ + public function setEncoding($v) { + $this->encoding = $v; + } + + /** + * Sets the config params. + * @param array Parameter[] + * @see chain() + */ + public function setConfigParameters($params) + { + $this->configParameters = $params; + } + + /** + * Adds a element (which is a Parameter). + * @return Parameter + */ + public function createConfig() { + $num = array_push($this->configParameters, new Parameter()); + return $this->configParameters[$num-1]; + } + + /** + * Converts the Parameter objects being used to store configuration into a simle assoc array. + * @return array + */ + private function getDistilledConfig() { + $config = array(); + foreach($this->configParameters as $p) { + $config[$p->getName()] = $p->getValue(); + } + return $config; + } + + /** + * Reads input and returns Tidy-filtered output. + * + * @return the resulting stream, or -1 if the end of the resulting stream has been reached + * + * @throws IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + + if (!class_exists('Tidy')) { + throw new BuildException("You must enable the 'tidy' extension in your PHP configuration in order to use the Tidy filter."); + } + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + if($buffer === -1) { + return -1; + } + + $config = $this->getDistilledConfig(); + + $tidy = new Tidy(); + $tidy->parseString($buffer, $config, $this->encoding); + $tidy->cleanRepair(); + + return tidy_get_output($tidy); + + } + + + /** + * Creates a new TidyFilter using the passed in Reader for instantiation. + * + * @param reader A Reader object providing the underlying stream. + * Must not be null. + * + * @return a new filter based on this configuration, but filtering + * the specified reader + */ + public function chain(Reader $reader) { + $newFilter = new TidyFilter($reader); + $newFilter->setConfigParameters($this->configParameters); + $newFilter->setEncoding($this->encoding); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Initializes any parameters (e.g. config options). + * This method is only called when this filter is used through a tag in build file. + */ + private function _initialize() { + $params = $this->getParameters(); + if ($params) { + foreach($params as $param) { + if ($param->getType() == "config") { + $this->configParameters[] = $param; + } else { + + if ($param->getName() == "encoding") { + $this->setEncoding($param->getValue()); + } + + } + + } + } + } + +} diff --git a/buildscripts/phing/classes/phing/filters/TranslateGettext.php b/buildscripts/phing/classes/phing/filters/TranslateGettext.php new file mode 100755 index 00000000..b2a4264c --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/TranslateGettext.php @@ -0,0 +1,285 @@ +. +*/ + +require_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Replaces gettext("message id") and _("message id") with the translated string. + * + * Gettext is great for creating multi-lingual sites, but in some cases (e.g. for + * performance reasons) you may wish to replace the gettext calls with the translations + * of the strings; that's what this task is for. Note that this is similar to + * ReplaceTokens, but both the find and the replace aspect is more complicated -- hence + * this is a separate, stand-alone filter. + * + *

+ * Example:
+ *

+ * 
+ * 
+ * + * @author Hans Lellelid + * @version $Id$ + * @access public + * @see BaseFilterReader + * @package phing.filters + */ +class TranslateGettext extends BaseParamFilterReader implements ChainableReader { + + // constants for specifying keys to expect + // when this is called using + const DOMAIN_KEY = "domain"; + const DIR_KEY = "dir"; + const LOCALE_KEY = "locale"; + + /** The domain to use */ + private $domain = 'messages'; + + /** The dir containing LC_MESSAGES */ + private $dir; + + /** The locale to use */ + private $locale; + + /** The system locale before it was changed for this filter. */ + private $storedLocale; + + /** + * Set the text domain to use. + * The text domain must correspond to the name of the compiled .mo files. + * E.g. "messages" ==> $dir/LC_MESSAGES/messages.mo + * "mydomain" ==> $dir/LC_MESSAGES/mydomain.mo + * @param string $domain + */ + function setDomain($domain) { + $this->domain = $domain; + } + + /** + * Get the current domain. + * @return string + */ + function getDomain() { + return $this->domain; + } + + /** + * Sets the root locale directory. + * @param PhingFile $dir + */ + function setDir(PhingFile $dir) { + $this->dir = $dir; + } + + /** + * Gets the root locale directory. + * @return PhingFile + */ + function getDir() { + return $this->dir; + } + + /** + * Sets the locale to use for translation. + * Note that for gettext() to work, you have to make sure this locale + * is specific enough for your system (e.g. some systems may allow an 'en' locale, + * but others will require 'en_US', etc.). + * @param string $locale + */ + function setLocale($locale) { + $this->locale = $locale; + } + + /** + * Gets the locale to use for translation. + * @return string + */ + function getLocale() { + return $this->locale; + } + + /** + * Make sure that required attributes are set. + * @throws BuldException - if any required attribs aren't set. + */ + protected function checkAttributes() { + if (!$this->domain || !$this->locale || !$this->dir) { + throw new BuildException("You must specify values for domain, locale, and dir attributes."); + } + } + + /** + * Initialize the gettext/locale environment. + * This method will change some env vars and locale settings; the + * restoreEnvironment should put them all back :) + * + * @return void + * @throws BuildException - if locale cannot be set. + * @see restoreEnvironment() + */ + protected function initEnvironment() { + $this->storedLocale = getenv("LANG"); + + $this->log("Setting locale to " . $this->locale, Project::MSG_DEBUG); + putenv("LANG=".$this->locale); + $ret = setlocale(LC_ALL, $this->locale); + if ($ret === false) { + $msg = "Could not set locale to " . $this->locale + . ". You may need to use fully qualified name" + . " (e.g. en_US instead of en)."; + throw new BuildException($msg); + } + + $this->log("Binding domain '".$this->domain."' to " . $this->dir, Project::MSG_DEBUG); + bindtextdomain($this->domain, $this->dir->getAbsolutePath()); + textdomain($this->domain); + } + + /** + * Restores environment settings and locale. + * This does _not_ restore any gettext-specific settings + * (e.g. textdomain()). + * + * @return void + */ + protected function restoreEnvironment() { + putenv("LANG=".$this->storedLocale); + setlocale(LC_ALL, $this->storedLocale); + } + + /** + * Performs gettext translation of msgid and returns translated text. + * + * This function simply wraps gettext() call, but provides ability to log + * string replacements. (alternative would be using preg_replace with /e which + * would probably be faster, but no ability to debug/log.) + * + * @param array $matches Array of matches; we're interested in $matches[2]. + * @return string Translated text + */ + private function xlateStringCallback($matches) { + $charbefore = $matches[1]; + $msgid = $matches[2]; + $translated = gettext($msgid); + $this->log("Translating \"$msgid\" => \"$translated\"", Project::MSG_DEBUG); + return $charbefore . '"' . $translated . '"'; + } + + /** + * Returns the filtered stream. + * The original stream is first read in fully, and then translation is performed. + * + * @return mixed the filtered stream, or -1 if the end of the resulting stream has been reached. + * + * @throws IOException - if the underlying stream throws an IOException during reading + * @throws BuildException - if the correct params are not supplied + */ + function read($len = null) { + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + // Make sure correct params/attribs have been set + $this->checkAttributes(); + + $buffer = $this->in->read($len); + if($buffer === -1) { + return -1; + } + + // Setup the locale/gettext environment + $this->initEnvironment(); + + + // replace any occurrences of _("") or gettext("") with + // the translated value. + // + // ([^\w]|^)_\("((\\"|[^"])*)"\) + // --$1--- -----$2---- + // ---$3-- [match escaped quotes or any char that's not a quote] + // + // also match gettext() -- same as above + + $buffer = preg_replace_callback('/([^\w]|^)_\("((\\\"|[^"])*)"\)/', array($this, 'xlateStringCallback'), $buffer); + $buffer = preg_replace_callback('/([^\w]|^)gettext\("((\\\"|[^"])*)"\)/', array($this, 'xlateStringCallback'), $buffer); + + // Check to see if there are any _('') calls and flag an error + + // Check to see if there are any unmatched gettext() calls -- and flag an error + + $matches = array(); + if (preg_match('/([^\w]|^)(gettext\([^\)]+\))/', $buffer, $matches)) { + $this->log("Unable to perform translation on: " . $matches[2], Project::MSG_WARN); + } + + $this->restoreEnvironment(); + + return $buffer; + } + + /** + * Creates a new TranslateGettext filter using the passed in + * Reader for instantiation. + * + * @param Reader $reader A Reader object providing the underlying stream. + * Must not be null. + * + * @return TranslateGettext A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new TranslateGettext($reader); + $newFilter->setProject($this->getProject()); + $newFilter->setDomain($this->getDomain()); + $newFilter->setLocale($this->getLocale()); + $newFilter->setDir($this->getDir()); + return $newFilter; + } + + /** + * Parses the parameters if this filter is being used in "generic" mode. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + foreach($params as $param) { + switch($param->getType()) { + case self::DOMAIN_KEY: + $this->setDomain($param->getValue()); + break; + case self::DIR_KEY: + $this->setDir($this->project->resolveFile($param->getValue())); + break; + + case self::LOCALE_KEY: + $this->setLocale($param->getValue()); + break; + } // switch + } + } // if params !== null + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/XincludeFilter.php b/buildscripts/phing/classes/phing/filters/XincludeFilter.php new file mode 100644 index 00000000..e2b3cd00 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/XincludeFilter.php @@ -0,0 +1,176 @@ +. + */ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Applies Xinclude parsing to incoming text. + * + * Uses PHP DOM XML support + * + * @author Bill Karwin + * @version $Id: 6c47e03d52cf26c183b05e347dac83735dd8c8dd $ + * @see FilterReader + * @package phing.filters + */ +class XincludeFilter extends BaseParamFilterReader implements ChainableReader { + + private $basedir = null; + + /** + * @var bool + */ + private $processed = false; + + /** + * Whether to resolve entities. + * + * @var bool + * + * @since 2.4 + */ + private $resolveExternals = false; + + /** + * Whether to resolve entities. + * + * @param $resolveExternals + * + * @since 2.4 + */ + public function setResolveExternals($resolveExternals) + { + $this->resolveExternals = (bool)$resolveExternals; + } + + /** + * @return bool + * + * @since 2.4 + */ + public function getResolveExternals() + { + return $this->resolveExternals; + } + + public function setBasedir(PhingFile $dir) + { + $this->basedir = $dir; + } + + public function getBasedir() + { + return $this->basedir; + } + + /** + * Reads stream, applies XSLT and returns resulting stream. + * @return string transformed buffer. + * @throws BuildException - if XSLT support missing, if error in xslt processing + */ + function read($len = null) { + + if (!class_exists('DomDocument')) { + throw new BuildException("Could not find the DomDocument class. Make sure PHP has been compiled/configured to support DOM XML."); + } + + if ($this->processed === true) { + return -1; // EOF + } + + // Read XML + $_xml = null; + while ( ($data = $this->in->read($len)) !== -1 ) + $_xml .= $data; + + if ($_xml === null ) { // EOF? + return -1; + } + + if (empty($_xml)) { + $this->log("XML file is empty!", Project::MSG_WARN); + return ''; + } + + $this->log("Transforming XML " . $this->in->getResource() . " using Xinclude ", Project::MSG_VERBOSE); + + $out = ''; + try { + $out = $this->process($_xml); + $this->processed = true; + } catch (IOException $e) { + throw new BuildException($e); + } + + return $out; + } + + /** + * Try to process the Xinclude transformation + * + * @param string XML to process. + * + * @throws BuildException On errors + */ + protected function process($xml) { + + if ($this->basedir) { + $cwd = getcwd(); + chdir($this->basedir); + } + + // Create and setup document. + $xmlDom = new DomDocument(); + $xmlDom->resolveExternals = $this->resolveExternals; + + $xmlDom->loadXML($xml); + + $xmlDom->xinclude(); + + if ($this->basedir) { + chdir($cwd); + } + + return $xmlDom->saveXML(); + } + + /** + * Creates a new XincludeFilter using the passed in + * Reader for instantiation. + * + * @param Reader A Reader object providing the underlying stream. + * Must not be null. + * + * @return Reader A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new XincludeFilter($reader); + $newFilter->setProject($this->getProject()); + $newFilter->setBasedir($this->getBasedir()); + return $newFilter; + } + +} + + diff --git a/buildscripts/phing/classes/phing/filters/XsltFilter.php b/buildscripts/phing/classes/phing/filters/XsltFilter.php new file mode 100644 index 00000000..8866bff7 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/XsltFilter.php @@ -0,0 +1,408 @@ +. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Applies XSL stylesheet to incoming text. + * + * Uses PHP XSLT support (libxslt). + * + * @author Hans Lellelid + * @author Yannick Lecaillez + * @author Andreas Aderhold + * @version $Id: 057af49d450e4c137127acc0f5331368e7a76183 $ + * @see FilterReader + * @package phing.filters + */ +class XsltFilter extends BaseParamFilterReader implements ChainableReader { + + /** + * Path to XSL stylesheet. + * @var string + */ + private $xslFile = null; + + /** + * Whether XML file has been transformed. + * @var boolean + */ + private $processed = false; + + /** + * XSLT Params. + * @var array + */ + private $xsltParams = array(); + + /** + * Whether to use loadHTML() to parse the input XML file. + */ + private $html = false; + + /** + * Whether to resolve entities in the XML document (see + * {@link http://www.php.net/manual/en/class.domdocument.php#domdocument.props.resolveexternals} + * for more details). + * + * @var bool + * + * @since 2.4 + */ + private $resolveDocumentExternals = false; + + /** + * Whether to resolve entities in the stylesheet. + * + * @var bool + * + * @since 2.4 + */ + private $resolveStylesheetExternals = false; + + /** + * Create new XSLT Param object, to handle the nested element. + * @return XSLTParam + */ + function createParam() { + $num = array_push($this->xsltParams, new XSLTParam()); + return $this->xsltParams[$num-1]; + } + + /** + * Sets the XSLT params for this class. + * This is used to "clone" this class, in the chain() method. + * @param array $params + */ + function setParams($params) { + $this->xsltParams = $params; + } + + /** + * Returns the XSLT params set for this class. + * This is used to "clone" this class, in the chain() method. + * @return array + */ + function getParams() { + return $this->xsltParams; + } + + /** + * Set the XSLT stylesheet. + * @param mixed $file PhingFile object or path. + */ + function setStyle(PhingFile $file) { + $this->xslFile = $file; + } + + /** + * Whether to use HTML parser for the XML. + * This is supported in libxml2 -- Yay! + * @return boolean + */ + function getHtml() { + return $this->html; + } + + /** + * Whether to use HTML parser for XML. + * @param boolean $b + */ + function setHtml($b) { + $this->html = (boolean) $b; + } + + /** + * Get the path to XSLT stylesheet. + * @return mixed XSLT stylesheet path. + */ + function getStyle() { + return $this->xslFile; + } + + /** + * Whether to resolve entities in document. + * + * @param bool $resolveExternals + * + * @since 2.4 + */ + function setResolveDocumentExternals($resolveExternals) { + $this->resolveDocumentExternals = (bool)$resolveExternals; + } + + /** + * @return bool + * + * @since 2.4 + */ + function getResolveDocumentExternals() { + return $this->resolveDocumentExternals; + } + + /** + * Whether to resolve entities in stylesheet. + * + * @param bool $resolveExternals + * + * @since 2.4 + */ + function setResolveStylesheetExternals($resolveExternals) { + $this->resolveStylesheetExternals = (bool)$resolveExternals; + } + + /** + * @return bool + * + * @since 2.4 + */ + function getResolveStylesheetExternals() { + return $this->resolveStylesheetExternals; + } + + /** + * Reads stream, applies XSLT and returns resulting stream. + * @return string transformed buffer. + * @throws BuildException - if XSLT support missing, if error in xslt processing + */ + function read($len = null) { + + if (!class_exists('XSLTProcessor')) { + throw new BuildException("Could not find the XSLTProcessor class. Make sure PHP has been compiled/configured to support XSLT."); + } + + if ($this->processed === true) { + return -1; // EOF + } + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + // Read XML + $_xml = null; + while ( ($data = $this->in->read($len)) !== -1 ) + $_xml .= $data; + + if ($_xml === null ) { // EOF? + return -1; + } + + if(empty($_xml)) { + $this->log("XML file is empty!", Project::MSG_WARN); + return ''; // return empty string, don't attempt to apply XSLT + } + + // Read XSLT + $_xsl = null; + $xslFr = new FileReader($this->xslFile); + $xslFr->readInto($_xsl); + + $this->log("Tranforming XML " . $this->in->getResource() . " using style " . $this->xslFile->getPath(), Project::MSG_VERBOSE); + + $out = ''; + try { + $out = $this->process($_xml, $_xsl); + $this->processed = true; + } catch (IOException $e) { + throw new BuildException($e); + } + + return $out; + } + + // {{{ method _ProcessXsltTransformation($xml, $xslt) throws BuildException + /** + * Try to process the XSLT transformation + * + * @param string XML to process. + * @param string XSLT sheet to use for the processing. + * + * @throws BuildException On XSLT errors + */ + protected function process($xml, $xsl) { + + $processor = new XSLTProcessor(); + + // Create and setup document. + $xmlDom = new DOMDocument(); + $xmlDom->resolveExternals = $this->resolveDocumentExternals; + + // Create and setup stylesheet. + $xslDom = new DOMDocument(); + $xslDom->resolveExternals = $this->resolveStylesheetExternals; + + if ($this->html) { + $xmlDom->loadHTML($xml); + } else { + $xmlDom->loadXML($xml); + } + + $xslDom->loadxml($xsl); + + $processor->importStylesheet($xslDom); + + // ignoring param "type" attrib, because + // we're only supporting direct XSL params right now + foreach($this->xsltParams as $param) { + $this->log("Setting XSLT param: " . $param->getName() . "=>" . $param->getExpression(), Project::MSG_DEBUG); + $processor->setParameter(null, $param->getName(), $param->getExpression()); + } + + $errorlevel = error_reporting(); + error_reporting($errorlevel & ~E_WARNING); + @$result = $processor->transformToXML($xmlDom); + error_reporting($errorlevel); + + if (false === $result) { + //$errno = xslt_errno($processor); + //$err = xslt_error($processor); + throw new BuildException("XSLT Error"); + } else { + return $result; + } + } + + /** + * Creates a new XsltFilter using the passed in + * Reader for instantiation. + * + * @param Reader A Reader object providing the underlying stream. + * Must not be null. + * + * @return Reader A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new XsltFilter($reader); + $newFilter->setProject($this->getProject()); + $newFilter->setStyle($this->getStyle()); + $newFilter->setInitialized(true); + $newFilter->setParams($this->getParams()); + $newFilter->setHtml($this->getHtml()); + return $newFilter; + } + + /** + * Parses the parameters to get stylesheet path. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0, $_i=count($params) ; $i < $_i; $i++) { + if ( $params[$i]->getType() === null ) { + if ($params[$i]->getName() === "style") { + $this->setStyle($params[$i]->getValue()); + } + } elseif ($params[$i]->getType() == "param") { + $xp = new XSLTParam(); + $xp->setName($params[$i]->getName()); + $xp->setExpression($params[$i]->getValue()); + $this->xsltParams[] = $xp; + } + } + } + } + +} + + +/** + * Class that holds an XSLT parameter. + * + * @package phing.filters + */ +class XSLTParam { + + private $name; + + private $expr; + + /** + * Sets param name. + * @param string $name + */ + public function setName($name) { + $this->name = $name; + } + + /** + * Get param name. + * @return string + */ + public function getName() { + return $this->name; + } + + /** + * Sets expression value (alias to the setExpression()) method. + * + * @param string $v + * @see setExpression() + */ + public function setValue($v) + { + $this->setExpression($v); + } + + /** + * Gets expression value (alias to the getExpression()) method. + * + * @param string $v + * @see getExpression() + */ + public function getValue() + { + return $this->getExpression(); + } + + /** + * Sets expression value. + * @param string $expr + */ + public function setExpression($expr) { + $this->expr = $expr; + } + + /** + * Sets expression to dynamic register slot. + * @param RegisterSlot $expr + */ + public function setListeningExpression(RegisterSlot $expr) { + $this->expr = $expr; + } + + /** + * Returns expression value -- performs lookup if expr is registerslot. + * @return string + */ + public function getExpression() { + if ($this->expr instanceof RegisterSlot) { + return $this->expr->getValue(); + } else { + return $this->expr; + } + } +} + diff --git a/buildscripts/phing/classes/phing/filters/util/ChainReaderHelper.php b/buildscripts/phing/classes/phing/filters/util/ChainReaderHelper.php new file mode 100755 index 00000000..c465d0a1 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/util/ChainReaderHelper.php @@ -0,0 +1,183 @@ +. +*/ + +include_once 'phing/Project.php'; +include_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/types/PhingFilterReader.php'; +include_once 'phing/types/FilterChain.php'; +include_once 'phing/types/Parameter.php'; +include_once 'phing/util/FileUtils.php'; +include_once 'phing/util/StringHelper.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Process a FilterReader chain. + * + * Here, the interesting method is 'getAssembledReader'. + * The purpose of this one is to create a simple Reader object which + * apply all filters on another primary Reader object. + * + * For example : In copyFile (phing.util.FileUtils) the primary Reader + * is a FileReader object (more accuratly, a BufferedReader) previously + * setted for the source file to copy. So, consider this filterchain : + * + * + * + * + * + * + * + * + * + * getAssembledReader will return a Reader object wich read on each + * of these filters. Something like this : ('->' = 'which read data from') : + * + * [TABTOSPACES] -> [LINECONTAINS] -> [STRIPPHPCOMMENTS] -> [FILEREADER] + * (primary reader) + * + * So, getAssembledReader will return the TABTOSPACES Reader object. Then + * each read done with this Reader object will follow this path. + * + * Hope this explanation is clear :) + * + * TODO: Implement the classPath feature. + * + * @author Yannick Lecaillez + * @version $Id$ + * @access public + * @package phing.filters.util +*/ +class ChainReaderHelper { + + /** Primary reader to wich the reader chain is to be attached */ + private $primaryReader = null; + + /** The site of the buffer to be used. */ + private $bufferSize = 8192; + + /** Chain of filters */ + private $filterChains = array(); + + /** The Phing project */ + private $project; + + /* + * Sets the primary reader + */ + function setPrimaryReader(Reader $reader) { + $this->primaryReader = $reader; + } + + /* + * Set the project to work with + */ + function setProject(Project $project) { + $this->project = $project; + } + + /* + * Get the project + */ + function getProject() { + return $this->project; + } + + /* + * Sets the buffer size to be used. Defaults to 8192, + * if this method is not invoked. + */ + function setBufferSize($size) { + $this->bufferSize = $size; + } + + /* + * Sets the collection of filter reader sets + */ + function setFilterChains(&$fchain) { + $this->filterChains = &$fchain; + } + + /* + * Assemble the reader + */ + function getAssembledReader() { + + $instream = $this->primaryReader; + $filterReadersCount = count($this->filterChains); + $finalFilters = array(); + + // Collect all filter readers of all filter chains used ... + for($i = 0 ; $i<$filterReadersCount ; $i++) { + $filterchain = &$this->filterChains[$i]; + $filterReaders = $filterchain->getFilterReaders(); + $readerCount = count($filterReaders); + for($j = 0 ; $j<$readerCount ; $j++) { + $finalFilters[] = $filterReaders[$j]; + } + } + + // ... then chain the filter readers. + $filtersCount = count($finalFilters); + if ( $filtersCount > 0 ) { + for($i = 0 ; $i<$filtersCount ; $i++) { + $filter = $finalFilters[$i]; + + if ( $filter instanceof PhingFilterReader ) { + + // This filter reader is an external class. + $className = $filter->getClassName(); + $classpath = $filter->getClasspath(); + $project = $filter->getProject(); + + if ( $className !== null ) { + $cls = Phing::import($className, $classpath); + $impl = new $cls(); + } + + if ( !($impl instanceof FilterReader) ) { + throw new Exception($className." does not extend phing.system.io.FilterReader"); + } + + $impl->setReader($instream); // chain + $impl->setProject($this->getProject()); // what about $project above ? + + if ( $impl instanceof Parameterizable ) { + $impl->setParameters($filter->getParams()); + } + + $instream = $impl; // now that it's been chained + + } elseif (($filter instanceof ChainableReader) && ($filter instanceof Reader)) { + if ( $this->getProject() !== null && ($filter instanceof BaseFilterReader) ) { + $filter->setProject($this->getProject()); + } + $instream = $filter->chain($instream); + } else { + throw new Exception("Cannot chain invalid filter: " . get_class($filter)); + } + } + } + + return $instream; + } + +} + diff --git a/buildscripts/phing/classes/phing/filters/util/IniFileTokenReader.php b/buildscripts/phing/classes/phing/filters/util/IniFileTokenReader.php new file mode 100755 index 00000000..f47e155c --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/util/IniFileTokenReader.php @@ -0,0 +1,97 @@ +. +*/ + +include_once 'phing/types/TokenReader.php'; +include_once 'phing/system/io/IOException.php'; +include_once 'phing/filters/ReplaceTokens.php'; // For class Token + +/** + * Class that allows reading tokens from INI files. + * + * @author Manuel Holtgewe + * @version $Id$ + * @package phing.filters.util + */ +class IniFileTokenReader extends TokenReader { + + /** + * Holds the path to the INI file that is to be read. + * @var object Reference to a PhingFile Object representing + * the path to the INI file. + */ + private $file = null; + + /** + * @var string Sets the section to load from the INI file. + * if omitted, all sections are loaded. + */ + private $section = null; + + /** + * Reads the next token from the INI file + * + * @throws IOException On error + * @return Token + */ + function readToken() { + if ($this->file === null) { + throw new BuildException("No File set for IniFileTokenReader"); + } + + static $tokens = null; + if ($tokens === null) { + $tokens = array(); + $arr = parse_ini_file($this->file->getAbsolutePath(), true); + if ($this->section === null) { + foreach ($arr as $sec_name => $values) { + foreach($arr[$sec_name] as $key => $value) { + $tok = new Token; + $tok->setKey($key); + $tok->setValue($value); + $tokens[] = $tok; + } + } + } else if (isset($arr[$this->section])) { + foreach ($arr[$this->section] as $key => $value) { + $tok = new Token; + $tok->setKey($key); + $tok->setValue($value); + $tokens[] = $tok; + } + } + } + + if (count($tokens) > 0) { + return array_pop($tokens); + } else + return null; + } + + function setFile(PhingFile $file) { + $this->file = $file; + } + + function setSection($str) { + $this->section = (string) $str; + } +} + + diff --git a/buildscripts/phing/classes/phing/input/DefaultInputHandler.php b/buildscripts/phing/classes/phing/input/DefaultInputHandler.php new file mode 100755 index 00000000..5e53c878 --- /dev/null +++ b/buildscripts/phing/classes/phing/input/DefaultInputHandler.php @@ -0,0 +1,85 @@ +. + */ + +require_once 'phing/input/InputHandler.php'; +include_once 'phing/system/io/ConsoleReader.php'; + +/** + * Prompts using print(); reads input from Console. + * + * @author Hans Lellelid (Phing) + * @author Stefan Bodewig (Ant) + * @version $Id$ + * @package phing.input + */ +class DefaultInputHandler implements InputHandler { + + /** + * Prompts and requests input. May loop until a valid input has + * been entered. + * @throws BuildException + */ + public function handleInput(InputRequest $request) { + $prompt = $this->getPrompt($request); + $in = new ConsoleReader(); + do { + print $prompt; + try { + $input = $in->readLine(); + if ($input === "" && ($request->getDefaultValue() !== null) ) { + $input = $request->getDefaultValue(); + } + $request->setInput($input); + } catch (Exception $e) { + throw new BuildException("Failed to read input from Console.", $e); + } + } while (!$request->isInputValid()); + } + + /** + * Constructs user prompt from a request. + * + *

This implementation adds (choice1,choice2,choice3,...) to the + * prompt for MultipleChoiceInputRequests.

+ * + * @param $request the request to construct the prompt for. + * Must not be null. + */ + protected function getPrompt(InputRequest $request) { + $prompt = $request->getPrompt(); + $defaultValue = $request->getDefaultValue(); + + if ($request instanceof YesNoInputRequest) { + $choices = $request->getChoices(); + $defaultValue = $choices[(int) !$request->getDefaultValue()]; + $prompt .= '(' . implode('/', $request->getChoices()) .')'; + } elseif ($request instanceof MultipleChoiceInputRequest) { // (a,b,c,d) + $prompt .= '(' . implode(',', $request->getChoices()) . ')'; + } + + if ($request->getDefaultValue() !== null) { + $prompt .= ' ['.$defaultValue.']'; + } + $pchar = $request->getPromptChar(); + return $prompt . ($pchar ? $pchar . ' ' : ' '); + } +} diff --git a/buildscripts/phing/classes/phing/input/InputHandler.php b/buildscripts/phing/classes/phing/input/InputHandler.php new file mode 100755 index 00000000..9a414fd4 --- /dev/null +++ b/buildscripts/phing/classes/phing/input/InputHandler.php @@ -0,0 +1,45 @@ +. + */ + +/** + * Plugin to Phing to handle requests for user input. + * + * @author Stefan Bodewig + * @version $Id$ + * @package phing.input + */ +interface InputHandler { + + /** + * Handle the request encapsulated in the argument. + * + *

Precondition: the request.getPrompt will return a non-null + * value.

+ * + *

Postcondition: request.getInput will return a non-null + * value, request.isInputValid will return true.

+ * @return void + * @throws BuildException + */ + public function handleInput(InputRequest $request); + +} diff --git a/buildscripts/phing/classes/phing/input/InputRequest.php b/buildscripts/phing/classes/phing/input/InputRequest.php new file mode 100755 index 00000000..1d9e156d --- /dev/null +++ b/buildscripts/phing/classes/phing/input/InputRequest.php @@ -0,0 +1,107 @@ +. + */ + +/** + * Encapsulates an input request. + * + * @author Hans Lellelid (Phing) + * @author Stefan Bodewig (Ant) + * @version $Id$ + * @package phing.input + */ +class InputRequest { + + protected $prompt; + protected $input; + protected $defaultValue; + protected $promptChar; + + /** + * @param string $prompt The prompt to show to the user. Must not be null. + */ + public function __construct($prompt) { + if ($prompt === null) { + throw new BuildException("prompt must not be null"); + } + $this->prompt = $prompt; + } + + /** + * Retrieves the prompt text. + */ + public function getPrompt() { + return $this->prompt; + } + + /** + * Sets the user provided input. + */ + public function setInput($input) { + $this->input = $input; + } + + /** + * Is the user input valid? + */ + public function isInputValid() { + return true; + } + + /** + * Retrieves the user input. + */ + public function getInput() { + return $this->input; + } + + /** + * Set the default value to use. + * @param mixed $v + */ + public function setDefaultValue($v) { + $this->defaultValue = $v; + } + + /** + * Return the default value to use. + * @return mixed + */ + public function getDefaultValue() { + return $this->defaultValue; + } + + /** + * Set the default value to use. + * @param string $c + */ + public function setPromptChar($c) { + $this->promptChar = $c; + } + + /** + * Return the default value to use. + * @return string + */ + public function getPromptChar() { + return $this->promptChar; + } +} diff --git a/buildscripts/phing/classes/phing/input/MultipleChoiceInputRequest.php b/buildscripts/phing/classes/phing/input/MultipleChoiceInputRequest.php new file mode 100755 index 00000000..24e93b58 --- /dev/null +++ b/buildscripts/phing/classes/phing/input/MultipleChoiceInputRequest.php @@ -0,0 +1,58 @@ +. + */ + +require_once 'phing/input/InputRequest.php'; + +/** + * Encapsulates an input request. + * + * @author Stefan Bodewig + * @version $Id$ + * @package phing.input + */ +class MultipleChoiceInputRequest extends InputRequest { + + protected $choices = array(); + + /** + * @param string $prompt The prompt to show to the user. Must not be null. + * @param array $choices holds all input values that are allowed. + * Must not be null. + */ + public function __construct($prompt, $choices) { + parent::__construct($prompt); + $this->choices = $choices; + } + + /** + * @return The possible values. + */ + public function getChoices() { + return $this->choices; + } + + /** + * @return true if the input is one of the allowed values. + */ + public function isInputValid() { + return in_array($this->getInput(), $this->choices); // not strict (?) + } +} diff --git a/buildscripts/phing/classes/phing/input/YesNoInputRequest.php b/buildscripts/phing/classes/phing/input/YesNoInputRequest.php new file mode 100755 index 00000000..1a712327 --- /dev/null +++ b/buildscripts/phing/classes/phing/input/YesNoInputRequest.php @@ -0,0 +1,47 @@ +. + */ + +require_once 'phing/input/MultipleChoiceInputRequest.php'; + +/** + * Encapsulates an input request that returns a boolean (yes/no). + * + * @author Hans Lellelid + * @version $Id: 659526fec1ed2e66d5b9308fba48924ea3dda494 $ + * @package phing.input + */ +class YesNoInputRequest extends MultipleChoiceInputRequest { + + /** + * @return true if the input is one of the allowed values. + */ + public function isInputValid() { + return StringHelper::isBoolean($this->input); + } + + /** + * Converts input to boolean. + * @return boolean + */ + public function getInput() { + return StringHelper::booleanValue($this->input); + } +} diff --git a/buildscripts/phing/classes/phing/lib/Capsule.php b/buildscripts/phing/classes/phing/lib/Capsule.php new file mode 100755 index 00000000..e6885a6a --- /dev/null +++ b/buildscripts/phing/classes/phing/lib/Capsule.php @@ -0,0 +1,267 @@ + + * @version $Id$ + * @package phing.lib + */ +class Capsule { + + /** + * Look for templates here (if relative path provided). + * @var string + */ + protected $templatePath; + + /** + * Where should output files be written? + * (This is named inconsistently to be compatible w/ Texen.) + * @var string + */ + protected $outputDirectory; + + /** + * The variables that can be used by the templates. + * @var array Hash of variables. + */ + public $vars = array(); + + /** + * Has template been initialized. + */ + protected $initialized = false; + + /** + * Stores the pre-parse() include_path. + * @var string + */ + private $old_include_path; + + function __construct() { + } + + /** + * Clears one or several or all variables. + * @param mixed $which String name of var, or array of names. + * @return void + */ + function clear($which = null) { + if ($which === null) { + $this->vars = array(); + } elseif (is_array($which)) { + foreach($which as $var) { + unset($this->vars[$var]); + } + } else { + unset($this->vars[$which]); + } + } + + /** + * Set the basepath to use for template lookups. + * @param string $v + */ + function setTemplatePath($v) { + $this->templatePath = rtrim($v, DIRECTORY_SEPARATOR.'/'); + } + + /** + * Get the basepath to use for template lookups. + * @return string + */ + function getTemplatePath() { + return $this->templatePath; + } + + /** + * Set a basepath to use for output file creation. + * @param string $v + */ + function setOutputDirectory($v) { + $this->outputDirectory = rtrim($v, DIRECTORY_SEPARATOR.'/'); + } + + /** + * Get basepath to use for output file creation. + * @return string + */ + function getOutputDirectory() { + return $this->outputDirectory; + } + + /** + * Low overhead (no output buffering) method to simply dump template + * to buffer. + * + * @param string $__template + * @return void + * @throws Exception - if template cannot be found + */ + function display($__template) { + + // Prepend "private" variable names with $__ in this function + // to keep namespace conflict potential to a minimum. + + // Alias this class to $generator. + $generator = $this; + + if (isset($this->vars['this'])) { + throw new Exception("Assigning a variable named \$this to a context conflicts with class namespace."); + } + + // extract variables into local namespace + extract($this->vars); + + // prepend template path to include path, + // so that include "path/relative/to/templates"; can be used within templates + $__old_inc_path = ini_get('include_path'); + ini_set('include_path', $this->templatePath . PATH_SEPARATOR . $__old_inc_path); + + @ini_set('track_errors', true); + include $__template; + @ini_restore('track_errors'); + + // restore the include path + ini_set('include_path', $__old_inc_path); + + if (!empty($php_errormsg)) { + throw new Exception("Unable to parse template " . $__template . ": " . $php_errormsg); + } + } + + /** + * Fetches the results of a tempalte parse and either returns + * the string or writes results to a specified output file. + * + * @param string $template The template filename (relative to templatePath or absolute). + * @param string $outputFile If specified, contents of template will also be written to this file. + * @param boolean $append Should output be appended to source file? + * @return string The "parsed" template output. + * @throws Exception - if template not found. + */ + function parse($template, $outputFile = null, $append = false) { + + // main work done right here: + // hopefully this works recursively ... fingers crossed. + ob_start(); + + try { + $this->display($template); + } catch (Exception $e) { + ob_end_flush(); // flush the output on error (so we can see up to what point it parsed everything) + throw $e; + } + + $output = ob_get_contents(); + ob_end_clean(); + + if ($outputFile !== null) { + $outputFile = $this->resolvePath($outputFile, $this->outputDirectory); + + $flags = null; + if ($append) $flags = FILE_APPEND; + + if (!file_put_contents($outputFile, $output, $flags) && $output != "") { + throw new Exception("Unable to write output to " . $outputFile); + } + } + + return $output; + } + + /** + * This returns a "best guess" path for the given file. + * + * @param string $file File name or possibly absolute path. + * @param string $basepath The basepath that should be prepended if $file is not absolute. + * @return string "Best guess" path for this file. + */ + protected function resolvePath($file, $basepath) { + if ( !($file{0} == DIRECTORY_SEPARATOR || $file{0} == '/') + // also account for C:\ style path + && !($file{1} == ':' && ($file{2} == DIRECTORY_SEPARATOR || $file{2} == '/'))) { + if ($basepath != null) { + $file = $basepath . DIRECTORY_SEPARATOR . $file; + } + } + return $file; + } + + /** + * Gets value of specified var or NULL if var has not been put(). + * @param string $name Variable name to retrieve. + * @return mixed + */ + function get($name) { + if (!isset($this->vars[$name])) return null; + return $this->vars[$name]; + } + + /** + * Merges in passed hash to vars array. + * + * Given an array like: + * + * array( 'myvar' => 'Hello', + * 'myvar2' => 'Hello') + * + * Resulting template will have access to $myvar and $myvar2. + * + * @param array $vars + * @param boolean $recursiveMerge Should matching keys be recursively merged? + * @return void + */ + function putAll($vars, $recursiveMerge = false) { + if ($recursiveMerge) { + $this->vars = array_merge_recursive($this->vars, $vars); + } else { + $this->vars = array_merge($this->vars, $vars); + } + } + + /** + * Adds a variable to the context. + * + * Resulting template will have access to ${$name$} variable. + * + * @param string $name + * @param mixed $value + */ + function put($name, $value) { + $this->vars[$name] = $value; + } + + /** + * Put a variable into the context, assigning it by reference. + * This means that if the template modifies the variable, then it + * will also be modified in the context. + * + * @param $name + * @param &$value + */ + function putRef($name, &$value) { + $this->vars[$name] = &$value; + } + + /** + * Makes a copy of the value and puts it into the context. + * This is primarily to force copying (cloning) of objects, rather + * than the default behavior which is to assign them by reference. + * @param string $name + * @param mixed $value + */ + function putCopy($name, $value) { + if (is_object($value)) { + $value = clone $value; + } + $this->vars[$name] = $value; + } + +} \ No newline at end of file diff --git a/buildscripts/phing/classes/phing/listener/AnsiColorLogger.php b/buildscripts/phing/classes/phing/listener/AnsiColorLogger.php new file mode 100755 index 00000000..fc12c9d2 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/AnsiColorLogger.php @@ -0,0 +1,234 @@ +. + */ + +require_once 'phing/listener/DefaultLogger.php'; +include_once 'phing/system/util/Properties.php'; + +/** + * Uses ANSI Color Code Sequences to colorize messages + * sent to the console. + * + * 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. + * + * 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.) + * + * NOTE: + * It doesn't work on WinNT's COMMAND.COM even with + * ANSI.SYS loaded. + * + * The default colors used for differentiating + * the message levels can be changed by editing the + * phing/listener/defaults.properties file. + * + * This file contains 5 key/value pairs: + * 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 + * + * 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 java command using -D option. + * + * To change these colors use the following chart: + * + * 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 + * + * @author Hans Lellelid (Phing) + * @author Magesh Umasankar (Ant) + * @package phing.listener + * @version $Id$ + */ +class AnsiColorLogger extends DefaultLogger { + + const ATTR_NORMAL = 0; + const ATTR_BRIGHT = 1; + const ATTR_DIM = 2; + const ATTR_UNDERLINE = 3; + const ATTR_BLINK = 5; + const ATTR_REVERSE = 7; + const ATTR_HIDDEN = 8; + + const FG_BLACK = 30; + const FG_RED = 31; + const FG_GREEN = 32; + const FG_YELLOW = 33; + const FG_BLUE = 34; + const FG_MAGENTA = 35; + const FG_CYAN = 36; + const FG_WHITE = 37; + + const BG_BLACK = 40; + const BG_RED = 41; + const BG_GREEN = 42; + const BG_YELLOW = 44; + const BG_BLUE = 44; + const BG_MAGENTA = 45; + const BG_CYAN = 46; + const BG_WHITE = 47; + + const PREFIX = "\x1b["; + const SUFFIX = "m"; + const SEPARATOR = ';'; + const END_COLOR = "\x1b[m"; // self::PREFIX . self::SUFFIX; + + private $errColor; + private $warnColor; + private $infoColor; + private $verboseColor; + private $debugColor; + + private $colorsSet = false; + + /** + * Construct new AnsiColorLogger + * Perform initializations that cannot be done in var declarations. + */ + public function __construct() { + parent::__construct(); + $this->errColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_RED . self::SUFFIX; + $this->warnColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_MAGENTA . self::SUFFIX; + $this->infoColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_CYAN . self::SUFFIX; + $this->verboseColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_GREEN . self::SUFFIX; + $this->debugColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_BLUE . self::SUFFIX; + } + + /** + * Set the colors to use from a property file specified by the + * special ant property ant.logger.defaults + */ + private final function setColors() { + + $userColorFile = Phing::getProperty("phing.logger.defaults"); + $systemColorFile = new PhingFile(Phing::getResourcePath("phing/listener/defaults.properties")); + + $in = null; + + try { + $prop = new Properties(); + + if ($userColorFile !== null) { + $prop->load($userColorFile); + } else { + $prop->load($systemColorFile); + } + + $err = $prop->getProperty("AnsiColorLogger.ERROR_COLOR"); + $warn = $prop->getProperty("AnsiColorLogger.WARNING_COLOR"); + $info = $prop->getProperty("AnsiColorLogger.INFO_COLOR"); + $verbose = $prop->getProperty("AnsiColorLogger.VERBOSE_COLOR"); + $debug = $prop->getProperty("AnsiColorLogger.DEBUG_COLOR"); + if ($err !== null) { + $this->errColor = self::PREFIX . $err . self::SUFFIX; + } + if ($warn !== null) { + $this->warnColor = self::PREFIX . $warn . self::SUFFIX; + } + if ($info !== null) { + $this->infoColor = self::PREFIX . $info . self::SUFFIX; + } + if ($verbose !== null) { + $this->verboseColor = self::PREFIX . $verbose . self::SUFFIX; + } + if ($debug !== null) { + $this->debugColor = self::PREFIX . $debug . self::SUFFIX; + } + } catch (IOException $ioe) { + //Ignore exception - we will use the defaults. + } + } + + /** + * @see DefaultLogger#printMessage + * @param string $message + * @param OutputStream $stream + * @param int $priority + */ + protected final function printMessage($message, OutputStream $stream, $priority) { + if ($message !== null) { + + if (!$this->colorsSet) { + $this->setColors(); + $this->colorsSet = true; + } + + switch ($priority) { + case Project::MSG_ERR: + $message = $this->errColor . $message . self::END_COLOR; + break; + case Project::MSG_WARN: + $message = $this->warnColor . $message . self::END_COLOR; + break; + case Project::MSG_INFO: + $message = $this->infoColor . $message . self::END_COLOR; + break; + case Project::MSG_VERBOSE: + $message = $this->verboseColor . $message . self::END_COLOR; + break; + case Project::MSG_DEBUG: + $message = $this->debugColor . $message . self::END_COLOR; + break; + } + + $stream->write($message . PHP_EOL); + } + } +} diff --git a/buildscripts/phing/classes/phing/listener/DefaultLogger.php b/buildscripts/phing/classes/phing/listener/DefaultLogger.php new file mode 100755 index 00000000..31051a75 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/DefaultLogger.php @@ -0,0 +1,279 @@ +. + */ + +require_once 'phing/listener/StreamRequiredBuildLogger.php'; +include_once 'phing/BuildEvent.php'; + +/** + * Writes a build event to the console. + * + * Currently, it only writes which targets are being executed, and + * any messages that get logged. + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @see BuildEvent + * @package phing.listener + */ +class DefaultLogger implements StreamRequiredBuildLogger { + + /** + * Size of the left column in output. The default char width is 12. + * @var int + */ + const LEFT_COLUMN_SIZE = 12; + + /** + * The message output level that should be used. The default is + * Project::MSG_VERBOSE. + * @var int + */ + protected $msgOutputLevel = Project::MSG_ERR; + + /** + * Time that the build started + * @var int + */ + protected $startTime; + + /** + * @var OutputStream Stream to use for standard output. + */ + protected $out; + + /** + * @var OutputStream Stream to use for error output. + */ + protected $err; + + /** + * Construct a new default logger. + */ + public function __construct() { + + } + + /** + * Set the msgOutputLevel this logger is to respond to. + * + * Only messages with a message level lower than or equal to the given + * level are output to the log. + * + *

Constants for the message levels are in Project.php. The order of + * the levels, from least to most verbose, is: + * + *

    + *
  • Project::MSG_ERR
  • + *
  • Project::MSG_WARN
  • + *
  • Project::MSG_INFO
  • + *
  • Project::MSG_VERBOSE
  • + *
  • Project::MSG_DEBUG
  • + *
+ * + * The default message level for DefaultLogger is Project::MSG_ERR. + * + * @param int $level The logging level for the logger. + * @see BuildLogger#setMessageOutputLevel() + */ + public function setMessageOutputLevel($level) { + $this->msgOutputLevel = (int) $level; + } + + /** + * Sets the output stream. + * @param OutputStream $output + * @see BuildLogger#setOutputStream() + */ + public function setOutputStream(OutputStream $output) { + $this->out = $output; + } + + /** + * Sets the error stream. + * @param OutputStream $err + * @see BuildLogger#setErrorStream() + */ + public function setErrorStream(OutputStream $err) { + $this->err = $err; + } + + /** + * Sets the start-time when the build started. Used for calculating + * the build-time. + * + * @param object The BuildEvent + * @access public + */ + public function buildStarted(BuildEvent $event) { + $this->startTime = Phing::currentTimeMillis(); + if ($this->msgOutputLevel >= Project::MSG_INFO) { + $this->printMessage("Buildfile: ".$event->getProject()->getProperty("phing.file"), $this->out, Project::MSG_INFO); + } + } + + /** + * Prints whether the build succeeded or failed, and any errors that + * occured during the build. Also outputs the total build-time. + * + * @param object The BuildEvent + * @see BuildEvent::getException() + */ + public function buildFinished(BuildEvent $event) { + $error = $event->getException(); + if ($error === null) { + $msg = PHP_EOL . $this->getBuildSuccessfulMessage() . PHP_EOL; + } else { + $msg = PHP_EOL . $this->getBuildFailedMessage() . PHP_EOL; + if (Project::MSG_VERBOSE <= $this->msgOutputLevel || !($error instanceof BuildException)) { + $msg .= $error->__toString().PHP_EOL; + } else { + $msg .= $error->getMessage(); + } + } + $msg .= PHP_EOL . "Total time: " .self::formatTime(Phing::currentTimeMillis() - $this->startTime) . PHP_EOL; + + if ($error === null) { + $this->printMessage($msg, $this->out, Project::MSG_VERBOSE); + } else { + $this->printMessage($msg, $this->err, Project::MSG_ERR); + } + } + + /** + * Get the message to return when a build failed. + * @return string The classic "BUILD FAILED" + */ + protected function getBuildFailedMessage() { + return "BUILD FAILED"; + } + + /** + * Get the message to return when a build succeeded. + * @return string The classic "BUILD FINISHED" + */ + protected function getBuildSuccessfulMessage() { + return "BUILD FINISHED"; + } + + /** + * Prints the current target name + * + * @param object The BuildEvent + * @access public + * @see BuildEvent::getTarget() + */ + public function targetStarted(BuildEvent $event) { + if (Project::MSG_INFO <= $this->msgOutputLevel) { + $showLongTargets = $event->getProject()->getProperty("phing.showlongtargets"); + $msg = PHP_EOL . $event->getProject()->getName() . ' > ' . $event->getTarget()->getName() . ($showLongTargets ? ' [' . $event->getTarget()->getDescription() . ']' : '') . ':' . PHP_EOL; + $this->printMessage($msg, $this->out, $event->getPriority()); + } + } + + /** + * Fired when a target has finished. We don't need specific action on this + * event. So the methods are empty. + * + * @param object The BuildEvent + * @see BuildEvent::getException() + */ + public function targetFinished(BuildEvent $event) {} + + /** + * Fired when a task is started. We don't need specific action on this + * event. So the methods are empty. + * + * @param object The BuildEvent + * @access public + * @see BuildEvent::getTask() + */ + public function taskStarted(BuildEvent $event) {} + + /** + * Fired when a task has finished. We don't need specific action on this + * event. So the methods are empty. + * + * @param object The BuildEvent + * @access public + * @see BuildEvent::getException() + */ + public function taskFinished(BuildEvent $event) {} + + /** + * Print a message to the stdout. + * + * @param object The BuildEvent + * @access public + * @see BuildEvent::getMessage() + */ + public function messageLogged(BuildEvent $event) { + $priority = $event->getPriority(); + if ($priority <= $this->msgOutputLevel) { + $msg = ""; + if ($event->getTask() !== null) { + $name = $event->getTask(); + $name = $name->getTaskName(); + $msg = str_pad("[$name] ", self::LEFT_COLUMN_SIZE, " ", STR_PAD_LEFT); + } + + $msg .= $event->getMessage(); + + if ($priority != Project::MSG_ERR) { + $this->printMessage($msg, $this->out, $priority); + } else { + $this->printMessage($msg, $this->err, $priority); + } + } + } + + /** + * Formats a time micro integer to human readable format. + * + * @param integer The time stamp + * @access private + */ + public static function formatTime($micros) { + $seconds = $micros; + $minutes = $seconds / 60; + if ($minutes > 1) { + return sprintf("%1.0f minute%s %0.2f second%s", + $minutes, ($minutes === 1 ? " " : "s "), + $seconds - floor($seconds/60) * 60, ($seconds%60 === 1 ? "" : "s")); + } else { + return sprintf("%0.4f second%s", $seconds, ($seconds%60 === 1 ? "" : "s")); + } + } + + /** + * Prints a message to console. + * + * @param string $message The message to print. + * Should not be null. + * @param resource $stream The stream to use for message printing. + * @param int $priority The priority of the message. + * (Ignored in this implementation.) + * @return void + */ + protected function printMessage($message, OutputStream $stream, $priority) { + $stream->write($message . PHP_EOL); + } +} diff --git a/buildscripts/phing/classes/phing/listener/HtmlColorLogger.php b/buildscripts/phing/classes/phing/listener/HtmlColorLogger.php new file mode 100755 index 00000000..6514f8d1 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/HtmlColorLogger.php @@ -0,0 +1,175 @@ +. + */ + +require_once 'phing/listener/DefaultLogger.php'; +include_once 'phing/system/util/Properties.php'; + +/** + * Uses CSS class that must be defined in the HTML page + * where the Phing output is displayed. + * + * If used with the -logfile option, the output + * will contain the text wrapped in html elements + * with those css classes. + * + * The default classes used for differentiating + * the message levels can be changed by editing the + * phing/listener/defaults.properties file. + * + * This file can contain 5 key/value pairs: + * HtmlColorLogger.ERROR_CLASS=_your_css_class_name_ + * HtmlColorLogger.WARNING_CLASS=_your_css_class_name_ + * HtmlColorLogger.INFO_CLASS=_your_css_class_name_ + * HtmlColorLogger.VERBOSE_CLASS=_your_css_class_name_ + * HtmlColorLogger.DEBUG_CLASS=_your_css_class_name_ + * + * This stems from the Ansi Color Logger done by Hans Lellelid: + * + * @author Anton Stöckl (Phing HTML Color Logger) + * @author Hans Lellelid (Phing Ansi Color Logger) + * @author Magesh Umasankar (Ant) + * @package phing.listener + * @version $Id: 4b57f4d435b61b6501688394f1ff8534d4b7e93f $ + */ +class HtmlColorLogger extends DefaultLogger { + + const CLASS_ERR = 'phing_err'; + const CLASS_VERBOSE = 'phing_verbose'; + const CLASS_DEBUG = 'phing_debug'; + const CLASS_WARN = 'phing_warn'; + const CLASS_INFO = 'phing_info'; + + const PREFIX = ''; + const END_COLOR = ''; + + private $errColor; + private $warnColor; + private $infoColor; + private $verboseColor; + private $debugColor; + + private $colorsSet = false; + + /** + * Construct new HtmlColorLogger + * Perform initializations that cannot be done in var declarations. + */ + public function __construct() { + parent::__construct(); + $this->errColor = self::PREFIX . self::CLASS_ERR . self::SUFFIX; + $this->warnColor = self::PREFIX . self::CLASS_WARN . self::SUFFIX; + $this->infoColor = self::PREFIX . self::CLASS_INFO . self::SUFFIX; + $this->verboseColor = self::PREFIX . self::CLASS_VERBOSE . self::SUFFIX; + $this->debugColor = self::PREFIX . self::CLASS_DEBUG . self::SUFFIX; + } + + /** + * Set the colors to use from a property file specified in the + * special phing property file "phing/listener/defaults.properties". + */ + private final function setColors() { + + $systemColorFile = new PhingFile(Phing::getResourcePath("phing/listener/defaults.properties")); + + try { + $prop = new Properties(); + + $prop->load($systemColorFile); + + $err = $prop->getProperty("HtmlColorLogger.ERROR_CLASS"); + $warn = $prop->getProperty("HtmlColorLogger.WARNING_CLASS"); + $info = $prop->getProperty("HtmlColorLogger.INFO_CLASS"); + $verbose = $prop->getProperty("HtmlColorLogger.VERBOSE_CLASS"); + $debug = $prop->getProperty("HtmlColorLogger.DEBUG_CLASS"); + if ($err !== null) { + $this->errColor = self::PREFIX . $err . self::SUFFIX; + } + if ($warn !== null) { + $this->warnColor = self::PREFIX . $warn . self::SUFFIX; + } + if ($info !== null) { + $this->infoColor = self::PREFIX . $info . self::SUFFIX; + } + if ($verbose !== null) { + $this->verboseColor = self::PREFIX . $verbose . self::SUFFIX; + } + if ($debug !== null) { + $this->debugColor = self::PREFIX . $debug . self::SUFFIX; + } + } catch (IOException $ioe) { + //Ignore exception - we will use the defaults. + } + } + + /** + * @see DefaultLogger#printMessage + * @param string $message + * @param OutputStream $stream + * @param int $priority + */ + protected final function printMessage($message, OutputStream $stream, $priority) { + if ($message !== null) { + + if (!$this->colorsSet) { + $this->setColors(); + $this->colorsSet = true; + } + + $search = array('<', '>'); + $replace = array('<', '>'); + $message = str_replace($search, $replace, $message); + + $search = array("\t", "\n", "\r"); + $replace = array('   ', '
', ''); + $message = str_replace($search, $replace, $message); + + if (preg_match('@^( +)([^ ].+)@', $message, $matches)) { + $len = strlen($matches[1]); + $space = ' '; + for ($i = 1; $i < $len; $i++) { + $space .= ' '; + } + $message = $space . $matches[2]; + } + + switch ($priority) { + case Project::MSG_ERR: + $message = $this->errColor . $message . self::END_COLOR; + break; + case Project::MSG_WARN: + $message = $this->warnColor . $message . self::END_COLOR; + break; + case Project::MSG_INFO: + $message = $this->infoColor . $message . self::END_COLOR; + break; + case Project::MSG_VERBOSE: + $message = $this->verboseColor . $message . self::END_COLOR; + break; + case Project::MSG_DEBUG: + $message = $this->debugColor . $message . self::END_COLOR; + break; + } + + $stream->write($message . '
'); + } + } +} diff --git a/buildscripts/phing/classes/phing/listener/MailLogger.php b/buildscripts/phing/classes/phing/listener/MailLogger.php new file mode 100755 index 00000000..344f3de4 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/MailLogger.php @@ -0,0 +1,105 @@ +. + */ + +require_once 'phing/listener/DefaultLogger.php'; +include_once 'phing/system/util/Properties.php'; + +/** + * Uses PEAR Mail package to send the build log to one or + * more recipients. + * + * @author Michiel Rook + * @package phing.listener + * @version $Id$ + */ +class MailLogger extends DefaultLogger +{ + private $_mailMessage = ""; + + private $_from = "phing@phing.info"; + private $_subject = "Phing build result"; + private $_tolist = null; + + /** + * Construct new MailLogger + */ + public function __construct() { + parent::__construct(); + + @require_once 'Mail.php'; + + if (!class_exists('Mail')) { + throw new BuildException('Need the PEAR Mail package to send logs'); + } + + $from = Phing::getDefinedProperty('phing.log.mail.from'); + $subject = Phing::getDefinedProperty('phing.log.mail.subject'); + $tolist = Phing::getDefinedProperty('phing.log.mail.recipients'); + + if (!empty($from)) { + $this->_from = $from; + } + + if (!empty($subject)) { + $this->_subject = $subject; + } + + if (!empty($tolist)) { + $this->_tolist = $tolist; + } + } + + /** + * @see DefaultLogger#printMessage + * @param string $message + * @param OutputStream $stream + * @param int $priority + */ + protected final function printMessage($message, OutputStream $stream, $priority) + { + if ($message !== null) { + $this->_mailMessage .= $message . "\n"; + } + } + + /** + * Sends the mail + * + * @see DefaultLogger#buildFinished + * @param BuildEvent $event + */ + public function buildFinished(BuildEvent $event) + { + parent::buildFinished($event); + + if (empty($this->_tolist)) { + return; + } + + $hdrs = array( + 'From' => $this->_from, + 'Subject' => $this->_subject . (empty($event) ? " (build succesful)" : " (build failed)") + ); + + $mail = Mail::factory('mail'); + $mail->send($this->_tolist, $hdrs, $this->_mailMessage); + } +} diff --git a/buildscripts/phing/classes/phing/listener/NoBannerLogger.php b/buildscripts/phing/classes/phing/listener/NoBannerLogger.php new file mode 100755 index 00000000..887f6678 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/NoBannerLogger.php @@ -0,0 +1,59 @@ +. + */ + +require_once 'phing/listener/DefaultLogger.php'; + +/** + * Extends DefaultLogger to strip out empty targets. + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @package phing.listener + */ +class NoBannerLogger extends DefaultLogger { + + private $targetName = null; + + function targetStarted(BuildEvent $event) { + $target = $event->getTarget(); + $this->targetName = $target->getName(); + } + + function targetFinished(BuildEvent $event) { + $this->targetName = null; + } + + function messageLogged(BuildEvent $event) { + + if ($event->getPriority() > $this->msgOutputLevel || null === $event->getMessage() || trim($event->getMessage() === "")) { + return; + } + + if ($this->targetName !== null) { + $msg = PHP_EOL . $event->getProject()->getName() . ' > ' . $this->targetName . ':' . PHP_EOL; + $this->printMessage($msg, $this->out, $event->getPriority()); + $this->targetName = null; + } + + parent::messageLogged($event); + } +} diff --git a/buildscripts/phing/classes/phing/listener/PearLogListener.php b/buildscripts/phing/classes/phing/listener/PearLogListener.php new file mode 100755 index 00000000..76efdbbb --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/PearLogListener.php @@ -0,0 +1,197 @@ +. + */ + +require_once 'phing/BuildListener.php'; + +/** + * Writes build messages to PEAR Log. + * + * By default it will log to file in current directory w/ name 'phing.log'. You can customize + * this behavior by setting properties: + * - pear.log.type + * - pear.log.name + * - pear.log.ident (note that this class changes ident to project name) + * - pear.log.conf (note that array values are currently unsupported in Phing property files) + * + * + * phing -f build.xml -logger phing.listener.PearLogger -Dpear.log.type=file -Dpear.log.name=/path/to/log.log + * + * + * @author Hans Lellelid + * @version $Id$ + * @see BuildEvent + * @package phing.listener + */ +class PearLogListener implements BuildListener { + + /** + * Size of the left column in output. The default char width is 12. + * @var int + */ + const LEFT_COLUMN_SIZE = 12; + + /** + * Time that the build started + * @var int + */ + protected $startTime; + + /** + * Maps Phing Project::MSG_* constants to PEAR_LOG_* constants. + * @var array + */ + protected static $levelMap = array( Project::MSG_DEBUG => PEAR_LOG_DEBUG, + Project::MSG_INFO => PEAR_LOG_INFO, + Project::MSG_VERBOSE => PEAR_LOG_NOTICE, + Project::MSG_WARN => PEAR_LOG_WARNING, + Project::MSG_ERR => PEAR_LOG_ERR + ); + /** + * Whether logging has been configured. + * @var boolean + */ + protected $logConfigured = false; + + /** + * @var Log PEAR Log object. + */ + protected $logger; + + /** + * Configure the logger. + */ + protected function configureLogging() { + + $type = Phing::getDefinedProperty('pear.log.type'); + $name = Phing::getDefinedProperty('pear.log.name'); + $ident = Phing::getDefinedProperty('pear.log.ident'); + $conf = Phing::getDefinedProperty('pear.log.conf'); + + if ($type === null) $type = 'file'; + if ($name === null) $name = 'phing.log'; + if ($ident === null) $ident = 'phing'; + if ($conf === null) $conf = array(); + + include_once 'Log.php'; + if (!class_exists('Log')) { + throw new BuildException("Cannot find PEAR Log class for use by PearLogger."); + } + + $this->logger = Log::singleton($type, $name, $ident, $conf, self::$levelMap[$this->msgOutputLevel]); + } + + /** + * Get the configured PEAR logger to use. + * This method just ensures that logging has been configured and returns the configured logger. + * @return Log + */ + protected function logger() { + if (!$this->logConfigured) { + $this->configureLogging(); + } + return $this->logger; + } + + /** + * Sets the start-time when the build started. Used for calculating + * the build-time. + * + * @param BuildEvent The BuildEvent + */ + public function buildStarted(BuildEvent $event) { + $this->startTime = Phing::currentTimeMillis(); + $this->logger()->setIdent($event->getProject()->getName()); + $this->logger()->info("Starting build with buildfile: ". $event->getProject()->getProperty("phing.file")); + } + + /** + * Logs whether the build succeeded or failed, and any errors that + * occured during the build. Also outputs the total build-time. + * + * @param BuildEvent The BuildEvent + * @see BuildEvent::getException() + */ + public function buildFinished(BuildEvent $event) { + $error = $event->getException(); + if ($error === null) { + $msg = "Finished successful build."; + } else { + $msg = "Build failed. [reason: " . $error->getMessage() ."]"; + } + $this->logger()->log($msg . " Total time: " . DefaultLogger::formatTime(Phing::currentTimeMillis() - $this->startTime)); + } + + /** + * Logs the current target name + * + * @param BuildEvent The BuildEvent + * @see BuildEvent::getTarget() + */ + public function targetStarted(BuildEvent $event) {} + + /** + * Fired when a target has finished. We don't need specific action on this + * event. So the methods are empty. + * + * @param BuildEvent The BuildEvent + * @access public + * @see BuildEvent::getException() + */ + public function targetFinished(BuildEvent $event) {} + + /** + * Fired when a task is started. We don't need specific action on this + * event. So the methods are empty. + * + * @param BuildEvent The BuildEvent + * @access public + * @see BuildEvent::getTask() + */ + public function taskStarted(BuildEvent $event) {} + + /** + * Fired when a task has finished. We don't need specific action on this + * event. So the methods are empty. + * + * @param BuildEvent The BuildEvent + * @see BuildEvent::getException() + */ + public function taskFinished(BuildEvent $event) {} + + /** + * Logs a message to the configured PEAR logger. + * + * @param BuildEvent The BuildEvent + * @see BuildEvent::getMessage() + */ + public function messageLogged(BuildEvent $event) { + if ($event->getPriority() <= $this->msgOutputLevel) { + $msg = ""; + if ($event->getTask() !== null) { + $name = $event->getTask(); + $name = $name->getTaskName(); + $msg = str_pad("[$name] ", self::LEFT_COLUMN_SIZE, " ", STR_PAD_LEFT); + } + $msg .= $event->getMessage(); + $this->logger()->log($msg, self::$levelMap[$event->getPriority()]); + } + } +} diff --git a/buildscripts/phing/classes/phing/listener/StreamRequiredBuildLogger.php b/buildscripts/phing/classes/phing/listener/StreamRequiredBuildLogger.php new file mode 100755 index 00000000..c463a59d --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/StreamRequiredBuildLogger.php @@ -0,0 +1,39 @@ +. + */ + +require_once 'phing/BuildLogger.php'; + +/** + * Interface for build loggers that require that out/err streams be set in order to function. + * + * This is just an empty sub-interface to BuildLogger, but is used by Phing to throw + * graceful errors when classes like phing.listener.DefaultLogger are being used as + * -listener. + * + * @author Hans Lellelid + * @version $Id$ + * @see BuildEvent + * @see Project::addBuildListener() + * @package phing + */ +interface StreamRequiredBuildLogger extends BuildLogger { + +} \ No newline at end of file diff --git a/buildscripts/phing/classes/phing/listener/XmlLogger.php b/buildscripts/phing/classes/phing/listener/XmlLogger.php new file mode 100755 index 00000000..25ff0ba2 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/XmlLogger.php @@ -0,0 +1,354 @@ +. + */ + +require_once 'phing/BuildLogger.php'; +require_once 'phing/listener/DefaultLogger.php'; +require_once 'phing/system/util/Timer.php'; + +/** + * 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 XmlLogger.file. + * + * @author Michiel Rook + * @version $Id: aaf7d77e9952319b9598d786c556be005d34c188 $ + * @package phing.listener + */ +class XmlLogger implements BuildLogger { + + /** XML element name for a build. */ + const BUILD_TAG = "build"; + + /** XML element name for a target. */ + const TARGET_TAG = "target"; + + /** XML element name for a task. */ + const TASK_TAG = "task"; + + /** XML element name for a message. */ + const MESSAGE_TAG = "message"; + + /** XML attribute name for a name. */ + const NAME_ATTR = "name"; + + /** XML attribute name for a time. */ + const TIME_ATTR = "time"; + + /** XML attribute name for a message priority. */ + const PRIORITY_ATTR = "priority"; + + /** XML attribute name for a file location. */ + const LOCATION_ATTR = "location"; + + /** XML attribute name for an error description. */ + const ERROR_ATTR = "error"; + + /** XML element name for a stack trace. */ + const STACKTRACE_TAG = "stacktrace"; + + /** + * @var DOMDocument The XML document created by this logger. + */ + private $doc; + + /** + * @var int Start time for entire build. + */ + private $buildTimerStart = 0; + + /** + * @var DOMElement Top-level (root) build element + */ + private $buildElement; + + /** + * @var array DOMElement[] The parent of the element being processed. + */ + private $elementStack = array(); + + /** + * @var array int[] Array of millisecond times for the various elements being processed. + */ + private $timesStack = array(); + + /** + * @var int + */ + private $msgOutputLevel = Project::MSG_DEBUG; + + /** + * @var OutputStream Stream to use for standard output. + */ + private $out; + + /** + * @var OutputStream Stream to use for error output. + */ + private $err; + + /** + * @var string Name of filename to create. + */ + private $outFilename; + + /** + * Constructs a new BuildListener that logs build events to an XML file. + */ + public function __construct() { + $this->doc = new DOMDocument("1.0", "UTF-8"); + $this->doc->formatOutput = true; + } + + /** + * 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 BuildEvent Ignored. + */ + function buildStarted(BuildEvent $event) { + $this->buildTimerStart = Phing::currentTimeMillis(); + $this->buildElement = $this->doc->createElement(XmlLogger::BUILD_TAG); + array_push($this->elementStack, $this->buildElement); + array_push($this->timesStack, $this->buildTimerStart); + } + + /** + * 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 BuildEvent $event An event with any relevant extra information. + * Will not be null. + */ + public function buildFinished(BuildEvent $event) { + + $elapsedTime = Phing::currentTimeMillis() - $this->buildTimerStart; + + $this->buildElement->setAttribute(XmlLogger::TIME_ATTR, DefaultLogger::formatTime($elapsedTime)); + + if ($event->getException() != null) { + $this->buildElement->setAttribute(XmlLogger::ERROR_ATTR, $event->getException()->getMessage()); + $errText = $this->doc->createCDATASection($event->getException()->getTraceAsString()); + $stacktrace = $this->doc->createElement(XmlLogger::STACKTRACE_TAG); + $stacktrace->appendChild($errText); + $this->buildElement->appendChild($stacktrace); + } + + $this->doc->appendChild($this->buildElement); + + $outFilename = $event->getProject()->getProperty("XmlLogger.file"); + if ($outFilename == null) { + $outFilename = "log.xml"; + } + + try { + $stream = $this->out; + if ($stream === null) { + $stream = new FileOutputStream($outFilename); + } + + // Yes, we could just stream->write() but this will eventually be the better + // way to do this (when we need to worry about charset conversions. + $writer = new OutputStreamWriter($stream); + $writer->write($this->doc->saveXML()); + $writer->close(); + } catch (IOException $exc) { + try { + $stream->close(); // in case there is a stream open still ... + } catch (Exception $x) {} + throw new BuildException("Unable to write log file.", $exc); + } + + // cleanup:remove the buildElement + $this->buildElement = null; + + array_pop($this->elementStack); + array_pop($this->timesStack); + } + + + /** + * Fired when a target starts building, remembers the current time and the name of the target. + * + * @param BuildEvent $event An event with any relevant extra information. + * Will not be null. + */ + public function targetStarted(BuildEvent $event) { + $target = $event->getTarget(); + + $targetElement = $this->doc->createElement(XmlLogger::TARGET_TAG); + $targetElement->setAttribute(XmlLogger::NAME_ATTR, $target->getName()); + + array_push($this->timesStack, Phing::currentTimeMillis()); + array_push($this->elementStack, $targetElement); + } + + /** + * Fired when a target finishes building, this adds the time taken + * to the appropriate target element in the log. + * + * @param BuildEvent $event An event with any relevant extra information. + * Will not be null. + */ + public function targetFinished(BuildEvent $event) { + $targetTimerStart = array_pop($this->timesStack); + $targetElement = array_pop($this->elementStack); + + $elapsedTime = Phing::currentTimeMillis() - $targetTimerStart; + $targetElement->setAttribute(XmlLogger::TIME_ATTR, DefaultLogger::formatTime($elapsedTime)); + + $parentElement = $this->elementStack[ count($this->elementStack) - 1 ]; + $parentElement->appendChild($targetElement); + } + + /** + * Fired when a task starts building, remembers the current time and the name of the task. + * + * @param BuildEvent $event An event with any relevant extra information. + * Will not be null. + */ + public function taskStarted(BuildEvent $event) { + $task = $event->getTask(); + + $taskElement = $this->doc->createElement(XmlLogger::TASK_TAG); + $taskElement->setAttribute(XmlLogger::NAME_ATTR, $task->getTaskName()); + $taskElement->setAttribute(XmlLogger::LOCATION_ATTR, $task->getLocation()->toString()); + + array_push($this->timesStack, Phing::currentTimeMillis()); + array_push($this->elementStack, $taskElement); + } + + /** + * Fired when a task finishes building, this adds the time taken + * to the appropriate task element in the log. + * + * @param BuildEvent $event An event with any relevant extra information. + * Will not be null. + */ + public function taskFinished(BuildEvent $event) { + $taskTimerStart = array_pop($this->timesStack); + $taskElement = array_pop($this->elementStack); + + $elapsedTime = Phing::currentTimeMillis() - $taskTimerStart; + $taskElement->setAttribute(XmlLogger::TIME_ATTR, DefaultLogger::formatTime($elapsedTime)); + + $parentElement = $this->elementStack[ count($this->elementStack) - 1 ]; + $parentElement->appendChild($taskElement); + } + + /** + * 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 BuildEvent An event with any relevant extra information. + * Will not be null. + */ + public function messageLogged(BuildEvent $event) + { + $priority = $event->getPriority(); + + if ($priority > $this->msgOutputLevel) { + return; + } + + $messageElement = $this->doc->createElement(XmlLogger::MESSAGE_TAG); + + 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(XmlLogger::PRIORITY_ATTR, $name); + + if (function_exists('mb_convert_encoding')) + { + $messageConverted = mb_convert_encoding($event->getMessage(), 'UTF-8'); + } + else + { + $messageConverted = utf8_encode($event->getMessage()); + } + + $messageText = $this->doc->createCDATASection($messageConverted); + + $messageElement->appendChild($messageText); + + if (!empty($this->elementStack)) { + $this->elementStack[count($this->elementStack)-1]->appendChild($messageElement); + } + } + + /** + * Set the msgOutputLevel this logger is to respond to. + * + * Only messages with a message level lower than or equal to the given + * level are output to the log. + * + *

Constants for the message levels are in Project.php. The order of + * the levels, from least to most verbose, is: + * + *

    + *
  • Project::MSG_ERR
  • + *
  • Project::MSG_WARN
  • + *
  • Project::MSG_INFO
  • + *
  • Project::MSG_VERBOSE
  • + *
  • Project::MSG_DEBUG
  • + *
+ * + * The default message level for DefaultLogger is Project::MSG_ERR. + * + * @param int $level The logging level for the logger. + * @see BuildLogger#setMessageOutputLevel() + */ + public function setMessageOutputLevel($level) { + $this->msgOutputLevel = (int) $level; + } + + /** + * Sets the output stream. + * @param OutputStream $output + * @see BuildLogger#setOutputStream() + */ + public function setOutputStream(OutputStream $output) { + $this->out = $output; + } + + /** + * Sets the error stream. + * @param OutputStream $err + * @see BuildLogger#setErrorStream() + */ + public function setErrorStream(OutputStream $err) { + $this->err = $err; + } + +} diff --git a/buildscripts/phing/classes/phing/listener/defaults.properties b/buildscripts/phing/classes/phing/listener/defaults.properties new file mode 100644 index 00000000..4a4dec68 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/defaults.properties @@ -0,0 +1,43 @@ +#################################################### +# +# 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=01;31 +AnsiColorLogger.WARNING_COLOR=01;35 +AnsiColorLogger.INFO_COLOR=00;36 +AnsiColorLogger.VERBOSE_COLOR=00;32 +AnsiColorLogger.DEBUG_COLOR=01;34 diff --git a/buildscripts/phing/classes/phing/mappers/FileNameMapper.php b/buildscripts/phing/classes/phing/mappers/FileNameMapper.php new file mode 100755 index 00000000..f4306b4c --- /dev/null +++ b/buildscripts/phing/classes/phing/mappers/FileNameMapper.php @@ -0,0 +1,59 @@ +. + */ + +/** + * Interface for filename mapper classes. + * + * @author Andreas Aderhold, andi@binarycloud.com + * @author Hans Lellelid + * @version $Id$ + * @package phing.mappers + */ +interface FileNameMapper { + + /** + * The mapper implementation. + * + * @param mixed $sourceFileName The data the mapper works on. + * @return array The data after the mapper has been applied; must be in array format (for some reason). + */ + public function main($sourceFileName); + + /** + * Accessor. Sets the to property. The actual implementation + * depends on the child class. + * + * @param string $to To what this mapper should convert the from string + * @return void + */ + public function setTo($to); + + /** + * Accessor. Sets the from property. What this mapper should + * recognize. The actual implementation is dependent upon the + * child class + * + * @param string $from On what this mapper should work + * @return void + */ + public function setFrom($from); + +} diff --git a/buildscripts/phing/classes/phing/mappers/FlattenMapper.php b/buildscripts/phing/classes/phing/mappers/FlattenMapper.php new file mode 100755 index 00000000..55ed4113 --- /dev/null +++ b/buildscripts/phing/classes/phing/mappers/FlattenMapper.php @@ -0,0 +1,55 @@ +. + */ + +require_once 'phing/mappers/FileNameMapper.php'; + +/** + * Removes any directory information from the passed path. + * + * @author Andreas Aderhold + * @version $Id$ + * @package phing.mappers + */ +class FlattenMapper implements FileNameMapper { + + /** + * The mapper implementation. Returns string with source filename + * but without leading directory information + * + * @param string $sourceFileName The data the mapper works on + * @return array The data after the mapper has been applied + */ + function main($sourceFileName) { + $f = new PhingFile($sourceFileName); + return array($f->getName()); + } + + /** + * Ignored here. + */ + function setTo($to) {} + + /** + * Ignored here. + */ + function setFrom($from) {} + +} diff --git a/buildscripts/phing/classes/phing/mappers/GlobMapper.php b/buildscripts/phing/classes/phing/mappers/GlobMapper.php new file mode 100755 index 00000000..79df94d6 --- /dev/null +++ b/buildscripts/phing/classes/phing/mappers/GlobMapper.php @@ -0,0 +1,113 @@ +. + */ + +include_once 'phing/mappers/FileNameMapper.php'; + +/** + * description here + * + * @author Andreas Aderhold, andi@binarycloud.com + * @version $Id$ + * @package phing.mappers + */ +class GlobMapper implements FileNameMapper { + + /** + * Part of "from" pattern before the *. + */ + private $fromPrefix = null; + + /** + * Part of "from" pattern after the *. + */ + private $fromPostfix = null; + + /** + * Length of the prefix ("from" pattern). + */ + private $prefixLength; + + /** + * Length of the postfix ("from" pattern). + */ + private $postfixLength; + + /** + * Part of "to" pattern before the *. + */ + private $toPrefix = null; + + /** + * Part of "to" pattern after the *. + */ + private $toPostfix = null; + + + function main($_sourceFileName) { + if (($this->fromPrefix === null) + || !StringHelper::startsWith($this->fromPrefix, $_sourceFileName) + || !StringHelper::endsWith($this->fromPostfix, $_sourceFileName)) { + return null; + } + $varpart = $this->_extractVariablePart($_sourceFileName); + $substitution = $this->toPrefix.$varpart.$this->toPostfix; + return array($substitution); + } + + + + function setFrom($from) { + $index = strrpos($from, '*'); + + if ($index === false) { + $this->fromPrefix = $from; + $this->fromPostfix = ""; + } else { + $this->fromPrefix = substr($from, 0, $index); + $this->fromPostfix = substr($from, $index+1); + } + $this->prefixLength = strlen($this->fromPrefix); + $this->postfixLength = strlen($this->fromPostfix); + } + + /** + * Sets the "to" pattern. Required. + */ + function setTo($to) { + $index = strrpos($to, '*'); + if ($index === false) { + $this->toPrefix = $to; + $this->toPostfix = ""; + } else { + $this->toPrefix = substr($to, 0, $index); + $this->toPostfix = substr($to, $index+1); + } + } + + private function _extractVariablePart($_name) { + // ergh, i really hate php's string functions .... all but natural + $start = ($this->prefixLength === 0) ? 0 : $this->prefixLength; + $end = ($this->postfixLength === 0) ? strlen($_name) : strlen($_name) - $this->postfixLength; + $len = $end-$start; + return substr($_name, $start, $len); + } + +} diff --git a/buildscripts/phing/classes/phing/mappers/IdentityMapper.php b/buildscripts/phing/classes/phing/mappers/IdentityMapper.php new file mode 100755 index 00000000..e608a71d --- /dev/null +++ b/buildscripts/phing/classes/phing/mappers/IdentityMapper.php @@ -0,0 +1,54 @@ +. + */ + +require_once 'phing/mappers/FileNameMapper.php'; + +/** + * This mapper does nothing ;) + * + * @author Andreas Aderhold + * @author Hans Lellelid + * @version $Id$ + * @package phing.mappers + */ +class IdentityMapper implements FileNameMapper { + + /** + * The mapper implementation. Basically does nothing in this case. + * + * @param string $sourceFileName The data the mapper works on. + * @return array The data after the mapper has been applied + */ + function main($sourceFileName) { + return array($sourceFileName); + } + + /** + * Ignored here. + */ + function setTo($to) {} + + /** + * Ignored here. + */ + function setFrom($from) {} + +} diff --git a/buildscripts/phing/classes/phing/mappers/MergeMapper.php b/buildscripts/phing/classes/phing/mappers/MergeMapper.php new file mode 100755 index 00000000..dc60ab34 --- /dev/null +++ b/buildscripts/phing/classes/phing/mappers/MergeMapper.php @@ -0,0 +1,69 @@ +. + */ + +include_once 'phing/mappers/FileNameMapper.php'; + +/** + * For merging files into a single file. In practice just returns whatever value + * was set for "to". + * + * @author Andreas Aderhold + * @version $Id$ + * @package phing.mappers + */ +class MergeMapper implements FileNameMapper { + + /** the merge */ + private $mergedFile; + + /** + * The mapper implementation. Basically does nothing in this case. + * + * @param mixed The data the mapper works on + * @return mixed The data after the mapper has been applied + * @access public + * @author Andreas Aderhold, andi@binarycloud.com + */ + function main($sourceFileName) { + if ($this->mergedFile === null) { + throw new BuildException("MergeMapper error, to attribute not set"); + } + return array($this->mergedFile); + } + + /** + * Accessor. Sets the to property + * + * @param string To what this mapper should convert the from string + * @return boolean True + * @access public + * @author Andreas Aderhold, andi@binarycloud.com + */ + function setTo($to) { + $this->mergedFile = $to; + } + + /** + * Ignored. + */ + function setFrom($from) {} + +} diff --git a/buildscripts/phing/classes/phing/mappers/RegexpMapper.php b/buildscripts/phing/classes/phing/mappers/RegexpMapper.php new file mode 100755 index 00000000..ca387241 --- /dev/null +++ b/buildscripts/phing/classes/phing/mappers/RegexpMapper.php @@ -0,0 +1,97 @@ +. + */ + +require_once 'phing/mappers/FileNameMapper.php'; +include_once 'phing/util/StringHelper.php'; +include_once 'phing/util/regexp/Regexp.php'; + +/** + * Uses regular expressions to perform filename transformations. + * + * @author Andreas Aderhold + * @author Hans Lellelid + * @version $Id$ + * @package phing.mappers + */ +class RegexpMapper implements FileNameMapper { + + /** + * @var string + */ + private $to; + + /** + * The Regexp engine. + * @var Regexp + */ + private $reg; + + function __construct() { + // instantiage regexp matcher here + $this->reg = new Regexp(); + } + + /** + * Sets the "from" pattern. Required. + */ + function setFrom($from) { + $this->reg->SetPattern($from); + } + + /** + * Sets the "to" pattern. Required. + */ + function setTo($to) { + + // [HL] I'm changing the way this works for now to just use string + //$this->to = StringHelper::toCharArray($to); + + $this->to = $to; + } + + function main($sourceFileName) { + if ($this->reg === null || $this->to === null || !$this->reg->matches((string) $sourceFileName)) { + return null; + } + return array($this->replaceReferences($sourceFileName)); + } + + /** + * Replace all backreferences in the to pattern with the matched groups. + * groups of the source. + * @param string $source The source filename. + */ + private function replaceReferences($source) { + + // FIXME + // Can't we just use engine->replace() to handle this? the Preg engine + // will automatically convert \1 references to $1 + + // the expression has already been processed (when ->matches() was run in Main()) + // so no need to pass $source again to the engine. + $groups = (array) $this->reg->getGroups(); + + // replace \1 with value of $groups[1] and return the modified "to" string + return preg_replace('/\\\([\d]+)/e', "\$groups[$1]", $this->to); + } + +} + diff --git a/buildscripts/phing/classes/phing/parser/AbstractHandler.php b/buildscripts/phing/classes/phing/parser/AbstractHandler.php new file mode 100755 index 00000000..1837bbbf --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/AbstractHandler.php @@ -0,0 +1,98 @@ +. + */ + +include_once 'phing/parser/ExpatParseException.php'; + +/** + * This is an abstract class all SAX handler classes must extend + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @package phing.parser + */ +abstract class AbstractHandler { + + public $parentHandler = null; + public $parser = null; + + /** + * Constructs a SAX handler parser. + * + * The constructor must be called by all derived classes. + * + * @param object the parser object + * @param object the parent handler of this handler + */ + protected function __construct($parser, $parentHandler) { + $this->parentHandler = $parentHandler; + $this->parser = $parser; + $this->parser->setHandler($this); + } + + /** + * Gets invoked when a XML open tag occurs + * + * Must be overloaded by the child class. Throws an ExpatParseException + * if there is no handler registered for an element. + * + * @param string the name of the XML element + * @param array the attributes of the XML element + */ + public function startElement($name, $attribs) { + throw new ExpatParseException("Unexpected element $name"); + } + + /** + * Gets invoked when element closes method. + * + */ + protected function finished() {} + + /** + * Gets invoked when a XML element ends. + * + * Can be overloaded by the child class. But should not. It hands + * over control to the parentHandler of this. + * + * @param string the name of the XML element + */ + public function endElement($name) { + $this->finished(); + $this->parser->setHandler($this->parentHandler); + } + + /** + * Invoked by occurance of #PCDATA. + * + * @param string the name of the XML element + * @exception ExpatParserException if there is no CDATA but method + * was called + * @access public + */ + public function characters($data) { + $s = trim($data); + if (strlen($s) > 0) { + throw new ExpatParseException("Unexpected text '$s'", $this->parser->getLocation()); + } + } +} diff --git a/buildscripts/phing/classes/phing/parser/AbstractSAXParser.php b/buildscripts/phing/classes/phing/parser/AbstractSAXParser.php new file mode 100755 index 00000000..449386e7 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/AbstractSAXParser.php @@ -0,0 +1,116 @@ +. + */ + +/** + * The abstract SAX parser class. + * + * This class represents a SAX parser. It is a abstract calss that must be + * implemented by the real parser that must extend this class + * + * @author Andreas Aderhold + * @author Hans Lellelid + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @package phing.parser + */ +abstract class AbstractSAXParser { + + /** The AbstractHandler object. */ + protected $handler; + + /** + * Constructs a SAX parser + */ + function __construct() {} + + /** + * Sets options for PHP interal parser. Must be implemented by the parser + * class if it should be used. + */ + abstract function parserSetOption($opt, $val); + + /** + * Sets the current element handler object for this parser. Usually this + * is an object using extending "AbstractHandler". + * + * @param AbstractHandler $obj The handler object. + */ + function setHandler( $obj) { + $this->handler = $obj; + } + + /** + * Method that gets invoked when the parser runs over a XML start element. + * + * This method is called by PHP's internal parser functions and registered + * in the actual parser implementation. + * It gives control to the current active handler object by calling the + * startElement() method. + * + * @param object the php's internal parser handle + * @param string the open tag name + * @param array the tag's attributes if any + * @throws Exception - Exceptions may be thrown by the Handler + */ + function startElement($parser, $name, $attribs) { + $this->handler->startElement($name, $attribs); + } + + /** + * Method that gets invoked when the parser runs over a XML close element. + * + * This method is called by PHP's internal parser funcitons and registered + * in the actual parser implementation. + * + * It gives control to the current active handler object by calling the + * endElement() method. + * + * @param object the php's internal parser handle + * @param string the closing tag name + * @throws Exception - Exceptions may be thrown by the Handler + */ + function endElement($parser, $name) { + $this->handler->endElement($name); + } + + /** + * Method that gets invoked when the parser runs over CDATA. + * + * This method is called by PHP's internal parser functions and registered + * in the actual parser implementation. + * + * It gives control to the current active handler object by calling the + * characters() method. That processes the given CDATA. + * + * @param resource $parser php's internal parser handle. + * @param string $data the CDATA + * @throws Exception - Exceptions may be thrown by the Handler + */ + function characters($parser, $data) { + $this->handler->characters($data); + } + + /** + * Entrypoint for parser. This method needs to be implemented by the + * child classt that utilizes the concrete parser + */ + abstract function parse(); +} diff --git a/buildscripts/phing/classes/phing/parser/DataTypeHandler.php b/buildscripts/phing/classes/phing/parser/DataTypeHandler.php new file mode 100755 index 00000000..7531baaf --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/DataTypeHandler.php @@ -0,0 +1,144 @@ +. + */ + +include_once 'phing/RuntimeConfigurable.php'; + +/** + * Configures a Project (complete with Targets and Tasks) based on + * a XML build file. + *

+ * Design/ZE2 migration note: + * If PHP would support nested classes. All the phing/parser/*Filter + * classes would be nested within this class + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing.parser + */ + +class DataTypeHandler extends AbstractHandler { + + private $target; + private $element; + private $wrapper; + + /** + * Constructs a new DataTypeHandler and sets up everything. + * + * @param AbstractSAXParser $parser The XML parser (default: ExpatParser) + * @param AbstractHandler $parentHandler The parent handler that invoked this handler. + * @param ProjectConfigurator $configurator The ProjectConfigurator object + * @param Target $target The target object this datatype is contained in (null for top-level datatypes). + */ + function __construct(AbstractSAXParser $parser, AbstractHandler $parentHandler, ProjectConfigurator $configurator, $target = null) { // FIXME b2 typehinting + parent::__construct($parser, $parentHandler); + $this->target = $target; + $this->configurator = $configurator; + } + + /** + * Executes initialization actions required to setup the data structures + * related to the tag. + *

+ * This includes: + *

    + *
  • creation of the datatype object
  • + *
  • calling the setters for attributes
  • + *
  • adding the type to the target object if any
  • + *
  • adding a reference to the task (if id attribute is given)
  • + *
+ * + * @param string the tag that comes in + * @param array attributes the tag carries + * @throws ExpatParseException if attributes are incomplete or invalid + * @access public + */ + function init($propType, $attrs) { + // shorthands + $project = $this->configurator->project; + $configurator = $this->configurator; + + try {//try + $this->element = $project->createDataType($propType); + + if ($this->element === null) { + throw new BuildException("Unknown data type $propType"); + } + + if ($this->target !== null) { + $this->wrapper = new RuntimeConfigurable($this->element, $propType); + $this->wrapper->setAttributes($attrs); + $this->target->addDataType($this->wrapper); + } else { + $configurator->configure($this->element, $attrs, $project); + $configurator->configureId($this->element, $attrs); + } + + } catch (BuildException $exc) { + throw new ExpatParseException($exc, $this->parser->getLocation()); + } + } + + /** + * Handles character data. + * + * @param string the CDATA that comes in + * @access public + */ + function characters($data) { + $project = $this->configurator->project; + try {//try + $this->configurator->addText($project, $this->element, $data); + } catch (BuildException $exc) { + throw new ExpatParseException($exc->getMessage(), $this->parser->getLocation()); + } + } + + /** + * Checks for nested tags within the current one. Creates and calls + * handlers respectively. + * + * @param string the tag that comes in + * @param array attributes the tag carries + * @access public + */ + function startElement($name, $attrs) { + $nef = new NestedElementHandler($this->parser, $this, $this->configurator, $this->element, $this->wrapper, $this->target); + $nef->init($name, $attrs); + } + + /** + * Overrides endElement for data types. Tells the type + * handler that processing the element had been finished so + * handlers know they can perform actions that need to be + * based on the data contained within the element. + * + * @param string the name of the XML element + * @return void + */ + function endElement($name) { + $this->element->parsingComplete(); + parent::endElement($name); + } + +} diff --git a/buildscripts/phing/classes/phing/parser/ExpatParseException.php b/buildscripts/phing/classes/phing/parser/ExpatParseException.php new file mode 100755 index 00000000..65461576 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/ExpatParseException.php @@ -0,0 +1,31 @@ +. + */ + +require_once 'phing/BuildException.php'; + +/** + * This class throws errors for Expat, the XML processor. + * + * @author Andreas Aderhold, andi@binarycloud.com + * @version $Id$ + * @package phing.parser + */ +class ExpatParseException extends BuildException {} diff --git a/buildscripts/phing/classes/phing/parser/ExpatParser.php b/buildscripts/phing/classes/phing/parser/ExpatParser.php new file mode 100755 index 00000000..ef5348e5 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/ExpatParser.php @@ -0,0 +1,140 @@ +. + */ + +require_once 'phing/parser/AbstractSAXParser.php'; +include_once 'phing/parser/ExpatParseException.php'; +include_once 'phing/system/io/IOException.php'; +include_once 'phing/system/io/FileReader.php'; + +/** + * This class is a wrapper for the PHP's internal expat parser. + * + * It takes an XML file represented by a abstract path name, and starts + * parsing the file and calling the different "trap" methods inherited from + * the AbstractParser class. + * + * Those methods then invoke the represenatative methods in the registered + * handler classes. + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing.parser + */ + +class ExpatParser extends AbstractSAXParser { + + /** @var resource */ + private $parser; + + /** @var Reader */ + private $reader; + + private $file; + + private $buffer = 4096; + + private $error_string = ""; + + private $line = 0; + + /** @var Location Current cursor pos in XML file. */ + private $location; + + /** + * Constructs a new ExpatParser object. + * + * The constructor accepts a PhingFile object that represents the filename + * for the file to be parsed. It sets up php's internal expat parser + * and options. + * + * @param Reader $reader The Reader Object that is to be read from. + * @param string $filename Filename to read. + * @throws Exception if the given argument is not a PhingFile object + */ + function __construct(Reader $reader, $filename=null) { + + $this->reader = $reader; + if ($filename !== null) { + $this->file = new PhingFile($filename); + } + $this->parser = xml_parser_create(); + $this->buffer = 4096; + $this->location = new Location(); + xml_set_object($this->parser, $this); + xml_set_element_handler($this->parser, array($this,"startElement"),array($this,"endElement")); + xml_set_character_data_handler($this->parser, array($this, "characters")); + } + + /** + * Override PHP's parser default settings, created in the constructor. + * + * @param string the option to set + * @throws mixed the value to set + * @return boolean true if the option could be set, otherwise false + * @access public + */ + function parserSetOption($opt, $val) { + return xml_parser_set_option($this->parser, $opt, $val); + } + + /** + * Returns the location object of the current parsed element. It describes + * the location of the element within the XML file (line, char) + * + * @return object the location of the current parser + * @access public + */ + function getLocation() { + if ($this->file !== null) { + $path = $this->file->getAbsolutePath(); + } else { + $path = $this->reader->getResource(); + } + $this->location = new Location($path, xml_get_current_line_number($this->parser), xml_get_current_column_number($this->parser)); + return $this->location; + } + + /** + * Starts the parsing process. + * + * @param string the option to set + * @return int 1 if the parsing succeeded + * @throws ExpatParseException if something gone wrong during parsing + * @throws IOException if XML file can not be accessed + * @access public + */ + function parse() { + + while ( ($data = $this->reader->read()) !== -1 ) { + if (!xml_parse($this->parser, $data, $this->reader->eof())) { + $error = xml_error_string(xml_get_error_code($this->parser)); + $e = new ExpatParseException($error, $this->getLocation()); + xml_parser_free($this->parser); + throw $e; + } + } + xml_parser_free($this->parser); + + return 1; + } +} diff --git a/buildscripts/phing/classes/phing/parser/Location.php b/buildscripts/phing/classes/phing/parser/Location.php new file mode 100755 index 00000000..4110bd00 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/Location.php @@ -0,0 +1,76 @@ +. + */ + +/** + * Stores the file name and line number of a XML file + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing.parser + */ + +class Location { + + private $fileName; + private $lineNumber; + private $columnNumber; + + /** + * Constructs the location consisting of a file name and line number + * + * @param string the filename + * @param integer the line number + * @param integer the column number + * @access public + */ + function Location($fileName = null, $lineNumber = null, $columnNumber = null) { + $this->fileName = $fileName; + $this->lineNumber = $lineNumber; + $this->columnNumber = $columnNumber; + } + + /** + * Returns the file name, line number and a trailing space. + * + * An error message can be appended easily. For unknown locations, + * returns empty string. + * + * @return string the string representation of this Location object + * @access public + */ + function toString() { + $buf = ""; + if ($this->fileName !== null) { + $buf.=$this->fileName; + if ($this->lineNumber !== null) { + $buf.= ":".$this->lineNumber; + } + $buf.=":".$this->columnNumber; + } + return (string) $buf; + } + + function __toString () { + return $this->toString(); + } +} diff --git a/buildscripts/phing/classes/phing/parser/NestedElementHandler.php b/buildscripts/phing/classes/phing/parser/NestedElementHandler.php new file mode 100755 index 00000000..15d0e173 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/NestedElementHandler.php @@ -0,0 +1,186 @@ +. + */ + +include_once 'phing/IntrospectionHelper.php'; +include_once 'phing/TaskContainer.php'; + +/** + * The nested element handler class. + * + * This class handles the occurance of runtime registered tags like + * datatypes (fileset, patternset, etc) and it's possible nested tags. It + * introspects the implementation of the class and sets up the data structures. + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing.parser + */ + +class NestedElementHandler extends AbstractHandler { + + /** + * Reference to the parent object that represents the parent tag + * of this nested element + * @var object + */ + private $parent; + + /** + * Reference to the child object that represents the child tag + * of this nested element + * @var object + */ + private $child; + + /** + * Reference to the parent wrapper object + * @var object + */ + private $parentWrapper; + + /** + * Reference to the child wrapper object + * @var object + */ + private $childWrapper; + + /** + * Reference to the related target object + * @var object the target instance + */ + private $target; + + /** + * Constructs a new NestedElement handler and sets up everything. + * + * @param object the ExpatParser object + * @param object the parent handler that invoked this handler + * @param object the ProjectConfigurator object + * @param object the parent object this element is contained in + * @param object the parent wrapper object + * @param object the target object this task is contained in + * @access public + */ + function __construct($parser, $parentHandler, $configurator, $parent, $parentWrapper, $target) { + parent::__construct($parser, $parentHandler); + $this->configurator = $configurator; + if ($parent instanceof TaskAdapter) { + $this->parent = $parent->getProxy(); + } else { + $this->parent = $parent; + } + $this->parentWrapper = $parentWrapper; + $this->target = $target; + } + + /** + * Executes initialization actions required to setup the data structures + * related to the tag. + *

+ * This includes: + *

    + *
  • creation of the nested element
  • + *
  • calling the setters for attributes
  • + *
  • adding the element to the container object
  • + *
  • adding a reference to the element (if id attribute is given)
  • + *
+ * + * @param string the tag that comes in + * @param array attributes the tag carries + * @throws ExpatParseException if the setup process fails + * @access public + */ + function init($propType, $attrs) { + $configurator = $this->configurator; + $project = $this->configurator->project; + + // introspect the parent class that is custom + $parentClass = get_class($this->parent); + $ih = IntrospectionHelper::getHelper($parentClass); + try { + if ($this->parent instanceof UnknownElement) { + $this->child = new UnknownElement(strtolower($propType)); + $this->parent->addChild($this->child); + } else { + $this->child = $ih->createElement($project, $this->parent, strtolower($propType)); + } + + $configurator->configureId($this->child, $attrs); + + if ($this->parentWrapper !== null) { + $this->childWrapper = new RuntimeConfigurable($this->child, $propType); + $this->childWrapper->setAttributes($attrs); + $this->parentWrapper->addChild($this->childWrapper); + } else { + $configurator->configure($this->child, $attrs, $project); + $ih->storeElement($project, $this->parent, $this->child, strtolower($propType)); + } + } catch (BuildException $exc) { + throw new ExpatParseException("Error initializing nested element <$propType>", $exc, $this->parser->getLocation()); + } + } + + /** + * Handles character data. + * + * @param string the CDATA that comes in + * @throws ExpatParseException if the CDATA could not be set-up properly + * @access public + */ + function characters($data) { + + $configurator = $this->configurator; + $project = $this->configurator->project; + + if ($this->parentWrapper === null) { + try { + $configurator->addText($project, $this->child, $data); + } catch (BuildException $exc) { + throw new ExpatParseException($exc->getMessage(), $this->parser->getLocation()); + } + } else { + $this->childWrapper->addText($data); + } + } + + /** + * Checks for nested tags within the current one. Creates and calls + * handlers respectively. + * + * @param string the tag that comes in + * @param array attributes the tag carries + * @access public + */ + function startElement($name, $attrs) { + //print(get_class($this) . " name = $name, attrs = " . implode(",",$attrs) . "\n"); + if ($this->child instanceof TaskContainer) { + // taskcontainer nested element can contain other tasks - no other + // nested elements possible + $tc = new TaskHandler($this->parser, $this, $this->configurator, $this->child, $this->childWrapper, $this->target); + $tc->init($name, $attrs); + } else { + $neh = new NestedElementHandler($this->parser, $this, $this->configurator, $this->child, $this->childWrapper, $this->target); + $neh->init($name, $attrs); + } + } +} diff --git a/buildscripts/phing/classes/phing/parser/PhingXMLContext.php b/buildscripts/phing/classes/phing/parser/PhingXMLContext.php new file mode 100755 index 00000000..0e2dd0f4 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/PhingXMLContext.php @@ -0,0 +1,81 @@ +. + */ + +/** + * Track the current state of the Xml parse operation. + * + * @author Bryan Davis + * @version $Id$ + * @access public + * @package phing.parser + */ +class PhingXMLContext { + + /** + * Constructor + * @param $project the project to which this antxml context belongs to + */ + public function __construct ($project) { + $this->project = $project; + } + + /** The project to configure. */ + private $project; + + private $configurators = array(); + + public function startConfigure ($cfg) { + $this->configurators[] = $cfg; + } + + public function endConfigure () { + array_pop($this->configurators); + } + + public function getConfigurator () { + $l = count($this->configurators); + if (0 == $l) { + return null; + } else { + return $this->configurators[$l - 1]; + } + } + + /** Impoerted files */ + private $importStack = array(); + + public function addImport ($file) { + $this->importStack[] = $file; + } + + public function getImportStack () { + return $this->importStack; + } + + /** + * find out the project to which this context belongs + * @return project + */ + public function getProject() { + return $this->project; + } + +} //end PhingXMLContext diff --git a/buildscripts/phing/classes/phing/parser/ProjectConfigurator.php b/buildscripts/phing/classes/phing/parser/ProjectConfigurator.php new file mode 100755 index 00000000..90471336 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/ProjectConfigurator.php @@ -0,0 +1,387 @@ +. + */ + +include_once 'phing/system/io/BufferedReader.php'; +include_once 'phing/system/io/FileReader.php'; +include_once 'phing/BuildException.php'; +include_once 'phing/system/lang/FileNotFoundException.php'; +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/parser/PhingXMLContext.php'; +include_once 'phing/IntrospectionHelper.php'; + +/** + * The datatype handler class. + * + * This class handles the occurance of registered datatype tags like + * FileSet + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id: ae9b63bef4c1423d88bb36ddfc5196763828123e $ + * @access public + * @package phing.parser + */ +class ProjectConfigurator { + + public $project; + public $locator; + + public $buildFile; + public $buildFileParent; + + /** Targets in current file */ + private $currentTargets; + + /** Synthetic target that will be called at the end to the parse phase */ + private $parseEndTarget; + + /** Name of the current project */ + private $currentProjectName; + + private $isParsing = true; + + /** + * Indicates whether the project tag attributes are to be ignored + * when processing a particular build file. + */ + private $ignoreProjectTag = false; + + /** + * Static call to ProjectConfigurator. Use this to configure a + * project. Do not use the new operator. + * + * @param object the Project instance this configurator should use + * @param object the buildfile object the parser should use + * @access public + */ + public static function configureProject(Project $project, PhingFile $buildFile) { + $pc = new ProjectConfigurator($project, $buildFile); + $pc->parse(); + } + + /** + * Constructs a new ProjectConfigurator object + * This constructor is private. Use a static call to + * configureProject to configure a project. + * + * @param object the Project instance this configurator should use + * @param object the buildfile object the parser should use + * @access private + */ + function __construct(Project $project, PhingFile $buildFile) { + $this->project = $project; + $this->buildFile = new PhingFile($buildFile->getAbsolutePath()); + $this->buildFileParent = new PhingFile($this->buildFile->getParent()); + $this->currentTargets = array(); + $this->parseEndTarget = new Target(); + } + + /** + * find out the build file + * @return the build file to which the xml context belongs + */ + public function getBuildFile() { + return $this->buildFile; + } + + /** + * find out the parent build file of this build file + * @return the parent build file of this build file + */ + public function getBuildFileParent() { + return $this->buildFileParent; + } + + /** + * find out the current project name + * @return current project name + */ + public function getCurrentProjectName() { + return $this->currentProjectName; + } + + /** + * set the name of the current project + * @param name name of the current project + */ + public function setCurrentProjectName($name) { + $this->currentProjectName = $name; + } + + /** + * tells whether the project tag is being ignored + * @return whether the project tag is being ignored + */ + public function isIgnoringProjectTag() { + return $this->ignoreProjectTag; + } + + /** + * sets the flag to ignore the project tag + * @param flag to ignore the project tag + */ + public function setIgnoreProjectTag($flag) { + $this->ignoreProjectTag = $flag; + } + + public function &getCurrentTargets () { + return $this->currentTargets; + } + + public function isParsing () { + return $this->isParsing; + } + + /** + * Creates the ExpatParser, sets root handler and kick off parsing + * process. + * + * @throws BuildException if there is any kind of execption during + * the parsing process + * @access private + */ + protected function parse() { + try { + // get parse context + $ctx = $this->project->getReference("phing.parsing.context"); + if (null == $ctx) { + // make a new context and register it with project + $ctx = new PhingXMLContext($this->project); + $this->project->addReference("phing.parsing.context", $ctx); + } + + //record this parse with context + $ctx->addImport($this->buildFile); + + if (count($ctx->getImportStack()) > 1) { + // this is an imported file + // modify project tag parse behavior + $this->setIgnoreProjectTag(true); + } + // push action onto global stack + $ctx->startConfigure($this); + + $reader = new BufferedReader(new FileReader($this->buildFile)); + $parser = new ExpatParser($reader); + $parser->parserSetOption(XML_OPTION_CASE_FOLDING,0); + $parser->setHandler(new RootHandler($parser, $this)); + $this->project->log("parsing buildfile ".$this->buildFile->getName(), Project::MSG_VERBOSE); + $parser->parse(); + $reader->close(); + + // mark parse phase as completed + $this->isParsing = false; + // execute delayed tasks + $this->parseEndTarget->main(); + // pop this action from the global stack + $ctx->endConfigure(); + } catch (Exception $exc) { + throw new BuildException("Error reading project file", $exc); + } + } + + /** + * Delay execution of a task until after the current parse phase has + * completed. + * + * @param Task $task Task to execute after parse + */ + public function delayTaskUntilParseEnd ($task) { + $this->parseEndTarget->addTask($task); + } + + /** + * Configures an element and resolves eventually given properties. + * + * @param object the element to configure + * @param array the element's attributes + * @param object the project this element belongs to + * @throws Exception if arguments are not valid + * @throws BuildException if attributes can not be configured + * @access public + */ + public static function configure($target, $attrs, Project $project) { + + if ($target instanceof TaskAdapter) { + $target = $target->getProxy(); + } + + // if the target is an UnknownElement, this means that the tag had not been registered + // when the enclosing element (task, target, etc.) was configured. It is possible, however, + // that the tag was registered (e.g. using ) after the original configuration. + // ... so, try to load it again: + if ($target instanceof UnknownElement) { + $tryTarget = $project->createTask($target->getTaskType()); + if ($tryTarget) { + $target = $tryTarget; + } + } + + $bean = get_class($target); + $ih = IntrospectionHelper::getHelper($bean); + + foreach ($attrs as $key => $value) { + if ($key == 'id') { + continue; + // throw new BuildException("Id must be set Extermnally"); + } + $value = self::replaceProperties($project, $value, $project->getProperties()); + try { // try to set the attribute + $ih->setAttribute($project, $target, strtolower($key), $value); + } catch (BuildException $be) { + // id attribute must be set externally + if ($key !== "id") { + throw $be; + } + } + } + } + + /** + * Configures the #CDATA of an element. + * + * @param object the project this element belongs to + * @param object the element to configure + * @param string the element's #CDATA + * @access public + */ + public static function addText($project, $target, $text = null) { + if ($text === null || strlen(trim($text)) === 0) { + return; + } + $ih = IntrospectionHelper::getHelper(get_class($target)); + $text = self::replaceProperties($project, $text, $project->getProperties()); + $ih->addText($project, $target, $text); + } + + /** + * Stores a configured child element into its parent object + * + * @param object the project this element belongs to + * @param object the parent element + * @param object the child element + * @param string the XML tagname + * @access public + */ + public static function storeChild($project, $parent, $child, $tag) { + $ih = IntrospectionHelper::getHelper(get_class($parent)); + $ih->storeElement($project, $parent, $child, $tag); + } + + // The following three properties are a sort of hack + // to enable a static function to serve as the callback + // for preg_replace_callback(). Clearly we cannot use object + // variables, since the replaceProperties() is called statically. + // This is IMO better than using global variables in the callback. + + private static $propReplaceProject; + private static $propReplaceProperties; + private static $propReplaceLogLevel = Project::MSG_VERBOSE; + + /** + * Replace ${} style constructions in the given value with the + * string value of the corresponding data types. This method is + * static. + * + * @param object $project the project that should be used for property look-ups + * @param string $value the string to be scanned for property references + * @param array $keys property keys + * @param integer $logLevel the level of generated log messages + * @return string the replaced string or null if the string + * itself was null + */ + public static function replaceProperties(Project $project, $value, $keys, $logLevel = Project::MSG_VERBOSE) { + + if ($value === null) { + return null; + } + + // These are a "hack" to support static callback for preg_replace_callback() + + // make sure these get initialized every time + self::$propReplaceProperties = $keys; + self::$propReplaceProject = $project; + self::$propReplaceLogLevel = $logLevel; + + // Because we're not doing anything special (like multiple passes), + // regex is the simplest / fastest. PropertyTask, though, uses + // the old parsePropertyString() method, since it has more stringent + // requirements. + + $sb = $value; + $iteration = 0; + + // loop to recursively replace tokens + while (strpos($sb, '${') !== false) + { + $sb = preg_replace_callback('/\$\{([^\$}]+)\}/', array('ProjectConfigurator', 'replacePropertyCallback'), $sb); + + // keep track of iterations so we can break out of otherwise infinite loops. + $iteration++; + if ($iteration == 5) + { + return $sb; + } + } + + return $sb; + } + + /** + * Private [static] function for use by preg_replace_callback to replace a single param. + * This method makes use of a static variable to hold the + */ + private static function replacePropertyCallback($matches) + { + $propertyName = $matches[1]; + if (!isset(self::$propReplaceProperties[$propertyName])) { + self::$propReplaceProject->log('Property ${'.$propertyName.'} has not been set.', self::$propReplaceLogLevel); + return $matches[0]; + } else { + self::$propReplaceProject->log('Property ${'.$propertyName.'} => ' . self::$propReplaceProperties[$propertyName], self::$propReplaceLogLevel); + } + + $propertyValue = self::$propReplaceProperties[$propertyName]; + + if (is_bool($propertyValue)) { + if ($propertyValue === true) { + $propertyValue = "true"; + } else { + $propertyValue = "false"; + } + } + + return $propertyValue; + } + + /** + * Scan Attributes for the id attribute and maybe add a reference to + * project. + * + * @param object the element's object + * @param array the element's attributes + */ + public function configureId($target, $attr) { + if (isset($attr['id']) && $attr['id'] !== null) { + $this->project->addReference($attr['id'], $target); + } + } +} diff --git a/buildscripts/phing/classes/phing/parser/ProjectHandler.php b/buildscripts/phing/classes/phing/parser/ProjectHandler.php new file mode 100755 index 00000000..a027c1e6 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/ProjectHandler.php @@ -0,0 +1,176 @@ +. + */ + +require_once 'phing/parser/AbstractHandler.php'; +require_once 'phing/system/io/PhingFile.php'; + +/** + * Handler class for the XML element This class handles all elements + * under the element. + * + * @author Andreas Aderhold + * @copyright (c) 2001,2002 THYRELL. All rights reserved + * @version $Id: 393b1f3d8758281af2fd0153ffc6151333457527 $ + * @access public + * @package phing.parser + */ +class ProjectHandler extends AbstractHandler { + + /** + * The phing project configurator object. + * @var ProjectConfigurator + */ + private $configurator; + + /** + * Constructs a new ProjectHandler + * + * @param object the ExpatParser object + * @param object the parent handler that invoked this handler + * @param object the ProjectConfigurator object + * @access public + */ + function __construct($parser, $parentHandler, $configurator) { + $this->configurator = $configurator; + parent::__construct($parser, $parentHandler); + } + + /** + * Executes initialization actions required to setup the project. Usually + * this method handles the attributes of a tag. + * + * @param string the tag that comes in + * @param array attributes the tag carries + * @param object the ProjectConfigurator object + * @throws ExpatParseException if attributes are incomplete or invalid + * @access public + */ + function init($tag, $attrs) { + $def = null; + $name = null; + $id = null; + $desc = null; + $baseDir = null; + $ver = null; + + // some shorthands + $project = $this->configurator->project; + $buildFileParent = $this->configurator->buildFileParent; + + foreach ($attrs as $key => $value) { + if ($key === "default") { + $def = $value; + } elseif ($key === "name") { + $name = $value; + } elseif ($key === "id") { + $id = $value; + } elseif ($key === "basedir") { + $baseDir = $value; + } elseif ($key === "description") { + $desc = $value; + } elseif ($key === "phingVersion") { + $ver = $value; + } else { + throw new ExpatParseException("Unexpected attribute '$key'"); + } + } + // these things get done no matter what + if (null != $name) { + $canonicalName = self::canonicalName($name); + $this->configurator->setCurrentProjectName($canonicalName); + $project->setUserProperty("phing.file.{$canonicalName}", + (string) $this->configurator->getBuildFile()); + } + + if (!$this->configurator->isIgnoringProjectTag()) { + if ($def === null) { + throw new ExpatParseException( + "The default attribute of project is required"); + } + $project->setDefaultTarget($def); + + if ($name !== null) { + $project->setName($name); + $project->addReference($name, $project); + + } + + if ($id !== null) { + $project->addReference($id, $project); + } + + if ($desc !== null) { + $project->setDescription($desc); + } + + if($ver !== null) { + $project->setPhingVersion($ver); + } + + if ($project->getProperty("project.basedir") !== null) { + $project->setBasedir($project->getProperty("project.basedir")); + } else { + if ($baseDir === null) { + $project->setBasedir($buildFileParent->getAbsolutePath()); + } else { + // check whether the user has specified an absolute path + $f = new PhingFile($baseDir); + if ($f->isAbsolute()) { + $project->setBasedir($baseDir); + } else { + $project->setBaseDir($project->resolveFile($baseDir, new PhingFile(getcwd()))); + } + } + } + } + } + + /** + * Handles start elements within the tag by creating and + * calling the required handlers for the detected element. + * + * @param string the tag that comes in + * @param array attributes the tag carries + * @throws ExpatParseException if a unxepected element occurs + * @access public + */ + function startElement($name, $attrs) { + + $project = $this->configurator->project; + $types = $project->getDataTypeDefinitions(); + + if ($name == "target") { + $tf = new TargetHandler($this->parser, $this, $this->configurator); + $tf->init($name, $attrs); + } elseif (isset($types[$name])) { + $tyf = new DataTypeHandler($this->parser, $this, $this->configurator); + $tyf->init($name, $attrs); + } else { + $tf = new TaskHandler($this->parser, $this, $this->configurator); + $tf->init($name, $attrs); + } + } + + static function canonicalName ($name) { + return preg_replace('/\W/', '_', strtolower($name)); + } +} + diff --git a/buildscripts/phing/classes/phing/parser/RootHandler.php b/buildscripts/phing/classes/phing/parser/RootHandler.php new file mode 100755 index 00000000..435aae40 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/RootHandler.php @@ -0,0 +1,82 @@ +. + */ + +require_once 'phing/parser/AbstractHandler.php'; +include_once 'phing/parser/ExpatParseException.php'; +include_once 'phing/parser/ProjectHandler.php'; + +/** + * Root filter class for a phing buildfile. + * + * The root filter is called by the parser first. This is where the phing + * specific parsing starts. RootHandler decides what to do next. + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @package phing.parser + */ +class RootHandler extends AbstractHandler { + + /** + * The phing project configurator object + */ + private $configurator; + + /** + * Constructs a new RootHandler + * + * The root filter is required so the parser knows what to do. It's + * called by the ExpatParser that is instatiated in ProjectConfigurator. + * + * It recieves the expat parse object ref and a reference to the + * configurator + * + * @param AbstractSAXParser $parser The ExpatParser object. + * @param ProjectConfigurator $configurator The ProjectConfigurator object. + */ + function __construct(AbstractSAXParser $parser, ProjectConfigurator $configurator) { + $this->configurator = $configurator; + parent::__construct($parser, $this); + } + + /** + * Kick off a custom action for a start element tag. + * + * The root element of our buildfile is the <project> element. The + * root filter handles this element if it occurs, creates ProjectHandler + * to handle any nested tags & attributes of the <project> tag, + * and calls init. + * + * @param string $tag The xml tagname + * @param array $attrs The attributes of the tag + * @throws ExpatParseException if the first element within our build file + * is not the >project< element + */ + function startElement($tag, $attrs) { + if ($tag === "project") { + $ph = new ProjectHandler($this->parser, $this, $this->configurator); + $ph->init($tag, $attrs); + } else { + throw new ExpatParseException("Unexpected tag <$tag> in top-level of build file.", $this->parser->getLocation()); + } + } +} diff --git a/buildscripts/phing/classes/phing/parser/TargetHandler.php b/buildscripts/phing/classes/phing/parser/TargetHandler.php new file mode 100755 index 00000000..a6c4d037 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/TargetHandler.php @@ -0,0 +1,196 @@ +. + */ + +require_once 'phing/parser/AbstractHandler.php'; + +/** + * The target handler class. + * + * This class handles the occurance of a tag and it's possible + * nested tags (datatypes and tasks). + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id: f73d7c67a353cf16f048af3ba013d84ec726a926 $ + * @package phing.parser + */ +class TargetHandler extends AbstractHandler { + + /** + * Reference to the target object that represents the currently parsed + * target. + * @var object the target instance + */ + private $target; + + /** + * The phing project configurator object + * @var ProjectConfigurator + */ + private $configurator; + + /** + * Constructs a new TargetHandler + * + * @param object the ExpatParser object + * @param object the parent handler that invoked this handler + * @param object the ProjectConfigurator object + */ + function __construct(AbstractSAXParser $parser, AbstractHandler $parentHandler, ProjectConfigurator $configurator) { + parent::__construct($parser, $parentHandler); + $this->configurator = $configurator; + } + + /** + * Executes initialization actions required to setup the data structures + * related to the tag. + *

+ * This includes: + *

    + *
  • creation of the target object
  • + *
  • calling the setters for attributes
  • + *
  • adding the target to the project
  • + *
  • adding a reference to the target (if id attribute is given)
  • + *
+ * + * @param string the tag that comes in + * @param array attributes the tag carries + * @throws ExpatParseException if attributes are incomplete or invalid + */ + function init($tag, $attrs) { + $name = null; + $depends = ""; + $ifCond = null; + $unlessCond = null; + $id = null; + $description = null; + $isHidden = false; + + foreach($attrs as $key => $value) { + if ($key==="name") { + $name = (string) $value; + } else if ($key==="depends") { + $depends = (string) $value; + } else if ($key==="if") { + $ifCond = (string) $value; + } else if ($key==="unless") { + $unlessCond = (string) $value; + } else if ($key==="id") { + $id = (string) $value; + } else if ($key==="hidden") { + $isHidden = ($value == 'true' || $value == '1') ? true : false; + } else if ($key==="description") { + $description = (string)$value; + } else { + throw new ExpatParseException("Unexpected attribute '$key'", $this->parser->getLocation()); + } + } + + if ($name === null) { + throw new ExpatParseException("target element appears without a name attribute", $this->parser->getLocation()); + } + + // shorthand + $project = $this->configurator->project; + + // check to see if this target is a dup within the same file + if (isset($this->configurator->getCurrentTargets[$name])) { + throw new BuildException("Duplicate target: $targetName", + $this->parser->getLocation()); + } + + $this->target = new Target(); + $this->target->setName($name); + $this->target->setHidden($isHidden); + $this->target->setIf($ifCond); + $this->target->setUnless($unlessCond); + $this->target->setDescription($description); + // take care of dependencies + if (strlen($depends) > 0) { + $this->target->setDepends($depends); + } + + $usedTarget = false; + // check to see if target with same name is already defined + $projectTargets = $project->getTargets(); + if (isset($projectTargets[$name])) { + $project->log("Already defined in main or a previous import, " . + "ignore {$name}", Project::MSG_VERBOSE); + } else { + $project->addTarget($name, $this->target); + if ($id !== null && $id !== "") { + $project->addReference($id, $this->target); + } + $usedTarget = true; + } + + if ($this->configurator->isIgnoringProjectTag() && + $this->configurator->getCurrentProjectName() != null && + strlen($this->configurator->getCurrentProjectName()) != 0) { + // In an impored file (and not completely + // ignoring the project tag) + $newName = $this->configurator->getCurrentProjectName() . "." . $name; + if ($usedTarget) { + // clone needs to make target->children a shared reference + $newTarget = clone $this->target; + } else { + $newTarget = $this->target; + } + $newTarget->setName($newName); + $ct = $this->configurator->getCurrentTargets(); + $ct[$newName] = $newTarget; + $project->addTarget($newName, $newTarget); + } + } + + /** + * Checks for nested tags within the current one. Creates and calls + * handlers respectively. + * + * @param string the tag that comes in + * @param array attributes the tag carries + */ + function startElement($name, $attrs) { + // shorthands + $project = $this->configurator->project; + $types = $project->getDataTypeDefinitions(); + + if (isset($types[$name])) { + $th = new DataTypeHandler($this->parser, $this, $this->configurator, $this->target); + $th->init($name, $attrs); + } else { + $tmp = new TaskHandler($this->parser, $this, $this->configurator, $this->target, null, $this->target); + $tmp->init($name, $attrs); + } + } + + /** + * Checks if this target has dependencies and/or nested tasks. + * If the target has neither, show a warning. + */ + protected function finished() + { + if (!count($this->target->getDependencies()) && !count($this->target->getTasks())) { + $this->configurator->project->log("Warning: target '" . $this->target->getName() . + "' has no tasks or dependencies", Project::MSG_WARN); + } + } +} diff --git a/buildscripts/phing/classes/phing/parser/TaskHandler.php b/buildscripts/phing/classes/phing/parser/TaskHandler.php new file mode 100755 index 00000000..53e07a56 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/TaskHandler.php @@ -0,0 +1,234 @@ +. + */ + +include_once 'phing/UnknownElement.php'; + +/** + * The task handler class. + * + * This class handles the occurance of a tag and it's possible + * nested tags (datatypes and tasks) that may be unknown off bat and are + * initialized on the fly. + * + * @author Andreas Aderhold + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id: 3b31bb3e2c1ae122411833c67617e7cebf6967b8 $ + * @package phing.parser + */ +class TaskHandler extends AbstractHandler { + + /** + * Reference to the target object that contains the currently parsed + * task + * @var object the target instance + */ + private $target; + + /** + * Reference to the target object that represents the currently parsed + * target. This must not necessarily be a target, hence extra variable. + * @var object the target instance + */ + private $container; + + /** + * Reference to the task object that represents the currently parsed + * target. + * @var Task + */ + private $task; + + /** + * Wrapper for the parent element, if any. The wrapper for this + * element will be added to this wrapper as a child. + * @var RuntimeConfigurable + */ + private $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 $wrapper; + + /** + * The phing project configurator object + * @var ProjectConfigurator + */ + private $configurator; + + /** + * Constructs a new TaskHandler and sets up everything. + * + * @param AbstractSAXParser The ExpatParser object + * @param object $parentHandler The parent handler that invoked this handler + * @param ProjectConfigurator $configurator + * @param TaskContainer $container The container object this task is contained in (null for top-level tasks). + * @param RuntimeConfigurable $parentWrapper Wrapper for the parent element, if any. + * @param Target $target The target object this task is contained in (null for top-level tasks). + */ + function __construct(AbstractSAXParser $parser, $parentHandler, ProjectConfigurator $configurator, $container = null, $parentWrapper = null, $target = null) { + + parent::__construct($parser, $parentHandler); + + if (($container !== null) && !($container instanceof TaskContainer)) { + throw new Exception("Argument expected to be a TaskContainer, got something else"); + } + if (($parentWrapper !== null) && !($parentWrapper instanceof RuntimeConfigurable)) { + throw new Exception("Argument expected to be a RuntimeConfigurable, got something else."); + } + if (($target !== null) && !($target instanceof Target)) { + throw new Exception("Argument expected to be a Target, got something else"); + } + + $this->configurator = $configurator; + $this->container = $container; + $this->parentWrapper = $parentWrapper; + $this->target = $target; + } + + /** + * Executes initialization actions required to setup the data structures + * related to the tag. + *

+ * This includes: + *

    + *
  • creation of the task object
  • + *
  • calling the setters for attributes
  • + *
  • adding the task to the container object
  • + *
  • adding a reference to the task (if id attribute is given)
  • + *
  • executing the task if the container is the <project> + * element
  • + *
+ * + * @param string $tag The tag that comes in + * @param array $attrs Attributes the tag carries + * @throws ExpatParseException if attributes are incomplete or invalid + */ + function init($tag, $attrs) { + // shorthands + try { + $configurator = $this->configurator; + $project = $this->configurator->project; + + $this->task = $project->createTask($tag); + } catch (BuildException $be) { + // swallow here, will be thrown again in + // UnknownElement->maybeConfigure if the problem persists. + print("Swallowing exception: ".$be->getMessage() . "\n"); + } + + // the task is not known of bat, try to load it on thy fly + if ($this->task === null) { + $this->task = new UnknownElement($tag); + $this->task->setProject($project); + $this->task->setTaskType($tag); + $this->task->setTaskName($tag); + } + + // add file position information to the task (from parser) + // should be used in task exceptions to provide details + $this->task->setLocation($this->parser->getLocation()); + $configurator->configureId($this->task, $attrs); + + if ($this->container) { + $this->container->addTask($this->task); + } + + // Top level tasks don't have associated targets + // FIXME: if we do like Ant 1.6 and create an implicitTarget in the projectconfigurator object + // then we don't need to check for null here ... but there's a lot of stuff that will break if we + // do that at this point. + if ($this->target !== null) { + $this->task->setOwningTarget($this->target); + $this->task->init(); + $this->wrapper = $this->task->getRuntimeConfigurableWrapper(); + $this->wrapper->setAttributes($attrs); + /* + Commenting this out as per thread on Premature configurate of ReuntimeConfigurables + with Matthias Pigulla: http://phing.tigris.org/servlets/ReadMsg?list=dev&msgNo=251 + + if ($this->parentWrapper !== null) { // this may not make sense only within this if-block, but it + // seems to address current use cases adequately + $this->parentWrapper->addChild($this->wrapper); + } + */ + } else { + $this->task->init(); + $configurator->configure($this->task, $attrs, $project); + } + } + + /** + * Executes the task at once if it's directly beneath the tag. + */ + protected function finished() { + if ($this->task !== null && $this->target === null && $this->container === null) { + try { + $this->task->perform(); + } catch (Exception $e) { + $this->task->log($e->getMessage(), Project::MSG_ERR); + throw $e; + } + } + } + + /** + * Handles character data. + * + * @param string $data The CDATA that comes in + */ + function characters($data) { + if ($this->wrapper === null) { + $configurator = $this->configurator; + $project = $this->configurator->project; + try { // try + $configurator->addText($project, $this->task, $data); + } catch (BuildException $exc) { + throw new ExpatParseException($exc->getMessage(), $this->parser->getLocation()); + } + } else { + $this->wrapper->addText($data); + } + } + + /** + * Checks for nested tags within the current one. Creates and calls + * handlers respectively. + * + * @param string $name The tag that comes in + * @param array $attrs Attributes the tag carries + */ + function startElement($name, $attrs) { + $project = $this->configurator->project; + if ($this->task instanceof TaskContainer) { + //print("TaskHandler::startElement() (TaskContainer) name = $name, attrs = " . implode(",",$attrs) . "\n"); + $th = new TaskHandler($this->parser, $this, $this->configurator, $this->task, $this->wrapper, $this->target); + $th->init($name, $attrs); + } else { + //print("TaskHandler::startElement() name = $name, attrs = " . implode(",",$attrs) . "\n"); + $tmp = new NestedElementHandler($this->parser, $this, $this->configurator, $this->task, $this->wrapper, $this->target); + $tmp->init($name, $attrs); + } + } +} diff --git a/buildscripts/phing/classes/phing/system/io/BufferedReader.php b/buildscripts/phing/classes/phing/system/io/BufferedReader.php new file mode 100755 index 00000000..a392f5be --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/BufferedReader.php @@ -0,0 +1,168 @@ +. +*/ + +include_once 'phing/system/io/Reader.php'; + +/** + * Convenience class for reading files. + * + * @author Yannick Lecaillez + * @version $Id$ + * @access public + * @see FilterReader + * @package phing.system.io + */ +class BufferedReader extends Reader { + + private $bufferSize = 0; + private $buffer = null; + private $bufferPos = 0; + + /** + * The Reader we are buffering for. + */ + private $in; + + /** + * + * @param object $reader The reader (e.g. FileReader). + * @param integer $buffsize The size of the buffer we should use for reading files. + * A large buffer ensures that most files (all scripts?) are parsed in 1 buffer. + */ + function __construct(Reader $reader, $buffsize = 65536) { + $this->in = $reader; + $this->bufferSize = $buffsize; + } + + /** + * Reads and returns a chunk of data. + * @param int $len Number of bytes to read. Default is to read configured buffer size number of bytes. + * @return mixed buffer or -1 if EOF. + */ + function read($len = null) { + + // if $len is specified, we'll use that; otherwise, use the configured buffer size. + if ($len === null) $len = $this->bufferSize; + + if ( ($data = $this->in->read($len)) !== -1 ) { + + // not all files end with a newline character, so we also need to check EOF + if (!$this->in->eof()) { + + $notValidPart = strrchr($data, "\n"); + $notValidPartSize = strlen($notValidPart); + + if ( $notValidPartSize > 1 ) { + // Block doesn't finish on a EOL + // Find the last EOL and forget all following stuff + $dataSize = strlen($data); + $validSize = $dataSize - $notValidPartSize + 1; + + $data = substr($data, 0, $validSize); + + // Rewind to the begining of the forgotten stuff. + $this->in->skip(-$notValidPartSize+1); + } + + } // if !EOF + } + return $data; + } + + function skip($n) { + return $this->in->skip($n); + } + + function reset() { + return $this->in->reset(); + } + + function close() { + return $this->in->close(); + } + + function open() { + return $this->in->open(); + } + + /** + * Read a line from input stream. + */ + function readLine() { + $line = null; + while ( ($ch = $this->readChar()) !== -1 ) { + if ( $ch === "\n" ) { + break; + } + $line .= $ch; + } + + // Warning : Not considering an empty line as an EOF + if ( $line === null && $ch !== -1 ) + return ""; + + return $line; + } + + /** + * Reads a single char from the reader. + * @return string single char or -1 if EOF. + */ + function readChar() { + + if ( $this->buffer === null ) { + // Buffer is empty, fill it ... + $read = $this->in->read($this->bufferSize); + if ($read === -1) { + $ch = -1; + } else { + $this->buffer = $read; + return $this->readChar(); // recurse + } + } else { + // Get next buffered char ... + // handle case where buffer is read-in, but is empty. The next readChar() will return -1 EOF, + // so we just return empty string (char) at this point. (Probably could also return -1 ...?) + $ch = ($this->buffer !== "") ? $this->buffer{$this->bufferPos} : ''; + $this->bufferPos++; + if ( $this->bufferPos >= strlen($this->buffer) ) { + $this->buffer = null; + $this->bufferPos = 0; + } + } + + return $ch; + } + + /** + * Returns whether eof has been reached in stream. + * This is important, because filters may want to know if the end of the file (and not just buffer) + * has been reached. + * @return boolean + */ + function eof() { + return $this->in->eof(); + } + + function getResource() { + return $this->in->getResource(); + } +} diff --git a/buildscripts/phing/classes/phing/system/io/BufferedWriter.php b/buildscripts/phing/classes/phing/system/io/BufferedWriter.php new file mode 100755 index 00000000..88520ce9 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/BufferedWriter.php @@ -0,0 +1,71 @@ +. + */ + +include_once 'phing/system/io/Writer.php'; + +/** + * Convenience class for writing files. + * + * @author Hans Lellelid + * @version $Id$ + * @package phing.system.io + */ +class BufferedWriter extends Writer { + + /** + * The size of the buffer in kb. + */ + private $bufferSize = 0; + + /** + * @var Writer The Writer we are buffering output to. + */ + private $out; + + public function __construct(Writer $writer, $buffsize = 8192) { + $this->out = $writer; + $this->bufferSize = $buffsize; + } + + public function write($buf, $off = null, $len = null) { + return $this->out->write($buf, $off, $len); + } + + public function newLine() { + $this->write(PHP_EOL); + } + + public function getResource() { + return $this->out->getResource(); + } + + public function flush() { + $this->out->flush(); + } + + /** + * Close attached stream. + */ + public function close() { + return $this->out->close(); + } + +} diff --git a/buildscripts/phing/classes/phing/system/io/ConsoleReader.php b/buildscripts/phing/classes/phing/system/io/ConsoleReader.php new file mode 100755 index 00000000..048f1866 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/ConsoleReader.php @@ -0,0 +1,84 @@ +. + */ + +include_once 'phing/system/io/Reader.php'; + +/** + * Convenience class for reading console input. + * + * @author Hans Lellelid + * @author Matthew Hershberger + * @version $Id$ + * @package phing.system.io + */ +class ConsoleReader extends Reader { + + function readLine() { + + $out = fgets(STDIN); // note: default maxlen is 1kb + $out = rtrim($out); + + return $out; + } + + /** + * + * @param int $len Num chars to read. + * @return string chars read or -1 if eof. + */ + function read($len = null) { + + $out = fread(STDIN, $len); + + + return $out; + // FIXME + // read by chars doesn't work (yet?) with PHP stdin. Maybe + // this is just a language feature, maybe there's a way to get + // ability to read chars w/o ? + + } + + function close() { + // STDIN is always open + } + + function open() { + // STDIN is always open + } + + /** + * Whether eof has been reached with stream. + * @return boolean + */ + function eof() { + return feof(STDIN); + } + + /** + * Returns path to file we are reading. + * @return string + */ + function getResource() { + return "console"; + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/FileInputStream.php b/buildscripts/phing/classes/phing/system/io/FileInputStream.php new file mode 100644 index 00000000..64778860 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/FileInputStream.php @@ -0,0 +1,79 @@ +. + */ + +require_once 'phing/system/io/InputStream.php'; +require_once 'phing/system/io/PhingFile.php'; + +/** + * Input stream subclass for file streams. + * + * @package phing.system.io + */ +class FileInputStream extends InputStream { + + /** + * The associated file. + * @var PhingFile + */ + protected $file; + + /** + * Construct a new FileInputStream. + * + * @param PhingFile|string $file Path to the file + * @param boolean $append Whether to append (ignored) + * @throws Exception - if invalid argument specified. + * @throws IOException - if unable to open file. + */ + public function __construct($file, $append = false) { + if ($file instanceof PhingFile) { + $this->file = $file; + } elseif (is_string($file)) { + $this->file = new PhingFile($file); + } else { + throw new Exception("Invalid argument type for \$file."); + } + + $stream = @fopen($this->file->getAbsolutePath(), "rb"); + if ($stream === false) { + throw new IOException("Unable to open " . $this->file->__toString() . " for reading: " . $php_errormsg); + } + + parent::__construct($stream); + } + + /** + * Returns a string representation of the attached file. + * @return string + */ + public function __toString() { + return $this->file->getPath(); + } + + /** + * Mark is supported by FileInputStream. + * @return boolean TRUE + */ + public function markSupported() { + return true; + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/FileOutputStream.php b/buildscripts/phing/classes/phing/system/io/FileOutputStream.php new file mode 100644 index 00000000..35457d17 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/FileOutputStream.php @@ -0,0 +1,71 @@ +. + */ + +require_once 'phing/system/io/OutputStream.php'; +require_once 'phing/system/io/PhingFile.php'; + +/** + * Output stream subclass for file streams. + * + * @package phing.system.io + */ +class FileOutputStream extends OutputStream { + + /** + * @var PhingFile The associated file. + */ + protected $file; + + /** + * Construct a new FileOutputStream. + * @param mixed $file + * @param boolean $append Whether to append bytes to end of file rather than beginning. + * @throws Exception - if invalid argument specified. + * @throws IOException - if unable to open file. + */ + public function __construct($file, $append = false) { + if ($file instanceof PhingFile) { + $this->file = $file; + } elseif (is_string($file)) { + $this->file = new PhingFile($file); + } else { + throw new Exception("Invalid argument type for \$file."); + } + if ($append) { + $stream = @fopen($this->file->getAbsolutePath(), "ab"); + } else { + $stream = @fopen($this->file->getAbsolutePath(), "wb"); + } + if ($stream === false) { + throw new IOException("Unable to open " . $this->file->__toString() . " for writing: " . $php_errormsg); + } + parent::__construct($stream); + } + + /** + * Returns a string representation of the attached file. + * @return string + */ + public function __toString() { + return $this->file->getPath(); + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/FileReader.php b/buildscripts/phing/classes/phing/system/io/FileReader.php new file mode 100644 index 00000000..43ebda69 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/FileReader.php @@ -0,0 +1,41 @@ +. + */ + +require_once 'phing/system/io/InputStreamReader.php'; +require_once 'phing/system/io/FileInputStream.php'; + +/** + * Convenience class for reading files. + * @package phing.system.io + */ +class FileReader extends InputStreamReader { + + /** + * Construct a new FileReader. + * @param mixed $file PhingFile or string pathname. + */ + public function __construct($file) { + $in = new FileInputStream($file); + parent::__construct($in); + } + +} + diff --git a/buildscripts/phing/classes/phing/system/io/FileSystem.php b/buildscripts/phing/classes/phing/system/io/FileSystem.php new file mode 100755 index 00000000..284be830 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/FileSystem.php @@ -0,0 +1,840 @@ +. + */ + +/** + * This is an abstract class for platform specific filesystem implementations + * you have to implement each method in the platform specific filesystem implementation + * classes Your local filesytem implementation must extend this class. + * You should also use this class as a template to write your local implementation + * Some native PHP filesystem specific methods are abstracted here as well. Anyway + * you _must_ always use this methods via a PhingFile object (that by nature uses the + * *FileSystem drivers to access the real filesystem via this class using natives. + * + * FIXME: + * - Error handling reduced to min fallthrough runtime exceptions + * more precise errorhandling is done by the PhingFile class + * + * @author Charlie Killian + * @author Hans Lellelid + * @version $Id$ + * @package phing.system.io + */ +abstract class FileSystem { + + /** + * @var int + */ + const BA_EXISTS = 0x01; + + /** + * @var int + */ + const BA_REGULAR = 0x02; + + /** + * @var int + */ + const BA_DIRECTORY = 0x04; + + /** + * @var int + */ + const BA_HIDDEN = 0x08; + + /** + * Instance for getFileSystem() method. + * @var FileSystem + */ + private static $fs; + + /** + * Static method to return the FileSystem singelton representing + * this platform's local filesystem driver. + * + * @return FileSystem + * @throws IOException + */ + public static function getFileSystem() { + if (self::$fs === null) { + switch(Phing::getProperty('host.fstype')) { + case 'UNIX': + include_once 'phing/system/io/UnixFileSystem.php'; + self::$fs = new UnixFileSystem(); + break; + case 'WIN32': + include_once 'phing/system/io/Win32FileSystem.php'; + self::$fs = new Win32FileSystem(); + break; + case 'WINNT': + include_once 'phing/system/io/WinNTFileSystem.php'; + self::$fs = new WinNTFileSystem(); + break; + default: + throw new IOException("Host uses unsupported filesystem, unable to proceed"); + } + } + return self::$fs; + } + + /* -- Normalization and construction -- */ + + /** + * Return the local filesystem's name-separator character. + */ + abstract function getSeparator(); + + /** + * Return the local filesystem's path-separator character. + */ + abstract function getPathSeparator(); + + /** + * Convert the given pathname string to normal form. If the string is + * already in normal form then it is simply returned. + * + * @param string $strPath + */ + abstract function normalize($strPath); + + /** + * Compute the length of this pathname string's prefix. The pathname + * string must be in normal form. + * + * @param string $pathname + */ + abstract function prefixLength($pathname); + + /** + * Resolve the child pathname string against the parent. + * Both strings must be in normal form, and the result + * will be a string in normal form. + * + * @param string $parent + * @param string $child + */ + abstract function resolve($parent, $child); + + /** + * Resolve the given abstract pathname into absolute form. Invoked by the + * getAbsolutePath and getCanonicalPath methods in the PhingFile class. + * + * @param PhingFile $f + */ + abstract function resolveFile(PhingFile $f); + + /** + * Return the parent pathname string to be used when the parent-directory + * argument in one of the two-argument PhingFile constructors is the empty + * pathname. + */ + abstract function getDefaultParent(); + + /** + * Post-process the given URI path string if necessary. This is used on + * win32, e.g., to transform "/c:/foo" into "c:/foo". The path string + * still has slash separators; code in the PhingFile class will translate them + * after this method returns. + * + * @param string $path + */ + abstract function fromURIPath($path); + + /* -- Path operations -- */ + + /** + * Tell whether or not the given abstract pathname is absolute. + * + * @param PhingFile $f + */ + abstract function isAbsolute(PhingFile $f); + + /** + * canonicalize filename by checking on disk + * @param string $strPath + * @return mixed Canonical path or false if the file doesn't exist. + */ + function canonicalize($strPath) { + return @realpath($strPath); + } + + /* -- Attribute accessors -- */ + + /** + * Return the simple boolean attributes for the file or directory denoted + * by the given abstract pathname, or zero if it does not exist or some + * other I/O error occurs. + * + * @param PhingFile $f + */ + function getBooleanAttributes($f) { + throw new IOException("getBooleanAttributes() not implemented by fs driver"); + } + + /** + * Check whether the file or directory denoted by the given abstract + * pathname may be accessed by this process. If the second argument is + * false, then a check for read access is made; if the second + * argument is true, then a check for write (not read-write) + * access is made. Return false if access is denied or an I/O error + * occurs. + * + * @param PhingFile $f + * @param boolean $write + */ + function checkAccess(PhingFile $f, $write = false) { + // we clear stat cache, its expensive to look up from scratch, + // but we need to be sure + @clearstatcache(); + + + // Shouldn't this be $f->GetAbsolutePath() ? + // And why doesn't GetAbsolutePath() work? + + $strPath = (string) $f->getPath(); + + // FIXME + // if file object does denote a file that yet not existst + // path rights are checked + if (!@file_exists($strPath) && !is_dir($strPath)) { + $strPath = $f->getParent(); + if ($strPath === null || !is_dir($strPath)) { + $strPath = Phing::getProperty("user.dir"); + } + //$strPath = dirname($strPath); + } + + if (!$write) { + return (boolean) @is_readable($strPath); + } else { + return (boolean) @is_writable($strPath); + } + } + + /** + * Whether file can be deleted. + * @param PhingFile $f + * @return boolean + */ + function canDelete(PhingFile $f) + { + clearstatcache(); + $dir = dirname($f->getAbsolutePath()); + return (bool) @is_writable($dir); + } + + /** + * Return the time at which the file or directory denoted by the given + * abstract pathname was last modified, or zero if it does not exist or + * some other I/O error occurs. + * + * @param PhingFile $f + * @return int + * @throws IOException + */ + function getLastModifiedTime(PhingFile $f) { + + if (!$f->exists()) { + return 0; + } + + @clearstatcache(); + $strPath = (string) $f->getPath(); + + if (@is_link($strPath)) { + $stats = @lstat($strPath); + + if (!isset($stats['mtime'])) { + $mtime = false; + } else { + $mtime = $stats['mtime']; + } + } else { + $mtime = @filemtime($strPath); + } + + if (false === $mtime) { + $msg = "FileSystem::getLastModifiedTime() FAILED. Can not get modified time of $strPath. $php_errormsg"; + throw new IOException($msg); + } + + return (int) $mtime; + } + + /** + * Return the length in bytes of the file denoted by the given abstract + * pathname, or zero if it does not exist, is a directory, or some other + * I/O error occurs. + * + * @param PhingFile $f + * @throws IOException + * @return int + */ + function getLength(PhingFile $f) { + $strPath = (string) $f->getAbsolutePath(); + $fs = filesize((string) $strPath); + if ($fs !== false) { + return $fs; + } else { + $msg = "FileSystem::Read() FAILED. Cannot get filesize of $strPath. $php_errormsg"; + throw new IOException($msg); + } + } + + /* -- File operations -- */ + + /** + * Create a new empty file with the given pathname. Return + * true if the file was created and false if a + * file or directory with the given pathname already exists. Throw an + * IOException if an I/O error occurs. + * + * @param string $strPathname Path of the file to be created. + * @throws IOException + * @return boolean + */ + function createNewFile($strPathname) { + if (@file_exists($strPathname)) + return false; + + // Create new file + $fp = @fopen($strPathname, "w"); + if ($fp === false) { + throw new IOException("The file \"$strPathname\" could not be created"); + } + @fclose($fp); + return true; + } + + /** + * Delete the file or directory denoted by the given abstract pathname, + * returning true if and only if the operation succeeds. + * + * @param PhingFile $f + * @param boolean $recursive + * @return void + */ + function delete(PhingFile $f, $recursive = false) { + if ($f->isDirectory()) { + return $this->rmdir($f->getPath(), $recursive); + } else { + return $this->unlink($f->getPath()); + } + } + + /** + * Arrange for the file or directory denoted by the given abstract + * pathname to be deleted when Phing::shutdown is called, returning + * true if and only if the operation succeeds. + * + * @param PhingFile $f + * @throws IOException + */ + function deleteOnExit($f) { + throw new IOException("deleteOnExit() not implemented by local fs driver"); + } + + /** + * List the elements of the directory denoted by the given abstract + * pathname. Return an array of strings naming the elements of the + * directory if successful; otherwise, return null. + * + * @param PhingFile $f + */ + function listDir(PhingFile $f) { + $strPath = (string) $f->getAbsolutePath(); + $d = @dir($strPath); + if (!$d) { + return null; + } + $list = array(); + while($entry = $d->read()) { + if ($entry != "." && $entry != "..") { + array_push($list, $entry); + } + } + $d->close(); + unset($d); + return $list; + } + + /** + * Create a new directory denoted by the given abstract pathname, + * returning true if and only if the operation succeeds. + * + * NOTE: umask() is reset to 0 while executing mkdir(), and restored afterwards + * + * @param PhingFile $f + * @param int $mode + * @return boolean + */ + function createDirectory(&$f, $mode = 0755) { + $old_umask = umask(0); + $return = @mkdir($f->getAbsolutePath(), $mode); + umask($old_umask); + return $return; + } + + /** + * Rename the file or directory denoted by the first abstract pathname to + * the second abstract pathname, returning true if and only if + * the operation succeeds. + * + * @param PhingFile $f1 abstract source file + * @param PhingFile $f2 abstract destination file + * @return void + * @throws IOException if rename cannot be performed + */ + function rename(PhingFile $f1, PhingFile $f2) { + // get the canonical paths of the file to rename + $src = $f1->getAbsolutePath(); + $dest = $f2->getAbsolutePath(); + if (false === @rename($src, $dest)) { + $msg = "Rename FAILED. Cannot rename $src to $dest. $php_errormsg"; + throw new IOException($msg); + } + } + + /** + * Set the last-modified time of the file or directory denoted by the + * given abstract pathname returning true if and only if the + * operation succeeds. + * + * @param PhingFile $f + * @param int $time + * @return void + * @throws IOException + */ + function setLastModifiedTime(PhingFile $f, $time) { + $path = $f->getPath(); + $success = @touch($path, $time); + if (!$success) { + throw new IOException("Could not touch '" . $path . "' due to: $php_errormsg"); + } + } + + /** + * Mark the file or directory denoted by the given abstract pathname as + * read-only, returning true if and only if the operation + * succeeds. + * + * @param PhingFile $f + * @throws IOException + */ + function setReadOnly($f) { + throw new IOException("setReadonly() not implemented by local fs driver"); + } + + /* -- Filesystem interface -- */ + + /** + * List the available filesystem roots, return array of PhingFile objects + * @throws IOException + */ + function listRoots() { + throw new IOException("listRoots() not implemented by local fs driver"); + } + + /* -- Basic infrastructure -- */ + + /** + * Compare two abstract pathnames lexicographically. + * + * @param PhingFile $f1 + * @param PhingFile $f2 + */ + function compare(PhingFile $f1, PhingFile $f2) { + throw new IOException("compare() not implemented by local fs driver"); + } + + /** + * Copy a file. + * + * @param PhingFile $src Source path and name file to copy. + * @param PhingFile $dest Destination path and name of new file. + * + * @return void + * @throws IOException if file cannot be copied. + */ + function copy(PhingFile $src, PhingFile $dest) { + global $php_errormsg; + + // Recursively copy a directory + if($src->isDirectory()) { + return $this->copyr($src->getAbsolutePath(), $dest->getAbsolutePath()); + } + + $srcPath = $src->getAbsolutePath(); + $destPath = $dest->getAbsolutePath(); + + if (false === @copy($srcPath, $destPath)) { // Copy FAILED. Log and return err. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::copy() FAILED. Cannot copy $srcPath to $destPath. $php_errormsg"; + throw new IOException($msg); + } + + try { + $dest->setMode($src->getMode()); + } catch(Exception $exc) { + // [MA] does chmod returns an error on systems that do not support it ? + // eat it up for now. + } + } + + /** + * Copy a file, or recursively copy a folder and its contents + * + * @author Aidan Lister + * @version 1.0.1 + * @link http://aidanlister.com/repos/v/function.copyr.php + * @param string $source Source path + * @param string $dest Destination path + * @return bool Returns TRUE on success, FALSE on failure + */ + function copyr($source, $dest) + { + // Check for symlinks + if (is_link($source)) { + return symlink(readlink($source), $dest); + } + + // Simple copy for a file + if (is_file($source)) { + return copy($source, $dest); + } + + // Make destination directory + if (!is_dir($dest)) { + mkdir($dest); + } + + // Loop through the folder + $dir = dir($source); + while (false !== $entry = $dir->read()) { + // Skip pointers + if ($entry == '.' || $entry == '..') { + continue; + } + + // Deep copy directories + $this->copyr("$source/$entry", "$dest/$entry"); + } + + // Clean up + $dir->close(); + return true; + } + + /** + * Change the ownership on a file or directory. + * + * @param string $pathname Path and name of file or directory. + * @param string $user The user name or number of the file or directory. See http://us.php.net/chown + * + * @return void + * @throws Exception if operation failed. + */ + function chown($pathname, $user) { + if (false === @chown($pathname, $user)) {// FAILED. + $msg = "FileSystem::chown() FAILED. Cannot chown $pathname. User $user." . (isset($php_errormsg) ? ' ' . $php_errormsg : ""); + throw new IOException($msg); + } + } + + /** + * Change the group on a file or directory. + * + * @param string $pathname Path and name of file or directory. + * @param string $group The group of the file or directory. See http://us.php.net/chgrp + * + * @return void + * @throws IOException if operation failed. + */ + function chgrp($pathname, $group) { + if (false === @chgrp($pathname, $group)) {// FAILED. + $msg = "FileSystem::chgrp() FAILED. Cannot chown $pathname. Group $group." . (isset($php_errormsg) ? ' ' . $php_errormsg : ""); + throw new IOException($msg); + } + } + + /** + * Change the permissions on a file or directory. + * + * @param string $pathname Path and name of file or directory. + * @param int $mode The mode (permissions) of the file or + * directory. If using octal add leading 0. eg. 0777. + * Mode is affected by the umask system setting. + * + * @return void + * @throws IOException if operation failed. + */ + function chmod($pathname, $mode) { + $str_mode = decoct($mode); // Show octal in messages. + if (false === @chmod($pathname, $mode)) {// FAILED. + $msg = "FileSystem::chmod() FAILED. Cannot chmod $pathname. Mode $str_mode." . (isset($php_errormsg) ? ' ' . $php_errormsg : ""); + throw new IOException($msg); + } + } + + /** + * Locks a file and throws an Exception if this is not possible. + * + * @param PhingFile $f + * @return void + * @throws Exception + */ + function lock(PhingFile $f) { + $filename = $f->getPath(); + $fp = @fopen($filename, "w"); + $result = @flock($fp, LOCK_EX); + @fclose($fp); + if (!$result) { + throw new IOException("Could not lock file '$filename'"); + } + } + + /** + * Unlocks a file and throws an IO Error if this is not possible. + * + * @param PhingFile $f + * @throws IOException + * @return void + */ + function unlock(PhingFile $f) { + $filename = $f->getPath(); + $fp = @fopen($filename, "w"); + $result = @flock($fp, LOCK_UN); + fclose($fp); + if (!$result) { + throw new Exception("Could not unlock file '$filename'"); + } + } + + /** + * Delete a file. + * + * @param string $file Path and/or name of file to delete. + * + * @return void + * @throws IOException - if an error is encountered. + */ + function unlink($file) { + global $php_errormsg; + if (false === @unlink($file)) { + $msg = "FileSystem::unlink() FAILED. Cannot unlink '$file'. $php_errormsg"; + throw new IOException($msg); + } + } + + /** + * Symbolically link a file to another name. + * + * Currently symlink is not implemented on Windows. Don't use if the application is to be portable. + * + * @param string $target Path and/or name of file to link. + * @param string $link Path and/or name of link to be created. + * @return void + */ + function symlink($target, $link) { + + // If Windows OS then symlink() will report it is not supported in + // the build. Use this error instead of checking for Windows as the OS. + + if (false === @symlink($target, $link)) { + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::Symlink() FAILED. Cannot symlink '$target' to '$link'. $php_errormsg"; + throw new IOException($msg); + } + + } + + /** + * Set the modification and access time on a file to the present time. + * + * @param string $file Path and/or name of file to touch. + * @param int $time + * @return void + */ + function touch($file, $time = null) { + global $php_errormsg; + + if (null === $time) { + $error = @touch($file); + } else { + $error = @touch($file, $time); + } + + if (false === $error) { // FAILED. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::touch() FAILED. Cannot touch '$file'. $php_errormsg"; + throw new Exception($msg); + } + } + + /** + * Delete an empty directory OR a directory and all of its contents. + * + * @param dir String. Path and/or name of directory to delete. + * @param children Boolean. False: don't delete directory contents. + * True: delete directory contents. + * + * @return void + */ + function rmdir($dir, $children = false) { + global $php_errormsg; + + // If children=FALSE only delete dir if empty. + if (false === $children) { + + if (false === @rmdir($dir)) { // FAILED. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::rmdir() FAILED. Cannot rmdir $dir. $php_errormsg"; + throw new Exception($msg); + } + + } else { // delete contents and dir. + + $handle = @opendir($dir); + + if (false === $handle) { // Error. + + $msg = "FileSystem::rmdir() FAILED. Cannot opendir() $dir. $php_errormsg"; + throw new Exception($msg); + + } else { // Read from handle. + + // Don't error on readdir(). + while (false !== ($entry = @readdir($handle))) { + + if ($entry != '.' && $entry != '..') { + + // Only add / if it isn't already the last char. + // This ONLY serves the purpose of making the Logger + // output look nice:) + + if (strpos(strrev($dir), DIRECTORY_SEPARATOR) === 0) {// there is a / + $next_entry = $dir . $entry; + } else { // no / + $next_entry = $dir . DIRECTORY_SEPARATOR . $entry; + } + + // NOTE: As of php 4.1.1 is_dir doesn't return FALSE it + // returns 0. So use == not ===. + + // Don't error on is_dir() + if (false == @is_dir($next_entry)) { // Is file. + + try { + self::unlink($next_entry); // Delete. + } catch (Exception $e) { + $msg = "FileSystem::Rmdir() FAILED. Cannot FileSystem::Unlink() $next_entry. ". $e->getMessage(); + throw new Exception($msg); + } + + } else { // Is directory. + + try { + self::rmdir($next_entry, true); // Delete + } catch (Exception $e) { + $msg = "FileSystem::rmdir() FAILED. Cannot FileSystem::rmdir() $next_entry. ". $e->getMessage(); + throw new Exception($msg); + } + + } // end is_dir else + } // end .. if + } // end while + } // end handle if + + // Don't error on closedir() + @closedir($handle); + + if (false === @rmdir($dir)) { // FAILED. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::rmdir() FAILED. Cannot rmdir $dir. $php_errormsg"; + throw new Exception($msg); + } + + } + + } + + /** + * Set the umask for file and directory creation. + * + * @param mode Int. Permissions ususally in ocatal. Use leading 0 for + * octal. Number between 0 and 0777. + * + * @return void + * @throws Exception if there is an error performing operation. + */ + function umask($mode) { + global $php_errormsg; + + // CONSIDERME: + // Throw a warning if mode is 0. PHP converts illegal octal numbers to + // 0 so 0 might not be what the user intended. + + $str_mode = decoct($mode); // Show octal in messages. + + if (false === @umask($mode)) { // FAILED. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::Umask() FAILED. Value $mode. $php_errormsg"; + throw new Exception($msg); + } + } + + /** + * Compare the modified time of two files. + * + * @param file1 String. Path and name of file1. + * @param file2 String. Path and name of file2. + * + * @return Int. 1 if file1 is newer. + * -1 if file2 is newer. + * 0 if files have the same time. + * Err object on failure. + * + * @throws Exception - if cannot get modified time of either file. + */ + function compareMTimes($file1, $file2) { + + $mtime1 = filemtime($file1); + $mtime2 = filemtime($file2); + + if ($mtime1 === false) { // FAILED. Log and return err. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::compareMTimes() FAILED. Cannot can not get modified time of $file1."; + throw new Exception($msg); + } elseif ($mtime2 === false) { // FAILED. Log and return err. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::compareMTimes() FAILED. Cannot can not get modified time of $file2."; + throw new Exception($msg); + } else { // Worked. Log and return compare. + // Compare mtimes. + if ($mtime1 == $mtime2) { + return 0; + } else { + return ($mtime1 < $mtime2) ? -1 : 1; + } // end compare + } + } + +} diff --git a/buildscripts/phing/classes/phing/system/io/FileWriter.php b/buildscripts/phing/classes/phing/system/io/FileWriter.php new file mode 100644 index 00000000..d58b0513 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/FileWriter.php @@ -0,0 +1,42 @@ +. + */ + +require_once 'phing/system/io/OutputStreamWriter.php'; +require_once 'phing/system/io/FileOutputStream.php'; + +/** + * Convenience class for performing file write operations. + * + * @package phing.system.io + */ +class FileWriter extends OutputStreamWriter { + + /** + * Construct a new FileWriter. + * @param mixed $file PhingFile or string pathname. + * @param boolean $append Append to existing file? + */ + function __construct($file, $append = false) { + $out = new FileOutputStream($file, $append); + parent::__construct($out); + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/FilterReader.php b/buildscripts/phing/classes/phing/system/io/FilterReader.php new file mode 100644 index 00000000..527ce17f --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/FilterReader.php @@ -0,0 +1,68 @@ +. + */ + +require_once 'phing/system/io/Reader.php'; + +/** + * Wrapper class for readers, which can be used to apply filters. + * @package phing.system.io + */ +class FilterReader extends Reader { + + /** + * @var Reader + */ + protected $in; + + function __construct(Reader $in = null) { + $this->in = $in; + } + + public function setReader(Reader $in) { + $this->in = $in; + } + + public function skip($n) { + return $this->in->skip($n); + } + + /** + * Read data from source. + * FIXME: Clean up this function signature, as it a) params aren't being used + * and b) it doesn't make much sense. + */ + public function read($len = null) { + return $this->in->read($len); + } + + public function reset() { + return $this->in->reset(); + } + + public function close() { + return $this->in->close(); + } + + function getResource() { + return $this->in->getResource(); + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/IOException.php b/buildscripts/phing/classes/phing/system/io/IOException.php new file mode 100644 index 00000000..8aa1465f --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/IOException.php @@ -0,0 +1,27 @@ +. + */ + +/** + * Extends Exception to take advantage of methods therein. + * + * @package phing.system.io + */ +class IOException extends Exception {} diff --git a/buildscripts/phing/classes/phing/system/io/InputStream.php b/buildscripts/phing/classes/phing/system/io/InputStream.php new file mode 100644 index 00000000..f25fbe61 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/InputStream.php @@ -0,0 +1,178 @@ +. + */ + +/** + * Wrapper class for PHP stream that supports read operations. + * + * @package phing.system.io + */ +class InputStream { + + /** + * @var resource The attached PHP stream. + */ + protected $stream; + + /** + * @var int Position of stream cursor. + */ + protected $currentPosition = 0; + + /** + * @var int Marked position of stream cursor. + */ + protected $mark = 0; + + /** + * Construct a new InputStream. + * @param resource $stream Configured PHP stream for writing. + */ + public function __construct($stream) { + if (!is_resource($stream)) { + throw new IOException("Passed argument is not a valid stream."); + } + $this->stream = $stream; + } + + /** + * Skip over $n bytes. + * @param int $n + */ + public function skip($n) { + $start = $this->currentPosition; + + $ret = @fseek($this->stream, $n, SEEK_CUR); + if ( $ret === -1 ) + return -1; + + $this->currentPosition = ftell($this->stream); + + if ( $start > $this->currentPosition ) + $skipped = $start - $this->currentPosition; + else + $skipped = $this->currentPosition - $start; + + return $skipped; + } + + /** + * Read data from stream until $len chars or EOF. + * @param int $len Num chars to read. If not specified this stream will read until EOF. + * @return string chars read or -1 if eof. + */ + public function read($len = null) { + + if ($this->eof()) { + return -1; + } + + if ($len === null) { // we want to keep reading until we get an eof + $out = ""; + while(!$this->eof()) { + $out .= fread($this->stream, 8192); + $this->currentPosition = ftell($this->stream); + } + } else { + $out = fread($this->stream, $len); // adding 1 seems to ensure that next call to read() will return EOF (-1) + $this->currentPosition = ftell($this->stream); + } + + return $out; + } + + /** + * Marks the current position in this input stream. + * @throws IOException - if the underlying stream doesn't support this method. + */ + public function mark() { + if (!$this->markSupported()) { + throw new IOException(get_class($this) . " does not support mark() and reset() methods."); + } + $this->mark = $this->currentPosition; + } + + /** + * Whether the input stream supports mark and reset methods. + * @return boolean + */ + public function markSupported() { + return false; + } + + /** + * Repositions this stream to the position at the time the mark method was last called on this input stream. + * @throws IOException - if the underlying stream doesn't support this method. + */ + function reset() { + if (!$this->markSupported()) { + throw new IOException(get_class($this) . " does not support mark() and reset() methods."); + } + // goes back to last mark, by default this would be 0 (i.e. rewind file). + fseek($this->stream, SEEK_SET, $this->mark); + $this->mark = 0; + } + + /** + * Closes stream. + * @throws IOException if stream cannot be closed (note that calling close() on an already-closed stream will not raise an exception) + */ + public function close() { + if ($this->stream === null) { + return; + } + if (false === @fclose($this->stream)) { + // FAILED. + $msg = "Cannot fclose " . $this->file->__toString() . " $php_errormsg"; + throw new IOException($msg); + } + $this->stream = null; + } + + /** + * Whether eof has been reached with stream. + * @return boolean + */ + public function eof() { + return feof($this->stream); + } + + /** + * Reads a entire until EOF and places contents in passed-in variable. Stream is closed after read. + * + * @param string &$rBuffer String variable where read contents will be put. + * @return TRUE on success. + * @author Charlie Killian, charlie@tizac.com + * @throws IOException - if there is an error reading from stream. + * @deprecated - Instead, use the read() method or a BufferedReader. + */ + public function readInto(&$rBuffer) { + $rBuffer = $this->read(); + $this->close(); + } + + /** + * Returns string representation of attached stream. + * @return string + */ + public function __toString() { + return (string) $this->stream; + } +} diff --git a/buildscripts/phing/classes/phing/system/io/InputStreamReader.php b/buildscripts/phing/classes/phing/system/io/InputStreamReader.php new file mode 100644 index 00000000..a21f9f05 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/InputStreamReader.php @@ -0,0 +1,127 @@ +. + */ + +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/system/io/Reader.php'; + +/** + * Writer class for OutputStream objects. + * + * Unlike the Java counterpart, this class does not (yet) handle + * character set transformations. This will be an important function + * of this class with move to supporting PHP6. + * + * @package phing.system.io + */ +class InputStreamReader extends Reader { + + /** + * @var InputStream + */ + protected $inStream; + + /** + * Construct a new InputStreamReader. + * @param InputStream $$inStream InputStream to read from + */ + public function __construct(InputStream $inStream) { + $this->inStream = $inStream; + } + + /** + * Close the stream. + */ + public function close() { + return $this->inStream->close(); + } + + /** + * Skip over $n bytes. + * @param int $n + */ + public function skip($n) { + return $this->inStream->skip($n); + } + + /** + * Read data from file. + * @param int $len Num chars to read. + * @return string chars read or -1 if eof. + */ + public function read($len = null) { + return $this->inStream->read($len); + } + + /** + * Marks the current position in this input stream. + * @throws IOException - if the underlying stream doesn't support this method. + */ + public function mark() { + $this->inStream->mark(); + } + + /** + * Whether the attached stream supports mark/reset. + * @return boolean + */ + public function markSupported() { + return $this->inStream->markSupported(); + } + + /** + * Repositions this stream to the position at the time the mark method was last called on this input stream. + * @throws IOException - if the underlying stream doesn't support this method. + */ + public function reset() { + $this->inStream->reset(); + } + + /** + * Whether eof has been reached with stream. + * @return boolean + */ + public function eof() { + return $this->inStream->eof(); + } + + /** + * Reads a entire file and stores the data in the variable + * passed by reference. + * + * @param string $file String. Path and/or name of file to read. + * @param object &$rBuffer Reference. Variable of where to put contents. + * + * @return TRUE on success. Err object on failure. + * @author Charlie Killian, charlie@tizac.com + * @deprecated Use read() or BufferedReader instead. + */ + public function readInto(&$rBuffer) { + return $this->inStream->readInto($rBuffer); + } + + /** + * Returns string representation of attached stream. + * @return string + */ + public function getResource() { + return $this->inStream->__toString(); + } +} diff --git a/buildscripts/phing/classes/phing/system/io/OutputStream.php b/buildscripts/phing/classes/phing/system/io/OutputStream.php new file mode 100644 index 00000000..09e15c0e --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/OutputStream.php @@ -0,0 +1,108 @@ +. + */ + +/** + * Wrapper class for PHP stream that supports write operations. + * + * @package phing.system.io + */ +class OutputStream { + + /** + * @var resource The configured PHP stream. + */ + protected $stream; + + /** + * Construct a new OutputStream. + * @param resource $stream Configured PHP stream for writing. + */ + public function __construct($stream) { + if (!is_resource($stream)) { + throw new IOException("Passed argument is not a valid stream."); + } + $this->stream = $stream; + } + + /** + * Closes attached stream, flushing output first. + * @throws IOException if cannot close stream (note that attempting to close an already closed stream will not raise an IOException) + * @return void + */ + public function close() { + if ($this->stream === null) { + return; + } + $this->flush(); + if (false === @fclose($this->stream)) { + $msg = "Cannot close " . $this->getResource() . ": $php_errormsg"; + throw new IOException($msg); + } + $this->stream = null; + } + + /** + * Flushes stream. + * + * @throws IOException if unable to flush data (e.g. stream is not open). + */ + public function flush() { + if (false === @fflush($this->stream)) { + throw new IOException("Could not flush stream: " . $php_errormsg); + } + } + + /** + * Writes data to stream. + * + * @param string $buf Binary/character data to write. + * @param int $off (Optional) offset. + * @param int $len (Optional) number of bytes/chars to write. + * @return void + * @throws IOException - if there is an error writing to stream + */ + public function write($buf, $off = null, $len = null) { + if ( $off === null && $len === null ) { + $to_write = $buf; + } elseif ($off !== null && $len === null) { + $to_write = substr($buf, $off); + } elseif ($off === null && $len !== null) { + $to_write = substr($buf, 0, $len); + } else { + $to_write = substr($buf, $off, $len); + } + + $result = @fwrite($this->stream, $to_write); + + if ( $result === false ) { + throw new IOException("Error writing to stream."); + } + } + + /** + * Returns a string representation of the attached PHP stream. + * @return string + */ + public function __toString() { + return (string) $this->stream; + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/OutputStreamWriter.php b/buildscripts/phing/classes/phing/system/io/OutputStreamWriter.php new file mode 100644 index 00000000..0b821e67 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/OutputStreamWriter.php @@ -0,0 +1,84 @@ +. + */ + +include_once 'phing/system/io/PhingFile.php'; +require_once 'phing/system/io/Writer.php'; + +/** + * Writer class for OutputStream objects. + * + * Unlike the Java counterpart, this class does not (yet) handle + * character set transformations. This will be an important function + * of this class with move to supporting PHP6. + * + * @package phing.system.io + */ +class OutputStreamWriter extends Writer { + + /** + * @var OutputStream + */ + protected $outStream; + + /** + * Construct a new OutputStreamWriter. + * @param OutputStream $outStream OutputStream to write to + */ + public function __construct(OutputStream $outStream) { + $this->outStream = $outStream; + } + + /** + * Close the stream. + */ + public function close() { + return $this->outStream->close(); + } + + /** + * Write char data to stream. + * + * @param unknown_type $buf + * @param unknown_type $off + * @param unknown_type $len + * @return unknown + */ + public function write($buf, $off = null, $len = null) { + return $this->outStream->write($buf, $off, $len); + } + + /** + * Flush output to the stream. + */ + public function flush() { + $this->outStream->flush(); + } + + /** + * Gets a string representation of attached stream resource. + * + * @return string String representation of output stream + */ + public function getResource() { + return $this->outStream->__toString(); + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/PhingFile.php b/buildscripts/phing/classes/phing/system/io/PhingFile.php new file mode 100755 index 00000000..871afedd --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/PhingFile.php @@ -0,0 +1,996 @@ +. + */ + +include_once 'phing/system/io/FileSystem.php'; +include_once 'phing/system/lang/NullPointerException.php'; + +/** + * An abstract representation of file and directory pathnames. + * + * @version $Id$ + * @package phing.system.io + */ +class PhingFile { + + /** separator string, static, obtained from FileSystem */ + public static $separator; + + /** path separator string, static, obtained from FileSystem (; or :)*/ + public static $pathSeparator; + + /** + * This abstract pathname's normalized pathname string. A normalized + * pathname string uses the default name-separator character and does not + * contain any duplicate or redundant separators. + */ + private $path = null; + + /** + * The length of this abstract pathname's prefix, or zero if it has no prefix. + * @var int + */ + private $prefixLength = 0; + + /** constructor */ + function __construct($arg1 = null, $arg2 = null) { + + if (self::$separator === null || self::$pathSeparator === null) { + $fs = FileSystem::getFileSystem(); + self::$separator = $fs->getSeparator(); + self::$pathSeparator = $fs->getPathSeparator(); + } + + /* simulate signature identified constructors */ + if ($arg1 instanceof PhingFile && is_string($arg2)) { + $this->_constructFileParentStringChild($arg1, $arg2); + } elseif (is_string($arg1) && ($arg2 === null)) { + $this->_constructPathname($arg1); + } elseif(is_string($arg1) && is_string($arg2)) { + $this->_constructStringParentStringChild($arg1, $arg2); + } else { + if ($arg1 === null) { + throw new NullPointerException("Argument1 to function must not be null"); + } + $this->path = (string) $arg1; + $this->prefixLength = (int) $arg2; + } + } + + /** + * Returns the length of this abstract pathname's prefix. + * + * @return int + */ + function getPrefixLength() { + return (int) $this->prefixLength; + } + + /* -- constructors not called by signature match, so we need some helpers --*/ + + /** + * + * Enter description here ... + * @param unknown_type $pathname + */ + protected function _constructPathname($pathname) { + // obtain ref to the filesystem layer + $fs = FileSystem::getFileSystem(); + + if ($pathname === null) { + throw new NullPointerException("Argument to function must not be null"); + } + + $this->path = (string) $fs->normalize($pathname); + $this->prefixLength = (int) $fs->prefixLength($this->path); + } + + /** + * + * Enter description here ... + * @param unknown_type $parent + * @param unknown_type $child + */ + protected function _constructStringParentStringChild($parent, $child = null) { + // obtain ref to the filesystem layer + $fs = FileSystem::getFileSystem(); + + if ($child === null) { + throw new NullPointerException("Argument to function must not be null"); + } + if ($parent !== null) { + if ($parent === "") { + $this->path = $fs->resolve($fs->getDefaultParent(), $fs->normalize($child)); + } else { + $this->path = $fs->resolve($fs->normalize($parent), $fs->normalize($child)); + } + } else { + $this->path = (string) $fs->normalize($child); + } + $this->prefixLength = (int) $fs->prefixLength($this->path); + } + + /** + * + * Enter description here ... + * @param unknown_type $parent + * @param unknown_type $child + */ + protected function _constructFileParentStringChild($parent, $child = null) { + // obtain ref to the filesystem layer + $fs = FileSystem::getFileSystem(); + + if ($child === null) { + throw new NullPointerException("Argument to function must not be null"); + } + + if ($parent !== null) { + if ($parent->path === "") { + $this->path = $fs->resolve($fs->getDefaultParent(), $fs->normalize($child)); + } else { + $this->path = $fs->resolve($parent->path, $fs->normalize($child)); + } + } else { + $this->path = $fs->normalize($child); + } + $this->prefixLength = $fs->prefixLength($this->path); + } + + /* -- Path-component accessors -- */ + + /** + * Returns the name of the file or directory denoted by this abstract + * pathname. This is just the last name in the pathname's name + * sequence. If the pathname's name sequence is empty, then the empty + * string is returned. + * + * @return The name of the file or directory denoted by this abstract + * pathname, or the empty string if this pathname's name sequence + * is empty + */ + function getName() { + // that's a lastIndexOf + $index = ((($res = strrpos($this->path, self::$separator)) === false) ? -1 : $res); + if ($index < $this->prefixLength) { + return substr($this->path, $this->prefixLength); + } + return substr($this->path, $index + 1); + } + + /** + * Returns the pathname string of this abstract pathname's parent, or + * null if this pathname does not name a parent directory. + * + * The parent of an abstract pathname consists of the pathname's prefix, + * if any, and each name in the pathname's name sequence except for the last. + * If the name sequence is empty then the pathname does not name a parent + * directory. + * + * @return The pathname string of the parent directory named by this + * abstract pathname, or null if this pathname does not name a parent + */ + function getParent() { + // that's a lastIndexOf + $index = ((($res = strrpos($this->path, self::$separator)) === false) ? -1 : $res); + if ($index < $this->prefixLength) { + if (($this->prefixLength > 0) && (strlen($this->path) > $this->prefixLength)) { + return substr($this->path, 0, $this->prefixLength); + } + return null; + } + return substr($this->path, 0, $index); + } + + /** + * Returns the abstract pathname of this abstract pathname's parent, + * or null if this pathname does not name a parent directory. + * + * The parent of an abstract pathname consists of the pathname's prefix, + * if any, and each name in the pathname's name sequence except for the + * last. If the name sequence is empty then the pathname does not name + * a parent directory. + * + * @return The abstract pathname of the parent directory named by this + * abstract pathname, or null if this pathname + * does not name a parent + */ + function getParentFile() { + $p = $this->getParent(); + if ($p === null) { + return null; + } + return new PhingFile((string) $p, (int) $this->prefixLength); + } + + /** + * Converts this abstract pathname into a pathname string. The resulting + * string uses the default name-separator character to separate the names + * in the name sequence. + * + * @return string The string form of this abstract pathname + */ + function getPath() { + return (string) $this->path; + } + + /** + * Returns path without leading basedir. + * + * @param string $basedir Base directory to strip + * + * @return string Path without basedir + * + * @uses getPath() + */ + function getPathWithoutBase($basedir) + { + if (!StringHelper::endsWith(self::$separator, $basedir)) { + $basedir .= self::$separator; + } + $path = $this->getPath(); + if (!substr($path, 0, strlen($basedir)) == $basedir) { + //path does not begin with basedir, we don't modify it + return $path; + } + return substr($path, strlen($basedir)); + } + + /** + * Tests whether this abstract pathname is absolute. The definition of + * absolute pathname is system dependent. On UNIX systems, a pathname is + * absolute if its prefix is "/". On Win32 systems, a pathname is absolute + * if its prefix is a drive specifier followed by "\\", or if its prefix + * is "\\". + * + * @return boolean true if this abstract pathname is absolute, false otherwise + */ + function isAbsolute() { + return ($this->prefixLength !== 0); + } + + + /** + * Returns the absolute pathname string of this abstract pathname. + * + * If this abstract pathname is already absolute, then the pathname + * string is simply returned as if by the getPath method. + * If this abstract pathname is the empty abstract pathname then + * the pathname string of the current user directory, which is named by the + * system property user.dir, is returned. Otherwise this + * pathname is resolved in a system-dependent way. On UNIX systems, a + * relative pathname is made absolute by resolving it against the current + * user directory. On Win32 systems, a relative pathname is made absolute + * by resolving it against the current directory of the drive named by the + * pathname, if any; if not, it is resolved against the current user + * directory. + * + * @return string The absolute pathname string denoting the same file or + * directory as this abstract pathname + * @see #isAbsolute() + */ + function getAbsolutePath() { + $fs = FileSystem::getFileSystem(); + return $fs->resolveFile($this); + } + + /** + * Returns the absolute form of this abstract pathname. Equivalent to + * getAbsolutePath. + * + * @return string The absolute abstract pathname denoting the same file or + * directory as this abstract pathname + */ + function getAbsoluteFile() { + return new PhingFile((string) $this->getAbsolutePath()); + } + + + /** + * Returns the canonical pathname string of this abstract pathname. + * + * A canonical pathname is both absolute and unique. The precise + * definition of canonical form is system-dependent. This method first + * converts this pathname to absolute form if necessary, as if by invoking the + * getAbsolutePath() method, and then maps it to its unique form in a + * system-dependent way. This typically involves removing redundant names + * such as "." and .. from the pathname, resolving symbolic links + * (on UNIX platforms), and converting drive letters to a standard case + * (on Win32 platforms). + * + * Every pathname that denotes an existing file or directory has a + * unique canonical form. Every pathname that denotes a nonexistent file + * or directory also has a unique canonical form. The canonical form of + * the pathname of a nonexistent file or directory may be different from + * the canonical form of the same pathname after the file or directory is + * created. Similarly, the canonical form of the pathname of an existing + * file or directory may be different from the canonical form of the same + * pathname after the file or directory is deleted. + * + * @return string The canonical pathname string denoting the same file or + * directory as this abstract pathname + */ + function getCanonicalPath() { + $fs = FileSystem::getFileSystem(); + return $fs->canonicalize($this->path); + } + + + /** + * Returns the canonical form of this abstract pathname. Equivalent to + * getCanonicalPath(. + * + * @return PhingFile The canonical pathname string denoting the same file or + * directory as this abstract pathname + */ + function getCanonicalFile() { + return new PhingFile($this->getCanonicalPath()); + } + + /** + * Converts this abstract pathname into a file: URL. The + * exact form of the URL is system-dependent. If it can be determined that + * the file denoted by this abstract pathname is a directory, then the + * resulting URL will end with a slash. + * + * Usage note: This method does not automatically escape + * characters that are illegal in URLs. It is recommended that new code + * convert an abstract pathname into a URL by first converting it into a + * URI, via the toURI() method, and then converting the URI + * into a URL via the URI::toURL() + * + * @return void A URL object representing the equivalent file URL + * @todo Not implemented yet + * + */ + function toURL() { + /* + // URL class not implemented yet + return new URL("file", "", $this->_slashify($this->getAbsolutePath(), $this->isDirectory())); + */ + } + + /** + * Constructs a file: URI that represents this abstract pathname. + * @todo Not implemented yet + * @return void + */ + function toURI() { + /* + $f = $this->getAbsoluteFile(); + $sp = (string) $this->slashify($f->getPath(), $f->isDirectory()); + if (StringHelper::startsWith('//', $sp)) + $sp = '//' + sp; + return new URI('file', null, $sp, null); + */ + } + + /** + * + * Enter description here ... + * @param PhingFile|string $path + * @param boolean $isDirectory + * @return string + */ + function _slashify($path, $isDirectory) { + $p = (string) $path; + + if (self::$separator !== '/') { + $p = str_replace(self::$separator, '/', $p); + } + + if (!StringHelper::startsWith('/', $p)) { + $p = '/'.$p; + } + + if (!StringHelper::endsWith('/', $p) && $isDirectory) { + $p = $p.'/'; + } + + return $p; + } + + /* -- Attribute accessors -- */ + + /** + * Tests whether the application can read the file denoted by this + * abstract pathname. + * + * @return boolean true if and only if the file specified by this + * abstract pathname exists and can be read by the + * application; false otherwise + */ + function canRead() { + $fs = FileSystem::getFileSystem(); + + if ($fs->checkAccess($this)) { + return (boolean) @is_link($this->getAbsolutePath()) || @is_readable($this->getAbsolutePath()); + } + return false; + } + + /** + * Tests whether the application can modify to the file denoted by this + * abstract pathname. + * + * @return boolean true if and only if the file system actually + * contains a file denoted by this abstract pathname and + * the application is allowed to write to the file; + * false otherwise. + */ + function canWrite() { + $fs = FileSystem::getFileSystem(); + return $fs->checkAccess($this, true); + } + + /** + * Tests whether the file denoted by this abstract pathname exists. + * + * @return boolean true if and only if the file denoted by this + * abstract pathname exists; false otherwise + */ + function exists() { + clearstatcache(); + + if (is_link($this->path)) { + return true; + } else if ($this->isFile()) { + return @file_exists($this->path) || is_link($this->path); + } else { + return @is_dir($this->path); + } + } + + /** + * Tests whether the file denoted by this abstract pathname is a + * directory. + * + * @return boolean true if and only if the file denoted by this + * abstract pathname exists and is a directory; + * false otherwise + */ + function isDirectory() { + clearstatcache(); + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this) !== true) { + throw new IOException("No read access to ".$this->path); + } + return @is_dir($this->path) && !@is_link($this->path); + } + + /** + * Tests whether the file denoted by this abstract pathname is a normal + * file. A file is normal if it is not a directory and, in + * addition, satisfies other system-dependent criteria. Any non-directory + * file created by a Java application is guaranteed to be a normal file. + * + * @return boolean true if and only if the file denoted by this + * abstract pathname exists and is a normal file; + * false otherwise + */ + function isFile() { + clearstatcache(); + //$fs = FileSystem::getFileSystem(); + return @is_file($this->path); + } + + /** + * Tests whether the file named by this abstract pathname is a hidden + * file. The exact definition of hidden is system-dependent. On + * UNIX systems, a file is considered to be hidden if its name begins with + * a period character ('.'). On Win32 systems, a file is considered to be + * hidden if it has been marked as such in the filesystem. Currently there + * seems to be no way to dermine isHidden on Win file systems via PHP + * + * @return boolean true if and only if the file denoted by this + * abstract pathname is hidden according to the conventions of the + * underlying platform + */ + function isHidden() { + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this) !== true) { + throw new IOException("No read access to ".$this->path); + } + return (($fs->getBooleanAttributes($this) & $fs->BA_HIDDEN) !== 0); + } + + /** + * Tests whether the file denoted by this abstract pathname is a symbolic link. + * + * @return boolean true if and only if the file denoted by this + * abstract pathname exists and is a symbolic link; + * false otherwise + */ + public function isLink() + { + clearstatcache(); + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this) !== true) { + throw new IOException("No read access to ".$this->path); + } + return @is_link($this->path); + } + + /** + * Returns the target of the symbolic link denoted by this abstract pathname + * + * @return string the target of the symbolic link denoted by this abstract pathname + */ + public function getLinkTarget() + { + return @readlink($this->path); + } + + /** + * Returns the time that the file denoted by this abstract pathname was + * last modified. + * + * @return int An integer value representing the time the file was + * last modified, measured in milliseconds since the epoch + * (00:00:00 GMT, January 1, 1970), or 0 if the + * file does not exist or if an I/O error occurs + */ + function lastModified() { + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this) !== true) { + throw new IOException("No read access to " . $this->path); + } + return $fs->getLastModifiedTime($this); + } + + /** + * Returns the length of the file denoted by this abstract pathname. + * The return value is unspecified if this pathname denotes a directory. + * + * @return int The length, in bytes, of the file denoted by this abstract + * pathname, or 0 if the file does not exist + */ + function length() { + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this) !== true) { + throw new IOException("No read access to ".$this->path."\n"); + } + return $fs->getLength($this); + } + + /** + * Convenience method for returning the contents of this file as a string. + * This method uses file_get_contents() to read file in an optimized way. + * @return string + * @throws Exception - if file cannot be read + */ + function contents() { + if (!$this->canRead() || !$this->isFile()) { + throw new IOException("Cannot read file contents!"); + } + return file_get_contents($this->getAbsolutePath()); + } + + /* -- File operations -- */ + + /** + * Atomically creates a new, empty file named by this abstract pathname if + * and only if a file with this name does not yet exist. The check for the + * existence of the file and the creation of the file if it does not exist + * are a single operation that is atomic with respect to all other + * filesystem activities that might affect the file. + * + * @return boolean true if the named file does not exist and was + * successfully created; false if the named file + * already exists + * @throws IOException if file can't be created + */ + function createNewFile($parents=true, $mode=0777) { + $file = FileSystem::getFileSystem()->createNewFile($this->path); + return $file; + } + + /** + * Deletes the file or directory denoted by this abstract pathname. If + * this pathname denotes a directory, then the directory must be empty in + * order to be deleted. + * + * @return boolean true if and only if the file or directory is + * successfully deleted; false otherwise + */ + function delete($recursive = false) { + $fs = FileSystem::getFileSystem(); + if ($fs->canDelete($this) !== true) { + throw new IOException("Cannot delete " . $this->path . "\n"); + } + return $fs->delete($this, $recursive); + } + + /** + * Requests that the file or directory denoted by this abstract pathname + * be deleted when php terminates. Deletion will be attempted only for + * normal termination of php and if and if only Phing::shutdown() is + * called. + * + * Once deletion has been requested, it is not possible to cancel the + * request. This method should therefore be used with care. + * + */ + function deleteOnExit() { + $fs = FileSystem::getFileSystem(); + $fs->deleteOnExit($this); + } + + /** + * Returns an array of strings naming the files and directories in the + * directory denoted by this abstract pathname. + * + * If this abstract pathname does not denote a directory, then this + * method returns null Otherwise an array of strings is + * returned, one for each file or directory in the directory. Names + * denoting the directory itself and the directory's parent directory are + * not included in the result. Each string is a file name rather than a + * complete path. + * + * There is no guarantee that the name strings in the resulting array + * will appear in any specific order; they are not, in particular, + * guaranteed to appear in alphabetical order. + * + * @param $filter string + * @return array An array of strings naming the files and directories in the + * directory denoted by this abstract pathname. The array will be + * empty if the directory is empty. Returns null if + * this abstract pathname does not denote a directory, or if an + * I/O error occurs. + */ + function listDir($filter = null) { + $fs = FileSystem::getFileSystem(); + return $fs->lister($this, $filter); + } + + /** + * + * Enter description here ... + * @param PhingFile[] $filter + */ + function listFiles($filter = null) { + $ss = $this->listDir($filter); + if ($ss === null) { + return null; + } + $n = count($ss); + $fs = array(); + for ($i = 0; $i < $n; $i++) { + $fs[$i] = new PhingFile((string)$this->path, (string)$ss[$i]); + } + return $fs; + } + + /** + * Creates the directory named by this abstract pathname, including any + * necessary but nonexistent parent directories. Note that if this + * operation fails it may have succeeded in creating some of the necessary + * parent directories. + * + * @return boolean true if and only if the directory was created, + * along with all necessary parent directories; false + * otherwise + * @throws IOException + */ + function mkdirs($mode = 0755) { + if ($this->exists()) { + return false; + } + try { + if ($this->mkdir($mode)) { + return true; + } + } catch (IOException $ioe) { + // IOException from mkdir() means that directory propbably didn't exist. + } + $parentFile = $this->getParentFile(); + return (($parentFile !== null) && ($parentFile->mkdirs($mode) && $this->mkdir($mode))); + } + + /** + * Creates the directory named by this abstract pathname. + * + * @return boolean true if and only if the directory was created; false otherwise + * @throws IOException + */ + function mkdir($mode = 0755) { + $fs = FileSystem::getFileSystem(); + + if ($fs->checkAccess(new PhingFile($this->path), true) !== true) { + throw new IOException("No write access to " . $this->getPath()); + } + return $fs->createDirectory($this, $mode); + } + + /** + * Renames the file denoted by this abstract pathname. + * + * @param PhingFile $destFile The new abstract pathname for the named file + * @return boolean true if and only if the renaming succeeded; false otherwise + */ + function renameTo(PhingFile $destFile) { + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this) !== true) { + throw new IOException("No write access to ".$this->getPath()); + } + return $fs->rename($this, $destFile); + } + + /** + * Simple-copies file denoted by this abstract pathname into another + * PhingFile + * + * @param PhingFile $destFile The new abstract pathname for the named file + * @return boolean true if and only if the renaming succeeded; false otherwise + */ + function copyTo(PhingFile $destFile) { + $fs = FileSystem::getFileSystem(); + + if ($fs->checkAccess($this) !== true) { + throw new IOException("No read access to ".$this->getPath()."\n"); + } + + if ($fs->checkAccess($destFile, true) !== true) { + throw new IOException("File::copyTo() No write access to ".$destFile->getPath()); + } + return $fs->copy($this, $destFile); + } + + /** + * Sets the last-modified time of the file or directory named by this + * abstract pathname. + * + * All platforms support file-modification times to the nearest second, + * but some provide more precision. The argument will be truncated to fit + * the supported precision. If the operation succeeds and no intervening + * operations on the file take place, then the next invocation of the + * lastModified method will return the (possibly truncated) time argument + * that was passed to this method. + * + * @param int $time The new last-modified time, measured in milliseconds since + * the epoch (00:00:00 GMT, January 1, 1970) + * @return boolean true if and only if the operation succeeded; false otherwise + */ + function setLastModified($time) { + $time = (int) $time; + if ($time < 0) { + throw new Exception("IllegalArgumentException, Negative $time\n"); + } + + $fs = FileSystem::getFileSystem(); + return $fs->setLastModifiedTime($this, $time); + } + + /** + * Marks the file or directory named by this abstract pathname so that + * only read operations are allowed. After invoking this method the file + * or directory is guaranteed not to change until it is either deleted or + * marked to allow write access. Whether or not a read-only file or + * directory may be deleted depends upon the underlying system. + * + * @return boolean true if and only if the operation succeeded; false otherwise + */ + function setReadOnly() { + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this, true) !== true) { + // Error, no write access + throw new IOException("No write access to " . $this->getPath()); + } + return $fs->setReadOnly($this); + } + + /** + * Sets the owner of the file. + * @param mixed $user User name or number. + */ + public function setUser($user) { + $fs = FileSystem::getFileSystem(); + return $fs->chown($this->getPath(), $user); + } + + /** + * Retrieve the owner of this file. + * @return int User ID of the owner of this file. + */ + function getUser() { + return @fileowner($this->getPath()); + } + + /** + * Sets the group of the file. + * @param mixed $user User name or number. + */ + public function setGroup($group) { + $fs = FileSystem::getFileSystem(); + return $fs->chgrp($this->getPath(), $group); + } + + /** + * Retrieve the group of this file. + * @return int User ID of the owner of this file. + */ + function getGroup() { + return @filegroup($this->getPath()); + } + + /** + * Sets the mode of the file + * @param int $mode Ocatal mode. + */ + function setMode($mode) { + $fs = FileSystem::getFileSystem(); + return $fs->chmod($this->getPath(), $mode); + } + + /** + * Retrieve the mode of this file. + * @return int + */ + function getMode() { + return @fileperms($this->getPath()); + } + + /* -- Filesystem interface -- */ + + /** + * List the available filesystem roots. + * + * A particular platform may support zero or more hierarchically-organized + * file systems. Each file system has a root directory from which all + * other files in that file system can be reached. + * Windows platforms, for example, have a root directory for each active + * drive; UNIX platforms have a single root directory, namely "/". + * The set of available filesystem roots is affected by various system-level + * operations such the insertion or ejection of removable media and the + * disconnecting or unmounting of physical or virtual disk drives. + * + * This method returns an array of PhingFile objects that + * denote the root directories of the available filesystem roots. It is + * guaranteed that the canonical pathname of any file physically present on + * the local machine will begin with one of the roots returned by this + * method. + * + * The canonical pathname of a file that resides on some other machine + * and is accessed via a remote-filesystem protocol such as SMB or NFS may + * or may not begin with one of the roots returned by this method. If the + * pathname of a remote file is syntactically indistinguishable from the + * pathname of a local file then it will begin with one of the roots + * returned by this method. Thus, for example, PhingFile objects + * denoting the root directories of the mapped network drives of a Windows + * platform will be returned by this method, while PhingFile + * objects containing UNC pathnames will not be returned by this method. + * + * @return array An array of PhingFile objects denoting the available + * filesystem roots, or null if the set of roots + * could not be determined. The array will be empty if there are + * no filesystem roots. + */ + function listRoots() { + $fs = FileSystem::getFileSystem(); + return (array) $fs->listRoots(); + } + + /* -- Tempfile management -- */ + + /** + * Returns the path to the temp directory. + * @return string + */ + public static function getTempDir() { + return Phing::getProperty('php.tmpdir'); + } + + /** + * Static method that creates a unique filename whose name begins with + * $prefix and ends with $suffix in the directory $directory. $directory + * is a reference to a PhingFile Object. + * Then, the file is locked for exclusive reading/writing. + * + * @author manuel holtgrewe, grin@gmx.net + * @throws IOException + * @return PhingFile + */ + public static function createTempFile($prefix, $suffix, PhingFile $directory) { + + // quick but efficient hack to create a unique filename ;-) + $result = null; + do { + $result = new PhingFile($directory, $prefix . substr(md5(time()), 0, 8) . $suffix); + } while (file_exists($result->getPath())); + + $fs = FileSystem::getFileSystem(); + $fs->createNewFile($result->getPath()); + $fs->lock($result); + + return $result; + } + + /** + * If necessary, $File the lock on $File is removed and then the file is + * deleted + * + * @access public + */ + function removeTempFile() { + $fs = FileSystem::getFileSystem(); + // catch IO Exception + $fs->unlock($this); + $this->delete(); + } + + + /* -- Basic infrastructure -- */ + + /** + * Compares two abstract pathnames lexicographically. The ordering + * defined by this method depends upon the underlying system. On UNIX + * systems, alphabetic case is significant in comparing pathnames; on Win32 + * systems it is not. + * + * @param PhingFile $file Th file whose pathname sould be compared to the pathname of this file. + * + * @return int Zero if the argument is equal to this abstract pathname, a + * value less than zero if this abstract pathname is + * lexicographically less than the argument, or a value greater + * than zero if this abstract pathname is lexicographically + * greater than the argument + */ + function compareTo(PhingFile $file) { + $fs = FileSystem::getFileSystem(); + return $fs->compare($this, $file); + } + + /** + * Tests this abstract pathname for equality with the given object. + * Returns true if and only if the argument is not + * null and is an abstract pathname that denotes the same file + * or directory as this abstract pathname. Whether or not two abstract + * pathnames are equal depends upon the underlying system. On UNIX + * systems, alphabetic case is significant in comparing pathnames; on Win32 + * systems it is not. + * @return boolean + */ + function equals($obj) { + if (($obj !== null) && ($obj instanceof PhingFile)) { + return ($this->compareTo($obj) === 0); + } + return false; + } + + /** + * Backwards compatibility - @see __toString() + * + * @return string + */ + public function toString() + { + return $this->__toString(); + } + + /** + * Return string representation of the object + * + * @return string + */ + public function __toString() + { + return $this->getPath(); + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/Reader.php b/buildscripts/phing/classes/phing/system/io/Reader.php new file mode 100755 index 00000000..92159469 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/Reader.php @@ -0,0 +1,91 @@ +. +*/ + +/** + * Abstract class for reading character streams. + * + * @author Hans Lellelid + * @author Yannick Lecaillez + * @version $Id$ + * @package phing.system.io + */ +abstract class Reader { + + /** + * Read data from source. + * + * If length is specified, then only that number of chars is read, + * otherwise stream is read until EOF. + * + * @param int $len + */ + abstract public function read($len = null); + + /** + * Close stream. + * @throws IOException if there is an error closing stream + */ + abstract public function close(); + + /** + * Returns the filename, url, etc. that is being read from. + * This is critical for, e.g., ExpatParser's ability to know + * the filename that is throwing an ExpatParserException, etc. + * @return string + */ + abstract function getResource(); + + /** + * Move stream position relative to current pos. + * @param int $n + */ + public function skip($n) {} + + /** + * Reset the current position in stream to beginning or last mark (if supported). + */ + public function reset() {} + + /** + * If supported, places a "marker" (like a bookmark) at current stream position. + * A subsequent call to reset() will move stream position back + * to last marker (if supported). + */ + public function mark() {} + + /** + * Whether marking is supported. + * @return boolean + */ + public function markSupported() { + return false; + } + + /** + * Is stream ready for reading. + * @return boolean + */ + public function ready() { + return true; + } + +} + diff --git a/buildscripts/phing/classes/phing/system/io/StringReader.php b/buildscripts/phing/classes/phing/system/io/StringReader.php new file mode 100644 index 00000000..e8b493e9 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/StringReader.php @@ -0,0 +1,84 @@ +. + */ + +/** + * Dummy class for reading from string of characters. + * @package phing.system.io + */ +class StringReader extends Reader { + + /** + * @var string + */ + private $_string; + + /** + * @var int + */ + private $mark = 0; + + /** + * @var int + */ + private $currPos = 0; + + function __construct($string) { + $this->_string = $string; + } + + function skip($n) {} + + function read($len = null) { + if ($len === null) { + return $this->_string; + } else { + if ($this->currPos >= strlen($this->_string)) { + return -1; + } + $out = substr($this->_string, $this->currPos, $len); + $this->currPos += $len; + return $out; + } + } + + function mark() { + $this->mark = $this->currPos; + } + + function reset() { + $this->currPos = $this->mark; + } + + function close() {} + + function open() {} + + function ready() {} + + function markSupported() { + return true; + } + + function getResource() { + return '(string) "'.$this->_string . '"'; + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/UnixFileSystem.php b/buildscripts/phing/classes/phing/system/io/UnixFileSystem.php new file mode 100755 index 00000000..739ff6f6 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/UnixFileSystem.php @@ -0,0 +1,302 @@ +. + */ + +include_once 'phing/system/io/FileSystem.php'; + +/** + * UnixFileSystem class. This class encapsulates the basic file system functions + * for platforms using the unix (posix)-stylish filesystem. It wraps php native + * functions suppressing normal PHP error reporting and instead uses Exception + * to report and error. + * + * This class is part of a oop based filesystem abstraction and targeted to run + * on all supported php platforms. + * + * Note: For debugging turn track_errors on in the php.ini. The error messages + * and log messages from this class will then be clearer because $php_errormsg + * is passed as part of the message. + * + * FIXME: + * - Comments + * - Error handling reduced to min, error are handled by PhingFile mainly + * + * @author Andreas Aderhold, andi@binarycloud.com + * @version $Id$ + * @package phing.system.io + */ +class UnixFileSystem extends FileSystem { + + /** + * returns OS dependant path separator char + */ + function getSeparator() { + return '/'; + } + + /** + * returns OS dependant directory separator char + */ + function getPathSeparator() { + return ':'; + } + + /** + * A normal Unix pathname contains no duplicate slashes and does not end + * with a slash. It may be the empty string. + * + * Check that the given pathname is normal. If not, invoke the real + * normalizer on the part of the pathname that requires normalization. + * This way we iterate through the whole pathname string only once. + */ + function normalize($strPathname) { + + if (!strlen($strPathname)) { + return; + } + + // Resolve home directories. We assume /home is where all home + // directories reside, b/c there is no other way to do this with + // PHP AFAIK. + if ($strPathname{0} === "~") { + if ($strPathname{1} === "/") { // like ~/foo => /home/user/foo + $strPathname = "/home/" . get_current_user() . substr($strPathname, 1); + } else { // like ~foo => /home/foo + $pos = strpos($strPathname, "/"); + $name = substr($strPathname, 1, $pos - 2); + $strPathname = "/home/" . $name . substr($strPathname, $pos); + } + } + + $n = strlen($strPathname); + $prevChar = 0; + for ($i=0; $i < $n; $i++) { + $c = $strPathname{$i}; + if (($prevChar === '/') && ($c === '/')) { + return self::normalizer($strPathname, $n, $i - 1); + } + $prevChar = $c; + } + if ($prevChar === '/') { + return self::normalizer($strPathname, $n, $n - 1); + } + return $strPathname; + } + + /** + * Normalize the given pathname, whose length is $len, starting at the given + * $offset; everything before this offset is already normal. + */ + protected function normalizer($pathname, $len, $offset) { + if ($len === 0) { + return $pathname; + } + $n = (int) $len; + while (($n > 0) && ($pathname{$n-1} === '/')) { + $n--; + } + if ($n === 0) { + return '/'; + } + $sb = ""; + + if ($offset > 0) { + $sb .= substr($pathname, 0, $offset); + } + $prevChar = 0; + for ($i = $offset; $i < $n; $i++) { + $c = $pathname{$i}; + if (($prevChar === '/') && ($c === '/')) { + continue; + } + $sb .= $c; + $prevChar = $c; + } + return $sb; + } + + /** + * Compute the length of the pathname string's prefix. The pathname + * string must be in normal form. + */ + function prefixLength($pathname) { + if (strlen($pathname === 0)) { + return 0; + } + return (($pathname{0} === '/') ? 1 : 0); + } + + /** + * Resolve the child pathname string against the parent. + * Both strings must be in normal form, and the result + * will be in normal form. + */ + function resolve($parent, $child) { + + if ($child === "") { + return $parent; + } + + if ($child{0} === '/') { + if ($parent === '/') { + return $child; + } + return $parent.$child; + } + + if ($parent === '/') { + return $parent.$child; + } + + return $parent.'/'.$child; + } + + function getDefaultParent() { + return '/'; + } + + function isAbsolute(PhingFile $f) { + return ($f->getPrefixLength() !== 0); + } + + /** + * the file resolver + */ + function resolveFile(PhingFile $f) { + // resolve if parent is a file oject only + if ($this->isAbsolute($f)) { + return $f->getPath(); + } else { + return $this->resolve(Phing::getProperty("user.dir"), $f->getPath()); + } + } + + /* -- most of the following is mapped to the php natives wrapped by FileSystem */ + + /* -- Attribute accessors -- */ + function getBooleanAttributes($f) { + //$rv = getBooleanAttributes0($f); + $name = $f->getName(); + $hidden = (strlen($name) > 0) && ($name{0} == '.'); + return ($hidden ? $this->BA_HIDDEN : 0); + } + + /** + * set file readonly on unix + */ + function setReadOnly($f) { + if ($f instanceof File) { + $strPath = (string) $f->getPath(); + $perms = (int) (@fileperms($strPath) & 0444); + return FileSystem::Chmod($strPath, $perms); + } else { + throw new Exception("IllegalArgumentType: Argument is not File"); + } + } + + /** + * compares file paths lexicographically + */ + function compare(PhingFile $f1, PhingFile $f2) { + $f1Path = $f1->getPath(); + $f2Path = $f2->getPath(); + return strcmp((string) $f1Path, (string) $f2Path); + } + + /** + * Copy a file, takes care of symbolic links + * + * @param PhingFile $src Source path and name file to copy. + * @param PhingFile $dest Destination path and name of new file. + * + * @return void + * @throws Exception if file cannot be copied. + */ + function copy(PhingFile $src, PhingFile $dest) { + global $php_errormsg; + + if (!$src->isLink()) + { + return parent::copy($src, $dest); + } + + $srcPath = $src->getAbsolutePath(); + $destPath = $dest->getAbsolutePath(); + + $linkTarget = $src->getLinkTarget(); + if (false === @symlink($linkTarget, $destPath)) + { + $msg = "FileSystem::copy() FAILED. Cannot create symlink from $destPath to $linkTarget."; + throw new Exception($msg); + } + } + + /* -- fs interface --*/ + + function listRoots() { + if (!$this->checkAccess('/', false)) { + die ("Can not access root"); + } + return array(new PhingFile("/")); + } + + /** + * returns the contents of a directory in an array + */ + function lister($f) { + $dir = @opendir($f->getAbsolutePath()); + if (!$dir) { + throw new Exception("Can't open directory " . $f->__toString()); + } + $vv = array(); + while (($file = @readdir($dir)) !== false) { + if ($file == "." || $file == "..") { + continue; + } + $vv[] = (string) $file; + } + @closedir($dir); + return $vv; + } + + function fromURIPath($p) { + if (StringHelper::endsWith("/", $p) && (strlen($p) > 1)) { + + // "/foo/" --> "/foo", but "/" --> "/" + $p = substr($p, 0, strlen($p) - 1); + + } + + return $p; + } + + /** + * Whether file can be deleted. + * @param PhingFile $f + * @return boolean + */ + function canDelete(PhingFile $f) + { + @clearstatcache(); + $dir = dirname($f->getAbsolutePath()); + return (bool) @is_writable($dir); + } + +} diff --git a/buildscripts/phing/classes/phing/system/io/Win32FileSystem.php b/buildscripts/phing/classes/phing/system/io/Win32FileSystem.php new file mode 100644 index 00000000..58331cde --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/Win32FileSystem.php @@ -0,0 +1,477 @@ +. + */ + +include_once 'phing/system/io/FileSystem.php'; + +/** + * @package phing.system.io + */ +class Win32FileSystem extends FileSystem { + + protected $slash; + protected $altSlash; + protected $semicolon; + + private static $driveDirCache = array(); + + function __construct() { + $this->slash = self::getSeparator(); + $this->semicolon = self::getPathSeparator(); + $this->altSlash = ($this->slash === '\\') ? '/' : '\\'; + } + + function isSlash($c) { + return ($c == '\\') || ($c == '/'); + } + + function isLetter($c) { + return ((ord($c) >= ord('a')) && (ord($c) <= ord('z'))) + || ((ord($c) >= ord('A')) && (ord($c) <= ord('Z'))); + } + + function slashify($p) { + if ((strlen($p) > 0) && ($p{0} != $this->slash)) { + return $this->slash.$p; + } + else { + return $p; + } + } + + /* -- Normalization and construction -- */ + + function getSeparator() { + // the ascii value of is the \ + return chr(92); + } + + function getPathSeparator() { + return ';'; + } + + /** + * A normal Win32 pathname contains no duplicate slashes, except possibly + * for a UNC prefix, and does not end with a slash. It may be the empty + * string. Normalized Win32 pathnames have the convenient property that + * the length of the prefix almost uniquely identifies the type of the path + * and whether it is absolute or relative: + * + * 0 relative to both drive and directory + * 1 drive-relative (begins with '\\') + * 2 absolute UNC (if first char is '\\'), else directory-relative (has form "z:foo") + * 3 absolute local pathname (begins with "z:\\") + */ + function normalizePrefix($strPath, $len, &$sb) { + $src = 0; + while (($src < $len) && $this->isSlash($strPath{$src})) { + $src++; + } + $c = ""; + if (($len - $src >= 2) + && $this->isLetter($c = $strPath{$src}) + && $strPath{$src + 1} === ':') { + /* Remove leading slashes if followed by drive specifier. + * This hack is necessary to support file URLs containing drive + * specifiers (e.g., "file://c:/path"). As a side effect, + * "/c:/path" can be used as an alternative to "c:/path". */ + $sb .= $c; + $sb .= ':'; + $src += 2; + } + else { + $src = 0; + if (($len >= 2) + && $this->isSlash($strPath{0}) + && $this->isSlash($strPath{1})) { + /* UNC pathname: Retain first slash; leave src pointed at + * second slash so that further slashes will be collapsed + * into the second slash. The result will be a pathname + * beginning with "\\\\" followed (most likely) by a host + * name. */ + $src = 1; + $sb.=$this->slash; + } + } + return $src; + } + + /** Normalize the given pathname, whose length is len, starting at the given + offset; everything before this offset is already normal. */ + protected function normalizer($strPath, $len, $offset) { + if ($len == 0) { + return $strPath; + } + if ($offset < 3) { + $offset = 0; //Avoid fencepost cases with UNC pathnames + } + $src = 0; + $slash = $this->slash; + $sb = ""; + + if ($offset == 0) { + // Complete normalization, including prefix + $src = $this->normalizePrefix($strPath, $len, $sb); + } else { + // Partial normalization + $src = $offset; + $sb .= substr($strPath, 0, $offset); + } + + // Remove redundant slashes from the remainder of the path, forcing all + // slashes into the preferred slash + while ($src < $len) { + $c = $strPath{$src++}; + if ($this->isSlash($c)) { + while (($src < $len) && $this->isSlash($strPath{$src})) { + $src++; + } + if ($src === $len) { + /* Check for trailing separator */ + $sn = (int) strlen($sb); + if (($sn == 2) && ($sb{1} === ':')) { + // "z:\\" + $sb .= $slash; + break; + } + if ($sn === 0) { + // "\\" + $sb .= $slash; + break; + } + if (($sn === 1) && ($this->isSlash($sb{0}))) { + /* "\\\\" is not collapsed to "\\" because "\\\\" marks + the beginning of a UNC pathname. Even though it is + not, by itself, a valid UNC pathname, we leave it as + is in order to be consistent with the win32 APIs, + which treat this case as an invalid UNC pathname + rather than as an alias for the root directory of + the current drive. */ + $sb .= $slash; + break; + } + // Path does not denote a root directory, so do not append + // trailing slash + break; + } else { + $sb .= $slash; + } + } else { + $sb.=$c; + } + } + $rv = (string) $sb; + return $rv; + } + + /** + * Check that the given pathname is normal. If not, invoke the real + * normalizer on the part of the pathname that requires normalization. + * This way we iterate through the whole pathname string only once. + * @param string $strPath + * @return string + */ + function normalize($strPath) { + $n = strlen($strPath); + $slash = $this->slash; + $altSlash = $this->altSlash; + $prev = 0; + for ($i = 0; $i < $n; $i++) { + $c = $strPath{$i}; + if ($c === $altSlash) { + return $this->normalizer($strPath, $n, ($prev === $slash) ? $i - 1 : $i); + } + if (($c === $slash) && ($prev === $slash) && ($i > 1)) { + return $this->normalizer($strPath, $n, $i - 1); + } + if (($c === ':') && ($i > 1)) { + return $this->normalizer($strPath, $n, 0); + } + $prev = $c; + } + if ($prev === $slash) { + return $this->normalizer($strPath, $n, $n - 1); + } + return $strPath; + } + + function prefixLength($strPath) { + $path = (string) $strPath; + $slash = (string) $this->slash; + $n = (int) strlen($path); + if ($n === 0) { + return 0; + } + $c0 = $path{0}; + $c1 = ($n > 1) ? $path{1} : + 0; + if ($c0 === $slash) { + if ($c1 === $slash) { + return 2; // absolute UNC pathname "\\\\foo" + } + return 1; // drive-relative "\\foo" + } + + if ($this->isLetter($c0) && ($c1 === ':')) { + if (($n > 2) && ($path{2}) === $slash) { + return 3; // Absolute local pathname "z:\\foo" */ + } + return 2; // Directory-relative "z:foo" + } + return 0; // Completely relative + } + + function resolve($parent, $child) { + $parent = (string) $parent; + $child = (string) $child; + $slash = (string) $this->slash; + + $pn = (int) strlen($parent); + if ($pn === 0) { + return $child; + } + $cn = (int) strlen($child); + if ($cn === 0) { + return $parent; + } + + $c = $child; + if (($cn > 1) && ($c{0} === $slash)) { + if ($c{1} === $slash) { + // drop prefix when child is a UNC pathname + $c = substr($c, 2); + } + else { + //Drop prefix when child is drive-relative */ + $c = substr($c, 1); + } + } + + $p = $parent; + if ($p{$pn - 1} === $slash) { + $p = substr($p, 0, $pn - 1); + } + return $p.$this->slashify($c); + } + + function getDefaultParent() { + return (string) ("".$this->slash); + } + + function fromURIPath($strPath) { + $p = (string) $strPath; + if ((strlen($p) > 2) && ($p{2} === ':')) { + + // "/c:/foo" --> "c:/foo" + $p = substr($p,1); + + // "c:/foo/" --> "c:/foo", but "c:/" --> "c:/" + if ((strlen($p) > 3) && StringHelper::endsWith('/', $p)) { + $p = substr($p, 0, strlen($p) - 1); + } + } elseif ((strlen($p) > 1) && StringHelper::endsWith('/', $p)) { + // "/foo/" --> "/foo" + $p = substr($p, 0, strlen($p) - 1); + } + return (string) $p; + } + + + /* -- Path operations -- */ + + function isAbsolute(PhingFile $f) { + $pl = (int) $f->getPrefixLength(); + $p = (string) $f->getPath(); + return ((($pl === 2) && ($p{0} === $this->slash)) || ($pl === 3) || ($pl === 1 && $p{0} === $this->slash)); + } + + /** private */ + function _driveIndex($d) { + $d = (string) $d{0}; + if ((ord($d) >= ord('a')) && (ord($d) <= ord('z'))) { + return ord($d) - ord('a'); + } + if ((ord($d) >= ord('A')) && (ord($d) <= ord('Z'))) { + return ord($d) - ord('A'); + } + return -1; + } + + /** private */ + function _getDriveDirectory($drive) { + $drive = (string) $drive{0}; + $i = (int) $this->_driveIndex($drive); + if ($i < 0) { + return null; + } + + $s = (isset(self::$driveDirCache[$i]) ? self::$driveDirCache[$i] : null); + + if ($s !== null) { + return $s; + } + + $s = $this->_getDriveDirectory($i + 1); + self::$driveDirCache[$i] = $s; + return $s; + } + + function _getUserPath() { + //For both compatibility and security, we must look this up every time + return (string) $this->normalize(Phing::getProperty("user.dir")); + } + + function _getDrive($path) { + $path = (string) $path; + $pl = $this->prefixLength($path); + return ($pl === 3) ? substr($path, 0, 2) : null; + } + + function resolveFile(PhingFile $f) { + $path = $f->getPath(); + $pl = (int) $f->getPrefixLength(); + + if (($pl === 2) && ($path{0} === $this->slash)) { + return $path; // UNC + } + + if ($pl === 3) { + return $path; // Absolute local + } + + if ($pl === 0) { + return (string) ($this->_getUserPath().$this->slashify($path)); //Completely relative + } + + if ($pl === 1) { // Drive-relative + $up = (string) $this->_getUserPath(); + $ud = (string) $this->_getDrive($up); + if ($ud !== null) { + return (string) $ud.$path; + } + return (string) $up.$path; //User dir is a UNC path + } + + if ($pl === 2) { // Directory-relative + $up = (string) $this->_getUserPath(); + $ud = (string) $this->_getDrive($up); + if (($ud !== null) && StringHelper::startsWith($ud, $path)) { + return (string) ($up . $this->slashify(substr($path,2))); + } + $drive = (string) $path{0}; + $dir = (string) $this->_getDriveDirectory($drive); + + $np = (string) ""; + if ($dir !== null) { + /* When resolving a directory-relative path that refers to a + drive other than the current drive, insist that the caller + have read permission on the result */ + $p = (string) $drive . (':'.$dir.$this->slashify(substr($path,2))); + + if (!$this->checkAccess($p, false)) { + // FIXME + // throw security error + die("Can't resolve path $p"); + } + return $p; + } + return (string) $drive.':'.$this->slashify(substr($path,2)); //fake it + } + + throw new Exception("Unresolvable path: " . $path); + } + + /* -- most of the following is mapped to the functions mapped th php natives in FileSystem */ + + /* -- Attribute accessors -- */ + + function setReadOnly($f) { + // dunno how to do this on win + throw new Exception("WIN32FileSystem doesn't support read-only yet."); + } + + /* -- Filesystem interface -- */ + + protected function _access($path) { + if (!$this->checkAccess($path, false)) { + throw new Exception("Can't resolve path $p"); + } + return true; + } + + function _nativeListRoots() { + // FIXME + } + + function listRoots() { + $ds = _nativeListRoots(); + $n = 0; + for ($i = 0; $i < 26; $i++) { + if ((($ds >> $i) & 1) !== 0) { + if (!$this->access((string)( chr(ord('A') + $i) . ':' . $this->slash))) { + $ds &= ~(1 << $i); + } else { + $n++; + } + } + } + $fs = array(); + $j = (int) 0; + $slash = (string) $this->slash; + for ($i = 0; $i < 26; $i++) { + if ((($ds >> $i) & 1) !== 0) { + $fs[$j++] = new PhingFile(chr(ord('A') + $i) . ':' . $this->slash); + } + } + return $fs; + } + + /* -- Basic infrastructure -- */ + + /** compares file paths lexicographically */ + function compare(PhingFile $f1, PhingFile $f2) { + $f1Path = $f1->getPath(); + $f2Path = $f2->getPath(); + return (boolean) strcasecmp((string) $f1Path, (string) $f2Path); + } + + + /** + * returns the contents of a directory in an array + */ + function lister($f) { + $dir = @opendir($f->getAbsolutePath()); + if (!$dir) { + throw new Exception("Can't open directory " . $f->__toString()); + } + $vv = array(); + while (($file = @readdir($dir)) !== false) { + if ($file == "." || $file == "..") { + continue; + } + $vv[] = (string) $file; + } + @closedir($dir); + return $vv; + } + +} + + diff --git a/buildscripts/phing/classes/phing/system/io/WinNTFileSystem.php b/buildscripts/phing/classes/phing/system/io/WinNTFileSystem.php new file mode 100644 index 00000000..1eae49c4 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/WinNTFileSystem.php @@ -0,0 +1,34 @@ +. + */ + +include_once 'phing/system/io/Win32FileSystem.php'; + +/** + * FileSystem for Windows NT/2000. + * @package phing.system.io + */ +class WinNTFileSystem extends Win32FileSystem { + + /* -- class only for convenience and future use everything is inherinted --*/ + + +} + diff --git a/buildscripts/phing/classes/phing/system/io/Writer.php b/buildscripts/phing/classes/phing/system/io/Writer.php new file mode 100644 index 00000000..86fa67e9 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/Writer.php @@ -0,0 +1,53 @@ +. + */ + +/** + * Abstract class for writing character streams. + * + * @package phing.system.io + */ +abstract class Writer { + + /** + * Writes data to output stream. + * @param string $buf + * @param int $off + * @param int $len + */ + abstract public function write($buf, $off = null, $len = null); + + /** + * Close the stream. + * @throws IOException - if there is an error closing stream. + */ + abstract public function close(); + + /** + * Flush the stream, if supported by the stream. + */ + public function flush() {} + + /** + * Returns a string representation of resource filename, url, etc. that is being written to. + * @return string + */ + abstract public function getResource(); +} diff --git a/buildscripts/phing/classes/phing/system/lang/Character.php b/buildscripts/phing/classes/phing/system/lang/Character.php new file mode 100644 index 00000000..60285df6 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/lang/Character.php @@ -0,0 +1,49 @@ +. + */ + +/** + * @package phing.system.lang + */ +class Character { + + // this class might be extended with plenty of ordinal char constants + // and the like to support the multibyte aware datatype (char) in php + // in form of an object. + // anyway just a thought + + public static function isLetter($char) { + + if (strlen($char) !== 1) + $char = 0; + + $char = (int) ord($char); + + if ($char >= ord('A') && $char <= ord('Z')) + return true; + + if ($char >= ord('a') && $char <= ord('z')) + return true; + + return false; + } + +} + diff --git a/buildscripts/phing/classes/phing/system/lang/EventObject.php b/buildscripts/phing/classes/phing/system/lang/EventObject.php new file mode 100644 index 00000000..489a82a8 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/lang/EventObject.php @@ -0,0 +1,52 @@ +. + */ + +/** + * @package phing.system.lang + */ +class EventObject { + + /** The object on which the Event initially occurred. */ + protected $source; + + /** Constructs a prototypical Event. */ + function __construct($source) { + if ($source === null) { + throw new Exception("Null source"); + } + $this->source = $source; + } + + /** The object on which the Event initially occurred. */ + function getSource() { + return $this->source; + } + + /** Returns a String representation of this EventObject.*/ + function toString() { + if (method_exists($this->source, "toString")) { + return get_class($this)."[source=".$this->source->toString()."]"; + } else { + return get_class($this)."[source=".get_class($this->source)."]"; + } + } +} + diff --git a/buildscripts/phing/classes/phing/system/lang/FileNotFoundException.php b/buildscripts/phing/classes/phing/system/lang/FileNotFoundException.php new file mode 100644 index 00000000..5da28838 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/lang/FileNotFoundException.php @@ -0,0 +1,26 @@ +. + */ + +/** + * @package phing.system.lang + */ +class FileNotFoundException extends Exception {} + diff --git a/buildscripts/phing/classes/phing/system/lang/NullPointerException.php b/buildscripts/phing/classes/phing/system/lang/NullPointerException.php new file mode 100644 index 00000000..ccf080f5 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/lang/NullPointerException.php @@ -0,0 +1,26 @@ +. + */ + +/** + * @package phing.system.lang + */ +class NullPointerException extends Exception {} + diff --git a/buildscripts/phing/classes/phing/system/lang/SecurityException.php b/buildscripts/phing/classes/phing/system/lang/SecurityException.php new file mode 100644 index 00000000..74013bc0 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/lang/SecurityException.php @@ -0,0 +1,26 @@ +. + */ + +/** + * @package phing.system.lang + */ +class SecurityException extends Exception {} + diff --git a/buildscripts/phing/classes/phing/system/util/Properties.php b/buildscripts/phing/classes/phing/system/util/Properties.php new file mode 100755 index 00000000..4ffd04fc --- /dev/null +++ b/buildscripts/phing/classes/phing/system/util/Properties.php @@ -0,0 +1,314 @@ +. + */ + +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/system/io/FileWriter.php'; + +/** + * Convenience class for reading and writing property files. + * + * FIXME + * - Add support for arrays (separated by ',') + * + * @package phing.system.util + * @version $Id: 577374dcb65bb9a2614bc80f605ce49600d64778 $ + */ +class Properties { + + private $properties = array(); + + /** + * @var PhingFile + */ + private $file = null; + + /** + * Constructor + * + * @param array $properties + */ + function __construct($properties = NULL) + { + if (is_array($properties)) { + foreach ($properties as $key => $value) { + $this->setProperty($key, $value); + } + } + } + + /** + * Load properties from a file. + * + * @param PhingFile $file + * @return void + * @throws IOException - if unable to read file. + */ + function load(PhingFile $file) { + if ($file->canRead()) { + $this->parse($file->getPath(), false); + + $this->file = $file; + } else { + throw new IOException("Can not read file ".$file->getPath()); + } + + } + + /** + * Replaces parse_ini_file() or better_parse_ini_file(). + * Saves a step since we don't have to parse and then check return value + * before throwing an error or setting class properties. + * + * @param string $filePath + * @param boolean $processSections Whether to honor [SectionName] sections in INI file. + * @return array Properties loaded from file (no prop replacements done yet). + */ + protected function parse($filePath) { + + // load() already made sure that file is readable + // but we'll double check that when reading the file into + // an array + + if (($lines = @file($filePath)) === false) { + throw new IOException("Unable to parse contents of $filePath"); + } + + $this->properties = array(); + $sec_name = ""; + + foreach($lines as $line) { + // strip comments and leading/trailing spaces + $line = trim(preg_replace("/\s+[;#]\s.+$/", "", $line)); + + if (empty($line) || $line[0] == ';' || $line[0] == '#') { + continue; + } + + $pos = strpos($line, '='); + $property = trim(substr($line, 0, $pos)); + $value = trim(substr($line, $pos + 1)); + $this->properties[$property] = $this->inVal($value); + + } // for each line + } + + /** + * Process values when being read in from properties file. + * does things like convert "true" => true + * @param string $val Trimmed value. + * @return mixed The new property value (may be boolean, etc.) + */ + protected function inVal($val) { + if ($val === "true") { + $val = true; + } elseif ($val === "false") { + $val = false; + } + return $val; + } + + /** + * Process values when being written out to properties file. + * does things like convert true => "true" + * @param mixed $val The property value (may be boolean, etc.) + * @return string + */ + protected function outVal($val) { + if ($val === true) { + $val = "true"; + } elseif ($val === false) { + $val = "false"; + } + return $val; + } + + /** + * Create string representation that can be written to file and would be loadable using load() method. + * + * Essentially this function creates a string representation of properties that is ready to + * write back out to a properties file. This is used by store() method. + * + * @return string + */ + public function toString() { + $buf = ""; + foreach($this->properties as $key => $item) { + $buf .= $key . "=" . $this->outVal($item) . PHP_EOL; + } + return $buf; + } + + /** + * Stores current properties to specified file. + * + * @param PhingFile $file File to create/overwrite with properties. + * @param string $header Header text that will be placed (within comments) at the top of properties file. + * @return void + * @throws IOException - on error writing properties file. + */ + function store(PhingFile $file = null, $header = null) { + if ($file == null) { + $file = $this->file; + } + + if ($file == null) { + throw new IOException("Unable to write to empty filename"); + } + + // stores the properties in this object in the file denoted + // if file is not given and the properties were loaded from a + // file prior, this method stores them in the file used by load() + try { + $fw = new FileWriter($file); + if ($header !== null) { + $fw->write( "# " . $header . PHP_EOL ); + } + $fw->write($this->toString()); + $fw->close(); + } catch (IOException $e) { + throw new IOException("Error writing property file: " . $e->getMessage()); + } + } + + /** + * Returns copy of internal properties hash. + * Mostly for performance reasons, property hashes are often + * preferable to passing around objects. + * + * @return array + */ + function getProperties() { + return $this->properties; + } + + /** + * Get value for specified property. + * This is the same as get() method. + * + * @param string $prop The property name (key). + * @return mixed + * @see get() + */ + function getProperty($prop) { + if (!isset($this->properties[$prop])) { + return null; + } + return $this->properties[$prop]; + } + + /** + * Get value for specified property. + * This function exists to provide a hashtable-like interface for + * properties. + * + * @param string $prop The property name (key). + * @return mixed + * @see getProperty() + */ + function get($prop) { + if (!isset($this->properties[$prop])) { + return null; + } + return $this->properties[$prop]; + } + + /** + * Set the value for a property. + * + * @param string $key + * @param mixed $value + * @return mixed Old property value or NULL if none was set. + */ + function setProperty($key, $value) { + $oldValue = null; + if (isset($this->properties[$key])) { + $oldValue = $this->properties[$key]; + } + $this->properties[$key] = $value; + return $oldValue; + } + + /** + * Set the value for a property. + * This function exists to provide hashtable-lie + * interface for properties. + * + * @param string $key + * @param mixed $value + */ + function put($key, $value) { + return $this->setProperty($key, $value); + } + + /** + * Appends a value to a property if it already exists with a delimiter + * + * If the property does not, it just adds it. + * + * @param string $key + * @param mixed $value + * @param string $delimiter + */ + function append($key, $value, $delimiter = ',') { + $newValue = $value; + if (isset($this->properties[$key]) && !empty($this->properties[$key]) ) { + $newValue = $this->properties[$key] . $delimiter . $value; + } + $this->properties[$key] = $newValue; + } + + /** + * Same as keys() function, returns an array of property names. + * @return array + */ + function propertyNames() { + return $this->keys(); + } + + /** + * Whether loaded properties array contains specified property name. + * @return boolean + */ + function containsKey($key) { + return isset($this->properties[$key]); + } + + /** + * Returns properties keys. + * Use this for foreach() {} iterations, as this is + * faster than looping through property values. + * @return array + */ + function keys() { + return array_keys($this->properties); + } + + /** + * Whether properties list is empty. + * @return boolean + */ + function isEmpty() { + return empty($this->properties); + } + +} + diff --git a/buildscripts/phing/classes/phing/system/util/Register.php b/buildscripts/phing/classes/phing/system/util/Register.php new file mode 100755 index 00000000..56ab0e45 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/util/Register.php @@ -0,0 +1,149 @@ + + * + * + * + * The task/type must provide a supporting setter for the attribute: + * + * + * function setListeningReplace(RegisterSlot $slot) { + * $this->replace = $slot; + * } + * + * // in main() + * if ($this->replace instanceof RegisterSlot) { + * $this->regexp->setReplace($this->replace->getValue()); + * } else { + * $this->regexp->setReplace($this->replace); + * } + * + * + * @author Hans Lellelid + * @version $Id$ + * @package phing.system.util + */ +class Register { + + /** Slots that have been registered */ + private static $slots = array(); + + /** + * Returns RegisterSlot for specified key. + * + * If not slot exists a new one is created for key. + * + * @param string $key + * @return RegisterSlot + */ + public static function getSlot($key) { + if (!isset(self::$slots[$key])) { + self::$slots[$key] = new RegisterSlot($key); + } + return self::$slots[$key]; + } +} + + +/** + * Represents a slot in the register. + * + * @package phing.system.util + */ +class RegisterSlot { + + /** The name of this slot. */ + private $key; + + /** The value for this slot. */ + private $value; + + /** + * Constructs a new RegisterSlot, setting the key to passed param. + * @param string $key + */ + public function __construct($key) { + $this->key = (string) $key; + } + + /** + * Sets the key / name for this slot. + * @param string $k + */ + public function setKey($k) { + $this->key = (string) $k; + } + + /** + * Gets the key / name for this slot. + * @return string + */ + public function getKey() { + return $this->key; + } + + /** + * Sets the value for this slot. + * @param mixed + */ + public function setValue($v) { + $this->value = $v; + } + + /** + * Returns the value at this slot. + * @return mixed + */ + public function getValue() { + return $this->value; + } + + /** + * Recursively implodes an array to a comma-separated string + * @param array $arr + * @return string + */ + private function implodeArray(array $arr) { + $values = array(); + + foreach ($arr as $value) { + if (is_array($value)) { + $values[] = $this->implodeArray($value); + } else { + $values[] = $value; + } + } + + return "{" . implode(",", $values) . "}"; + } + + /** + * Returns the value at this slot as a string value. + * @return string + */ + public function __toString() + { + if (is_array($this->value)) { + return $this->implodeArray($this->value); + } else { + return (string) $this->value; + } + } + +} + diff --git a/buildscripts/phing/classes/phing/system/util/Timer.php b/buildscripts/phing/classes/phing/system/util/Timer.php new file mode 100755 index 00000000..2d2bcc01 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/util/Timer.php @@ -0,0 +1,96 @@ +. + */ + + +/** + * This class can be used to obtain the execution time of all of the scripts + * that are executed in the process of building a page. + * + * Example: + * To be done before any scripts execute: + * + * $Timer = new Timer; + * $Timer->Start_Timer(); + * + * To be done after all scripts have executed: + * + * $timer->Stop_Timer(); + * $timer->Get_Elapsed_Time(int number_of_places); + * + * @author Charles Killian + * @author Hans Lellelid + * @package phing.system.util + * @version $Id$ + */ +class Timer { + + /** start time */ + protected $stime; + + /** end time */ + protected $etime; + + /** + * This function sets the class variable $stime to the current time in + * microseconds. + * @return void + */ + public function start() { + $this->stime = $this->getMicrotime(); + } + + /** + * This function sets the class variable $etime to the current time in + * microseconds. + * @return void + */ + function stop() { + $this->etime = $this->getMicrotime(); + } + + /** + * This function returns the elapsed time in seconds. + * + * Call start_time() at the beginning of script execution and end_time() at + * the end of script execution. Then, call elapsed_time() to obtain the + * difference between start_time() and end_time(). + * + * @param $places decimal place precision of elapsed time (default is 5) + * @return string Properly formatted time. + */ + function getElapsedTime($places=5) { + $etime = $this->etime - $this->stime; + $format = "%0.".$places."f"; + return (sprintf ($format, $etime)); + } + + /** + * This function returns the current time in microseconds. + * + * @author Everett Michaud, Zend.com + * @return current time in microseconds + * @access private + */ + function getMicrotime() { + list($usec, $sec) = explode(" ", microtime()); + return ((float)$usec + (float)$sec); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/defaults.properties b/buildscripts/phing/classes/phing/tasks/defaults.properties new file mode 100755 index 00000000..32a03295 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/defaults.properties @@ -0,0 +1,145 @@ +; ------------------------------------- +; These taskdefs are loaded at startup. +; ------------------------------------- + +; Internal system tasks +; +adhoc=phing.tasks.system.AdhocTask +adhoc-task=phing.tasks.system.AdhocTaskdefTask +adhoc-type=phing.tasks.system.AdhocTypedefTask +append=phing.tasks.system.AppendTask +available=phing.tasks.system.AvailableTask +chmod=phing.tasks.system.ChmodTask +chown=phing.tasks.system.ChownTask +concat=phing.tasks.system.AppendTask +condition=phing.tasks.system.ConditionTask +copy=phing.tasks.system.CopyTask +cvs=phing.tasks.system.CvsTask +cvspass=phing.tasks.system.CvsPassTask +delete=phing.tasks.system.DeleteTask +echo=phing.tasks.system.EchoTask +exec=phing.tasks.system.ExecTask +fail=phing.tasks.system.FailTask +foreach=phing.tasks.system.ForeachTask +includepath=phing.tasks.system.IncludePathTask +input=phing.tasks.system.InputTask +mkdir=phing.tasks.system.MkdirTask +move=phing.tasks.system.MoveTask +phing=phing.tasks.system.PhingTask +phingcall=phing.tasks.system.PhingCallTask +php=phing.tasks.system.PhpEvalTask +property=phing.tasks.system.PropertyTask +propertyprompt=phing.tasks.system.PropertyPromptTask +reflexive=phing.tasks.system.ReflexiveTask +resolvepath=phing.tasks.system.ResolvePathTask +taskdef=phing.tasks.system.TaskdefTask +touch=phing.tasks.system.TouchTask +tstamp=phing.tasks.system.TstampTask +typedef=phing.tasks.system.TypedefTask +uptodate=phing.tasks.system.UpToDateTask +xslt=phing.tasks.system.XsltTask +if=phing.tasks.system.IfTask +warn=phing.tasks.system.WarnTask +import=phing.tasks.system.ImportTask +loadfile=phing.tasks.system.LoadFileTask + +; "Core" contributed tasks +; -- i.e. no taskdef needed. + +creole=phing.tasks.ext.creole.CreoleSQLExecTask +pdo=phing.tasks.ext.pdo.PDOSQLExecTask +pdosqlexec=phing.tasks.ext.pdo.PDOSQLExecTask +package-as-path=phing.tasks.ext.PackageAsPathTask +smarty=phing.tasks.ext.SmartyTask +capsule=phing.tasks.ext.CapsuleTask +tar=phing.tasks.ext.TarTask +untar=phing.tasks.ext.UntarTask +pearpkg=phing.tasks.ext.PearPackageTask +pearpkg2=phing.tasks.ext.PearPackage2Task +mail=phing.tasks.ext.MailTask +zip=phing.tasks.ext.ZipTask +unzip=phing.tasks.ext.UnzipTask +waitfor=phing.tasks.system.WaitForTask +trycatch=phing.tasks.system.TryCatchTask + +; "ext" tasks +phpdoc=phing.tasks.ext.phpdoc.PhpDocumentorTask +phpdocext=phing.tasks.ext.phpdoc.PhpDocumentorExternalTask +svnlastrevision=phing.tasks.ext.svn.SvnLastRevisionTask +svncheckout=phing.tasks.ext.svn.SvnCheckoutTask +svnexport=phing.tasks.ext.svn.SvnExportTask +svnupdate=phing.tasks.ext.svn.SvnUpdateTask +svnswitch=phing.tasks.ext.svn.SvnSwitchTask +svncopy=phing.tasks.ext.svn.SvnCopyTask +svncommit=phing.tasks.ext.svn.SvnCommitTask +svnlist=phing.tasks.ext.svn.SvnListTask +svnlog=phing.tasks.ext.svn.SvnLogTask +svninfo=phing.tasks.ext.svn.SvnInfoTask +gitinit=phing.tasks.ext.git.GitInitTask +gitclone=phing.tasks.ext.git.GitCloneTask +gitgc=phing.tasks.ext.git.GitGcTask +gitbranch=phing.tasks.ext.git.GitBranchTask +gitfetch=phing.tasks.ext.git.GitFetchTask +gitmerge=phing.tasks.ext.git.GitMergeTask +gitcheckout=phing.tasks.ext.git.GitCheckoutTask +gitpull=phing.tasks.ext.git.GitPullTask +gitpush=phing.tasks.ext.git.GitPushTask +gitlog=phing.tasks.ext.git.GitLogTask +gittag=phing.tasks.ext.git.GitTagTask +phpunit3=phing.tasks.ext.phpunit.PHPUnitTask +phpunit3report=phing.tasks.ext.phpunit.PHPUnitReportTask +phpunit=phing.tasks.ext.phpunit.PHPUnitTask +phpunitreport=phing.tasks.ext.phpunit.PHPUnitReportTask +coverage-setup=phing.tasks.ext.coverage.CoverageSetupTask +coverage-merger=phing.tasks.ext.coverage.CoverageMergerTask +coverage-report=phing.tasks.ext.coverage.CoverageReportTask +coverage-threshold=phing.tasks.ext.coverage.CoverageThresholdTask +ioncubeencoder=phing.tasks.ext.ioncube.IoncubeEncoderTask +ioncubelicense=phing.tasks.ext.ioncube.IoncubeLicenseTask +simpletest=phing.tasks.ext.simpletest.SimpleTestTask +phplint=phing.tasks.ext.PhpLintTask +xmllint=phing.tasks.ext.XmlLintTask +analyze=phing.tasks.ext.ZendCodeAnalyzerTask +zendcodeanalyzer=phing.tasks.ext.ZendCodeAnalyzerTask +jsllint=phing.tasks.ext.JslLintTask +manifest=phing.tasks.ext.ManifestTask +phpcodesniffer=phing.tasks.ext.PhpCodeSnifferTask +phpcpd=phing.tasks.ext.phpcpd.PHPCPDTask +phpmd=phing.tasks.ext.phpmd.PHPMDTask +phpdepend=phing.tasks.ext.pdepend.PhpDependTask +ftpdeploy=phing.tasks.ext.FtpDeployTask +phkpackage=phing.tasks.ext.phk.PhkPackageTask +pharpackage=phing.tasks.ext.phar.PharPackageTask +scp=phing.tasks.ext.ScpTask +; deprecate ScpSendTask +scpsend=phing.tasks.ext.ScpTask +ssh=phing.tasks.ext.SshTask +replaceregexp=phing.tasks.ext.ReplaceRegexpTask +jsmin=phing.tasks.ext.jsmin.JsMinTask +version=phing.tasks.ext.VersionTask +filehash=phing.tasks.ext.FileHashTask +filesize=phing.tasks.ext.FileSizeTask +xmlproperty=phing.tasks.ext.XmlPropertyTask +exportproperties=phing.tasks.ext.ExportPropertiesTask +http-request=phing.tasks.ext.HttpRequestTask +httpget=phing.tasks.ext.HttpGetTask +patch=phing.tasks.ext.PatchTask +dbdeploy=phing.tasks.ext.dbdeploy.DbDeployTask +symlink=phing.tasks.ext.SymlinkTask +s3get=phing.tasks.ext.Service.Amazon.S3.S3GetTask +s3put=phing.tasks.ext.Service.Amazon.S3.S3PutTask +zendguardencode=phing.tasks.ext.zendguard.ZendGuardEncodeTask +zendguardlicense=phing.tasks.ext.zendguard.ZendGuardLicenseTask +docblox=phing.tasks.ext.docblox.DocBloxTask +phpdoc2=phing.tasks.ext.phpdoc.PhpDocumentor2Task +rST=phing.tasks.ext.rSTTask +apigen=phing.tasks.ext.apigen.ApiGenTask +parallel=phing.tasks.ext.ParallelTask +symfonyconsole=phing.tasks.ext.SymfonyConsole.SymfonyConsoleTask +; liquibase +liquibase-changelog=phing.tasks.ext.liquibase.LiquibaseChangeLogTask +liquibase-dbdoc=phing.tasks.ext.liquibase.LiquibaseDbDocTask +liquibase-diff=phing.tasks.ext.liquibase.LiquibaseDiffTask +liquibase-rollback=phing.tasks.ext.liquibase.LiquibaseRollbackTask +liquibase-tag=phing.tasks.ext.liquibase.LiquibaseTagTask +liquibase-update=phing.tasks.ext.liquibase.LiquibaseUpdateTask diff --git a/buildscripts/phing/classes/phing/tasks/ext/CapsuleTask.php b/buildscripts/phing/classes/phing/tasks/ext/CapsuleTask.php new file mode 100644 index 00000000..13ccd73d --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/CapsuleTask.php @@ -0,0 +1,480 @@ +. + */ + +include_once 'phing/Task.php'; +include_once 'phing/BuildException.php'; +include_once 'phing/lib/Capsule.php'; +include_once 'phing/util/StringHelper.php'; + +/** + * A phing task for generating output by using Capsule. + * + * This is based on the interface to TexenTask from Apache's Velocity engine. + * + * @author Hans Lellelid + * @version $Id: 205bc55fd1f7f36783d105ff2d0e27357282bbed $ + * @package phing.tasks.ext + */ +class CapsuleTask extends Task { + + /** + * Capsule "template" engine. + * @var Capsule + */ + protected $context; + + /** + * Any vars assigned via the build file. + * @var array AssignedVar[] + */ + protected $assignedVars = array(); + + /** + * This is the control template that governs the output. + * It may or may not invoke the services of worker + * templates. + * @var string + */ + protected $controlTemplate; + + /** + * This is where Velocity will look for templates + * using the file template loader. + * @var string + */ + protected $templatePath; + + /** + * This is where texen will place all the output + * that is a product of the generation process. + * @var string + */ + protected $outputDirectory; + + /** + * This is the file where the generated text + * will be placed. + * @var string + */ + protected $outputFile; + + /** + *

+ * These are properties that are fed into the + * initial context from a properties file. This + * is simply a convenient way to set some values + * that you wish to make available in the context. + *

+ *

+ * These values are not critical, like the template path + * or output path, but allow a convenient way to + * set a value that may be specific to a particular + * generation task. + *

+ *

+ * For example, if you are generating scripts to allow + * user to automatically create a database, then + * you might want the $databaseName + * to be placed + * in the initial context so that it is available + * in a script that might look something like the + * following: + *

+     * #!bin/sh
+     * 
+     * echo y | mysqladmin create $databaseName
+     * 
+ * The value of $databaseName isn't critical to + * output, and you obviously don't want to change + * the ant task to simply take a database name. + * So initial context values can be set with + * properties file. + * + * @var array + */ + protected $contextProperties; + + // ----------------------------------------------------------------------- + // The following getters & setters are used by phing to set properties + // specified in the XML for the capsule task. + // ----------------------------------------------------------------------- + + /** + * [REQUIRED] Set the control template for the + * generating process. + * @param string $controlTemplate + * @return void + */ + public function setControlTemplate ($controlTemplate) { + $this->controlTemplate = $controlTemplate; + } + + /** + * Get the control template for the + * generating process. + * @return string + */ + public function getControlTemplate() { + return $this->controlTemplate; + } + + /** + * [REQUIRED] Set the path where Velocity will look + * for templates using the file template + * loader. + * @return void + * @throws Exception + */ + public function setTemplatePath($templatePath) { + $resolvedPath = ""; + $tok = strtok($templatePath, ","); + while ( $tok ) { + // resolve relative path from basedir and leave + // absolute path untouched. + $fullPath = $this->project->resolveFile($tok); + $cpath = $fullPath->getCanonicalPath(); + if ($cpath === false) { + $this->log("Template directory does not exist: " . $fullPath->getAbsolutePath()); + } else { + $resolvedPath .= $cpath; + } + $tok = strtok(","); + if ( $tok ) { + $resolvedPath .= ","; + } + } + $this->templatePath = $resolvedPath; + } + + /** + * Get the path where Velocity will look + * for templates using the file template + * loader. + * @return string + */ + public function getTemplatePath() { + return $this->templatePath; + } + + /** + * [REQUIRED] Set the output directory. It will be + * created if it doesn't exist. + * @param PhingFile $outputDirectory + * @return void + * @throws Exception + */ + public function setOutputDirectory(PhingFile $outputDirectory) { + try { + if (!$outputDirectory->exists()) { + $this->log("Output directory does not exist, creating: " . $outputDirectory->getPath(),Project::MSG_VERBOSE); + if (!$outputDirectory->mkdirs()) { + throw new IOException("Unable to create Ouptut directory: " . $outputDirectory->getAbsolutePath()); + } + } + $this->outputDirectory = $outputDirectory->getCanonicalPath(); + } catch (IOException $ioe) { + throw new BuildException($ioe); + } + } + + /** + * Get the output directory. + * @return string + */ + public function getOutputDirectory() { + return $this->outputDirectory; + } + + /** + * [REQUIRED] Set the output file for the + * generation process. + * @param string $outputFile (TODO: change this to File) + * @return void + */ + public function setOutputFile($outputFile) { + $this->outputFile = $outputFile; + } + + /** + * Get the output file for the + * generation process. + * @return string + */ + public function getOutputFile() { + return $this->outputFile; + } + + /** + * Set the context properties that will be + * fed into the initial context be the + * generating process starts. + * @param string $file + * @return void + */ + public function setContextProperties($file) { + $sources = explode(",", $file); + $this->contextProperties = new Properties(); + + // Always try to get the context properties resource + // from a file first. Templates may be taken from a JAR + // file but the context properties resource may be a + // resource in the filesystem. If this fails than attempt + // to get the context properties resource from the + // classpath. + for ($i=0, $sourcesLength=count($sources); $i < $sourcesLength; $i++) { + $source = new Properties(); + + try { + + // resolve relative path from basedir and leave + // absolute path untouched. + $fullPath = $this->project->resolveFile($sources[$i]); + $this->log("Using contextProperties file: " . $fullPath->toString()); + $source->load($fullPath); + + } catch (Exception $e) { + + throw new BuildException("Context properties file " . $sources[$i] . + " could not be found in the file system!"); + + } + + $keys = $source->keys(); + + foreach ($keys as $key) { + $name = $key; + $value = $this->project->replaceProperties($source->getProperty($name)); + $this->contextProperties->setProperty($name, $value); + } + } + } + + /** + * Get the context properties that will be + * fed into the initial context be the + * generating process starts. + * @return Properties + */ + public function getContextProperties() { + return $this->contextProperties; + } + + /** + * Creates an "AssignedVar" class. + */ + public function createAssign() { + $a = new AssignedVar(); + $this->assignedVars[] = $a; + return $a; + } + + // --------------------------------------------------------------- + // End of XML setters & getters + // --------------------------------------------------------------- + + /** + * Creates a Smarty object. + * + * @return Smarty initialized (cleared) Smarty context. + * @throws Exception the execute method will catch + * and rethrow as a BuildException + */ + public function initControlContext() { + $this->context->clear(); + foreach($this->assignedVars as $var) { + $this->context->put($var->getName(), $var->getValue()); + } + return $this->context; + } + + /** + * Execute the input script with Velocity + * + * @throws BuildException + * BuildExceptions are thrown when required attributes are missing. + * Exceptions thrown by Velocity are rethrown as BuildExceptions. + */ + public function main() { + + // Make sure the template path is set. + if (empty($this->templatePath)) { + throw new BuildException("The template path needs to be defined!"); + } + + // Make sure the control template is set. + if ($this->controlTemplate === null) { + throw new BuildException("The control template needs to be defined!"); + } + + // Make sure the output directory is set. + if ($this->outputDirectory === null) { + throw new BuildException("The output directory needs to be defined!"); + } + + // Make sure there is an output file. + if ($this->outputFile === null) { + throw new BuildException("The output file needs to be defined!"); + } + + // Setup Smarty runtime. + + // Smarty uses one object to store properties and to store + // the context for the template (unlike Velocity). We setup this object, calling it + // $this->context, and then initControlContext simply zeros out + // any assigned variables. + $this->context = new Capsule(); + + if ($this->templatePath !== null) { + $this->log("Using templatePath: " . $this->templatePath); + $this->context->setTemplatePath($this->templatePath); + } + + // Make sure the output directory exists, if it doesn't + // then create it. + $outputDir = new PhingFile($this->outputDirectory); + if (!$outputDir->exists()) { + $this->log("Output directory does not exist, creating: " . $outputDir->getAbsolutePath()); + $outputDir->mkdirs(); + } + + $this->context->setOutputDirectory($outputDir->getAbsolutePath()); + + $path = $this->outputDirectory . DIRECTORY_SEPARATOR . $this->outputFile; + $this->log("Generating to file " . $path); + + //$writer = new FileWriter($path); + + // The generator and the output path should + // be placed in the init context here and + // not in the generator class itself. + $c = $this->initControlContext(); + + // Set any variables that need to always + // be loaded + $this->populateInitialContext($c); + + // Feed all the options into the initial + // control context so they are available + // in the control/worker templates. + if ($this->contextProperties !== null) { + + foreach($this->contextProperties->keys() as $property) { + + $value = $this->contextProperties->getProperty($property); + + // Special exception (from Texen) + // for properties ending in file.contents: + // in that case we dump the contents of the file + // as the "value" for the Property. + if (preg_match('/file\.contents$/', $property)) { + // pull in contents of file specified + + $property = substr($property, 0, strpos($property, "file.contents") - 1); + + // reset value, and then + // read in teh contents of the file into that var + $value = ""; + $f = new PhingFile($this->project->resolveFile($value)->getCanonicalPath()); + if ($f->exists()) { + $fr = new FileReader($f); + $fr->readInto($value); + } + + } // if ends with file.contents + + if (StringHelper::isBoolean($value)) { + $value = StringHelper::booleanValue($value); + } + + $c->put($property, $value); + + } // foreach property + + } // if contextProperties !== null + + try { + $this->log("Parsing control template: " . $this->controlTemplate); + $c->parse($this->controlTemplate, $path); + } catch (Exception $ioe) { + throw new BuildException("Cannot write parsed template: ". $ioe->getMessage()); + } + + $this->cleanup(); + } + + /** + * Place useful objects into the initial context. + * + * + * @param Capsule $context The context to populate, as retrieved from + * {@link #initControlContext()}. + * @return void + * @throws Exception Error while populating context. The {@link + * #main()} method will catch and rethrow as a + * BuildException. + */ + protected function populateInitialContext(Capsule $context) { + $this->context->put("now", strftime("%c", time())); + $this->context->put("task", $this); + } + + /** + * A hook method called at the end of {@link #execute()} which can + * be overridden to perform any necessary cleanup activities (such + * as the release of database connections, etc.). By default, + * does nothing. + * @return void + * @throws Exception Problem cleaning up. + */ + protected function cleanup() { + } +} + + +/** + * An "inner" class for holding assigned var values. + * May be need to expand beyond name/value in the future. + * + * @package phing.tasks.ext + */ +class AssignedVar { + + private $name; + private $value; + + public function setName($v) { + $this->name = $v; + } + + public function setValue($v) { + $this->value = $v; + } + + public function getName() { + return $this->name; + } + + public function getValue() { + return $this->value; + } + +} \ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/ExportPropertiesTask.php b/buildscripts/phing/classes/phing/tasks/ext/ExportPropertiesTask.php new file mode 100644 index 00000000..8bc64bbb --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ExportPropertiesTask.php @@ -0,0 +1,141 @@ +. + */ + +require_once "phing/Task.php"; + +/** + * Saves currently defined properties into a specified file + * + * @author Andrei Serdeliuc + * @extends Task + * @version $Id: 7d96a453b74edc40fdea85ba8befe6459334016d $ + * @package phing.tasks.ext + */ +class ExportPropertiesTask extends Task +{ + /** + * Array of project properties + * + * (default value: null) + * + * @var array + * @access private + */ + private $_properties = null; + + /** + * Target file for saved properties + * + * (default value: null) + * + * @var string + * @access private + */ + private $_targetFile = null; + + /** + * Exclude properties starting with these prefixes + * + * @var array + * @access private + */ + private $_disallowedPropertyPrefixes = array( + 'host.', + 'phing.', + 'os.', + 'php.', + 'line.', + 'env.', + 'user.' + ); + + /** + * setter for _targetFile + * + * @access public + * @param string $file + * @return bool + */ + public function setTargetFile($file) + { + if(!is_dir(dirname($file))) { + throw new BuildException("Parent directory of target file doesn't exist"); + } + + if(!is_writable(dirname($file)) && (file_exists($file) && !is_writable($file))) { + throw new BuildException("Target file isn't writable"); + } + + $this->_targetFile = $file; + return true; + } + + /** + * setter for _disallowedPropertyPrefixes + * + * @access public + * @param string $file + * @return bool + */ + public function setDisallowedPropertyPrefixes($prefixes) + { + $this->_disallowedPropertyPrefixes = explode(",", $prefixes); + return true; + } + + public function main() + { + // Sets the currently declared properties + $this->_properties = $this->getProject()->getProperties(); + + if(is_array($this->_properties) && !empty($this->_properties) && null !== $this->_targetFile) { + $propertiesString = ''; + foreach($this->_properties as $propertyName => $propertyValue) { + if(!$this->isDisallowedPropery($propertyName)) { + $propertiesString .= $propertyName . "=" . $propertyValue . PHP_EOL; + } + } + + if(!file_put_contents($this->_targetFile, $propertiesString)) { + throw new BuildException('Failed writing to ' . $this->_targetFile); + } + } + } + + /** + * Checks if a property name is disallowed + * + * @access protected + * @param string $propertyName + * @return bool + */ + protected function isDisallowedPropery($propertyName) + { + foreach($this->_disallowedPropertyPrefixes as $property) { + if(substr($propertyName, 0, strlen($property)) == $property) { + return true; + } + } + + return false; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/ExtractBaseTask.php b/buildscripts/phing/classes/phing/tasks/ext/ExtractBaseTask.php new file mode 100644 index 00000000..e47acc24 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ExtractBaseTask.php @@ -0,0 +1,199 @@ +. + */ + +require_once 'phing/tasks/system/MatchingTask.php'; + +/** + * Base class for extracting tasks such as Unzip and Untar. + * + * @author Joakim Bodin + * @version $Id: 8aa7996b72792da30f1ec94174f09b9c612bcc1a $ + * @package phing.tasks.ext + * @since 2.2.0 + */ +abstract class ExtractBaseTask extends MatchingTask { + /** + * @var PhingFile $file + */ + protected $file; + /** + * @var PhingFile $todir + */ + protected $todir; + protected $removepath; + protected $filesets = array(); // all fileset objects assigned to this task + + /** + * Set to true to always extract (and possibly overwrite) + * all files from the archive + * @var boolean + */ + protected $forceExtract = false; + + /** + * Add a new fileset. + * @return FileSet + */ + public function createFileSet() { + $this->fileset = new FileSet(); + $this->filesets[] = $this->fileset; + return $this->fileset; + } + + /** + * Set the name of the zip file to extract. + * @param PhingFile $file zip file to extract + */ + public function setFile(PhingFile $file) { + $this->file = $file; + } + + /** + * This is the base directory to look in for things to zip. + * @param PhingFile $baseDir + */ + public function setToDir(PhingFile $todir) { + $this->todir = $todir; + } + + public function setRemovePath($removepath) + { + $this->removepath = $removepath; + } + + /** + * Sets the forceExtract attribute + * @param boolean $forceExtract + */ + public function setForceExtract($forceExtract) + { + $this->forceExtract = (bool) $forceExtract; + } + + /** + * do the work + * @throws BuildException + */ + public function main() { + + $this->validateAttributes(); + + $filesToExtract = array(); + if ($this->file !== null) { + if(!$this->isDestinationUpToDate($this->file)) { + $filesToExtract[] = $this->file; + } else { + $this->log('Nothing to do: ' . $this->todir->getAbsolutePath() . ' is up to date for ' . $this->file->getCanonicalPath(), Project::MSG_INFO); + } + } + + foreach($this->filesets as $compressedArchiveFileset) { + $compressedArchiveDirScanner = $compressedArchiveFileset->getDirectoryScanner($this->project); + $compressedArchiveFiles = $compressedArchiveDirScanner->getIncludedFiles(); + $compressedArchiveDir = $compressedArchiveFileset->getDir($this->project); + + foreach ($compressedArchiveFiles as $compressedArchiveFilePath) { + $compressedArchiveFile = new PhingFile($compressedArchiveDir, $compressedArchiveFilePath); + if($compressedArchiveFile->isDirectory()) + { + throw new BuildException($compressedArchiveFile->getAbsolutePath() . ' compressed archive cannot be a directory.'); + } + + if ($this->forceExtract || !$this->isDestinationUpToDate($compressedArchiveFile)) { + $filesToExtract[] = $compressedArchiveFile; + } else { + $this->log('Nothing to do: ' . $this->todir->getAbsolutePath() . ' is up to date for ' . $compressedArchiveFile->getCanonicalPath(), Project::MSG_INFO); + } + } + } + + foreach ($filesToExtract as $compressedArchiveFile) { + $this->extractArchive($compressedArchiveFile); + } + } + + abstract protected function extractArchive(PhingFile $compressedArchiveFile); + + /** + * @param array $files array of filenames + * @param PhingFile $dir + * @return boolean + */ + protected function isDestinationUpToDate(PhingFile $compressedArchiveFile) { + if (!$compressedArchiveFile->exists()) { + throw new BuildException("Could not find file " . $compressedArchiveFile->__toString() . " to extract."); + } + + $compressedArchiveContent = $this->listArchiveContent($compressedArchiveFile); + if(is_array($compressedArchiveContent)) { + + $fileSystem = FileSystem::getFileSystem(); + foreach ($compressedArchiveContent as $compressArchivePathInfo) { + $compressArchiveFilename = $compressArchivePathInfo['filename']; + if(!empty($this->removepath) && strlen($compressArchiveFilename) >= strlen($this->removepath)) + { + $compressArchiveFilename = preg_replace('/^' . $this->removepath . '/','', $compressArchiveFilename); + } + $compressArchivePath = new PhingFile($this->todir, $compressArchiveFilename); + + if(!$compressArchivePath->exists() || + $fileSystem->compareMTimes($compressedArchiveFile->getCanonicalPath(), $compressArchivePath->getCanonicalPath()) == 1) { + return false; + } + } + + } + + return true; + } + + abstract protected function listArchiveContent(PhingFile $compressedArchiveFile); + + /** + * Validates attributes coming in from XML + * + * @access private + * @return void + * @throws BuildException + */ + protected function validateAttributes() { + + if ($this->file === null && count($this->filesets) === 0) { + throw new BuildException("Specify at least one source compressed archive - a file or a fileset."); + } + + if ($this->todir === null) { + throw new BuildException("todir must be set."); + } + + if ($this->todir !== null && $this->todir->exists() && !$this->todir->isDirectory()) { + throw new BuildException("todir must be a directory."); + } + + if ($this->file !== null && $this->file->exists() && $this->file->isDirectory()) { + throw new BuildException("Compressed archive file cannot be a directory."); + } + + if ($this->file !== null && !$this->file->exists()) { + throw new BuildException("Could not find compressed archive file " . $this->file->__toString() . " to extract."); + } + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/FileHashTask.php b/buildscripts/phing/classes/phing/tasks/ext/FileHashTask.php new file mode 100644 index 00000000..5660e793 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/FileHashTask.php @@ -0,0 +1,147 @@ +. + */ +require_once 'phing/Task.php'; + +/** + * fileHash + * + * Calculate either MD5 or SHA hash value of a specified file and retun the + * value in a property + * + * @author Johan Persson + * @version $Id: c59aff266a03f0e2cf22dc33143f2decf2d5e05c $ + * @package phing.tasks.ext + */ +class FileHashTask extends Task +{ + /** + * Property for File + * @var PhingFile file + */ + private $file; + + /** + * Property to be set + * @var string $property + */ + private $propertyName = "filehashvalue"; + + /** + * Specify which hash algorithm to use. + * 0 = MD5 + * 1 = SHA1 + * + * @var integer $hashtype + */ + private $hashtype=0; + + + /** + * Specify if MD5 or SHA1 hash should be used + * @param integer $type 0=MD5, 1=SHA1 + */ + public function setHashtype($type) + { + $this->hashtype = $type; + } + + /** + * Which file to calculate the hash value of + * @param PhingFile $file + */ + public function setFile($file) + { + $this->file = $file; + } + + /** + * Set the name of the property to store the hash value in + * @param $property + * @return void + */ + public function setPropertyName($property) + { + $this->propertyName = $property; + } + + /** + * Main-Method for the Task + * + * @return void + * @throws BuildException + */ + public function main() + { + $this->checkFile(); + $this->checkPropertyName(); + + // read file + if( (int)$this->hashtype === 0 ) { + $this->log("Calculating MD5 hash from: ".$this->file); + $hashValue = md5_file($this->file,false); + } + elseif( (int)$this->hashtype === 1 ) { + $this->log("Calculating SHA1 hash from: ".$this->file); + $hashValue = sha1_file($this->file,false); + } + else { + throw new BuildException( + sprintf('[FileHash] Unknown hashtype specified %d. Must be either 0 (=MD5) or 1 (=SHA1).',$this->hashtype)); + + } + + // publish hash value + $this->project->setProperty($this->propertyName, $hashValue); + + } + + /** + * checks file attribute + * @return void + * @throws BuildException + */ + private function checkFile() + { + // check File + if ($this->file === null || + strlen($this->file) == 0) { + throw new BuildException('[FileHash] You must specify an input file.', $this->file); + } + + if( ! is_readable($this->file) ) { + throw new BuildException(sprintf('[FileHash] Input file does not exist or is not readable: %s',$this->file)); + } + + } + + /** + * checks property attribute + * @return void + * @throws BuildException + */ + private function checkPropertyName() + { + if (is_null($this->propertyName) || + strlen($this->propertyName) === 0) { + throw new BuildException('Property name for publishing hashvalue is not set'); + } + } +} \ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/FileSizeTask.php b/buildscripts/phing/classes/phing/tasks/ext/FileSizeTask.php new file mode 100644 index 00000000..120197dd --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/FileSizeTask.php @@ -0,0 +1,120 @@ +. + */ +require_once 'phing/Task.php'; + +/** + * fileHash + * + * Calculate either MD5 or SHA hash value of a specified file and retun the + * value in a property + * + * @author Johan Persson + * @version $Id: 2a59c1a9b46f3fd71df0fd3b50908eff268fd630 $ + * @package phing.tasks.ext + */ +class FileSizeTask extends Task +{ + /** + * Property for File + * @var PhingFile file + */ + private $file; + + /** + * Property where the file size will be stored + * @var string $property + */ + private $propertyName = "filesize"; + + /** + * Which file to calculate the file size of + * @param PhingFile $file + */ + public function setFile($file) + { + $this->file = $file; + } + + /** + * Set the name of the property to store the file size + * @param $property + * @return void + */ + public function setPropertyName($property) + { + $this->propertyName = $property; + } + + /** + * Main-Method for the Task + * + * @return void + * @throws BuildException + */ + public function main() + { + $this->checkFile(); + $this->checkPropertyName(); + + $size = filesize($this->file); + + if( $size === false ) { + throw new BuildException(sprintf('[FileSize] Cannot determine size of file: %s',$this->file)); + + } + + // publish hash value + $this->project->setProperty($this->propertyName, $size); + + } + + /** + * checks file attribute + * @return void + * @throws BuildException + */ + private function checkFile() + { + // check File + if ($this->file === null || + strlen($this->file) == 0) { + throw new BuildException('[FileSize] You must specify an input file.', $this->file); + } + + if( ! is_readable($this->file) ) { + throw new BuildException(sprintf('[FileSize] Input file does not exist or is not readable: %s',$this->file)); + } + + } + + /** + * checks property attribute + * @return void + * @throws BuildException + */ + private function checkPropertyName() + { + if (is_null($this->propertyName) || + strlen($this->propertyName) === 0) { + throw new BuildException('[FileSize] Property name for publishing file size is not set'); + } + } +} \ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/FtpDeployTask.php b/buildscripts/phing/classes/phing/tasks/ext/FtpDeployTask.php new file mode 100644 index 00000000..136c0ac7 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/FtpDeployTask.php @@ -0,0 +1,233 @@ +. + */ + +require_once 'phing/Task.php'; + +/** + * FtpDeployTask + * + * Deploys a set of files to a remote FTP server. + * + * + * Example usage: + * + * + * + * + * + * + * + * + * + * + * + * @author Jorrit Schippers + * @version $Id: 87063ecf88b18eae74c2bca3918a1b4ac9f52807 $ + * @since 2.3.1 + * @package phing.tasks.ext + */ +class FtpDeployTask extends Task +{ + private $host = null; + private $port = 21; + private $username = null; + private $password = null; + private $dir = null; + private $filesets; + private $completeDirMap; + private $mode = FTP_BINARY; + private $clearFirst = false; + private $passive = false; + + protected $logLevel = Project::MSG_VERBOSE; + + public function __construct() { + $this->filesets = array(); + $this->completeDirMap = array(); + } + + public function setHost($host) { + $this->host = $host; + } + + public function setPort($port) { + $this->port = (int) $port; + } + + public function setUsername($username) { + $this->username = $username; + } + + public function setPassword($password) { + $this->password = $password; + } + + public function setDir($dir) { + $this->dir = $dir; + } + + public function setMode($mode) { + switch(strtolower($mode)) { + case 'ascii': + $this->mode = FTP_ASCII; + break; + case 'binary': + case 'bin': + $this->mode = FTP_BINARY; + break; + } + } + + public function setPassive($passive) + { + $this->passive = (bool) $passive; + } + + public function setClearFirst($clearFirst) { + $this->clearFirst = (bool) $clearFirst; + } + + public function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Set level of log messages generated (default = info) + * @param string $level + */ + public function setLevel($level) + { + switch ($level) + { + case "error": $this->logLevel = Project::MSG_ERR; break; + case "warning": $this->logLevel = Project::MSG_WARN; break; + case "info": $this->logLevel = Project::MSG_INFO; break; + case "verbose": $this->logLevel = Project::MSG_VERBOSE; break; + case "debug": $this->logLevel = Project::MSG_DEBUG; break; + } + } + + /** + * The init method: check if Net_FTP is available + */ + public function init() { + require_once 'PEAR.php'; + + $paths = explode(PATH_SEPARATOR, get_include_path()); + foreach($paths as $path) { + if(file_exists($path.DIRECTORY_SEPARATOR.'Net'.DIRECTORY_SEPARATOR.'FTP.php')) { + return true; + } + } + throw new BuildException('The FTP Deploy task requires the Net_FTP PEAR package.'); + } + + /** + * The main entry point method. + */ + public function main() { + $project = $this->getProject(); + + require_once 'Net/FTP.php'; + $ftp = new Net_FTP($this->host, $this->port); + $ret = $ftp->connect(); + if(@PEAR::isError($ret)) { + throw new BuildException('Could not connect to FTP server '.$this->host.' on port '.$this->port.': '.$ret->getMessage()); + } else { + $this->log('Connected to FTP server ' . $this->host . ' on port ' . $this->port, $this->logLevel); + } + + $ret = $ftp->login($this->username, $this->password); + if(@PEAR::isError($ret)) { + throw new BuildException('Could not login to FTP server '.$this->host.' on port '.$this->port.' with username '.$this->username.': '.$ret->getMessage()); + } else { + $this->log('Logged in to FTP server with username ' . $this->username, $this->logLevel); + } + + if ($this->passive) { + $this->log('Setting passive mode', $this->logLevel); + $ret = $ftp->setPassive(); + if(@PEAR::isError($ret)) { + $ftp->disconnect(); + throw new BuildException('Could not set PASSIVE mode: '.$ret->getMessage()); + } + } + + // append '/' to the end if necessary + $dir = substr($this->dir, -1) == '/' ? $this->dir : $this->dir.'/'; + + if($this->clearFirst) { + // TODO change to a loop through all files and directories within current directory + $this->log('Clearing directory '.$dir, $this->logLevel); + $ftp->rm($dir, true); + } + + // Create directory just in case + $ret = $ftp->mkdir($dir, true); + if(@PEAR::isError($ret)) { + $ftp->disconnect(); + throw new BuildException('Could not create directory '.$dir.': '.$ret->getMessage()); + } + + $ret = $ftp->cd($dir); + if(@PEAR::isError($ret)) { + $ftp->disconnect(); + throw new BuildException('Could not change to directory '.$dir.': '.$ret->getMessage()); + } else { + $this->log('Changed directory ' . $dir, $this->logLevel); + } + + $fs = FileSystem::getFileSystem(); + $convert = $fs->getSeparator() == '\\'; + + foreach($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($project); + $fromDir = $fs->getDir($project); + $srcFiles = $ds->getIncludedFiles(); + $srcDirs = $ds->getIncludedDirectories(); + foreach($srcDirs as $dirname) { + if($convert) + $dirname = str_replace('\\', '/', $dirname); + $this->log('Will create directory '.$dirname, $this->logLevel); + $ret = $ftp->mkdir($dirname, true); + if(@PEAR::isError($ret)) { + $ftp->disconnect(); + throw new BuildException('Could not create directory '.$dirname.': '.$ret->getMessage()); + } + } + foreach($srcFiles as $filename) { + $file = new PhingFile($fromDir->getAbsolutePath(), $filename); + if($convert) + $filename = str_replace('\\', '/', $filename); + $this->log('Will copy '.$file->getCanonicalPath().' to '.$filename, $this->logLevel); + $ret = $ftp->put($file->getCanonicalPath(), $filename, true, $this->mode); + if(@PEAR::isError($ret)) { + $ftp->disconnect(); + throw new BuildException('Could not deploy file '.$filename.': '.$ret->getMessage()); + } + } + } + + $ftp->disconnect(); + $this->log('Disconnected from FTP server', $this->logLevel); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/HttpGetTask.php b/buildscripts/phing/classes/phing/tasks/ext/HttpGetTask.php new file mode 100755 index 00000000..114b80a1 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/HttpGetTask.php @@ -0,0 +1,170 @@ +. + */ + +require_once 'phing/Task.php'; + +/** + * A HTTP request task. + * Making an HTTP request and try to match the response against an provided + * regular expression. + * + * @package phing.tasks.ext + * @author Ole Markus With + * @version $Id: f3fa317b72e2f70f1e483fa49dbf089094e2a476 $ + */ +class HttpGetTask extends Task +{ + /** + * Holds the request URL + * + * @var string + */ + protected $url = null; + + /** + * Holds the filename to store the output in + * + * @var string + */ + protected $filename = null; + + /** + * Holds the save location + * + * @var string + */ + protected $dir = null; + + /** + * Holds the proxy + * + * @var string + */ + protected $_proxy = null; + + /** + * Load the necessary environment for running this task. + * + * @throws BuildException + */ + public function init() + { + @include_once 'HTTP/Request2.php'; + + if (! class_exists('HTTP_Request2')) { + throw new BuildException( + 'HttpRequestTask depends on HTTP_Request2 being installed ' + . 'and on include_path.', + $this->getLocation() + ); + } + } + + + /** + * Make the GET request + * + * @throws BuildException + */ + public function main() + { + if (!isset($this->url)) { + throw new BuildException("Missing attribute 'url'"); + } + + if (!isset($this->dir)) { + throw new BuildException("Missing attribute 'dir'"); + } + + $config = array(); + if (isset($this->_proxy) && $url = parse_url($this->_proxy)) { + $config['proxy_user'] = $url['user']; + $config['proxy_password'] = $url['pass']; + $config['proxy_host'] = $url['host']; + $config['proxy_port'] = $url['port']; + } + + $this->log("Fetching " . $this->url); + + $request = new HTTP_Request2($this->url, '', $config); + $response = $request->send(); + if ($response->getStatus() != 200) { + throw new BuildException("Request unsuccessful. Response from server: " . $response->getStatus() . " " . $response->getReasonPhrase()); + } + + $content = $response->getBody(); + $disposition = $response->getHeader('content-disposition'); + + if ($this->filename) { + $filename = $this->filename; + } elseif ($disposition && 0 == strpos($disposition, 'attachment') + && preg_match('/filename="([^"]+)"/', $disposition, $m)) { + $filename = basename($m[1]); + } else { + $filename = basename(parse_url($this->url, PHP_URL_PATH)); + } + + if (!is_writable($this->dir)) { + throw new BuildException("Cannot write to directory: " . $this->dir); + } + + $filename = $this->dir . "/" . $filename; + file_put_contents($filename, $content); + + $this->log("Contents from " . $this->url . " saved to $filename"); + } + + /** + * Sets the request URL + * + * @param string $url + */ + public function setUrl($url) { + $this->url = $url; + } + + /** + * Sets the filename to store the output in + * + * @param string $filename + */ + public function setFilename($filename) { + $this->filename = $filename; + } + + /** + * Sets the save location + * + * @param string $dir + */ + public function setDir($dir) { + $this->dir = $dir; + } + + /** + * Sets the proxy + * + * @param string $proxy + */ + public function setProxy($proxy) { + $this->_proxy = $proxy; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/HttpRequestTask.php b/buildscripts/phing/classes/phing/tasks/ext/HttpRequestTask.php new file mode 100644 index 00000000..e6f15075 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/HttpRequestTask.php @@ -0,0 +1,286 @@ +. + */ + +require_once 'phing/Task.php'; + +/** + * A HTTP request task. + * Making an HTTP request and try to match the response against an provided + * regular expression. + * + * @package phing.tasks.ext + * @author Benjamin Schultz + * @version $Id: 495c02bc3a90d24694d8a4bf2d43ac077e0f9ec6 $ + * @since 2.4.1 + */ +class HttpRequestTask extends Task +{ + /** + * Holds the request URL + * + * @var string + */ + protected $_url = null; + + /** + * Holds the regular expression that should match the response + * + * @var string + */ + protected $_responseRegex = ''; + + /** + * Whether to enable detailed logging + * + * @var boolean + */ + protected $_verbose = false; + + /** + * Holds additional header data + * + * @var array + */ + protected $_headers = array(); + + /** + * Holds additional config data for HTTP_Request2 + * + * @var array + */ + protected $_configData = array(); + + /** + * Holds the authentication user name + * + * @var string + */ + protected $_authUser = null; + + /** + * Holds the authentication password + * + * @var string + */ + protected $_authPassword = ''; + + /** + * Holds the authentication scheme + * + * @var string + */ + protected $_authScheme; + + /** + * Holds the events that will be logged + * + * @var array + */ + protected $_observerEvents = array( + 'connect', + 'sentHeaders', + 'sentBodyPart', + 'receivedHeaders', + 'receivedBody', + 'disconnect', + ); + + /** + * Sets the request URL + * + * @param string $url + */ + public function setUrl($url) + { + $this->_url = $url; + } + + /** + * Sets the response regex + * + * @param string $regex + */ + public function setResponseRegex($regex) + { + $this->_responseRegex = $regex; + } + + /** + * Sets the authentication user name + * + * @param string $user + */ + public function setAuthUser($user) + { + $this->_authUser = $user; + } + + /** + * Sets the authentication password + * + * @param string $password + */ + public function setAuthPassword($password) + { + $this->_authPassword = $password; + } + + /** + * Sets the authentication scheme + * + * @param string $scheme + */ + public function setAuthScheme($scheme) + { + $this->_authScheme = $scheme; + } + + /** + * Sets whether to enable detailed logging + * + * @param boolean $verbose + */ + public function setVerbose($verbose) + { + $this->_verbose = StringHelper::booleanValue($verbose); + } + + /** + * Sets a list of observer events that will be logged + * if verbose output is enabled. + * + * @param string $observerEvents List of observer events + * + * @return void + */ + public function setObserverEvents($observerEvents) + { + $this->_observerEvents = array(); + + $token = ' ,;'; + $ext = strtok($observerEvents, $token); + + while ($ext !== false) { + $this->_observerEvents[] = $ext; + $ext = strtok($token); + } + } + + /** + * Creates an additional header for this task + * + * @return Parameter The created header + */ + public function createHeader() + { + $num = array_push($this->_headers, new Parameter()); + return $this->_headers[$num-1]; + } + + /** + * Creates a config parameter for this task + * + * @return Parameter The created parameter + */ + public function createConfig() + { + $num = array_push($this->_configData, new Parameter()); + return $this->_configData[$num-1]; + } + + /** + * Load the necessary environment for running this task. + * + * @throws BuildException + */ + public function init() + { + @include_once 'HTTP/Request2.php'; + + if (! class_exists('HTTP_Request2')) { + throw new BuildException( + 'HttpRequestTask depends on HTTP_Request2 being installed ' + . 'and on include_path.', + $this->getLocation() + ); + } + + $this->_authScheme = HTTP_Request2::AUTH_BASIC; + + // Other dependencies that should only be loaded + // when class is actually used + require_once 'HTTP/Request2/Observer/Log.php'; + } + + /** + * Make the http request + */ + public function main() + { + if (!isset($this->_url)) { + throw new BuildException("Missing attribute 'url' set"); + } + + $request = new HTTP_Request2($this->_url); + + // set the authentication data + if (!empty($this->_authUser)) { + $request->setAuth( + $this->_authUser, + $this->_authPassword, + $this->_authScheme + ); + } + + foreach ($this->_configData as $config) { + $request->setConfig($config->getName(), $config->getValue()); + } + + foreach ($this->_headers as $header) { + $request->setHeader($header->getName(), $header->getValue()); + } + + if ($this->_verbose) { + $observer = new HTTP_Request2_Observer_Log(); + + // set the events we want to log + $observer->events = $this->_observerEvents; + + $request->attach($observer); + } + + $response = $request->send(); + + if ($this->_responseRegex !== '') { + $matches = array(); + preg_match($this->_responseRegex, $response->getBody(), $matches); + + if (count($matches) === 0) { + throw new BuildException( + 'The received response body did not match the ' + . 'given regular expression' + ); + } else { + $this->log('The response body matched the provided regex.'); + } + } + } +} \ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/JslLintTask.php b/buildscripts/phing/classes/phing/tasks/ext/JslLintTask.php new file mode 100644 index 00000000..77a4aad5 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/JslLintTask.php @@ -0,0 +1,284 @@ +. + */ + +require_once 'phing/Task.php'; +require_once 'phing/util/DataStore.php'; + +/** + * A Javascript lint task. Checks syntax of Javascript files. + * Javascript lint (http://www.javascriptlint.com) must be in the system path. + * This class is based on Knut Urdalen's PhpLintTask. + * + * @author Stefan Priebsch + * @version $Id: 551de2e94aa21f44e19dd0806051f1eabf8b20f9 $ + * @package phing.tasks.ext + */ +class JslLintTask extends Task +{ + protected $file; // the source file (from xml attribute) + protected $filesets = array(); // all fileset objects assigned to this task + + protected $showWarnings = true; + protected $haltOnFailure = false; + protected $hasErrors = false; + private $badFiles = array(); + + private $cache = null; + private $conf = null; + + private $executable = "jsl"; + + /** + * @var PhingFile + */ + protected $tofile = null; + + /** + * Sets the flag if warnings should be shown + * @param boolean $show + */ + public function setShowWarnings($show) { + $this->showWarnings = StringHelper::booleanValue($show); + } + + /** + * The haltonfailure property + * @param boolean $aValue + */ + public function setHaltOnFailure($aValue) { + $this->haltOnFailure = $aValue; + } + + /** + * File to be performed syntax check on + * @param PhingFile $file + */ + public function setFile(PhingFile $file) { + $this->file = $file; + } + + /** + * Whether to store last-modified times in cache + * + * @param PhingFile $file + */ + public function setCacheFile(PhingFile $file) + { + $this->cache = new DataStore($file); + } + + /** + * jsl config file + * + * @param PhingFile $file + */ + public function setConfFile(PhingFile $file) + { + $this->conf = $file; + } + + public function setExecutable($path){ + $this->executable = $path; + + if (!@file_exists($path)) { + throw new BuildException("JavaScript Lint executable '{$path}' not found"); + } + } + + public function getExecutable(){ + return $this->executable; + } + + /** + * Nested creator, creates a FileSet for this task + * + * @return FileSet The created fileset object + */ + public function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * File to save error messages to + * + * @param PhingFile $file + */ + public function setToFile(PhingFile $tofile) + { + $this->tofile = $tofile; + } + + /** + * Execute lint check against PhingFile or a FileSet + */ + public function main() { + if(!isset($this->file) and count($this->filesets) == 0) { + throw new BuildException("Missing either a nested fileset or attribute 'file' set"); + } + + if (empty($this->executable)) { + throw new BuildException("Missing the 'executable' attribute"); + } + + if($this->file instanceof PhingFile) { + $this->lint($this->file->getPath()); + } else { // process filesets + $project = $this->getProject(); + foreach($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($project); + $files = $ds->getIncludedFiles(); + $dir = $fs->getDir($this->project)->getPath(); + foreach($files as $file) { + $this->lint($dir.DIRECTORY_SEPARATOR.$file); + } + } + } + + // write list of 'bad files' to file (if specified) + if ($this->tofile) { + $writer = new FileWriter($this->tofile); + + foreach ($this->badFiles as $file => $messages) { + foreach ($messages as $msg) { + $writer->write($file . "=" . $msg . PHP_EOL); + } + } + + $writer->close(); + } + + if ($this->haltOnFailure && $this->hasErrors) throw new BuildException('Syntax error(s) in JS files:' .implode(', ', array_keys($this->badFiles))); + } + + /** + * Performs the actual syntax check + * + * @param string $file + * @return void + */ + protected function lint($file) + { + $command = $this->executable . ' -output-format ' . escapeshellarg('file:__FILE__;line:__LINE__;message:__ERROR__') . ' '; + + if (isset($this->conf)) { + $command .= '-conf ' . escapeshellarg($this->conf->getPath()) . ' '; + } + + $command .= '-process '; + + if(file_exists($file)) + { + if(is_readable($file)) + { + if ($this->cache) + { + $lastmtime = $this->cache->get($file); + + if ($lastmtime >= filemtime($file)) + { + $this->log("Not linting '" . $file . "' due to cache", Project::MSG_DEBUG); + return false; + } + } + + $messages = array(); + exec($command.'"'.$file.'"', $messages, $return); + + if ($return > 100) { + throw new BuildException("Could not execute Javascript Lint executable '{$this->executable}'"); + } + + $summary = $messages[sizeof($messages) - 1]; + + preg_match('/(\d+)\serror/', $summary, $matches); + $errorCount = (count($matches) > 1 ? $matches[1] : 0); + + preg_match('/(\d+)\swarning/', $summary, $matches); + $warningCount = (count($matches) > 1 ? $matches[1] : 0); + + $errors = array(); + $warnings = array(); + if ($errorCount > 0 || $warningCount > 0) { + $last = false; + foreach ($messages as $message) { + $matches = array(); + if (preg_match('/^(\.*)\^$/', $message)) { + $column = strlen($message); + if ($last == 'error') { + $errors[count($errors) - 1]['column'] = $column; + } else if ($last == 'warning') { + $warnings[count($warnings) - 1]['column'] = $column; + } + $last = false; + } + if (!preg_match('/^file:(.+);line:(\d+);message:(.+)$/', $message, $matches)) continue; + $msg = $matches[3]; + $data = array('filename' => $matches[1], 'line' => $matches[2], 'message' => $msg); + if (preg_match('/^.*error:.+$/i', $msg)) { + $errors[] = $data; + $last = 'error'; + } else if (preg_match('/^.*warning:.+$/i', $msg)) { + $warnings[] = $data; + $last = 'warning'; + } + } + } + + if($this->showWarnings && $warningCount > 0) + { + $this->log($file . ': ' . $warningCount . ' warnings detected', Project::MSG_WARN); + foreach ($warnings as $warning) { + $this->log('- line ' . $warning['line'] . (isset($warning['column']) ? ' column ' . $warning['column'] : '') . ': ' . $warning['message'], Project::MSG_WARN); + } + } + + if($errorCount > 0) + { + $this->log($file . ': ' . $errorCount . ' errors detected', Project::MSG_ERR); + if (!isset($this->badFiles[$file])) { + $this->badFiles[$file] = array(); + } + + foreach ($errors as $error) { + $message = 'line ' . $error['line'] . (isset($error['column']) ? ' column ' . $error['column'] : '') . ': ' . $error['message']; + $this->log('- ' . $message, Project::MSG_ERR); + array_push($this->badFiles[$file], $message); + } + $this->hasErrors = true; + } else if (!$this->showWarnings || $warningCount == 0) { + $this->log($file . ': No syntax errors detected', Project::MSG_VERBOSE); + + if ($this->cache) + { + $this->cache->put($file, filemtime($file)); + } + } + } else { + throw new BuildException('Permission denied: '.$file); + } + } else { + throw new BuildException('File not found: '.$file); + } + } +} + + diff --git a/buildscripts/phing/classes/phing/tasks/ext/MailTask.php b/buildscripts/phing/classes/phing/tasks/ext/MailTask.php new file mode 100755 index 00000000..0fff88cc --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/MailTask.php @@ -0,0 +1,159 @@ +. + */ + +include_once 'phing/Task.php'; + +/** + * Send an e-mail message + * + * The build process is a success... + * + * @author Michiel Rook + * @author Francois Harvey at SecuriWeb (http://www.securiweb.net) + * @version $Id: 901fb0efa435ae78d249a50ed0e0f6e5d31e0d32 $ + * @package phing.tasks.ext + */ +class MailTask extends Task +{ + protected $tolist = null; + protected $subject = null; + protected $msg = null; + protected $from = null; + + protected $filesets = array(); + + public function main() + { + if (empty($this->from)) { + throw new BuildException('Missing "from" attribute'); + } + + $this->log('Sending mail to ' . $this->tolist); + + if (!empty($this->filesets)) { + @require_once 'Mail.php'; + @require_once 'Mail/mime.php'; + + if (!class_exists('Mail_mime')) { + throw new BuildException('Need the PEAR Mail_mime package to send attachments'); + } + + $mime = new Mail_mime(); + $hdrs = array( + 'From' => $this->from, + 'Subject' => $this->subject + ); + $mime->setTXTBody($this->msg); + + foreach ($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($this->project); + $fromDir = $fs->getDir($this->project); + $srcFiles = $ds->getIncludedFiles(); + + foreach ($srcFiles as $file) { + $mime->addAttachment($fromDir . DIRECTORY_SEPARATOR . $file, 'application/octet-stream'); + } + } + + $body = $mime->get(); + $hdrs = $mime->headers($hdrs); + + $mail = Mail::factory('mail'); + $mail->send($this->tolist, $hdrs, $body); + } else { + mail($this->tolist, $this->subject, $this->msg, "From: {$this->from}\n"); + } + } + + /** + * Setter for message + */ + public function setMsg($msg) + { + $this->setMessage($msg); + } + + /** + * Alias setter + */ + public function setMessage($msg) + { + $this->msg = (string) $msg; + } + + /** + * Setter for subject + */ + public function setSubject($subject) + { + $this->subject = (string) $subject; + } + + /** + * Setter for tolist + */ + public function setToList($tolist) + { + $this->tolist = $tolist; + } + + /** + * Alias for (deprecated) recipient + */ + public function setRecipient($recipient) + { + $this->tolist = (string) $recipient; + } + + /** + * Alias for to + */ + public function setTo($to) + { + $this->tolist = (string) $to; + } + + /** + * Supports the Message syntax. + */ + public function addText($msg) + { + $this->msg = (string) $msg; + } + + /** + * Sets email address of sender + */ + public function setFrom($from) + { + $this->from = $from; + } + + /** + * Adds a fileset + */ + public function createFileSet() + { + $fileset = new FileSet(); + $this->filesets[] = $fileset; + return $fileset; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/ManifestTask.php b/buildscripts/phing/classes/phing/tasks/ext/ManifestTask.php new file mode 100644 index 00000000..b9cfae64 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ManifestTask.php @@ -0,0 +1,343 @@ +. + */ + +require_once "phing/Task.php"; +require_once 'phing/system/io/PhingFile.php'; + +/** + * ManifestTask + * + * Generates a simple Manifest file with optional checksums. + * + * + * Manifest schema: + * ... + * path/to/file CHECKSUM [CHECKSUM2] [CHECKSUM3] + * path/to/secondfile CHECKSUM [CHECKSUM2] [CHECKSUM3] + * ... + * + * Example usage: + * + * + * + * + * + * + * + * + * + * + * @author David Persson + * @package phing.tasks.ext + * @version $Id: 7f8f119fe5dd44ca9f374e24d776a1a764260e33 $ + * @since 2.3.1 + */ +class ManifestTask extends Task +{ + var $taskname = 'manifest'; + + /** + * Action + * + * "w" for reading in files from fileSet + * and writing manifest + * + * or + * + * "r" for reading in files from fileSet + * and checking against manifest + * + * @var string "r" or "w" + */ + private $action = 'w'; + + /** + * The target file passed in the buildfile. + */ + private $destFile = null; + + /** + * Holds filesets + * + * @var array An Array of objects + */ + private $filesets = array(); + + /** + * Enable/Disable checksuming or/and select algorithm + * true defaults to md5 + * false disables checksuming + * string "md5,sha256,..." enables generation of multiple checksums + * string "sha256" generates sha256 checksum only + * + * @var mixed + */ + private $checksum = false; + + /** + * A string used in hashing method + * + * @var string + */ + private $salt = ''; + + /** + * Holds some data collected during runtime + * + * @var array + */ + private $meta = array('totalFileCount' => 0,'totalFileSize' => 0); + + + /** + * The setter for the attribute "file" + * This is where the manifest will be written to/read from + * + * @param string Path to readable file + * @return void + */ + public function setFile(PhingFile $file) + { + $this->file = $file; + } + + /** + * The setter for the attribute "checksum" + * + * @param mixed $mixed + * @return void + */ + public function setChecksum($mixed) + { + if(is_string($mixed)) { + $data = array(strtolower($mixed)); + + if(strpos($data[0],',')) { + $data = explode(',',$mixed); + } + + $this->checksum = $data; + + } elseif($mixed === true) { + $this->checksum = array('md5'); + + } + } + + /** + * The setter for the optional attribute "salt" + * + * @param string $string + * @return void + */ + public function setSalt($string) + { + $this->salt = $string; + } + + /** + * Nested creator, creates a FileSet for this task + * + * @access public + * @return object The created fileset object + */ + public function createFileSet() + { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * The init method: Do init steps. + */ + public function init() + { + // nothing to do here + } + + /** + * Delegate the work + */ + public function main() + { + $this->validateAttributes(); + + if($this->action == 'w') { + $this->write(); + + } elseif($this->action == 'r') { + $this->read(); + + } + } + + /** + * Creates Manifest file + * Writes to $this->file + * + * @throws BuildException + */ + private function write() + { + $project = $this->getProject(); + + if(!touch($this->file->getPath())) { + throw new BuildException("Unable to write to ".$this->file->getPath()."."); + } + + $this->log("Writing to " . $this->file->__toString(), Project::MSG_INFO); + + if(is_array($this->checksum)) { + $this->log("Using " . implode(', ',$this->checksum)." for checksuming.", Project::MSG_INFO); + } + + foreach($this->filesets as $fs) { + + $dir = $fs->getDir($this->project)->getPath(); + + $ds = $fs->getDirectoryScanner($project); + $fromDir = $fs->getDir($project); + $srcFiles = $ds->getIncludedFiles(); + $srcDirs = $ds->getIncludedDirectories(); + + foreach($ds->getIncludedFiles() as $file_path) { + $line = $file_path; + if($this->checksum) { + foreach($this->checksum as $algo) { + if(!$hash = $this->hashFile($dir.'/'.$file_path,$algo)) { + throw new BuildException("Hashing $dir/$file_path with $algo failed!"); + } + + $line .= "\t".$hash; + } + } + $line .= "\n"; + $manifest[] = $line; + $this->log("Adding file ".$file_path,Project::MSG_VERBOSE); + $this->meta['totalFileCount'] ++; + $this->meta['totalFileSize'] += filesize($dir.'/'.$file_path); + } + + } + + file_put_contents($this->file,$manifest); + + $this->log("Done. Total files: ".$this->meta['totalFileCount'].". Total file size: ".$this->meta['totalFileSize']." bytes.", Project::MSG_INFO); + } + + /** + * @todo implement + */ + private function read() + { + throw new BuildException("Checking against manifest not yet supported."); + } + + /** + * Wrapper method for hash generation + * Automatically selects extension + * Falls back to built-in functions + * + * @link http://www.php.net/mhash + * @link http://www.php.net/hash + * + * @param string $msg The string that should be hashed + * @param string $algo Algorithm + * @return mixed String on success, false if $algo is not available + */ + private function hash($msg,$algo) + { + if(extension_loaded('hash')) { + $algo = strtolower($algo); + + if(in_array($algo,hash_algos())) { + return hash($algo,$this->salt.$msg); + } + + } + + if(extension_loaded('mhash')) { + $algo = strtoupper($algo); + + if(defined('MHASH_'.$algo)) { + return mhash('MHASH_'.$algo,$this->salt.$msg); + + } + } + + switch(strtolower($algo)) { + case 'md5': + return md5($this->salt.$msg); + case 'crc32': + return abs(crc32($this->salt.$msg)); + } + + return false; + } + + /** + * Hash a files contents + * plus it's size an modification time + * + * @param string $file + * @param string $algo + * @return mixed String on success, false if $algo is not available + */ + private function hashFile($file,$algo) + { + if(!file_exists($file)) { + return false; + } + + $msg = file_get_contents($file).filesize($file).filemtime($file); + + return $this->hash($msg,$algo); + } + + /** + * Validates attributes coming in from XML + * + * @access private + * @return void + * @throws BuildException + */ + protected function validateAttributes() + { + if($this->action != 'r' && $this->action != 'w') { + throw new BuildException("'action' attribute has non valid value. Use 'r' or 'w'"); + } + + if(empty($this->salt)) { + $this->log("No salt provided. Specify one with the 'salt' attribute.", Project::MSG_WARN); + } + + if (is_null($this->file) && count($this->filesets) === 0) { + throw new BuildException("Specify at least sources and destination - a file or a fileset."); + } + + if (!is_null($this->file) && $this->file->exists() && $this->file->isDirectory()) { + throw new BuildException("Destination file cannot be a directory."); + } + + } +} + + diff --git a/buildscripts/phing/classes/phing/tasks/ext/PackageAsPathTask.php b/buildscripts/phing/classes/phing/tasks/ext/PackageAsPathTask.php new file mode 100644 index 00000000..2db5ad69 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/PackageAsPathTask.php @@ -0,0 +1,65 @@ +. + */ + +require_once 'phing/Task.php'; + +/** + * Convert dot-notation packages to relative paths. + * + * @author Hans Lellelid + * @version $Id: 976dafaf4cafd9ff8f47907a09943ae3963aea79 $ + * @package phing.tasks.ext + */ +class PackageAsPathTask extends Task { + + /** The package to convert. */ + protected $pckg; + + /** The value to store the conversion in. */ + protected $name; + + /** + * Executes the package to patch converstion and stores it + * in the user property value. + */ + public function main() + { + $this->project->setUserProperty($this->name, strtr($this->pckg, '.', '/')); + } + + /** + * @param string $pckg the package to convert + */ + public function setPackage($pckg) + { + $this->pckg = $pckg; + } + + /** + * @param string $name the Ant variable to store the path in + */ + public function setName($name) + { + $this->name = $name; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/ParallelTask.php b/buildscripts/phing/classes/phing/tasks/ext/ParallelTask.php new file mode 100755 index 00000000..b8da5cb4 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ParallelTask.php @@ -0,0 +1,83 @@ +. + * + * @package phing.tasks.ext + */ + +/** + * Uses the DocBlox_Parallel library to run nested Phing tasks concurrently. + * + * WARNING: this task is highly experimental! + * + * @author Michiel Rook + * @version $Id: 860b2b6cdbd797754660fe2c1554e22ab2db4967 $ + * @package phing.tasks.ext + * @see https://github.com/phpdocumentor/Parallel + * @since 2.4.10 + */ +class ParallelTask extends SequentialTask +{ + /** + * Maximum number of threads / processes + * @var int + */ + private $threadCount = 2; + + /** + * Sets the maximum number of threads / processes to use + * @param int $threadCount + */ + public function setThreadCount($threadCount) + { + $this->threadCount = $threadCount; + } + + public function init() + { + } + + public function main() + { + @include_once 'phing/contrib/DocBlox/Parallel/Manager.php'; + @include_once 'phing/contrib/DocBlox/Parallel/Worker.php'; + @include_once 'phing/contrib/DocBlox/Parallel/WorkerPipe.php'; + if (!class_exists('DocBlox_Parallel_Worker')) { + throw new BuildException( + 'ParallelTask depends on DocBlox being installed and on include_path.', + $this->getLocation() + ); + } + + $mgr = new DocBlox_Parallel_Manager(); + $mgr->setProcessLimit($this->threadCount); + + foreach ($this->nestedTasks as $task) { + $worker = new DocBlox_Parallel_Worker( + array($task, 'perform'), + array($task) + ); + + $mgr->addWorker($worker); + } + + $mgr->execute(); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/PatchTask.php b/buildscripts/phing/classes/phing/tasks/ext/PatchTask.php new file mode 100755 index 00000000..162f5fe5 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/PatchTask.php @@ -0,0 +1,301 @@ +patchFile = $file; + } + + /** + * The file to patch + * + * Optional if it can be inferred from the diff file. + * + * @param string $file File to patch + * @return void + */ + public function setOriginalFile($file) + { + $this->originalFile = $file; + } + + /** + * The name of a file to send the output to, instead of patching + * the file(s) in place + * + * Optional. + * + * @param string $file File to send the output to + * @return void + */ + public function setDestFile($file) + { + if ($file !== null) + { + $this->cmdArgs []= "--output=$file"; + } + } + + /** + * Flag to create backups + * + * Optional, default - false + * + * @param bool $backups If true create backups + * @return void + */ + public function setBackups($backups) + { + if ($backups) + { + $this->cmdArgs []= '--backup'; + } + } + + /** + * Flag to ignore whitespace differences; + * + * Default - false + * + * @param bool $ignore If true ignore whitespace differences + * @return void + */ + public function setIgnoreWhiteSpace($ignore) + { + if ($ignore) + { + $this->cmdArgs []= '--ignore-whitespace'; + } + } + + /** + * Strip the smallest prefix containing num leading slashes + * from filenames. + * + * patch's --strip option. + * + * @param int $num number of lines to strip + * @return void + * @throws BuildException if num is < 0, or other errors + */ + public function setStrip($num) + { + if ($num < 0) + { + throw new BuildException('strip has to be >= 0'); + } + + $this->strip = $num; + } + + /** + * Work silently unless an error occurs + * + * Optional, default - false + * @param bool $flag If true suppress set the -s option on the patch command + * @return void + */ + public function setQuiet($flag) + { + if ($flag) + { + $this->cmdArgs []= '--silent'; + } + } + + /** + * Assume patch was created with old and new files swapped + * + * Optional, default - false + * + * @param bool $flag If true set the -R option on the patch command + * @return void + */ + public function setReverse($flag) + { + if ($flag) + { + $this->cmdArgs []= '--reverse'; + } + } + + /** + * The directory to run the patch command in + * + * Defaults to the project's base directory. + * + * @param string $directory Directory to run the patch command in + * @return void + */ + public function setDir($directory) + { + $this->cmdArgs []= "--directory=$directory"; + } + + /** + * Ignore patches that seem to be reversed or already applied + * + * @param bool $flag If true set the -N (--forward) option + * @return void + */ + public function setForward($flag) + { + if ($flag) + { + $this->cmdArgs []= "--forward"; + } + } + + /** + * Set the maximum fuzz factor + * + * Defaults to 0 + * + * @param string $value Value of a fuzz factor + * @return void + */ + public function setFuzz($value) + { + $this->cmdArgs []= "--fuzz=$value"; + } + + /** + * If true, stop the build process if the patch command + * exits with an error status. + * + * The default is "false" + * + * @param bool $value "true" if it should halt, otherwise "false" + * @return void + */ + public function setHaltOnFailure($value) + { + $this->haltOnFailure = $value; + } + + /** + * Main task method + * + * @return void + * @throws BuildException when it all goes a bit pear shaped + */ + public function main() + { + if ($this->patchFile == null) + { + throw new BuildException('patchfile argument is required'); + } + + // Define patch file + $this->cmdArgs []= '-i ' . $this->patchFile; + // Define strip factor + if ($this->strip != null) + { + $this->cmdArgs []= '--strip=' . $this->strip; + } + // Define original file if specified + if ($this->originalFile != null) + { + $this->cmdArgs []= $this->originalFile; + } + + $cmd = self::CMD . implode(' ', $this->cmdArgs); + + $this->log('Applying patch: ' . $this->patchFile); + + exec($cmd, $output, $exitCode); + + foreach ($output as $line) + { + $this->log($line, Project::MSG_VERBOSE); + } + + if ($exitCode != 0 && $this->haltOnFailure) + { + throw new BuildException( "Task exited with code $exitCode" ); + } + + } +} \ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/PearPackage2Task.php b/buildscripts/phing/classes/phing/tasks/ext/PearPackage2Task.php new file mode 100644 index 00000000..f42231f7 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/PearPackage2Task.php @@ -0,0 +1,279 @@ +. + */ + +require_once 'phing/tasks/ext/PearPackageTask.php'; + +/** + * A task to create a PEAR package.xml version 2.0 file. + * + * This class uses the PEAR_PackageFileManager2 class to perform the work. + * + * This class is designed to be very flexible -- i.e. account for changes to the package.xml w/o + * requiring changes to this class. We've accomplished this by having generic