<?php
/* --------------------------------------------------------------------------
* XIRE - eXtendable Information Rendering Engine
* --------------------------------------------------------------------------
* LICENSE
* Copyright (C) 2006 David Duong
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* -------------------------------------------------------------------------- */
/**
* Handles the registration and utilization of plugins.
*
*/
class XIRE_PluginManager {
private $plugins; // Registered plugins
private $instances;
private $directories; // Registered plugin directories
public function __construct () {
$this->plugins = array();
$this->directories = array(dirname(__FILE__) . '/plugins');
}
/**
* Register a plugin with an editable node name
*
* @param string $name the local name of a node
* @param string $filename the filename of the plugin class
*/
public function set ($name, $filename) {
$this->plugins[$name] = $filename;
}
/**
* Register a plugin directory
*
* If a node is found without a registered plugin, all registered plugin directories
* will be searched for a plugin that handles the node.
* @param string $directory
*/
public function addDirectory ($directory) {
$this->directories[] = $directory;
}
/**
* Call the plugin that is associated with a node
*
* When a process finds a editable node, it will have this plugin manager locate and
* call the corresponding plugin for the node. Plugins, or directories containing a
* group of plugins, are registered so when a process finds a node that matches the
* registration, it will load, instantiate, and call the plugin, handing control of the
* node, and child nodes, to the plugin.
* @param DOMNode $node this method will seek a plugin that is associated (by the
* node's local name) and have the plugin handle the node
* @param XIRE_Template $sender the template making this request
* @throws XIRE_MissingPluginException if a the plugin could not be found
*/
public function call (DOMNode $node, XIRE_Process $sender) {
isset($this->plugins[$node->localName]) or $this->load($node);
$plugin = new $this->plugins[$node->localName]($node, $sender);
$plugin->execute();
}
/**
* @param string $name this method will search for a plugin that will handle the given
* node
* @throws XIRE_MissingPluginException if a the plugin could not be found
*/
private function load (DOMNode $node) {
// Convert element names to class names, ie. something like node-name to NodeName
if (strpos($node->localName, '-') !== false) {
$base_name = str_replace(' ', '', ucwords(strtr($node->localName, '-', ' ')));
} else {
$base_name = ucfirst($node->localName);
}
// Search each registered directory
foreach ($this->directories as $directory) {
if ($node instanceof DOMElement
&& file_exists("$directory/Element_$base_name.class.php")) {
include "$directory/Element_$base_name.class.php";
$class_name = "XIRE_Plugin_Element_$base_name";
break;
} elseif ($node instanceof DOMAttr
&& file_exists("$directory/Attribute_$base_name.class.php")) {
include "$directory/Attribute_$base_name.class.php";
$class_name = "XIRE_Plugin_Attribute_$base_name";
break;
} elseif (file_exists("$directory/$base_name.class.php")) {
include "$directory/$base_name.class.php";
$class_name = "XIRE_Plugin_$base_name";
break;
}
}
if (!isset($class_name)) {
// Plugin file not found!
throw new XIRE_MissingPluginException(
"Missing plugin file for: {$node->localName}", $node, 0);
} elseif (class_exists($class_name)) {
$this->plugins[$node->localName] = $class_name;
} elseif (class_exists($base_name)) {
$this->plugins[$node->localName] = $base_name;
} else {
// Plugin class not found!
throw new XIRE_MissingPluginException(
"Missing plugin class for: {$node->localName}", $node, 1);
}
}
}
/**
* Required superclass of all plugins
*/
abstract class XIRE_Plugin {
protected $template; // The template that is calling the plugin
protected $document;
protected $process;
protected $node;
protected $id;
private static $ids;
/**
* This method is called when a node is to be parsed and must be implemented
*/
abstract public function execute ();
public function __construct (DOMNode $node, XIRE_Process $sender) {
$this->node = $node;
$this->process = $sender;
$this->template = $sender->template;
$this->document = $sender->template->document;
// All XIRL elements must have a unique id
if ($node instanceof DOMElement) {
$current_id = $node->getAttribute('id');
if (empty($current_id)
|| $this->template->document->getElementById($current_id) !== $node) {
$current_id = $node->localName;
if (isset(self::$ids[$node->localName])) {
$current_id .= '_' . self::$ids[$node->localName]++;
} else {
self::$ids[$node->localName] = 1;
}
while ($this->template->document->getElementById($current_id) !== null) {
$current_id = "{$node->localName}_" . self::$ids[$node->localName]++;
}
$this->id = $current_id;
$node->setAttribute('id', $current_id);
}
}
}
/**
* Set a template variable called $name with the value $value within the scope of the
* current node
*/
protected function setVariable ($name, $value) {
$this->template["{$this->id}::$name"] = $value;
}
/**
* @param string $name
* @param boolean $cascade if set to true, this function will also search parent nodes
* @return bool true if the variable is found or false otherwise
*/
protected function hasVariable ($name, $cascade = false) {
if ($cumulative) {
return $this->template->has($name);
}
$current = $this->node;
do {
if ($current->namespaceURI === XIRL_NAMESPACE) {
$current_id = $current->getAttribute('id');
if (isset($this->template["$current_id::$name"])) {
return true;
}
}
$current = ($cumulative)? $current->parentNode : null;
} while ($current instanceof DOMElement);
}
/**
* @param string $name
* @param mixed $default determine what should be returned if the variable is not
* found
* @throws XIRE_MissingVariableException when $default is not set and the template
* variable is not set
* @return the template variable with the given name in the current node's scope
*/
protected function getVariable ($name, $default = null) {
if (isset($this->template["{$this->id}::$name"])) {
return $this->template["{$this->id}::$name"];
} elseif (func_num_args() > 1) {
return $default;
}
throw new XIRE_MissingVariableException("{$this->id}::$name");
}
/**
* @param string $name
* @param mixed $default determine what should be returned if the variable is not
* found
* @return the template variable with the given name in the node or the closest closest
* ancestors' scope
*/
protected function getVariable_cascade ($name, $default = null) {
$current = $this->node;
do {
if ($current->namespaceURI === XIRL_NAMESPACE) {
$current_id = $current->getAttribute('id');
if (isset($this->template["$current_id::$name"])) {
return $this->template["$current_id::$name"];
}
}
$current = $current->parentNode;
} while ($current instanceof DOMElement);
if (func_num_args() > 1) {
return $this->template->get($name, $default);
}
return $this->template->get($name);
}
}
class XIRE_MissingAttributeException extends XIRE_ProcessingNodeException
{
private $name;
public function __construct($name, DOMNode $node) {
$this->name = $name;
parent::__construct(
"{$node->localName} is missing required attribute: $name", $node);
}
/**
* @return the name of the missing required attribute
*/
public function getName () {
return $this->name;
}
}
class XIRE_MissingPluginException extends XIRE_ProcessingNodeException
{
private $template;
public function __construct($message = null, DOMNode $node, $code = 0) {
func_num_args() === 0 and $message = "Missing plugin: {$node->localName}";
parent::__construct($message, $node, $code);
}
/**
* @return the template that uses the missing plugin
*/
public function getTemplate () {
return $this->template;
}
}
/**
* Recomended superclass of exceptions originating from a plugin
*/
class XIRE_PluginException extends XIRE_ProcessingNodeException {}
?>
|