<?php

/*
 *  $Id: GeneratorConfig.php 989 2008-03-11 14:29:30Z heltem $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://propel.phpdb.org>.
 */

/**
 * A class that holds build properties and provide a class loading mechanism for the generator.
 *
 * @author     Hans Lellelid <hans@xmpl.org>
 * @package    propel.engine
 */
class GeneratorConfig {

	/**
	 * The build properties.
	 *
	 * @var        array
	 */
	private $buildProperties = array();

	/**
	 * Construct a new GeneratorConfig.
	 * @param      mixed $props Array or Iterator
	 */
	public function __construct($props = null)
	{
		if ($props) $this->setBuildProperties($props);
	}

	/**
	 * Gets the build properties.
	 * @return     array
	 */
	public function getBuildProperties()
	{
		return $this->buildProperties;
	}

	/**
	 * Parses the passed-in properties, renaming and saving eligible properties in this object.
	 *
	 * Renames the propel.xxx properties to just xxx and renames any xxx.yyy properties
	 * to xxxYyy as PHP doesn't like the xxx.yyy syntax.
	 *
	 * @param      mixed $props Array or Iterator
	 */
	public function setBuildProperties($props)
	{
		$this->buildProperties = array();

		$renamedPropelProps = array();
		foreach ($props as $key => $propValue) {
			if (strpos($key, "propel.") === 0) {
				$newKey = substr($key, strlen("propel."));
				$j = strpos($newKey, '.');
				while ($j !== false) {
					$newKey =  substr($newKey, 0, $j) . ucfirst(substr($newKey, $j + 1));
					$j = strpos($newKey, '.');
				}
				$this->setBuildProperty($newKey, $propValue);
			}
		}
	}

	/**
	 * Gets a specific propel (renamed) property from the build.
	 *
	 * @param      string $name
	 * @return     mixed
	 */
	public function getBuildProperty($name)
	{
		return isset($this->buildProperties[$name]) ? $this->buildProperties[$name] : null;
	}

	/**
	 * Sets a specific propel (renamed) property from the build.
	 *
	 * @param      string $name
	 * @param      mixed $value
	 */
	public function setBuildProperty($name, $value)
	{
		$this->buildProperties[$name] = $value;
	}

	/**
	 * Resolves and returns the class name based on the specified property value.
	 *
	 * @param      string $propname The name of the property that holds the class path (dot-path notation).
	 * @return     string The class name.
	 * @throws     BuildException If the classname cannot be determined or class cannot be loaded.
	 */
	public function getClassname($propname)
	{
		$classpath = $this->getBuildProperty($propname);
		if (empty($classpath)) {
			throw new BuildException("Unable to find class path for '$propname' property.");
		}

		// This is a slight hack to workaround camel case inconsistencies for the DDL classes.
		// Basically, we want to turn ?.?.?.sqliteDDLBuilder into ?.?.?.SqliteDDLBuilder
		$lastdotpos = strrpos($classpath, '.');
		if ($lastdotpos !== null) {
			$classpath{$lastdotpos+1} = strtoupper($classpath{$lastdotpos+1});
		} else {
			$classpath = ucfirst($classpath);
		}

		if (empty($classpath)) {
			throw new BuildException("Unable to find class path for '$propname' property.");
		}

		$clazz = Phing::import($classpath);

		return $clazz;
	}

	/**
	 * Resolves and returns the builder class name.
	 *
	 * @param      string $type
	 * @return     string The class name.
	 */
	public function getBuilderClassname($type)
	{
		$propname = 'builder' . ucfirst(strtolower($type)) . 'Class';
		return $this->getClassname($propname);
	}

	/**
	 * Creates and configures a new Platform class.
	 *
	 * @param      PDO $con
	 * @return     Platform
	 */
	public function getConfiguredPlatform(PDO $con = null)
	{
		$clazz = $this->getClassname("platformClass");
		$platform = new $clazz();

		if (!$platform instanceof Platform) {
			throw new BuildException("Specified platform class ($clazz) does not implement Platform interface.", $this->getLocation());
		}

		$platform->setConnection($con);
		$platform->setGeneratorConfig($this);
		return $platform;
	}

	/**
	 * Creates and configures a new SchemaParser class for specified platform.
	 * @param      PDO $con
	 * @return     SchemaParser
	 */
	public function getConfiguredSchemaParser(PDO $con = null)
	{
		$clazz = $this->getClassname("reverseParserClass");
		$parser = new $clazz();
		if (!$parser instanceof SchemaParser) {
			throw new BuildException("Specified platform class ($clazz) does implement SchemaParser interface.", $this->getLocation());
		}
		$parser->setConnection($con);
		$parser->setGeneratorConfig($this);
		return $parser;
	}

	/**
	 * Gets a configured data model builder class for specified table and based on type.
	 *
	 * @param      Table $table
	 * @param      string $type The type of builder ('ddl', 'sql', etc.)
	 * @return     DataModelBuilder
	 */
	public function getConfiguredBuilder(Table $table, $type, $cache = true)
	{
		$classname = $this->getBuilderClassname($type);
		$builder = new $classname($table);
		$builder->setGeneratorConfig($this);
		return $builder;
	}

	/**
	 * Gets a configured Pluralizer class.
	 *
	 * @return     Pluralizer
	 */
	public function getConfiguredPluralizer()
	{
		$classname = $this->getBuilderClassname('pluralizer');
		$pluralizer = new $classname();
		return $pluralizer;
	}
}
