[ Index ]
 

Code source de Symfony 1.0.0

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/lib/vendor/propel-generator/classes/propel/phing/ -> AbstractPropelDataModelTask.php (source)

   1  <?php
   2  
   3  /*
   4   *  $Id: AbstractPropelDataModelTask.php 337 2006-02-15 14:41:54Z hans $
   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://propel.phpdb.org>.
  21   */
  22  
  23  //include_once 'phing/tasks/ext/CapsuleTask.php';
  24  require_once 'phing/Task.php';
  25  include_once 'propel/engine/database/model/AppData.php';
  26  include_once 'propel/engine/database/model/Database.php';
  27  include_once 'propel/engine/database/transform/XmlToAppData.php';
  28  
  29  /**
  30   * An abstract base Propel task to perform work related to the XML schema file.
  31   *
  32   * The subclasses invoke templates to do the actual writing of the resulting files.
  33   *
  34   * @author Hans Lellelid <hans@xmpl.org> (Propel)
  35   * @author Jason van Zyl <jvanzyl@zenplex.com> (Torque)
  36   * @author Daniel Rall <dlr@finemaltcoding.com> (Torque)
  37   * @package propel.phing
  38   */
  39  abstract class AbstractPropelDataModelTask extends Task {
  40  
  41      /**
  42       * Fileset of XML schemas which represent our data models.
  43       * @var array Fileset[]
  44       */
  45      protected $schemaFilesets = array();
  46  
  47      /**
  48       * Data models that we collect. One from each XML schema file.
  49       */
  50      protected $dataModels = array();
  51  
  52      /**
  53       * Have datamodels been initialized?
  54       * @var boolean
  55       */
  56      private $dataModelsLoaded = false;
  57  
  58      /**
  59       * Map of data model name to database name.
  60       * Should probably stick to the convention
  61       * of them being the same but I know right now
  62       * in a lot of cases they won't be.
  63       */
  64      protected $dataModelDbMap;
  65  
  66      /**
  67       * Hashtable containing the names of all the databases
  68       * in our collection of schemas.
  69       */
  70      protected $databaseNames; // doesn't seem to be used anywhere
  71  
  72      /**
  73       * The target database(s) we are generating SQL
  74       * for. Right now we can only deal with a single
  75       * target, but we will support multiple targets
  76       * soon.
  77       */
  78      protected $targetDatabase;
  79  
  80      /**
  81       * DB encoding to use for XmlToAppData object
  82       */
  83      protected $dbEncoding = 'iso-8859-1';
  84  
  85      /**
  86       * Target PHP package to place the generated files in.
  87       */
  88      protected $targetPackage;
  89  
  90      /**
  91       * @var Mapper
  92       */
  93      protected $mapperElement;
  94  
  95      /**
  96       * Destination directory for results of template scripts.
  97       * @var PhingFile
  98       */
  99      protected $outputDirectory;
 100  
 101      /**
 102       * Path where Capsule looks for templates.
 103       * @var PhingFile
 104       */
 105      protected $templatePath;
 106  
 107      /**
 108       * Whether to package the datamodels or not
 109       * @var PhingFile
 110       */
 111      protected $packageObjectModel;
 112      
 113      /**
 114       * Whether to perform validation (XSD) on the schema.xml file(s).
 115       * @var boolean
 116       */
 117      protected $validate;
 118      
 119      /**
 120       * The XSD schema file to use for validation.
 121       * @var PhingFile
 122       */
 123      protected $xsdFile;
 124      
 125      /**
 126       * XSL file to use to normalize (or otherwise transform) schema before validation.
 127       * @var PhingFile
 128       */
 129      protected $xslFile;
 130      
 131      /**
 132       * Return the data models that have been
 133       * processed.
 134       *
 135       * @return List data models
 136       */
 137      public function getDataModels()
 138      {
 139          if (!$this->dataModelsLoaded) $this->loadDataModels();
 140          return $this->dataModels;
 141      }
 142  
 143      /**
 144       * Return the data model to database name map.
 145       *
 146       * @return Hashtable data model name to database name map.
 147       */
 148      public function getDataModelDbMap()
 149      {
 150          if (!$this->dataModelsLoaded) $this->loadDataModels();
 151          return $this->dataModelDbMap;
 152      }
 153  
 154      /**
 155       * Adds a set of xml schema files (nested fileset attribute).
 156       *
 157       * @param set a Set of xml schema files
 158       */
 159      public function addSchemaFileset(Fileset $set)
 160      {
 161          $this->schemaFilesets[] = $set;
 162      }
 163  
 164      /**
 165       * Get the current target database.
 166       *
 167       * @return String target database(s)
 168       */
 169      public function getTargetDatabase()
 170      {
 171          return $this->targetDatabase;
 172      }
 173  
 174      /**
 175       * Set the current target database. (e.g. mysql, oracle, ..)
 176       *
 177       * @param v target database(s)
 178       */
 179      public function setTargetDatabase($v)
 180      {
 181          $this->targetDatabase = $v;
 182      }
 183  
 184      /**
 185       * Get the current target package.
 186       *
 187       * @return string target PHP package.
 188       */
 189      public function getTargetPackage()
 190      {
 191          return $this->targetPackage;
 192      }
 193  
 194      /**
 195       * Set the current target package. This is where generated PHP classes will
 196       * live.
 197       *
 198       * @param string $v target PHP package.
 199       */
 200      public function setTargetPackage($v)
 201      {
 202          $this->targetPackage = $v;
 203      }
 204  
 205      /**
 206       * Set the packageObjectModel switch on/off
 207       *
 208       * @param string $v The build.property packageObjectModel
 209       */
 210      public function setPackageObjectModel($v)
 211      {
 212          $this->packageObjectModel = ($v === '1' ? true : false);
 213      }
 214      
 215      /**
 216       * Set whether to perform validation on the datamodel schema.xml file(s).
 217       * @param boolean $v
 218       */
 219  	public function setValidate($v)
 220      {
 221          $this->validate = $v;
 222      }
 223      
 224      /**
 225       * Set the XSD schema to use for validation of any datamodel schema.xml file(s).
 226       * @param $v PhingFile
 227       */
 228  	public function setXsd(PhingFile $v)
 229      {
 230          $this->xsdFile = $v;
 231      }
 232      
 233      /**
 234       * Set the normalization XSLT to use to transform datamodel schema.xml file(s) before validation and parsing.
 235       * @param $v PhingFile
 236       */
 237  	public function setXsl(PhingFile $v)
 238      {
 239          $this->xslFile = $v;
 240      }
 241      
 242      /**
 243       * [REQUIRED] Set the path where Capsule will look
 244       * for templates using the file template
 245       * loader.
 246       * @return void
 247       * @throws Exception
 248       */
 249      public function setTemplatePath($templatePath) {
 250          $resolvedPath = "";
 251          $tok = strtok($templatePath, ",");
 252          while ( $tok ) {
 253              // resolve relative path from basedir and leave
 254              // absolute path untouched.
 255              $fullPath = $this->project->resolveFile($tok);
 256              $cpath = $fullPath->getCanonicalPath();
 257              if ($cpath === false) {
 258                  $this->log("Template directory does not exist: " . $fullPath->getAbsolutePath());
 259              } else {
 260                  $resolvedPath .= $cpath;
 261              }
 262              $tok = strtok(",");
 263              if ( $tok ) {
 264                  $resolvedPath .= ",";
 265              }
 266          }
 267          $this->templatePath = $resolvedPath;
 268       }
 269  
 270      /**
 271       * Get the path where Velocity will look
 272       * for templates using the file template
 273       * loader.
 274       * @return string
 275       */
 276      public function getTemplatePath() {
 277          return $this->templatePath;
 278      }
 279  
 280      /**
 281       * [REQUIRED] Set the output directory. It will be
 282       * created if it doesn't exist.
 283       * @param PhingFile $outputDirectory
 284       * @return void
 285       * @throws Exception
 286       */
 287      public function setOutputDirectory(PhingFile $outputDirectory) {
 288          try {
 289              if (!$outputDirectory->exists()) {
 290                  $this->log("Output directory does not exist, creating: " . $outputDirectory->getPath(),PROJECT_MSG_VERBOSE);
 291                  if (!$outputDirectory->mkdirs()) {
 292                      throw new IOException("Unable to create Ouptut directory: " . $outputDirectory->getAbsolutePath());
 293                  }
 294              }
 295              $this->outputDirectory = $outputDirectory->getCanonicalPath();
 296          } catch (IOException $ioe) {
 297              throw new BuildException($ioe);
 298          }
 299      }
 300  
 301      /**
 302       * Set the current target database encoding.
 303       *
 304       * @param v target database encoding
 305       */
 306      public function setDbEncoding($v)
 307      {
 308         $this->dbEncoding = $v;
 309      }
 310  
 311      /**
 312       * Get the output directory.
 313       * @return string
 314       */
 315      public function getOutputDirectory() {
 316          return $this->outputDirectory;
 317      }
 318  
 319      /**
 320       * Nested creator, creates one Mapper for this task.
 321       *
 322       * @return  Mapper  The created Mapper type object.
 323       * @throws  BuildException
 324       */
 325      public function createMapper() {
 326          if ($this->mapperElement !== null) {
 327              throw new BuildException("Cannot define more than one mapper.", $this->location);
 328          }
 329          $this->mapperElement = new Mapper($this->project);
 330          return $this->mapperElement;
 331      }
 332  
 333      /**
 334       * Maps the passed in name to a new filename & returns resolved File object.
 335       * @param string $from
 336       * @return PhingFile Resolved File object.
 337       * @throws BuilException    - if no Mapper element se
 338       *                          - if unable to map new filename.
 339       */
 340      protected function getMappedFile($from)
 341      {
 342          if(!$this->mapperElement) {
 343              throw new BuildException("This task requires you to use a <mapper/> element to describe how filename changes should be handled.");
 344          }
 345  
 346          $mapper = $this->mapperElement->getImplementation();
 347          $mapped = $mapper->main($from);
 348          if (!$mapped) {
 349              throw new BuildException("Cannot create new filename based on: " . $from);
 350          }
 351          // Mappers always return arrays since it's possible for some mappers to map to multiple names.
 352          $outFilename = array_shift($mapped);
 353          $outFile = new PhingFile($this->getOutputDirectory(), $outFilename);
 354          return $outFile;
 355      }
 356      
 357      /**
 358       * Get the Platform class based on the target database type.
 359       * @return Platform Class that implements the Platform interface.
 360       */
 361  	protected function getPlatformForTargetDatabase()
 362      {
 363      
 364          $classpath = $this->getPropelProperty("platformClass");
 365          if (empty($classpath)) {
 366              throw new BuildException("Unable to find class path for '$propname' property.");
 367          }
 368          
 369          // This is a slight hack to workaround camel case inconsistencies for the DDL classes.
 370          // Basically, we want to turn ?.?.?.sqliteDDLBuilder into ?.?.?.SqliteDDLBuilder
 371          $lastdotpos = strrpos($classpath, '.');
 372          if ($lastdotpos) $classpath{$lastdotpos+1} = strtoupper($classpath{$lastdotpos+1});
 373          else ucfirst($classpath);
 374          
 375          if (empty($classpath)) {
 376              throw new BuildException("Unable to find class path for '$propname' property.");
 377          }
 378          
 379          $clazz = Phing::import($classpath);
 380          return new $clazz();
 381      }
 382      
 383      /**
 384       * Gets all matching XML schema files and loads them into data models for class.
 385       * @return void
 386       */
 387      protected function loadDataModels()
 388      {
 389          $ads = array();
 390          
 391          // Get all matched files from schemaFilesets
 392          foreach($this->schemaFilesets as $fs) {
 393              $ds = $fs->getDirectoryScanner($this->project);
 394              $srcDir = $fs->getDir($this->project);
 395  
 396              $dataModelFiles = $ds->getIncludedFiles();
 397              
 398              $platform = $this->getPlatformForTargetDatabase();
 399              
 400              // Make a transaction for each file
 401              foreach($dataModelFiles as $dmFilename) {
 402              
 403                  $this->log("Processing: ".$dmFilename);
 404                  $xmlFile = new PhingFile($srcDir, $dmFilename);
 405                  
 406                  $dom = new DomDocument('1.0', 'UTF-8');
 407                  $dom->load($xmlFile->getAbsolutePath());
 408  
 409                  // normalize (or transform) the XML document using XSLT
 410                  if ($this->xslFile) {
 411                      $this->log("Transforming " . $xmlFile->getPath() . " using stylesheet " . $this->xslFile->getPath(), PROJECT_MSG_VERBOSE);
 412                      if (!class_exists('XSLTProcessor')) {
 413                          $this->log("Could not perform XLST transformation.  Make sure PHP has been compiled/configured to support XSLT.", PROJECT_MSG_ERR);
 414                      } else {                        
 415                          // normalize the document using normalizer stylesheet
 416                          
 417                          $xsl = new XsltProcessor();
 418                          $xsl->importStyleSheet(DomDocument::load($this->xslFile->getAbsolutePath()));
 419                          $transformed = $xsl->transformToDoc($dom);
 420                          $newXmlFilename = substr($xmlFile->getName(), 0, strrpos($xmlFile->getName(), '.')) . '-transformed.xml';
 421                          
 422                          // now overwrite previous vars to point to newly transformed file
 423                          $xmlFile = new PhingFile($srcDir, $newXmlFilename);
 424                          $transformed->save($xmlFile->getAbsolutePath());
 425                          $this->log("\t- Using new (post-transformation) XML file: " . $xmlFile->getPath(), PROJECT_MSG_VERBOSE);                                        
 426                          
 427                          $dom = new DomDocument('1.0', 'UTF-8');
 428                          $dom->load($xmlFile->getAbsolutePath());        
 429                      }
 430                  }
 431                  
 432                  // validate the XML document using XSD schema
 433                  if ($this->validate && $this->xsdFile) {
 434                      $this->log("Validating XML doc (".$xmlFile->getPath().") using schema file " . $this->xsdFile->getPath(), PROJECT_MSG_VERBOSE);
 435                      if (!$dom->schemaValidate($this->xsdFile->getAbsolutePath())) {
 436                          throw new BuildException("XML schema file (".$xmlFile->getPath().") does not validate.  See warnings above for reasons validation failed (make sure error_reporting is set to show E_WARNING if you don't see any).");        throw new EngineException("XML schema does not validate (using schema file $xsdFile).  See warnings above for reasons validation failed (make sure error_reporting is set to show E_WARNING if you don't see any).", $this->getLocation());
 437                      }
 438                  }
 439                  
 440                  $xmlParser = new XmlToAppData($platform, $this->getTargetPackage(), $this->dbEncoding);
 441                  $ad = $xmlParser->parseFile($xmlFile->getAbsolutePath());
 442                  $ad->setName($dmFilename); // <-- Important: use the original name, not the -transformed name.
 443                  $ads[] = $ad;
 444              }
 445          }
 446          
 447          if (empty($ads)) {
 448              throw new BuildException("No schema files were found (matching your schema fileset definition).");
 449          }
 450          
 451          if (!$this->packageObjectModel) {
 452  
 453              $this->dataModels = $ads;
 454              $this->databaseNames = array(); // doesn't seem to be used anywhere
 455              $this->dataModelDbMap = array();
 456  
 457              // Different datamodels may state the same database
 458              // names, we just want the unique names of databases.
 459              foreach($this->dataModels as $dm) {
 460                  $database = $dm->getDatabase();
 461                  $this->dataModelDbMap[$dm->getName()] = $database->getName();
 462                  $this->databaseNames[$database->getName()] = $database->getName(); // making list of *unique* dbnames.
 463              }
 464          } else {
 465  
 466              $this->joinDatamodels($ads);
 467              $this->dataModels[0]->getDatabases(); // calls doFinalInitialization()
 468          }
 469  
 470          $this->dataModelsLoaded = true;
 471      }
 472  
 473      /**
 474       * Joins the datamodels collected from schema.xml files into one big datamodel
 475       *
 476       * This applies only when the the packageObjectModel option is set. We need to
 477       * join the datamodels in this case to allow for foreign keys that point to
 478       * tables in different packages.
 479       *
 480       * @param array $ads The datamodels to join
 481       */
 482      protected function joinDatamodels($ads) {
 483  
 484          foreach($ads as $ad) {
 485              $db = $ad->getDatabase(null, false);
 486              $this->dataModelDbMap[$ad->getName()] = $db->getName();
 487          }
 488  
 489          foreach ($ads as $addAd) {
 490  
 491              $ad = &$this->dataModels[0];
 492              if (!isset($ad)) {
 493                  $addAd->setName('JoinedDataModel');
 494                  $ad = $addAd;
 495                  continue;
 496              }
 497              foreach ($addAd->getDatabases(false) as $addDb) {
 498                  $addDbName = $addDb->getName();
 499                  if (!$package = $addDb->getPackage()) {
 500                      throw new BuildException('No package found for database "' . $addDbName . '" in ' . $addAd->getName() . '. The propel.packageObjectModel property requires the package attribute to be set for each database.');
 501                  }
 502                  $db = $ad->getDatabase($addDbName, false);
 503                  if (!$db) {
 504                      $ad->addDatabase($addDb);
 505                      continue;
 506                  }
 507                  foreach ($addDb->getTables() as $addTable) {
 508                      $table = $db->getTable($addTable->getName());
 509                      if ($table) {
 510                          throw new BuildException('Duplicate table found: ' . $addDbName . '.');
 511                      }
 512                      $db->addTable($addTable);
 513                  }
 514              }
 515          }
 516      }
 517  
 518      /**
 519       * Creates a new Capsule context with some basic properties set.
 520       * (Capsule is a simple PHP encapsulation system -- aka a php "template" class.)
 521       * @return Capsule
 522       */
 523      protected function createContext() {
 524  
 525          $context = new Capsule();
 526  
 527          // Make sure the output directory exists, if it doesn't
 528          // then create it.
 529          $outputDir = new PhingFile($this->outputDirectory);
 530          if (!$outputDir->exists()) {
 531              $this->log("Output directory does not exist, creating: " . $outputDir->getAbsolutePath());
 532              $outputDir->mkdirs();
 533          }
 534  
 535          // Place our set of data models into the context along
 536          // with the names of the databases as a convenience for now.
 537          $context->put("targetDatabase", $this->targetDatabase);
 538          $context->put("targetPackage", $this->targetPackage);
 539          $context->put("now", strftime("%c"));
 540  
 541          $this->log("Target database type: " . $this->targetDatabase);
 542          $this->log("Target package: " . $this->targetPackage);
 543          $this->log("Using template path: " . $this->templatePath);
 544          $this->log("Output directory: " . $this->outputDirectory);
 545  
 546          $context->setTemplatePath($this->templatePath);
 547          $context->setOutputDirectory($this->outputDirectory);
 548  
 549          $this->populateContextProperties($context);
 550  
 551          return $context;
 552      }
 553  
 554      /**
 555       * Fetches the propel.xxx properties from project, renaming the propel.xxx properties to just xxx.
 556       *
 557       * Also, renames any xxx.yyy properties to xxxYyy as PHP doesn't like the xxx.yyy syntax.
 558       *
 559       * @return array Assoc array of properties.
 560       */
 561  	protected function getPropelProperties()
 562      {
 563          $allProps = $this->getProject()->getProperties();
 564          $renamedPropelProps = array();
 565          foreach ($allProps as $key => $propValue) {
 566              if (strpos($key, "propel.") === 0) {
 567                  $newKey = substr($key, strlen("propel."));
 568                  $j = strpos($newKey, '.');
 569                  while ($j !== false) {
 570                      $newKey =  substr($newKey, 0, $j) . ucfirst(substr($newKey, $j + 1));
 571                      $j = strpos($newKey, '.');
 572                  }
 573                  $renamedPropelProps[$newKey] = $propValue;
 574              }
 575          }
 576          return $renamedPropelProps;
 577      }
 578      
 579      /**
 580       * Fetches a single propel.xxx property from project, using "converted" property names.
 581       * @see getPropelProperties()
 582       * @param string $name Name of property to fetch (in converted CamelCase)
 583       * @return string The value of the property (or NULL if not set)
 584       */
 585  	protected function getPropelProperty($name)
 586      {
 587          $props = $this->getPropelProperties();
 588          if (isset($props[$name])) {
 589              return $props[$name];
 590          }
 591          return null; // just to be explicit
 592      }
 593  
 594      /**
 595       * Adds the propel.xxx properties to the passed Capsule context, changing names to just xxx.
 596       *
 597       * Also, move xxx.yyy properties to xxxYyy as PHP doesn't like the xxx.yyy syntax.
 598       *
 599       * @param Capsule $context
 600       * @see getPropelProperties()
 601       */
 602      public function populateContextProperties(Capsule $context)
 603      {
 604          foreach ($this->getPropelProperties() as $key => $propValue) {
 605              $this->log('Adding property ${' . $key . '} to context', PROJECT_MSG_DEBUG);
 606              $context->put($key, $propValue);
 607          }
 608      }
 609  
 610    /**
 611     * Checks this class against Basic requrements of any propel datamodel task.
 612     *
 613     * @throws BuildException     - if schema fileset was not defined
 614     *                             - if no output directory was specified
 615     */
 616      protected function validate()
 617      {
 618          if (empty($this->schemaFilesets)) {
 619              throw new BuildException("You must specify a fileset of XML schemas.", $this->getLocation());
 620          }
 621  
 622          // Make sure the output directory is set.
 623          if ($this->outputDirectory === null) {
 624              throw new BuildException("The output directory needs to be defined!", $this->getLocation());
 625          }
 626          
 627          if ($this->validate) {
 628              if (!$this->xsdFile) {
 629                  throw new BuildException("'validate' set to TRUE, but no XSD specified (use 'xsd' attribute).", $this->getLocation());
 630              }
 631          }
 632  
 633      }
 634  
 635  }


Généré le : Fri Mar 16 22:42:14 2007 par Balluche grâce à PHPXref 0.7