home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / phing / Project.php < prev    next >
Encoding:
PHP Script  |  2007-03-14  |  32.8 KB  |  976 lines

  1. <?php
  2. /*
  3.  *  $Id: Project.php 175 2007-03-14 13:52:03Z hans $
  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.  
  22. // DEPRECATED logging constants
  23. define('PROJECT_MSG_DEBUG', Project::MSG_DEBUG);
  24. define('PROJECT_MSG_VERBOSE', Project::MSG_VERBOSE);
  25. define('PROJECT_MSG_INFO', Project::MSG_INFO);
  26. define('PROJECT_MSG_WARN', Project::MSG_WARN);
  27. define('PROJECT_MSG_ERR', Project::MSG_ERR);
  28.  
  29. include_once 'phing/system/io/PhingFile.php';
  30. include_once 'phing/util/FileUtils.php';
  31. include_once 'phing/TaskAdapter.php';
  32. include_once 'phing/util/StringHelper.php';
  33. include_once 'phing/BuildEvent.php';
  34. include_once 'phing/input/DefaultInputHandler.php';
  35.  
  36. /**
  37.  *  The Phing project class. Represents a completely configured Phing project.
  38.  *  The class defines the project and all tasks/targets. It also contains
  39.  *  methods to start a build as well as some properties and FileSystem
  40.  *  abstraction.
  41.  *
  42.  * @author    Andreas Aderhold <andi@binarycloud.com>
  43.  * @author    Hans Lellelid <hans@xmpl.org>
  44.  * @version   $Revision: 1.29 $
  45.  * @package   phing
  46.  */
  47. class Project {
  48.  
  49.     // Logging level constants.
  50.     const MSG_DEBUG = 4;
  51.     const MSG_VERBOSE = 3;
  52.     const MSG_INFO = 2;
  53.     const MSG_WARN = 1;
  54.     const MSG_ERR = 0;
  55.     
  56.     /** contains the targets */
  57.     private $targets         = array();
  58.     /** global filterset (future use) */
  59.     private $globalFilterSet = array();
  60.     /**  all globals filters (future use) */
  61.     private $globalFilters   = array();
  62.     
  63.     /** Project properties map (usually String to String). */
  64.     private $properties = array();
  65.     
  66.     /**
  67.      * Map of "user" properties (as created in the Ant task, for example).
  68.      * Note that these key/value pairs are also always put into the
  69.      * project properties, so only the project properties need to be queried.
  70.      * Mapping is String to String.
  71.      */
  72.     private $userProperties = array();
  73.     
  74.     /**
  75.      * Map of inherited "user" properties - that are those "user"
  76.      * properties that have been created by tasks and not been set
  77.      * from the command line or a GUI tool.
  78.      * Mapping is String to String.
  79.      */
  80.     private $inheritedProperties = array();
  81.     
  82.     /** task definitions for this project*/
  83.     private $taskdefs = array();
  84.     
  85.     /** type definitions for this project */
  86.     private $typedefs = array();
  87.     
  88.     /** holds ref names and a reference to the referred object*/
  89.     private $references = array();
  90.     
  91.     /** The InputHandler being used by this project. */
  92.     private $inputHandler;
  93.     
  94.     /* -- properties that come in via xml attributes -- */
  95.     
  96.     /** basedir (PhingFile object) */
  97.     private $basedir;
  98.     
  99.     /** the default target name */
  100.     private $defaultTarget = 'all';
  101.     
  102.     /** project name (required) */
  103.     private $name;
  104.     
  105.     /** project description */
  106.     private $description;
  107.  
  108.     /** a FileUtils object */
  109.     private $fileUtils;
  110.     
  111.     /**  Build listeneers */
  112.     private $listeners = array();
  113.  
  114.     /**
  115.      *  Constructor, sets any default vars.
  116.      */
  117.     function __construct() {
  118.         $this->fileUtils = new FileUtils();
  119.         $this->inputHandler = new DefaultInputHandler();
  120.     }
  121.  
  122.     /**
  123.      * Sets the input handler
  124.      */
  125.     public function setInputHandler(InputHandler $handler) {
  126.         $this->inputHandler = $handler;
  127.     }
  128.  
  129.     /**
  130.      * Retrieves the current input handler.
  131.      */
  132.     public function getInputHandler() {
  133.         return $this->inputHandler;
  134.     }
  135.  
  136.     /** inits the project, called from main app */
  137.     function init() {
  138.         // set builtin properties
  139.         $this->setSystemProperties();
  140.         
  141.         // load default tasks
  142.         $taskdefs = Phing::getResourcePath("phing/tasks/defaults.properties");
  143.         
  144.         try { // try to load taskdefs
  145.             $props = new Properties();
  146.             $in = new PhingFile((string)$taskdefs);
  147.  
  148.             if ($in === null) {
  149.                 throw new BuildException("Can't load default task list");
  150.             }
  151.             $props->load($in);
  152.  
  153.             $enum = $props->propertyNames();
  154.             foreach($enum as $key) {
  155.                 $value = $props->getProperty($key);
  156.                 $this->addTaskDefinition($key, $value);
  157.             }
  158.         } catch (IOException $ioe) {
  159.             throw new BuildException("Can't load default task list");
  160.         }
  161.  
  162.         // load default tasks
  163.         $typedefs = Phing::getResourcePath("phing/types/defaults.properties");
  164.  
  165.         try { // try to load typedefs
  166.             $props = new Properties();
  167.             $in    = new PhingFile((string)$typedefs);
  168.             if ($in === null) {
  169.                 throw new BuildException("Can't load default datatype list");
  170.             }
  171.             $props->load($in);
  172.  
  173.             $enum = $props->propertyNames();
  174.             foreach($enum as $key) {
  175.                 $value = $props->getProperty($key);
  176.                 $this->addDataTypeDefinition($key, $value);
  177.             }
  178.         } catch(IOException $ioe) {
  179.             throw new BuildException("Can't load default datatype list");
  180.         }
  181.     }
  182.  
  183.     /** returns the global filterset (future use) */
  184.     function getGlobalFilterSet() {
  185.         return $this->globalFilterSet;
  186.     }
  187.  
  188.     // ---------------------------------------------------------
  189.     // Property methods
  190.     // ---------------------------------------------------------
  191.     
  192.     /**
  193.      * Sets a property. Any existing property of the same name
  194.      * is overwritten, unless it is a user property.
  195.      * @param string $name The name of property to set.
  196.      *             Must not be <code>null</code>.
  197.      * @param string $value The new value of the property.
  198.      *              Must not be <code>null</code>.
  199.      * @return void
  200.      */
  201.     public function setProperty($name, $value) {
  202.     
  203.         // command line properties take precedence
  204.         if (isset($this->userProperties[$name])) {
  205.             $this->log("Override ignored for user property " . $name, Project::MSG_VERBOSE);
  206.             return;
  207.         }
  208.  
  209.         if (isset($this->properties[$name])) {
  210.             $this->log("Overriding previous definition of property " . $name, Project::MSG_VERBOSE);
  211.         }
  212.  
  213.         $this->log("Setting project property: " . $name . " -> " . $value, Project::MSG_DEBUG);
  214.         $this->properties[$name] = $value;
  215.     }
  216.  
  217.     /**
  218.      * Sets a property if no value currently exists. If the property
  219.      * exists already, a message is logged and the method returns with
  220.      * no other effect.
  221.      *
  222.      * @param string $name The name of property to set.
  223.      *             Must not be <code>null</code>.
  224.      * @param string $value The new value of the property.
  225.      *              Must not be <code>null</code>.
  226.      * @since 2.0
  227.      */
  228.     public function setNewProperty($name, $value) {
  229.         if (isset($this->properties[$name])) {
  230.             $this->log("Override ignored for property " . $name, Project::MSG_DEBUG);
  231.             return;
  232.         }
  233.         $this->log("Setting project property: " . $name . " -> " . $value, Project::MSG_DEBUG);
  234.         $this->properties[$name] = $value;
  235.     }
  236.  
  237.     /**
  238.      * Sets a user property, which cannot be overwritten by
  239.      * set/unset property calls. Any previous value is overwritten.
  240.      * @param string $name The name of property to set.
  241.      *             Must not be <code>null</code>.
  242.      * @param string $value The new value of the property.
  243.      *              Must not be <code>null</code>.
  244.      * @see #setProperty()
  245.      */
  246.     public function setUserProperty($name, $value) {
  247.         $this->log("Setting ro project property: " . $name . " -> " . $value, Project::MSG_DEBUG);
  248.         $this->userProperties[$name] = $value;
  249.         $this->properties[$name] = $value;
  250.     }
  251.  
  252.     /**
  253.      * Sets a user property, which cannot be overwritten by set/unset
  254.      * property calls. Any previous value is overwritten. Also marks
  255.      * these properties as properties that have not come from the
  256.      * command line.
  257.      *
  258.      * @param string $name The name of property to set.
  259.      *             Must not be <code>null</code>.
  260.      * @param string $value The new value of the property.
  261.      *              Must not be <code>null</code>.
  262.      * @see #setProperty()
  263.      */
  264.     public function setInheritedProperty($name, $value) {
  265.         $this->inheritedProperties[$name] = $value;
  266.         $this->setUserProperty($name, $value);
  267.     }
  268.  
  269.     /**
  270.      * Sets a property unless it is already defined as a user property
  271.      * (in which case the method returns silently).
  272.      *
  273.      * @param name The name of the property.
  274.      *             Must not be <code>null</code>.
  275.      * @param value The property value. Must not be <code>null</code>.
  276.      */
  277.     private function setPropertyInternal($name, $value) {
  278.         if (isset($this->userProperties[$name])) {
  279.             $this->log("Override ignored for user property " . $name, Project::MSG_VERBOSE);
  280.             return;
  281.         }
  282.         $this->properties[$name] = $value;
  283.     }
  284.  
  285.     /**
  286.      * Returns the value of a property, if it is set.
  287.      *
  288.      * @param string $name The name of the property.
  289.      *             May be <code>null</code>, in which case
  290.      *             the return value is also <code>null</code>.
  291.      * @return string The property value, or <code>null</code> for no match
  292.      *         or if a <code>null</code> name is provided.
  293.      */
  294.     public function getProperty($name) {
  295.         if (!isset($this->properties[$name])) {
  296.             return null;
  297.         }
  298.         return $this->properties[$name];
  299.     }
  300.  
  301.     /**
  302.      * Replaces ${} style constructions in the given value with the
  303.      * string value of the corresponding data types.
  304.      *
  305.      * @param value The string to be scanned for property references.
  306.      *              May be <code>null</code>.
  307.      *
  308.      * @return the given string with embedded property names replaced
  309.      *         by values, or <code>null</code> if the given string is
  310.      *         <code>null</code>.
  311.      *
  312.      * @exception BuildException if the given value has an unclosed
  313.      *                           property name, e.g. <code>${xxx</code>
  314.      */
  315.     public function replaceProperties($value) {
  316.         return ProjectConfigurator::replaceProperties($this, $value, $this->properties);
  317.     }
  318.  
  319.     /**
  320.      * Returns the value of a user property, if it is set.
  321.      *
  322.      * @param string $name The name of the property.
  323.      *             May be <code>null</code>, in which case
  324.      *             the return value is also <code>null</code>.
  325.      * @return string  The property value, or <code>null</code> for no match
  326.      *         or if a <code>null</code> name is provided.
  327.      */
  328.      public function getUserProperty($name) {
  329.         if (!isset($this->userProperties[$name])) {
  330.             return null;
  331.         }
  332.         return $this->userProperties[$name];
  333.     }
  334.  
  335.     /**
  336.      * Returns a copy of the properties table.
  337.      * @return array A hashtable containing all properties
  338.      *         (including user properties).
  339.      */
  340.     public function getProperties() {
  341.         return $this->properties;
  342.     }
  343.  
  344.     /**
  345.      * Returns a copy of the user property hashtable
  346.      * @return a hashtable containing just the user properties
  347.      */
  348.     public function getUserProperties() {
  349.         return $this->userProperties;
  350.     }
  351.  
  352.     /**
  353.      * Copies all user properties that have been set on the command
  354.      * line or a GUI tool from this instance to the Project instance
  355.      * given as the argument.
  356.      *
  357.      * <p>To copy all "user" properties, you will also have to call
  358.      * {@link #copyInheritedProperties copyInheritedProperties}.</p>
  359.      *
  360.      * @param Project $other the project to copy the properties to.  Must not be null.
  361.      * @return void
  362.      * @since phing 2.0
  363.      */
  364.     public function copyUserProperties(Project $other) {        
  365.         foreach($this->userProperties as $arg => $value) {
  366.             if (isset($this->inheritedProperties[$arg])) {
  367.                 continue;
  368.             }
  369.             $other->setUserProperty($arg, $value);
  370.         }
  371.     }
  372.  
  373.     /**
  374.      * Copies all user properties that have not been set on the
  375.      * command line or a GUI tool from this instance to the Project
  376.      * instance given as the argument.
  377.      *
  378.      * <p>To copy all "user" properties, you will also have to call
  379.      * {@link #copyUserProperties copyUserProperties}.</p>
  380.      *
  381.      * @param other the project to copy the properties to.  Must not be null.
  382.      *
  383.      * @since phing 2.0
  384.      */
  385.     public function copyInheritedProperties(Project $other) {
  386.         foreach($this->userProperties as $arg => $value) {
  387.             if ($other->getUserProperty($arg) !== null) {
  388.                 continue;
  389.             }
  390.             $other->setInheritedProperty($arg, $value);
  391.         }        
  392.     }
  393.     
  394.     // ---------------------------------------------------------
  395.     //  END Properties methods
  396.     // ---------------------------------------------------------
  397.  
  398.  
  399.     function setDefaultTarget($targetName) {
  400.         $this->defaultTarget = (string) trim($targetName);
  401.     }
  402.  
  403.     function getDefaultTarget() {
  404.         return (string) $this->defaultTarget;
  405.     }
  406.  
  407.     /**
  408.      * Sets the name of the current project
  409.      *
  410.      * @param    string   name of project
  411.      * @return   void
  412.      * @access   public
  413.      * @author   Andreas Aderhold, andi@binarycloud.com
  414.      */
  415.  
  416.     function setName($name) {
  417.         $this->name = (string) trim($name);
  418.         $this->setProperty("phing.project.name", $this->name);
  419.     }
  420.  
  421.     /**
  422.      * Returns the name of this project
  423.      *
  424.      * @returns  string  projectname
  425.      * @access   public
  426.      * @author   Andreas Aderhold, andi@binarycloud.com
  427.      */
  428.     function getName() {
  429.         return (string) $this->name;
  430.     }
  431.  
  432.     /** Set the projects description */
  433.     function setDescription($description) {
  434.         $this->description = (string) trim($description);
  435.     }
  436.  
  437.     /** return the description, null otherwise */
  438.     function getDescription() {
  439.         return $this->description;
  440.     }
  441.  
  442.     /** Set basedir object from xml*/
  443.     function setBasedir($dir) {
  444.         if ($dir instanceof PhingFile) {
  445.             $dir = $dir->getAbsolutePath();
  446.         }
  447.  
  448.         $dir = $this->fileUtils->normalize($dir);
  449.  
  450.         $dir = new PhingFile((string) $dir);
  451.         if (!$dir->exists()) {
  452.             throw new BuildException("Basedir ".$dir->getAbsolutePath()." does not exist");
  453.         }
  454.         if (!$dir->isDirectory()) {
  455.             throw new BuildException("Basedir ".$dir->getAbsolutePath()." is not a directory");
  456.         }
  457.         $this->basedir = $dir;
  458.         $this->setPropertyInternal("project.basedir", $this->basedir->getAbsolutePath());
  459.         $this->log("Project base dir set to: " . $this->basedir->getPath(), Project::MSG_VERBOSE);
  460.         
  461.         // [HL] added this so that ./ files resolve correctly.  This may be a mistake ... or may be in wrong place.                
  462.         chdir($dir->getAbsolutePath());
  463.     }
  464.  
  465.     /**
  466.      * Returns the basedir of this project
  467.      *
  468.      * @returns  PhingFile  Basedir PhingFile object
  469.      * @access   public
  470.      * @throws   BuildException
  471.      * @author   Andreas Aderhold, andi@binarycloud.com
  472.      */
  473.     function getBasedir() {
  474.         if ($this->basedir === null) {            
  475.             try { // try to set it
  476.                 $this->setBasedir(".");
  477.             } catch (BuildException $exc) {
  478.                 throw new BuildException("Can not set default basedir. ".$exc->getMessage());
  479.             }
  480.         }
  481.         return $this->basedir;
  482.     }
  483.  
  484.     /**
  485.      * Sets system properties and the environment variables for this project.
  486.      * 
  487.      * @return void
  488.      */
  489.     function setSystemProperties() {
  490.         
  491.         // first get system properties
  492.         $systemP = array_merge( self::getProperties(), Phing::getProperties() );
  493.         foreach($systemP as $name => $value) {
  494.             $this->setPropertyInternal($name, $value);
  495.         }
  496.         
  497.         // and now the env vars
  498.         foreach($_SERVER as $name => $value) {
  499.             // skip arrays
  500.             if (is_array($value)) {
  501.                 continue;
  502.             }
  503.             $this->setPropertyInternal('env.' . $name, $value);
  504.         }
  505.         return true;
  506.     }
  507.  
  508.  
  509.     /**
  510.      * Adds a task definition.
  511.      * @param string $name Name of tag.
  512.      * @param string $class The class path to use.
  513.      * @param string $classpath The classpat to use.
  514.      */
  515.     function addTaskDefinition($name, $class, $classpath = null) {
  516.         $name  = $name;
  517.         $class = $class;
  518.         if ($class === "") {
  519.             $this->log("Task $name has no class defined.", Project::MSG_ERR);
  520.         }  elseif (!isset($this->taskdefs[$name])) {
  521.             Phing::import($class, $classpath);
  522.             $this->taskdefs[$name] = $class;
  523.             $this->log("  +Task definiton: $name ($class)", Project::MSG_DEBUG);
  524.         } else {
  525.             $this->log("Task $name ($class) already registerd, skipping", Project::MSG_VERBOSE);
  526.         }
  527.     }
  528.  
  529.     function &getTaskDefinitions() {
  530.         return $this->taskdefs;
  531.     }
  532.  
  533.     /**
  534.      * Adds a data type definition.
  535.      * @param string $name Name of tag.
  536.      * @param string $class The class path to use.
  537.      * @param string $classpath The classpat to use.
  538.      */
  539.     function addDataTypeDefinition($typeName, $typeClass, $classpath = null) {    
  540.         if (!isset($this->typedefs[$typeName])) {        
  541.             Phing::import($typeClass, $classpath);
  542.             $this->typedefs[$typeName] = $typeClass;
  543.             $this->log("  +User datatype: $typeName ($typeClass)", Project::MSG_DEBUG);
  544.         } else {
  545.             $this->log("Type $name ($class) already registerd, skipping", Project::MSG_VERBOSE);
  546.         }
  547.     }
  548.  
  549.     function getDataTypeDefinitions() {
  550.         return $this->typedefs;
  551.     }
  552.  
  553.     /** add a new target to the project */
  554.     function addTarget($targetName, &$target) {
  555.         if (isset($this->targets[$targetName])) {
  556.             throw new BuildException("Duplicate target: $targetName");
  557.         }
  558.         $this->addOrReplaceTarget($targetName, $target);
  559.     }
  560.  
  561.     function addOrReplaceTarget($targetName, &$target) {
  562.         $this->log("  +Target: $targetName", Project::MSG_DEBUG);
  563.         $target->setProject($this);
  564.         $this->targets[$targetName] = $target;
  565.     }
  566.  
  567.     function getTargets() {
  568.         return $this->targets;
  569.     }
  570.  
  571.     /**
  572.      * Create a new task instance and return reference to it. This method is
  573.      * sorta factory like. A _local_ instance is created and a reference returned to
  574.      * that instance. Usually PHP destroys local variables when the function call
  575.      * ends. But not if you return a reference to that variable.
  576.      * This is kinda error prone, because if no reference exists to the variable
  577.      * it is destroyed just like leaving the local scope with primitive vars. There's no
  578.      * central place where the instance is stored as in other OOP like languages.
  579.      *
  580.      * [HL] Well, ZE2 is here now, and this is  still working. We'll leave this alone
  581.      * unless there's any good reason not to.
  582.      *
  583.      * @param    string    $taskType    Task name
  584.      * @returns  Task                A task object
  585.      * @throws   BuildException
  586.      *           Exception
  587.      */
  588.     function createTask($taskType) {
  589.         try {
  590.             $cls = "";
  591.             $tasklwr = strtolower($taskType);
  592.             foreach ($this->taskdefs as $name => $class) {
  593.                 if (strtolower($name) === $tasklwr) {
  594.                     $cls = StringHelper::unqualify($class);                                    
  595.                     break;
  596.                 }
  597.             }
  598.             
  599.             if ($cls === "") {
  600.                 return null;
  601.             }
  602.             
  603.             if (!class_exists($cls)) {
  604.                 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.)");
  605.             }
  606.             
  607.             $o = new $cls();        
  608.     
  609.             if ($o instanceof Task) {
  610.                 $task = $o;
  611.             } else {
  612.                 $this->log ("  (Using TaskAdapter for: $taskType)", Project::MSG_DEBUG);
  613.                 // not a real task, try adapter
  614.                 $taskA = new TaskAdapter();
  615.                 $taskA->setProxy($o);
  616.                 $task = $taskA;
  617.             }
  618.             $task->setProject($this);
  619.             $task->setTaskType($taskType);
  620.             // set default value, can be changed by the user
  621.             $task->setTaskName($taskType);
  622.             $this->log ("  +Task: " . $taskType, Project::MSG_DEBUG);
  623.         } catch (Exception $t) {
  624.             throw new BuildException("Could not create task of type: " . $taskType, $t);
  625.         }
  626.         // everything fine return reference
  627.         return $task;
  628.     }
  629.  
  630.     /**
  631.      * Create a task instance and return reference to it
  632.      * See createTask() for explanation how this works
  633.      *
  634.      * @param    string   Type name
  635.      * @returns  object   A datatype object
  636.      * @throws   BuildException
  637.      *           Exception
  638.      */
  639.     function createDataType($typeName) {        
  640.         try {
  641.             $cls = "";
  642.             $typelwr = strtolower($typeName);
  643.             foreach ($this->typedefs as $name => $class) {
  644.                 if (strtolower($name) === $typelwr) {
  645.                     $cls = StringHelper::unqualify($class);                                    
  646.                     break;
  647.                 }
  648.             }
  649.             
  650.             if ($cls === "") {
  651.                 return null;
  652.             }
  653.             
  654.             if (!class_exists($cls)) {
  655.                 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.)");
  656.             }
  657.             
  658.             $type = new $cls();
  659.             $this->log("  +Type: $typeName", Project::MSG_DEBUG);
  660.             if (!($type instanceof DataType)) {
  661.                 throw new Exception("$class is not an instance of phing.types.DataType");
  662.             }
  663.             if ($type instanceof ProjectComponent) {
  664.                 $type->setProject($this);
  665.             }
  666.         } catch (Exception $t) {
  667.             throw new BuildException("Could not create type: $typeName", $t);
  668.         }
  669.         // everything fine return reference
  670.         return $type;
  671.     }
  672.  
  673.     /**
  674.      * Executes a list of targets
  675.      *
  676.      * @param    array  List of target names to execute
  677.      * @returns  void
  678.      * @throws   BuildException
  679.      */
  680.     function executeTargets($targetNames) {
  681.         foreach($targetNames as $tname) {
  682.             $this->executeTarget($tname);
  683.         }
  684.     }
  685.  
  686.     /**
  687.      * Executes a target
  688.      *
  689.      * @param    string  Name of Target to execute
  690.      * @returns  void
  691.      * @throws   BuildException
  692.      */
  693.     function executeTarget($targetName) {
  694.  
  695.         // complain about executing void
  696.         if ($targetName === null) {
  697.             throw new BuildException("No target specified");
  698.         }
  699.  
  700.         // invoke topological sort of the target tree and run all targets
  701.         // until targetName occurs.
  702.         $sortedTargets = $this->_topoSort($targetName, $this->targets);        
  703.  
  704.         $curIndex = (int) 0;
  705.         $curTarget = null;
  706.         do {
  707.             try {
  708.                 $curTarget = $sortedTargets[$curIndex++];
  709.                 $curTarget->performTasks();
  710.             } catch (BuildException $exc) {
  711.                 $this->log("Execution of target \"".$curTarget->getName()."\" failed for the following reason: ".$exc->getMessage(), Project::MSG_ERR);
  712.                 throw $exc;
  713.             }
  714.         } while ($curTarget->getName() !== $targetName);
  715.     }
  716.  
  717.  
  718.     function resolveFile($fileName, $rootDir = null) {
  719.         if ($rootDir === null) {
  720.             return $this->fileUtils->resolveFile($this->basedir, $fileName);
  721.         } else {
  722.             return $this->fileUtils->resolveFile($rootDir, $fileName);
  723.         }
  724.     }    
  725.  
  726.     /**
  727.      * Topologically sort a set of Targets.
  728.      * @param  $root is the (String) name of the root Target. The sort is
  729.      *         created in such a way that the sequence of Targets until the root
  730.      *         target is the minimum possible such sequence.
  731.      * @param  $targets is a array representing a "name to Target" mapping
  732.      * @return An array of Strings with the names of the targets in
  733.      *         sorted order.
  734.      */
  735.     function _topoSort($root, &$targets) {
  736.  
  737.         $root     = (string) $root;
  738.         $ret      = array();
  739.         $state    = array();
  740.         $visiting = array();
  741.  
  742.         // We first run a DFS based sort using the root as the starting node.
  743.         // This creates the minimum sequence of Targets to the root node.
  744.         // We then do a sort on any remaining unVISITED targets.
  745.         // This is unnecessary for doing our build, but it catches
  746.         // circular dependencies or missing Targets on the entire
  747.         // dependency tree, not just on the Targets that depend on the
  748.         // build Target.
  749.  
  750.         $this->_tsort($root, $targets, $state, $visiting, $ret);
  751.  
  752.         $retHuman = "";
  753.         for ($i=0, $_i=count($ret); $i < $_i; $i++) {
  754.             $retHuman .= $ret[$i]->toString()." ";
  755.         }
  756.         $this->log("Build sequence for target '$root' is: $retHuman", Project::MSG_VERBOSE);
  757.  
  758.         $keys = array_keys($targets);
  759.         while($keys) {
  760.             $curTargetName = (string) array_shift($keys);
  761.             if (!isset($state[$curTargetName])) {
  762.                 $st = null;
  763.             } else {
  764.                 $st = (string) $state[$curTargetName];
  765.             }
  766.  
  767.             if ($st === null) {
  768.                 $this->_tsort($curTargetName, $targets, $state, $visiting, $ret);
  769.             } elseif ($st === "VISITING") {
  770.                 throw new Exception("Unexpected node in visiting state: $curTargetName");
  771.             }
  772.         }
  773.  
  774.         $retHuman = "";
  775.         for ($i=0,$_i=count($ret); $i < $_i; $i++) {
  776.             $retHuman .= $ret[$i]->toString()." ";
  777.         }
  778.         $this->log("Complete build sequence is: $retHuman", Project::MSG_VERBOSE);
  779.  
  780.         return $ret;
  781.     }
  782.  
  783.     // one step in a recursive DFS traversal of the target dependency tree.
  784.     // - The array "state" contains the state (VISITED or VISITING or null)
  785.     //   of all the target names.
  786.     // - The stack "visiting" contains a stack of target names that are
  787.     //   currently on the DFS stack. (NB: the target names in "visiting" are
  788.     //    exactly the target names in "state" that are in the VISITING state.)
  789.     // 1. Set the current target to the VISITING state, and push it onto
  790.     //    the "visiting" stack.
  791.     // 2. Throw a BuildException if any child of the current node is
  792.     //    in the VISITING state (implies there is a cycle.) It uses the
  793.     //    "visiting" Stack to construct the cycle.
  794.     // 3. If any children have not been VISITED, tsort() the child.
  795.     // 4. Add the current target to the Vector "ret" after the children
  796.     //    have been visited. Move the current target to the VISITED state.
  797.     //    "ret" now contains the sorted sequence of Targets upto the current
  798.     //    Target.
  799.  
  800.     function _tsort($root, &$targets, &$state, &$visiting, &$ret) {
  801.         $state[$root] = "VISITING";
  802.         $visiting[]  = $root;
  803.  
  804.         if (!isset($targets[$root]) || !($targets[$root] instanceof Target)) {
  805.             $target = null;
  806.         } else {
  807.             $target = $targets[$root];
  808.         }
  809.  
  810.         // make sure we exist
  811.         if ($target === null) {
  812.             $sb = "Target '$root' does not exist in this project.";
  813.             array_pop($visiting);
  814.             if (!empty($visiting)) {
  815.                 $parent = (string) $visiting[count($visiting)-1];
  816.                 $sb .= "It is used from target '$parent'.";
  817.             }
  818.             throw new BuildException($sb);
  819.         }
  820.  
  821.         $deps = $target->getDependencies();
  822.  
  823.         while($deps) {
  824.             $cur = (string) array_shift($deps);
  825.             if (!isset($state[$cur])) {
  826.                 $m = null;
  827.             } else {
  828.                 $m = (string) $state[$cur];
  829.             }
  830.             if ($m === null) {
  831.                 // not been visited
  832.                 $this->_tsort($cur, $targets, $state, $visiting, $ret);
  833.             } elseif ($m == "VISITING") {
  834.                 // currently visiting this node, so have a cycle
  835.                 throw $this->_makeCircularException($cur, $visiting);
  836.             }
  837.         }
  838.  
  839.         $p = (string) array_pop($visiting);
  840.         if ($root !== $p) {
  841.             throw new Exception("Unexpected internal error: expected to pop $root but got $p");
  842.         }
  843.  
  844.         $state[$root] = "VISITED";
  845.         $ret[] = $target;
  846.     }
  847.  
  848.     function _makeCircularException($end, $stk) {
  849.         $sb = "Circular dependency: $end";
  850.         do {
  851.             $c = (string) array_pop($stk);
  852.             $sb .= " <- ".$c;
  853.         } while($c != $end);
  854.         return new BuildException($sb);
  855.     }
  856.  
  857.     /**
  858.      * Adds a reference to an object. This method is called when the parser
  859.      * detects a id="foo" attribute. It passes the id as $name and a reference
  860.      * to the object assigned to this id as $value
  861.      */
  862.     function addReference($name, $object) {
  863.         if (isset($this->references[$name])) {
  864.             $this->log("Overriding previous definition of reference to $name", Project::MSG_WARN);
  865.         }
  866.         $this->log("Adding reference: $name -> ".get_class($object), Project::MSG_DEBUG);
  867.         $this->references[$name] = $object;
  868.     }
  869.  
  870.     /**
  871.      * Returns the references array.
  872.      * @return array
  873.      */
  874.     function getReferences() {
  875.         return $this->references;
  876.     }
  877.     
  878.     /**
  879.      * Returns a specific reference.
  880.      * @param string $key The reference id/key.
  881.      * @return Reference or null if not defined
  882.      */
  883.     function getReference($key)
  884.     {
  885.         if (isset($this->references[$key])) {
  886.             return $this->references[$key];
  887.         }
  888.         return null; // just to be explicit
  889.     }
  890.  
  891.     /**
  892.      * Abstracting and simplifyling Logger calls for project messages
  893.      */
  894.     function log($msg, $level = Project::MSG_INFO) {
  895.         $this->logObject($this, $msg, $level);
  896.     }
  897.  
  898.     function logObject($obj, $msg, $level) {
  899.         $this->fireMessageLogged($obj, $msg, $level);
  900.     }
  901.  
  902.     function addBuildListener(BuildListener $listener) {
  903.         $this->listeners[] = $listener;
  904.     }
  905.  
  906.     function removeBuildListener(BuildListener $listener) {
  907.         $newarray = array();
  908.         for ($i=0, $size=count($this->listeners); $i < $size; $i++) {
  909.             if ($this->listeners[$i] !== $listener) {
  910.                 $newarray[] = $this->listeners[$i];
  911.             }
  912.         }
  913.         $this->listeners = $newarray;
  914.     }
  915.  
  916.     function getBuildListeners() {
  917.         return $this->listeners;
  918.     }
  919.  
  920.     function fireBuildStarted() {
  921.         $event = new BuildEvent($this);        
  922.         foreach($this->listeners as $listener) {
  923.             $listener->buildStarted($event);
  924.         }
  925.     }
  926.  
  927.     function fireBuildFinished($exception) {        
  928.         $event = new BuildEvent($this);
  929.         $event->setException($exception);
  930.         foreach($this->listeners as $listener) {
  931.             $listener->buildFinished($event);
  932.         }
  933.     }
  934.  
  935.     function fireTargetStarted($target) {
  936.         $event = new BuildEvent($target);        
  937.            foreach($this->listeners as $listener) {
  938.             $listener->targetStarted($event);
  939.         }
  940.     }
  941.  
  942.     function fireTargetFinished($target, $exception) {
  943.         $event = new BuildEvent($target);        
  944.         $event->setException($exception);
  945.         foreach($this->listeners as $listener) {
  946.             $listener->targetFinished($event);
  947.         }
  948.     }
  949.  
  950.     function fireTaskStarted($task) {
  951.         $event = new BuildEvent($task);        
  952.         foreach($this->listeners as $listener) {
  953.             $listener->taskStarted($event);
  954.         }
  955.     }
  956.  
  957.     function fireTaskFinished($task, $exception) {
  958.         $event = new BuildEvent($task);        
  959.         $event->setException($exception);
  960.         foreach($this->listeners as $listener) {
  961.             $listener->taskFinished($event);
  962.         }
  963.     }
  964.  
  965.     function fireMessageLoggedEvent($event, $message, $priority) {
  966.         $event->setMessage($message, $priority);
  967.         foreach($this->listeners as $listener) {
  968.             $listener->messageLogged($event);
  969.         }
  970.     }
  971.  
  972.     function fireMessageLogged($object, $message, $priority) {
  973.         $this->fireMessageLoggedEvent(new BuildEvent($object), $message, $priority);
  974.     }
  975. }
  976.