source: classes/phing/tasks/ext/CapsuleTask.php @ c0fa2c7

Last change on this file since c0fa2c7 was c0fa2c7, checked in by Benjamin Schultz <bschultz@…>, 3 years ago

fixed "reference" to project instance (undefined variable)

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