<?php
/**
* Copyright (C) 2010 Kai Dorschner
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Kai Dorschner <the-hide@address.com>
* @copyright Copyright 2010, Kai Dorschner
* @license http://www.gnu.org/licenses/gpl.html GPLv3
* @package mocovi
*/
/**
* Require MVC Exception
*/
class_exists('MvcException') or require $GLOBALS['library'].'autoload/MvcException.php';
/**
* Control-factory class.
*
* Responsible for the control creation.
*
* @package mocovi
*/
class ControlFactory
{
/**
* Class name of the error control.
*
* @access public
* @static
*/
public static $errorcontrol = 'error_control';
public static $controlannex = '_control';
public static $extension = '.php';
protected static $controlPool = array();
protected static $controlPoolCache = array();
/**
* Registry for controls.
* You are able to map all XML Nodes ($control[$x]['node']) to the controls ($control[$x]['control'])
*
* @access public
* @static
* @var Array;
*/
public static $registry = array();
protected function __construct()
{
throw new Exception(get_class($this).' cannot be instantiated. It\'s a static class.');
}
/**
* Instantiates a specific control and fills it with its options.
*
* At first all properties and variables are checked for their correctness
* and throw an exception on fault.
* Then the source class gets included and instantiated.
*
* @return object The instatiated control
* @param DomElement $sourceNode Contains the original model-node
* @throws MvcException
* @access public
* @static
*/
public static function create(DomNode $sourceNode)
{
if($position = self::findControlInRegistry($sourceNode))
return self::$registry[$position]['control'];
switch($sourceNode->nodeType)
{
case XML_ELEMENT_NODE:
$name = $sourceNode->nodeName;
break;
case XML_TEXT_NODE:
$name = 'inline';
break;
default:
return self::createError(get_class(self), 'Cannot create Control from '.$sourceNode->nodeName);
break;
}
if(count(self::$controlPool) == 0)
throw new MvcException('Control pool is empty!');
// @todo
if($controlfile = self::findControl($name))
{
class_exists($name.self::$controlannex) or require $controlfile;
$class = $name.self::$controlannex;
if(!class_exists($class))
throw new MvcException('Cannot find class "'.$class.'" in file "'.$controlfile.'". The class name must be the filename without extension but with appended "'.self::$controlannex.'". Example: foobar'.self::$controlannex.' in foobar'.self::$extension);
self::register($sourceNode, $control = new $class($sourceNode));
return $control;
}
return self::createError($name, 'Control "'.$name.'" not found in pools.');
}
/**
* Creates a control out of the normal context.
*
* It has no relation to the original DomDocument where all other controls
* are linked in.
*
* @access public
* @static
* @return Control
* @param String $name Control name
* @param Array $attributes List of control attributes (associative array)
*/
public static function createVirtual($name, Array $attributes = array())
{
$dom = new DomDocument();
$node = $dom->createElement($name);
foreach($attributes as $k => $v)
$node->setAttribute($k, $v);
return self::create($node);
}
public static function register(DomNode $node, Control $control)
{
self::$registry[] = array
( 'node' => $node
, 'control' => $control
);
}
public static function getControl(DomNode $node)
{
$position = self::findControlInRegistry($node);
return ($position ? self::$registry[$position]['control'] : self::create($node));
}
public static function findControlInRegistry(DomNode $node)
{
for($i = 0; $i < count(self::$registry); $i++)
if(self::$registry[$i]['node'] === $node) // Same instance
return $i;
return null;
}
public static function getAllControls()
{
if(count(self::$controlPoolCache) > 0)
return self::$controlPoolCache;
foreach(self::$controlPool as $pool)
if(is_dir($pool))
foreach($pool as $element)
if(!$element->isDot() && $element->isFile())
{
$name = preg_replace('/\\'.self::$extension.'$/', '', $element->getFilename());
if(!array_key_exists($name, self::$controlPoolCache))
self::$controlPoolCache[$name] = $element->getPathname();
}
return self::$controlPoolCache;
}
/**
* this method is normally called when something went wrong.
*
* @param String $context Context.
* @param String $message Error message.
* @return Control Error control.
* @static
*/
public static function createError($context, $message)
{
class_exists(self::$errorcontrol) or require $GLOBALS['commonControls']
.substr
( self::$errorcontrol
, 0
, strlen(self::$errorcontrol) - strlen(self::$controlannex)
)
.self::$extension
;
return self::createVirtual
( 'error'
, array
( 'context' => $context
, 'message' => $message
)
);
}
public static function addPool(DirectoryIterator $pool)
{
self::$controlPool[] = $pool;
}
public static function findControl($name)
{
if(isset(self::$controlPoolCache[$name]))
return self::$controlPoolCache[$name]; // Return already used control from cache.
for($i = (count(self::$controlPool) - 1); $i >= 0; $i--) // Search backwards (last added pool is checked first!)
{
foreach(self::$controlPool[$i] as $element)
if(!$element->isDot() && $element->getFilename() == $name.self::$extension)
{
self::$controlPoolCache[$name] = $element->getPathname(); // Once it is found in one pool it is cached.
return $element->getPathname();
}
}
throw new Exception('Control with name "'.$name.'" could not be found in pools.');
}
public static function load($controlName)
{
class_exists($controlName.self::$controlannex) or require ControlFactory::findControl($controlName);
}
}