CapsuleTask.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. <?php
  2. /*
  3. * $Id: CapsuleTask.php 905 2010-10-05 16:28:03Z mrook $
  4. *
  5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. *
  17. * This software consists of voluntary contributions made by many individuals
  18. * and is licensed under the LGPL. For more information please see
  19. * <http://phing.info>.
  20. */
  21. include_once 'phing/Task.php';
  22. include_once 'phing/BuildException.php';
  23. include_once 'phing/lib/Capsule.php';
  24. include_once 'phing/util/StringHelper.php';
  25. /**
  26. * A phing task for generating output by using Capsule.
  27. *
  28. * This is based on the interface to TexenTask from Apache's Velocity engine.
  29. *
  30. * @author Hans Lellelid <hans@xmpl.org>
  31. * @version $Id: CapsuleTask.php 905 2010-10-05 16:28:03Z mrook $
  32. * @package phing.tasks.ext
  33. */
  34. class CapsuleTask extends Task {
  35. /**
  36. * Capsule "template" engine.
  37. * @var Capsule
  38. */
  39. protected $context;
  40. /**
  41. * Any vars assigned via the build file.
  42. * @var array AssignedVar[]
  43. */
  44. protected $assignedVars = array();
  45. /**
  46. * This is the control template that governs the output.
  47. * It may or may not invoke the services of worker
  48. * templates.
  49. * @var string
  50. */
  51. protected $controlTemplate;
  52. /**
  53. * This is where Velocity will look for templates
  54. * using the file template loader.
  55. * @var string
  56. */
  57. protected $templatePath;
  58. /**
  59. * This is where texen will place all the output
  60. * that is a product of the generation process.
  61. * @var string
  62. */
  63. protected $outputDirectory;
  64. /**
  65. * This is the file where the generated text
  66. * will be placed.
  67. * @var string
  68. */
  69. protected $outputFile;
  70. /**
  71. * <p>
  72. * These are properties that are fed into the
  73. * initial context from a properties file. This
  74. * is simply a convenient way to set some values
  75. * that you wish to make available in the context.
  76. * </p>
  77. * <p>
  78. * These values are not critical, like the template path
  79. * or output path, but allow a convenient way to
  80. * set a value that may be specific to a particular
  81. * generation task.
  82. * </p>
  83. * <p>
  84. * For example, if you are generating scripts to allow
  85. * user to automatically create a database, then
  86. * you might want the <code>$databaseName</code>
  87. * to be placed
  88. * in the initial context so that it is available
  89. * in a script that might look something like the
  90. * following:
  91. * <code><pre>
  92. * #!bin/sh
  93. *
  94. * echo y | mysqladmin create $databaseName
  95. * </pre></code>
  96. * The value of <code>$databaseName</code> isn't critical to
  97. * output, and you obviously don't want to change
  98. * the ant task to simply take a database name.
  99. * So initial context values can be set with
  100. * properties file.
  101. *
  102. * @var array
  103. */
  104. protected $contextProperties;
  105. // -----------------------------------------------------------------------
  106. // The following getters & setters are used by phing to set properties
  107. // specified in the XML for the capsule task.
  108. // -----------------------------------------------------------------------
  109. /**
  110. * [REQUIRED] Set the control template for the
  111. * generating process.
  112. * @param string $controlTemplate
  113. * @return void
  114. */
  115. public function setControlTemplate ($controlTemplate) {
  116. $this->controlTemplate = $controlTemplate;
  117. }
  118. /**
  119. * Get the control template for the
  120. * generating process.
  121. * @return string
  122. */
  123. public function getControlTemplate() {
  124. return $this->controlTemplate;
  125. }
  126. /**
  127. * [REQUIRED] Set the path where Velocity will look
  128. * for templates using the file template
  129. * loader.
  130. * @return void
  131. * @throws Exception
  132. */
  133. public function setTemplatePath($templatePath) {
  134. $resolvedPath = "";
  135. $tok = strtok($templatePath, ",");
  136. while ( $tok ) {
  137. // resolve relative path from basedir and leave
  138. // absolute path untouched.
  139. $fullPath = $this->project->resolveFile($tok);
  140. $cpath = $fullPath->getCanonicalPath();
  141. if ($cpath === false) {
  142. $this->log("Template directory does not exist: " . $fullPath->getAbsolutePath());
  143. } else {
  144. $resolvedPath .= $cpath;
  145. }
  146. $tok = strtok(",");
  147. if ( $tok ) {
  148. $resolvedPath .= ",";
  149. }
  150. }
  151. $this->templatePath = $resolvedPath;
  152. }
  153. /**
  154. * Get the path where Velocity will look
  155. * for templates using the file template
  156. * loader.
  157. * @return string
  158. */
  159. public function getTemplatePath() {
  160. return $this->templatePath;
  161. }
  162. /**
  163. * [REQUIRED] Set the output directory. It will be
  164. * created if it doesn't exist.
  165. * @param PhingFile $outputDirectory
  166. * @return void
  167. * @throws Exception
  168. */
  169. public function setOutputDirectory(PhingFile $outputDirectory) {
  170. try {
  171. if (!$outputDirectory->exists()) {
  172. $this->log("Output directory does not exist, creating: " . $outputDirectory->getPath(),Project::MSG_VERBOSE);
  173. if (!$outputDirectory->mkdirs()) {
  174. throw new IOException("Unable to create Ouptut directory: " . $outputDirectory->getAbsolutePath());
  175. }
  176. }
  177. $this->outputDirectory = $outputDirectory->getCanonicalPath();
  178. } catch (IOException $ioe) {
  179. throw new BuildException($ioe);
  180. }
  181. }
  182. /**
  183. * Get the output directory.
  184. * @return string
  185. */
  186. public function getOutputDirectory() {
  187. return $this->outputDirectory;
  188. }
  189. /**
  190. * [REQUIRED] Set the output file for the
  191. * generation process.
  192. * @param string $outputFile (TODO: change this to File)
  193. * @return void
  194. */
  195. public function setOutputFile($outputFile) {
  196. $this->outputFile = $outputFile;
  197. }
  198. /**
  199. * Get the output file for the
  200. * generation process.
  201. * @return string
  202. */
  203. public function getOutputFile() {
  204. return $this->outputFile;
  205. }
  206. /**
  207. * Set the context properties that will be
  208. * fed into the initial context be the
  209. * generating process starts.
  210. * @param string $file
  211. * @return void
  212. */
  213. public function setContextProperties($file) {
  214. $sources = explode(",", $file);
  215. $this->contextProperties = new Properties();
  216. // Always try to get the context properties resource
  217. // from a file first. Templates may be taken from a JAR
  218. // file but the context properties resource may be a
  219. // resource in the filesystem. If this fails than attempt
  220. // to get the context properties resource from the
  221. // classpath.
  222. for ($i=0, $sourcesLength=count($sources); $i < $sourcesLength; $i++) {
  223. $source = new Properties();
  224. try {
  225. // resolve relative path from basedir and leave
  226. // absolute path untouched.
  227. $fullPath = $this->project->resolveFile($sources[$i]);
  228. $this->log("Using contextProperties file: " . $fullPath->toString());
  229. $source->load($fullPath);
  230. } catch (Exception $e) {
  231. throw new BuildException("Context properties file " . $sources[$i] .
  232. " could not be found in the file system!");
  233. }
  234. $keys = $source->keys();
  235. foreach ($keys as $key) {
  236. $name = $key;
  237. $value = $this->project->replaceProperties($source->getProperty($name));
  238. $this->contextProperties->setProperty($name, $value);
  239. }
  240. }
  241. }
  242. /**
  243. * Get the context properties that will be
  244. * fed into the initial context be the
  245. * generating process starts.
  246. * @return Properties
  247. */
  248. public function getContextProperties() {
  249. return $this->contextProperties;
  250. }
  251. /**
  252. * Creates an "AssignedVar" class.
  253. */
  254. public function createAssign() {
  255. $a = new AssignedVar();
  256. $this->assignedVars[] = $a;
  257. return $a;
  258. }
  259. // ---------------------------------------------------------------
  260. // End of XML setters & getters
  261. // ---------------------------------------------------------------
  262. /**
  263. * Creates a Smarty object.
  264. *
  265. * @return Smarty initialized (cleared) Smarty context.
  266. * @throws Exception the execute method will catch
  267. * and rethrow as a <code>BuildException</code>
  268. */
  269. public function initControlContext() {
  270. $this->context->clear();
  271. foreach($this->assignedVars as $var) {
  272. $this->context->put($var->getName(), $var->getValue());
  273. }
  274. return $this->context;
  275. }
  276. /**
  277. * Execute the input script with Velocity
  278. *
  279. * @throws BuildException
  280. * BuildExceptions are thrown when required attributes are missing.
  281. * Exceptions thrown by Velocity are rethrown as BuildExceptions.
  282. */
  283. public function main() {
  284. // Make sure the template path is set.
  285. if (empty($this->templatePath)) {
  286. throw new BuildException("The template path needs to be defined!");
  287. }
  288. // Make sure the control template is set.
  289. if ($this->controlTemplate === null) {
  290. throw new BuildException("The control template needs to be defined!");
  291. }
  292. // Make sure the output directory is set.
  293. if ($this->outputDirectory === null) {
  294. throw new BuildException("The output directory needs to be defined!");
  295. }
  296. // Make sure there is an output file.
  297. if ($this->outputFile === null) {
  298. throw new BuildException("The output file needs to be defined!");
  299. }
  300. // Setup Smarty runtime.
  301. // Smarty uses one object to store properties and to store
  302. // the context for the template (unlike Velocity). We setup this object, calling it
  303. // $this->context, and then initControlContext simply zeros out
  304. // any assigned variables.
  305. $this->context = new Capsule();
  306. if ($this->templatePath !== null) {
  307. $this->log("Using templatePath: " . $this->templatePath);
  308. $this->context->setTemplatePath($this->templatePath);
  309. }
  310. // Make sure the output directory exists, if it doesn't
  311. // then create it.
  312. $outputDir = new PhingFile($this->outputDirectory);
  313. if (!$outputDir->exists()) {
  314. $this->log("Output directory does not exist, creating: " . $outputDir->getAbsolutePath());
  315. $outputDir->mkdirs();
  316. }
  317. $this->context->setOutputDirectory($outputDir->getAbsolutePath());
  318. $path = $this->outputDirectory . DIRECTORY_SEPARATOR . $this->outputFile;
  319. $this->log("Generating to file " . $path);
  320. //$writer = new FileWriter($path);
  321. // The generator and the output path should
  322. // be placed in the init context here and
  323. // not in the generator class itself.
  324. $c = $this->initControlContext();
  325. // Set any variables that need to always
  326. // be loaded
  327. $this->populateInitialContext($c);
  328. // Feed all the options into the initial
  329. // control context so they are available
  330. // in the control/worker templates.
  331. if ($this->contextProperties !== null) {
  332. foreach($this->contextProperties->keys() as $property) {
  333. $value = $this->contextProperties->getProperty($property);
  334. // Special exception (from Texen)
  335. // for properties ending in file.contents:
  336. // in that case we dump the contents of the file
  337. // as the "value" for the Property.
  338. if (preg_match('/file\.contents$/', $property)) {
  339. // pull in contents of file specified
  340. $property = substr($property, 0, strpos($property, "file.contents") - 1);
  341. // reset value, and then
  342. // read in teh contents of the file into that var
  343. $value = "";
  344. $f = new PhingFile($project->resolveFile($value)->getCanonicalPath());
  345. if ($f->exists()) {
  346. $fr = new FileReader($f);
  347. $fr->readInto($value);
  348. }
  349. } // if ends with file.contents
  350. if (StringHelper::isBoolean($value)) {
  351. $value = StringHelper::booleanValue($value);
  352. }
  353. $c->put($property, $value);
  354. } // foreach property
  355. } // if contextProperties !== null
  356. try {
  357. $this->log("Parsing control template: " . $this->controlTemplate);
  358. $c->parse($this->controlTemplate, $path);
  359. } catch (Exception $ioe) {
  360. throw new BuildException("Cannot write parsed template: ". $ioe->getMessage());
  361. }
  362. $this->cleanup();
  363. }
  364. /**
  365. * Place useful objects into the initial context.
  366. *
  367. *
  368. * @param Capsule $context The context to populate, as retrieved from
  369. * {@link #initControlContext()}.
  370. * @return void
  371. * @throws Exception Error while populating context. The {@link
  372. * #main()} method will catch and rethrow as a
  373. * <code>BuildException</code>.
  374. */
  375. protected function populateInitialContext(Capsule $context) {
  376. $this->context->put("now", strftime("%c", time()));
  377. $this->context->put("task", $this);
  378. }
  379. /**
  380. * A hook method called at the end of {@link #execute()} which can
  381. * be overridden to perform any necessary cleanup activities (such
  382. * as the release of database connections, etc.). By default,
  383. * does nothing.
  384. * @return void
  385. * @throws Exception Problem cleaning up.
  386. */
  387. protected function cleanup() {
  388. }
  389. }
  390. /**
  391. * An "inner" class for holding assigned var values.
  392. * May be need to expand beyond name/value in the future.
  393. *
  394. * @package phing.tasks.ext
  395. */
  396. class AssignedVar {
  397. private $name;
  398. private $value;
  399. function setName($v) {
  400. $this->name = $v;
  401. }
  402. function setValue($v) {
  403. $this->value = $v;
  404. }
  405. function getName() {
  406. return $this->name;
  407. }
  408. function getValue() {
  409. return $this->value;
  410. }
  411. }