* @author Kornel LesiĆski
* @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
* @version SVN: $Id: PHPTALDocumentBuilder.php 914 2010-06-20 21:02:58Z kornel $
* @link http://phptal.org/
*/
/**
* DOM Builder
*
* @package PHPTAL
* @subpackage Dom
*/
class PHPTAL_Dom_PHPTALDocumentBuilder extends PHPTAL_Dom_DocumentBuilder
{
private $_xmlns; /* PHPTAL_Dom_XmlnsState */
private $encoding;
public function __construct()
{
$this->_xmlns = new PHPTAL_Dom_XmlnsState(array(), '');
}
public function getResult()
{
return $this->documentElement;
}
protected function getXmlnsState()
{
return $this->_xmlns;
}
// ~~~~~ XmlParser implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public function onDocumentStart()
{
$this->documentElement = new PHPTAL_Dom_Element('documentElement', 'http://xml.zope.org/namespaces/tal', array(), $this->getXmlnsState());
$this->documentElement->setSource($this->file, $this->line);
$this->_current = $this->documentElement;
}
public function onDocumentEnd()
{
if (count($this->_stack) > 0) {
$left='_current->getQualifiedName().'>';
for ($i = count($this->_stack)-1; $i>0; $i--) $left .= '_stack[$i]->getQualifiedName().'>';
throw new PHPTAL_ParserException("Not all elements were closed before end of the document. Missing: ".$left,
$this->file, $this->line);
}
}
public function onDocType($doctype)
{
$this->pushNode(new PHPTAL_Dom_DocumentType($doctype, $this->encoding));
}
public function onXmlDecl($decl)
{
if (!$this->encoding) {
throw new PHPTAL_Exception("Encoding not set");
}
$this->pushNode(new PHPTAL_Dom_XmlDeclaration($decl, $this->encoding));
}
public function onComment($data)
{
$this->pushNode(new PHPTAL_Dom_Comment($data, $this->encoding));
}
public function onCDATASection($data)
{
$this->pushNode(new PHPTAL_Dom_CDATASection($data, $this->encoding));
}
public function onProcessingInstruction($data)
{
$this->pushNode(new PHPTAL_Dom_ProcessingInstruction($data, $this->encoding));
}
public function onElementStart($element_qname, array $attributes)
{
$this->_xmlns = $this->_xmlns->newElement($attributes);
if (preg_match('/^([^:]+):/', $element_qname, $m)) {
$namespace_uri = $this->_xmlns->prefixToNamespaceURI($m[1]);
if (false === $namespace_uri) {
throw new PHPTAL_ParserException("There is no namespace declared for prefix of element < $element_qname >",
$this->file, $this->line);
}
} else {
$namespace_uri = $this->_xmlns->getCurrentDefaultNamespaceURI();
}
$attrnodes = array();
foreach ($attributes as $qname=>$value) {
if (preg_match('/^([^:]+):(.+)$/', $qname, $m)) {
$local_name = $m[2];
$attr_namespace_uri = $this->_xmlns->prefixToNamespaceURI($m[1]);
} else {
$local_name = $qname;
$attr_namespace_uri = ''; // default NS. Attributes don't inherit namespace per XMLNS spec
}
if (false === $attr_namespace_uri) {
throw new PHPTAL_ParserException("There is no namespace declared for prefix of attribute $qname of element < $element_qname >",
$this->file, $this->line);
}
if ($this->_xmlns->isHandledNamespace($attr_namespace_uri)
&& !$this->_xmlns->isValidAttributeNS($attr_namespace_uri, $local_name)) {
throw new PHPTAL_ParserException("Attribute '$qname' is in '$attr_namespace_uri' namespace, but is not a supported PHPTAL attribute",
$this->file, $this->line);
}
$attrnodes[] = new PHPTAL_Dom_Attr($qname, $attr_namespace_uri, $value, $this->encoding);
}
$node = new PHPTAL_Dom_Element($element_qname, $namespace_uri, $attrnodes, $this->getXmlnsState());
$this->pushNode($node);
$this->_stack[] = $this->_current;
$this->_current = $node;
}
public function onElementData($data)
{
$this->pushNode(new PHPTAL_Dom_Text($data, $this->encoding));
}
public function onElementClose($qname)
{
if ($this->_current === $this->documentElement) {
throw new PHPTAL_ParserException("Found closing tag for < $qname > where there are no open tags",
$this->file, $this->line);
}
if ($this->_current->getQualifiedName() != $qname) {
throw new PHPTAL_ParserException("Tag closure mismatch, expected < /".$this->_current->getQualifiedName()." > (opened in line ".$this->_current->getSourceLine().") but found < /".$qname." >",
$this->file, $this->line);
}
$this->_current = array_pop($this->_stack);
if ($this->_current instanceof PHPTAL_Dom_Element) {
$this->_xmlns = $this->_current->getXmlnsState(); // restore namespace prefixes info to previous state
}
}
private function pushNode(PHPTAL_Dom_Node $node)
{
$node->setSource($this->file, $this->line);
$this->_current->appendChild($node);
}
public function setEncoding($encoding)
{
$this->encoding = $encoding;
}
}