/includes/Opl/Opt/Xml/Scannable.php
PHP | 904 lines | 652 code | 44 blank | 208 comment | 115 complexity | a7051dfe9e5b5890575be144f0b10fc1 MD5 | raw file
Possible License(s): GPL-3.0, MIT
- <?php
- /*
- * OPEN POWER LIBS <http://www.invenzzia.org>
- *
- * This file is subject to the new BSD license that is bundled
- * with this package in the file LICENSE. It is also available through
- * WWW at this URL: <http://www.invenzzia.org/license/new-bsd>
- *
- * Copyright (c) Invenzzia Group <http://www.invenzzia.org>
- * and other contributors. See website for details.
- *
- * $Id: Scannable.php 272 2009-12-23 08:42:26Z zyxist $
- */
- /**
- * The abstract class that represents the XML nodes which can contain
- * another nodes. It uses the DOM-like API to manage the nodes.
- *
- * @abstract
- */
- class Opt_Xml_Scannable extends Opt_Xml_Node implements Iterator
- {
- protected $_subnodes;
- protected $_i;
- protected $_size;
- protected $_copy;
-
- private $_prototypes;
- /**
- * Creates the scannable node.
- */
- public function __construct()
- {
- parent::__construct();
- } // end __construct();
-
- /*
- * Public DOM-like API
- */
- /**
- * Appends a new child to the end of the children list. The method
- * is DOM-compatible.
- *
- * @param Opt_Xml_Node $child The child to be appended.
- */
- public function appendChild(Opt_Xml_Node $child)
- {
- // Test if the node can be a child of this and initialize an
- // empty array if needed.
- $this->_testNode($child);
- if(!is_array($this->_subnodes))
- {
- $this->_subnodes = array();
- }
- $child->setParent($this);
- $this->_subnodes[] = $child;
- } // end appendChild();
- /**
- * Inserts the new node after the node identified by the '$refnode'. The
- * reference node can be identified either by the number or by the object.
- * If the reference node is empty, the new node is appended to the children
- * list, if the last argument allows for that.
- *
- * @param Opt_Xml_Node $newnode The new node.
- * @param Integer|Opt_Xml_Node $refnode The reference node.
- * @param Boolean $appendOnError Do we like to append the node, if $refnode does not exist?
- */
- public function insertBefore(Opt_Xml_Node $newnode, $refnode = null, $appendOnError = true)
- {
- // Test if the node can be a child of this and initialize an
- // empty array if needed.
- $this->_testNode($newnode);
- if(!is_array($this->_subnodes))
- {
- $this->_subnodes = array();
- }
- $newnode->setParent($this);
- if(is_null($refnode))
- {
- $this->_appendChild($newnode, $appendOnError);
- }
- if(is_object($refnode))
- {
- $i = 0;
- $cnt = sizeof($this->_subnodes);
- while($cnt > 0)
- {
- if(isset($this->_subnodes[$i]))
- {
- $cnt--;
- if($this->_subnodes[$i] === $refnode)
- {
- $this->_subnodes = $this->_arrayCreateHole($this->_subnodes, $i);
- // Opt_Array_Push($this->_subnodes, $i, $cnt);
- $this->_subnodes[$i] = $newnode;
- break;
- }
- }
- $i++;
- }
- }
- elseif(is_integer($refnode))
- {
- end($this->_subnodes);
- if($refnode <= key($this->_subnodes) && $refnode >= 0)
- {
- $this->_subnodes = $this->_arrayCreateHole($this->_subnodes, $refnode);
- // Opt_Array_Push($this->_subnodes, $refnode);
- $this->_subnodes[$refnode] = $newnode;
- }
- else
- {
- $this->_appendChild($newnode, $appendOnError);
- }
- }
- } // end insertBefore();
- /**
- * Removes the child identified either by the number or the object.
- *
- * @param Integer|Opt_Xml_Node $node The node to be removed.
- * @return Boolean
- */
- public function removeChild($node)
- {
- if(!is_array($this->_subnodes))
- {
- return false;
- }
- if(is_object($node))
- {
- $cnt = sizeof($this->_subnodes);
- $found = 0;
- for($i = 0; $i < $cnt; $i++)
- {
- if(isset($this->_subnodes[$i]))
- {
- if($this->_subnodes[$i] === $node)
- {
- $node->setParent(null);
- unset($this->_subnodes[$i]);
- $found++;
- }
- }
- }
- $this->_subnodes = $this->_arrayReduceHoles($this->_subnodes);
- return $found > 0;
- }
- elseif(is_integer($node) && isset($this->_subnodes[$node]))
- {
- $this->_subnodes[$node]->setParent(null);
- // $this->_subnodes[$node]->dispose();
- unset($this->_subnodes[$node]);
- $this->_subnodes = $this->_arrayReduceHoles($this->_subnodes);
- return true;
- }
- return false;
- } // end removeChild();
- /**
- * Removes all the children. The memory after the children is not freed.
- */
- public function removeChildren()
- {
- if(is_array($this->_subnodes))
- {
- foreach($this->_subnodes as $subnode)
- {
- if(is_object($subnode))
- {
- $subnode->setParent(null);
- }
- }
- }
- unset($this->_subnodes);
- $this->_subnodes = null;
- } // end removeChildren();
- /**
- * Moves all the children of another node to the current node.
- *
- * @param Opt_Xml_Node $node Another node.
- */
- public function moveChildren(Opt_Xml_Scannable $node)
- {
- // If there are already some nodes, we have to free the memory first.
- if(is_array($this->_subnodes))
- {
- foreach($this->_subnodes as $item)
- {
- if(is_object($item))
- {
- $item->dispose();
- }
- }
- }
- // Move the nodes by copying the internal data structures.
- $this->_subnodes = $node->_subnodes;
- $this->_i = $node->_i;
- $this->_size = $node->_size;
- $this->_copy = $node->_copy;
- // Reset the children list in the $node.
- $node->_subnodes = null;
- $node->_i = null;
- $node->_size = null;
- $node->_copy = null;
- // Create a connection between this node and the new children.
- if(is_array($this->_subnodes))
- {
- foreach($this->_subnodes as $subnode)
- {
- if(is_object($subnode))
- {
- // Test the node in order to check whether
- // we have moved them to the correct node.
- $this->_testNode($subnode);
- $subnode->setParent($this);
- }
- }
- }
- } // end moveChildren();
- /**
- * Replaces the child with the new node. The reference node can be
- * identified either by the number or by the object.
- *
- * @param Opt_Xml_Node $newnode The new node.
- * @param Integer|Opt_Xml_Node $refnode The old node.
- * @param Boolean $dispose Dispose the old node?
- * @return Boolean
- */
- public function replaceChild(Opt_Xml_Node $newnode, $refnode, $dispose = false)
- {
- $this->_testNode($newnode);
- if(!is_array($this->_subnodes))
- {
- return false;
- }
- $newnode->setParent($this);
- if(is_object($refnode))
- {
- $i = 0;
- $cnt = sizeof($this->_subnodes);
- while($cnt > 0)
- {
- if(isset($this->_subnodes[$i]))
- {
- // SEE: note about comparing" in bringToEnd()
- if($refnode === $this->_subnodes[$i])
- {
- $dispose and $this->_subnodes[$i]->dispose();
- $this->_subnodes[$i] = $newnode;
- return true;
- }
- }
- $i++;
- }
- }
- elseif(is_integer($refnode) && isset($this->_subnodes[$refnode]))
- {
- $dispose and $this->_subnodes[$refnode]->dispose();
- $this->_subnodes[$refnode] = $newnode;
- return true;
- }
- return false;
- } // end replaceChild();
- /**
- * Returns true, if the current node contains any children.
- *
- * @return Boolean
- */
- public function hasChildren()
- {
- if(!is_array($this->_subnodes))
- {
- return false;
- }
- return sizeof($this->_subnodes) > 0;
- } // end hasChildren();
- /**
- * Returns the number of the children.
- *
- * @return Integer
- */
- public function countChildren()
- {
- if(!is_array($this->_subnodes))
- {
- return 0;
- }
- return sizeof($this->_subnodes);
- } // end countChildren();
- /**
- * Returns the last child of the node.
- *
- * @return Opt_Xml_Node
- */
- public function getLastChild()
- {
- if(!is_array($this->_subnodes))
- {
- return NULL;
- }
- if(sizeof($this->_subnodes) > 0)
- {
- return end($this->_subnodes);
- }
- return NULL;
- } // end getLastChild();
- /**
- * Returns the array containing all the children.
- *
- * @return Array
- */
- public function getChildren()
- {
- $cnt = sizeof($this->_subnodes);
- $result = array();
- for($i = 0; $i < $cnt; $i++)
- {
- if(is_object($this->_subnodes[$i]))
- {
- $result[] = $this->_subnodes[$i];
- }
- }
- return $result;
- } // end getChildren();
- /**
- * Returns all the descendants of the current node.
- *
- * @return Array The list of descendants.
- */
- public function getElements()
- {
- $queue = array();
- foreach($this->_subnodes as $item)
- {
- if(is_object($item))
- {
- $queue[] = $item;
- }
- }
- $result = array();
- while(sizeof($queue) > 0)
- {
- $current = array_shift($queue);
- if($current instanceof Opt_Xml_Node)
- {
- $result[] = $current;
- }
- if($current instanceof Opt_Xml_Scannable)
- {
- foreach($current as $subnode)
- {
- $queue[] = $subnode;
- }
- }
- }
- return $result;
- } // end getElements();
- /**
- * Returns all the children or descendants with the specified name.
- *
- * @param String $name The tag name (without the namespace)
- * @param Boolean $recursive Scan the descendants recursively?
- * @return Array
- */
- public function getElementsByTagName($name, $recursive = true)
- {
- if($recursive)
- {
- return $this->_getElementsByTagName($name, '*');
- }
-
- if(!is_array($this->_subnodes))
- {
- return array();
- }
- $result = array();
- foreach($this->_subnodes as $subnode)
- {
- if(!$subnode instanceof Opt_Xml_Element)
- {
- continue;
- }
-
- if($subnode->getName() == $name || $name == '*')
- {
- $result[] = $subnode;
- }
- }
- return $result;
- } // end getElementsByTagName();
- /**
- * Returns all the children or descendants with the specified name
- * and namespace.
- *
- * @param String $namespace The tag namespace
- * @param String $name The tag name
- * @param Boolean $recursive Scan the descendants recursively?
- * @return Array
- */
- public function getElementsByTagNameNS($namespace, $name, $recursive = true)
- {
- if($recursive)
- {
- return $this->_getElementsByTagName($name, $namespace);
- }
- if(!is_array($this->_subnodes))
- {
- return array();
- }
- $result = array();
- foreach($this->_subnodes as $subnode)
- {
- if(!$subnode instanceof Opt_Xml_Element)
- {
- continue;
- }
- if(($subnode->getName() == $name || $name == '*') && ($subnode->getNamespace() == $namespace || $namespace == '*') )
- {
- $result[] = $subnode;
- }
- }
- return $result;
- } // end getElementsByTagNameNS();
- /**
- * Returns all the descendants with the specified name and namespace.
- * Contrary to getElementsByTagNameNS(), the method does not go into
- * the matching descendants.
- *
- * @param String $ns The namespace name
- * @param String $name The tag name
- * @return Array
- */
- public function getElementsExt($ns, $name)
- {
- if(!is_array($this->_subnodes))
- {
- return array();
- }
- // Use the queue to avoid recusive calls in this place.
- $queue = new SplQueue;
- foreach($this->_subnodes as $item)
- {
- if(is_object($item))
- {
- $queue->enqueue($item);
- }
- }
- $result = array();
- while($queue->count() > 0)
- {
- $current = $queue->dequeue();
- if($current instanceof Opt_Xml_Element)
- {
- if(is_null($ns))
- {
- if($current->getName() == $name || $name == '*')
- {
- $result[] = $current;
- continue; // Do not visit the children of the matching node.
- }
- }
- else
- {
- if(($current->getName() == $name || $name == '*') && ($current->getNamespace() == $ns || $ns == '*') )
- {
- $result[] = $current;
- continue; // Do not visit the children of the matching node.
- }
- }
- }
- if($current instanceof Opt_Xml_Scannable)
- {
- foreach($current as $subnode)
- {
- $queue->enqueue($subnode);
- }
- }
- }
- return $result;
- } // end getElementsExt();
- /**
- * Sorts the children with the order specified in the associative
- * array. The array must contain the pairs 'tag name' => order. Moreover,
- * it must contain the '*' element representing the new location of
- * the rest of the nodes.
- *
- * @param Array $prototypes The required order.
- */
- public function sort(Array $prototypes)
- {
- $this->_prototypes = $prototypes;
- if(!isset($prototypes['*']))
- {
- throw new Opt_APINoWildcard_Exception;
- }
- // To create a stable sort.
- $i = 0;
- foreach($this->_subnodes as &$node)
- {
- if($node instanceof Opt_Xml_Buffer)
- {
- $node->set('_ssort', $i++);
- }
- }
- // Sort!
- usort($this->_subnodes, array($this, 'sortCmp'));
- unset($this->_prototypes);
- } // end sort();
- /**
- * Moves the specified node to the end of the children list.
- *
- * @param Integer|Opt_Xml_Node $node The node to be moved.
- * @return Boolean
- */
- public function bringToEnd($node)
- {
- if(!is_array($this->_subnodes))
- {
- return false;
- }
- if(is_object($node))
- {
- $i = 0;
- $cnt = sizeof($this->_subnodes);
- while($cnt > 0)
- {
- if(isset($this->_subnodes[$i]))
- {
- // Information: NEVER compare two nodes using "=="!
- if($this->_subnodes[$i] === $node)
- {
- $this->_subnodes[] = $node;
- unset($this->_subnodes[$i]);
- return true;
- }
- }
- $i++;
- }
- }
- else
- {
- if(isset($this->_subnodes[$node]))
- {
- $this->_subnodes[] = $this->_subnodes[$node];
- unset($this->_subnodes[$node]);
- return true;
- }
- }
- return false;
- } // end bringToEnd();
- /**
- * Returns the internal subnode array.
- *
- * @internal
- * @return Array
- */
- public function getSubnodeArray()
- {
- return $this->_subnodes;
- } // end getSubnodeArray();
- /*
- public function clearNode()
- {
- $queue = new SplQueue;
- foreach($this->_subnodes as $subitem)
- {
- $queue->enqueue($subitem);
- }
- $this->_subnodes = NULL;
- $buffer = array();
- while($queue->count() > 0)
- {
- $item = $queue->dequeue();
-
- foreach($item as $subitem)
- {
- $queue->enqueue($subitem);
- $buffer[] = $subitem;
- }
- $item->_subnodes = NULL;
- }
- unset($buffer);
- } // end clearNode();
- */
- protected function _cloneHandler()
- {
- /* null */
- } // end _cloneHandler();
- /**
- * The cloning helper that clones also all the descendants. The cloning algorithm
- * does not use true recursion, so that it can be safely used even with very deep
- * trees.
- *
- * @internal
- */
- final public function __clone()
- {
- if($this->get('__nrc') === true)
- {
- // In this state, we do not clone the subnodes, because some else node takes
- // care of it
- $this->set('__nrc', NULL);
- $this->_subnodes = NULL;
- $this->_prototypes = NULL;
- $this->_i = NULL;
- $this->_size = 0;
- $this->_copy = NULL;
- $this->_cloneHandler();
- }
- else
- {
- if(!is_array($this->_subnodes))
- {
- $this->_subnodes = NULL;
- return;
- }
- // The main instance of cloning, it makes copies of all the subnodes.
- $queue = new SplQueue;
- foreach($this->_subnodes as $subitem)
- {
- if(is_object($subitem))
- {
- $queue->enqueue(array($subitem, $this));
- }
- }
- $this->_subnodes = NULL;
- $this->_prototypes = NULL;
- $this->_i = NULL;
- $this->_size = 0;
- $this->_copy = NULL;
- while($queue->count() > 0)
- {
- $pair = $queue->dequeue();
- $pair[0]->set('__nrc', true);
- $pair[1]->appendChild($clone = clone $pair[0]);
-
- // Only Opt_Xml_Scannable instances must go deeper.
- if($pair[0] instanceof Opt_Xml_Scannable)
- {
- foreach($pair[0] as $subitem)
- {
- if(is_object($subitem))
- {
- $queue->enqueue(array($subitem, $clone));
- }
- }
- }
- }
- }
- } // end __clone();
- /**
- * Removes the connections between all the descendants so that they can
- * be safely collected by the PHP garbage collector. Remember to use this
- * method before you free the last reference to the root node.
- */
- public function dispose()
- {
- // The main instance of cloning, it makes copies of all the subnodes.
- $queue = new SplQueue;
- if(is_array($this->_subnodes))
- {
- foreach($this->_subnodes as $subitem)
- {
- if(is_object($subitem))
- {
- $queue->enqueue($subitem);
- }
- }
- }
- while($queue->count() > 0)
- {
- $item = $queue->dequeue();
- if($item instanceof Opt_Xml_Scannable)
- {
- foreach($item as $subitem)
- {
- if(is_object($subitem))
- {
- $queue->enqueue($subitem);
- }
- }
- }
- $item->_dispose();
- }
- $this->_dispose();
- } // end dispose();
- /**
- * Extra dispose function.
- *
- * @internal
- */
- protected function _dispose()
- {
- parent::_dispose();
- $this->_subnodes = NULL;
- $this->_prototypes = NULL;
- $this->_i = NULL;
- $this->_size = 0;
- $this->_copy = NULL;
- } // end _dispose();
- /*
- * Iterator interface implementation
- */
-
- public function rewind()
- {
- if(!is_array($this->_subnodes))
- {
- $this->_subnodes = array();
- }
- ksort($this->_subnodes);
- $this->_i = 0;
- end($this->_subnodes);
- $this->_size = key($this->_subnodes);
-
- while(!isset($this->_subnodes[$this->_i]) && $this->_i <= $this->_size)
- {
- $this->_i++;
- }
-
- $this->_copy = $this->_subnodes;
- } // end rewind();
-
- public function valid()
- {
- if($this->_i <= $this->_size)
- {
- return true;
- }
- $this->_copy = null;
- return false;
- } // end valid();
-
- public function current()
- {
- return $this->_copy[$this->_i];
- } // end current();
-
- public function next()
- {
- do
- {
- $this->_i++;
- }
- while(!isset($this->_copy[$this->_i]) && $this->_i <= $this->_size);
- } // end next();
-
- public function key()
- {
- return $this->_i;
- } // end key();
-
- /*
- * Internal methods
- */
-
- final public function sortCmp($node1, $node2)
- {
- $name1 = (string)$node1;
- $name2 = (string)$node2;
- if(!isset($this->_prototypes[$name1]))
- {
- $name1 = '*';
- }
- if(!isset($this->_prototypes[$name2]))
- {
- $name2 = '*';
- }
- if($this->_prototypes[$name1] == $this->_prototypes[$name2])
- {
- if($node1->get('_ssort') < $node2->get('_ssort'))
- {
- return -1;
- }
- return 1;
- }
- elseif($this->_prototypes[$name1] < $this->_prototypes[$name2])
- {
- return -1;
- }
- return +1;
- } // end sortCmp();
-
- final protected function _getElementsByTagName($name, $ns)
- {
- if(!is_array($this->_subnodes))
- {
- return array();
- }
- // Use the queue to avoid recusive calls in this place.
- $queue = new SplQueue;
- foreach($this->_subnodes as $item)
- {
- if(is_object($item))
- {
- $queue->enqueue($item);
- }
- }
- $result = array();
- while($queue->count() > 0)
- {
- $current = $queue->dequeue();
- if($current instanceof Opt_Xml_Element)
- {
- if(is_null($ns))
- {
- if($current->getName() == $name || $name == '*')
- {
- $result[] = $current;
- }
- }
- else
- {
-
- if(($current->getName() == $name || $name == '*') && ($current->getNamespace() == $ns || $ns == '*') )
- {
- $result[] = $current;
- }
- }
- }
- if($current instanceof Opt_Xml_Scannable)
- {
- foreach($current as $subnode)
- {
- $queue->enqueue($subnode);
- }
- }
- }
- return $result;
- } // end _getElementsByTagName();
-
- final protected function _appendChild(Opt_Xml_Node $child, $appendOnError)
- {
- if($appendOnError)
- {
- if(!is_array($this->_subnodes))
- {
- return false;
- }
- $this->_subnodes[] = $child;
- }
- else
- {
- throw new Opt_APIInvalidBorders_Exception;
- }
- } // end _appendChild();
-
- protected function _testNode(Opt_Xml_Node $node)
- {
- // This method tests, whether the specified node can be a child
- // of the current note. By overwriting this method, we can change
- // this behavior.
- return true;
- } // end _testNode();
- private function _arrayReduceHoles($array)
- {
- $newArray = array();
- foreach($array as $value)
- {
- $newArray[] = $value;
- }
- return $newArray;
- } // end _arrayReduceHoles();
- private function _arrayCreateHole($array, $where)
- {
- $newArray = array();
- $i = 0;
- foreach($array as $value)
- {
- if($i == $where)
- {
- $newArray[$i] = null;
- $i++;
- }
- $newArray[$i] = $value;
- $i++;
- }
- return $newArray;
- } // end _arrayCreateHole();
- } // end Opt_Xml_Scannable;