[limb-svn] r4160 - in 3.x/packages/wact/trunk: framework/template/compiler framework/template/compiler/parser framework/template/compiler/tags tests/cases/template/compiler tests/cases/template/filters

svn at limb-project.com svn at limb-project.com
Sun Oct 15 23:52:26 MSD 2006


Author: serega
Date: 2006-10-15 23:52:26 +0400 (Sun, 15 Oct 2006)
New Revision: 4160

Added:
   3.x/packages/wact/trunk/framework/template/compiler/HTMLParser.class.php
   3.x/packages/wact/trunk/framework/template/compiler/OutputExpression.class.php
   3.x/packages/wact/trunk/framework/template/compiler/WactNodeBuilder.class.php
   3.x/packages/wact/trunk/framework/template/compiler/WactSourceLocation.class.php
   3.x/packages/wact/trunk/framework/template/compiler/parser/
   3.x/packages/wact/trunk/framework/template/compiler/parser/ParserListener.interface.php
   3.x/packages/wact/trunk/tests/cases/template/compiler/HtmlParserMalformedTest.class.php
   3.x/packages/wact/trunk/tests/cases/template/compiler/HtmlParserTest.class.php
   3.x/packages/wact/trunk/tests/cases/template/compiler/HtmlParserTruncatedTest.class.php
   3.x/packages/wact/trunk/tests/cases/template/compiler/SourceFileParserTest.class.php
Removed:
   3.x/packages/wact/trunk/framework/template/compiler/htmlparser.inc.php
   3.x/packages/wact/trunk/framework/template/compiler/nodebuilder.inc.php
   3.x/packages/wact/trunk/framework/template/compiler/outputexpression.inc.php
   3.x/packages/wact/trunk/tests/cases/template/compiler/htmlparser.test.php
   3.x/packages/wact/trunk/tests/cases/template/compiler/sourcefileparser.test.php
Modified:
   3.x/packages/wact/trunk/framework/template/compiler/SourceFileParser.class.php
   3.x/packages/wact/trunk/framework/template/compiler/parserstate.inc.php
   3.x/packages/wact/trunk/framework/template/compiler/tags/tagdictionary.inc.php
   3.x/packages/wact/trunk/framework/template/compiler/templatecompiler.inc.php
   3.x/packages/wact/trunk/tests/cases/template/compiler/parserstate.test.php
   3.x/packages/wact/trunk/tests/cases/template/filters/clip_filter.test.php
Log:
-- WACT package clean up and refactorings

Copied: 3.x/packages/wact/trunk/framework/template/compiler/HTMLParser.class.php (from rev 4159, 3.x/packages/wact/trunk/framework/template/compiler/htmlparser.inc.php)
===================================================================
--- 3.x/packages/wact/trunk/framework/template/compiler/HTMLParser.class.php	                        (rev 0)
+++ 3.x/packages/wact/trunk/framework/template/compiler/HTMLParser.class.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -0,0 +1,380 @@
+<?php
+//--------------------------------------------------------------------------------
+// Copyright 2003 Procata, Inc.
+// Released under the LGPL license (http://www.gnu.org/copyleft/lesser.html)
+//--------------------------------------------------------------------------------
+
+/**
+* HTML/XHTML/XML parser
+* fast parser robustly handles malformed input.
+* All events are triggered by valid markup.
+* If markup is invalid, it is treated as data event.
+* @access protected
+* @abstract
+*/
+class HTMLParser
+{
+    var $publicId;
+    var $Observer;
+    /**
+    * XML document being parsed
+    * @var string
+    * @access private
+    */
+    var $rawtext;
+    /**
+    * Position in XML document relative to start (0)
+    * @var int
+    * @access private
+    */
+    var $position;
+    /**
+    * Length of the XML document in characters
+    * @var int
+    * @access private
+    */
+    var $length;
+
+    /**
+    * @var Observer event handler
+    * @access protected
+    */
+    function HTMLParser(&$Observer) {
+        $this->Observer =& $Observer;
+    }
+
+    /*
+    * Calculates the line number from the byte index
+    * @return int the current line number
+    * @access private
+    */
+    function getLineNumber() {
+        return 1 + substr_count(substr($this->rawtext, 0, $this->position), "\n");
+    }
+
+    function getPublicId() {
+        return $this->publicId;
+    }
+
+    /*
+    * Calculates the column number from the byte index
+    * @return int the current line number
+    * @access private
+    */
+    function getColumnNumber() {
+        // Not implemented yet.
+    }
+
+    /**
+    * Moves the position forward past any whitespace characters
+    * @access protected
+    * @return void
+    */
+    function ignoreWhitespace() {
+        while ($this->position < $this->length &&
+            strpos(" \n\r\t", $this->rawtext{$this->position}) !== FALSE) {
+            $this->position++;
+        }
+    }
+
+    /**
+    * Begins the parsing operation, setting up any decorators, depending on
+    * parse options invoking _parse() to execute parsing
+    * @param string XML document to parse
+    * @access protected
+    * @return void
+    */
+    function parse($data, $publicId = NULL) {
+        $this->rawtext = $data;
+        $this->length = strlen($data);
+        $this->position = 0;
+        $this->publicId = $publicId;
+
+        $this->Observer->setDocumentLocator($this);
+
+        do {
+            $start = $this->position;
+            $this->position = strpos($this->rawtext, '<', $start);
+            if ($this->position === FALSE) {
+                if ($start < $this->length) {
+                    $this->Observer->characters(substr($this->rawtext, $start));
+                }
+                return;
+            }
+
+            if ($this->position > $start) {
+                $this->Observer->characters(substr($this->rawtext, $start, $this->position - $start));
+            }
+
+            $this->position += 1;   // ignore '<' character
+            if ($this->position >= $this->length) {
+                $this->Observer->unexpectedEOF('<');
+                return;
+            }
+
+            $element_pos = $this->position;
+            $this->position += 1;
+
+            switch($this->rawtext{$element_pos}) {
+            case '/':
+                $start = $this->position;
+                while ($this->position < $this->length && $this->rawtext{$this->position} != '>') {
+                    $this->position++;
+                }
+                if ($this->position >= $this->length) {
+                    $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                    return;
+                }
+
+                $tag = substr($this->rawtext, $start, $this->position - $start);
+
+                $this->Observer->endElement($tag);
+                $this->position += 1;   // ignore '>' string
+                break;
+            case '?':
+                $start = $this->position;
+                while ($this->position < $this->length && strpos(" \n\r\t", $this->rawtext{$this->position}) === FALSE) {
+                    $this->position++;
+                }
+                if ($this->position >= $this->length) {
+                    $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                    return;
+                }
+
+                $target = substr($this->rawtext, $start, $this->position - $start);
+
+                $this->ignoreWhitespace();
+
+                $start = $this->position;
+                $this->position = strpos($this->rawtext, '?>', $start);
+                if ($this->position === FALSE) {
+                    $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                    return;
+                }
+
+                $this->Observer->processingInstruction($target,
+                    substr($this->rawtext, $start, $this->position - $start));
+
+                $this->position += 2;   // ignore '? >' string
+                break;
+            case '%':
+                if ($this->position >= $this->length) {
+                    $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                    return;
+                }
+
+                $start = $this->position;
+                $this->position = strpos($this->rawtext, '%>', $start);
+                if ($this->position === FALSE) {
+                    $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                    return;
+                }
+
+                $this->Observer->jasp(substr($this->rawtext, $start, $this->position - $start));
+
+        /* changed to multi line comment per request on list
+        http://sourceforge.net/mailarchive/forum.php?thread_id=5925242&forum_id=35579
+        ignore '%>' string
+        */
+                $this->position += 2;
+                break;
+            case '!':
+                if ($this->position >= $this->length) {
+                    $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                    return;
+                }
+
+                $start = $this->position;
+                if (substr($this->rawtext, $start, 2) == "--") {
+                    $this->position = strpos($this->rawtext, '-->', $start);
+                    if ($this->position === FALSE) {
+                        $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                        return;
+                    }
+                    $this->Observer->comment(
+                        substr($this->rawtext, $start + 2, $this->position - $start - 2));
+                    $this->position += 3;
+                } else if (strcasecmp(substr($this->rawtext, $start, 7), 'DOCTYPE') == 0) {
+                    while ($this->position < $this->length && $this->rawtext{$this->position} != '>') {
+                        $this->position++;
+                    }
+                    if ($this->position >= $this->length) {
+                        $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                        return;
+                    }
+                    $this->Observer->doctype(
+                        substr($this->rawtext, $start, $this->position - $start));
+                    $this->position += 1;
+                } else if (substr($this->rawtext, $start, 7) == '[CDATA[') {
+                    $this->position = strpos($this->rawtext, ']]>', $start + 7);
+                    if ($this->position === FALSE) {
+                        $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                        return;
+                    }
+                    $this->Observer->cdata(
+                        substr($this->rawtext, $start + 7 , $this->position - $start - 7));
+                    $this->position += 3;
+                } else {
+                    while ($this->position < $this->length && $this->rawtext{$this->position} != '>') {
+                        $this->position++;
+                    }
+                    if ($this->position >= $this->length) {
+                        $this->Observer->unexpectedEOF(
+                            substr($this->rawtext, $element_pos - 1));
+                        return;
+                    }
+                    $this->position += 1;
+                    $this->Observer->escape(
+                        substr($this->rawtext, $start, $this->position - $start));
+                }
+                break;
+            default:
+                while ($this->position < $this->length && strpos("/> \n\r\t", $this->rawtext{$this->position}) === FALSE) {
+                    $this->position++;
+                }
+                if ($this->position >= $this->length) {
+                    $this->Observer->unexpectedEOF(
+                        substr($this->rawtext, $element_pos - 1));
+                    return;
+                }
+
+                $tag = substr($this->rawtext, $element_pos, $this->position - $element_pos);
+                $Attributes = array();
+
+                $this->ignoreWhitespace();
+
+                while ( $this->position < $this->length &&
+                        $this->rawtext{$this->position} != '/' &&
+                        $this->rawtext{$this->position} != '>') {
+
+                    $start = $this->position;
+                    while ($this->position < $this->length && strpos("/>= \n\r\t", $this->rawtext{$this->position}) === FALSE) {
+                        $this->position++;
+                    }
+                    if ($this->position >= $this->length) {
+                        $this->Observer->unexpectedEOF(
+                            substr($this->rawtext, $element_pos - 1));
+                        return;
+                    }
+
+                    $attributename = substr($this->rawtext, $start, $this->position - $start);
+                    $attributevalue = NULL;
+
+                    $this->ignoreWhitespace();
+                    if ($this->position >= $this->length) {
+                        $this->Observer->unexpectedEOF(
+                            substr($this->rawtext, $element_pos - 1));
+                        return;
+                    }
+
+                    if ( $this->rawtext{$this->position} == '=') {
+                        $this->position++;
+                        $this->ignoreWhitespace();
+                        if ($this->position >= $this->length) {
+                            $this->Observer->unexpectedEOF(
+                                substr($this->rawtext, $element_pos - 1));
+                            return;
+                        }
+
+                        $quote = $this->rawtext{$this->position};
+                        if ($quote == '"' || $quote == "'") {
+                            $start = $this->position + 1;
+                            $this->position = strpos($this->rawtext, $quote, $start);
+                            if ($this->position === FALSE) {
+                                $this->Observer->unexpectedEOF(
+                                    substr($this->rawtext, $element_pos - 1));
+                                return;
+                            }
+                            $attributevalue = substr($this->rawtext, $start, $this->position - $start);
+
+                            $this->position++;
+                            if ($this->position >= $this->length) {
+                                $this->Observer->unexpectedEOF(
+                                    substr($this->rawtext, $element_pos - 1));
+                                return;
+                            }
+
+                            if (strpos("/> \n\r\t", $this->rawtext{$this->position}) === FALSE) {
+                                $this->Observer->invalidAttributeSyntax();
+                            }
+
+                        } else {
+                            $start = $this->position;
+                            while ($this->position < $this->length && strpos("/> \n\r\t", $this->rawtext{$this->position}) === FALSE) {
+                                $this->position++;
+                            }
+                            if ($this->position >= $this->length) {
+                                $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                                return;
+                            }
+                            $attributevalue = substr($this->rawtext, $start, $this->position - $start);
+                        }
+                    }
+
+                    $Attributes[$attributename] = $attributevalue;
+
+                    $this->ignoreWhitespace();
+                }
+
+                if ($this->position >= $this->length) {
+                    $this->Observer->unexpectedEOF(
+                        substr($this->rawtext, $element_pos - 1));
+                    return;
+                }
+
+                if ($this->rawtext{$this->position} == '/') {
+                    $this->position += 1;
+                    if ($this->position >= $this->length) {
+                        $this->Observer->unexpectedEOF(
+                            substr($this->rawtext, $element_pos - 1));
+                        return;
+                    }
+
+                    if ($this->rawtext{$this->position} != '>') {
+                        $start = $this->position;
+                        while ($this->position < $this->length && $this->rawtext{$this->position} != '>') {
+                            $this->position++;
+                        }
+                        if ($this->position >= $this->length) {
+                            $this->Observer->invalidEntitySyntax(
+                                substr($this->rawtext, $element_pos - 1));
+                            break;
+                        }
+
+                        $this->Observer->invalidEntitySyntax(
+                            substr($this->rawtext, $element_pos - 1, $this->position - $element_pos + 2));
+                        $this->position += 1;
+                        break;
+                    }
+
+                    $this->Observer->emptyElement($tag, $Attributes);
+                } else {
+                    $this->Observer->startElement($tag, $Attributes);
+                }
+                $this->position += 1;
+
+                /*
+                removed due to bug #1000806
+                see http://www.w3.org/TR/REC-html40/appendix/notes.html#notes-specifying-data
+                if (strcasecmp($tag, 'script') == 0 || strcasecmp($tag, 'style') == 0) {
+                    $start = $this->position;
+                    $this->position = strpos($this->rawtext, '</', $start);
+                    if ($this->position == FALSE) {
+                        $this->Observer->unexpectedEOF(
+                            substr($this->rawtext, $start));
+                        return;
+                    }
+
+                    $this->Observer->characters(
+                        substr($this->rawtext, $start, $this->position - $start));
+
+                }
+                */
+
+                break;
+            }
+        } while ($this->position < $this->length);
+    }
+
+}
+?>

Copied: 3.x/packages/wact/trunk/framework/template/compiler/OutputExpression.class.php (from rev 4159, 3.x/packages/wact/trunk/framework/template/compiler/outputexpression.inc.php)
===================================================================
--- 3.x/packages/wact/trunk/framework/template/compiler/OutputExpression.class.php	                        (rev 0)
+++ 3.x/packages/wact/trunk/framework/template/compiler/OutputExpression.class.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -0,0 +1,34 @@
+<?php
+/**
+* Outputs the result of an expression like {$var} or {$'var'}
+*/
+class OutputExpression extends CompilerComponent
+{
+  protected $expression;
+
+  function OutputExpression($expression)
+  {
+    $this->expression = new Expression($expression, $this, 'html');
+  }
+
+  function prepare()
+  {
+    $this->expression->prepare();
+    parent::prepare();
+  }
+
+  function generate($code_writer)
+  {
+    if ($this->expression->isConstant())
+      $code_writer->writeHTML($this->expression->getValue());
+    else
+    {
+      $this->expression->generatePreStatement($code_writer);
+      $code_writer->writePHP('echo ');
+      $this->expression->generateExpression($code_writer);
+      $code_writer->writePHP(';');
+      $this->expression->generatePostStatement($code_writer);
+    }
+  }
+}
+?>
\ No newline at end of file

Modified: 3.x/packages/wact/trunk/framework/template/compiler/SourceFileParser.class.php
===================================================================
--- 3.x/packages/wact/trunk/framework/template/compiler/SourceFileParser.class.php	2006-10-13 21:27:19 UTC (rev 4159)
+++ 3.x/packages/wact/trunk/framework/template/compiler/SourceFileParser.class.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -19,10 +19,10 @@
 /**
 * Includes
 */
-require_once WACT_ROOT . '/template/compiler/nodebuilder.inc.php';
+require_once WACT_ROOT . '/template/compiler/WactNodeBuilder.class.php';
 require_once WACT_ROOT . '/template/compiler/WactTreeBuilder.class.php';
 require_once WACT_ROOT . '/template/compiler/parserstate.inc.php';
-require_once WACT_ROOT . '/template/compiler/htmlparser.inc.php';
+require_once WACT_ROOT . '/template/compiler/HtmlParser.class.php';
 
 //--------------------------------------------------------------------------------
 /**
@@ -31,7 +31,7 @@
 * @access public
 * @package WACT_TEMPLATE
 */
-class SourceFileParser
+class SourceFileParser implements ParserListener
 {
   /**
   * Path and filename of source template
@@ -118,7 +118,7 @@
   * @return void
   * @access protected
   */
-  function parse(&$ComponentRoot)
+  function parse($ComponentRoot)
   {
     $TagDictionary = WactDictionaryHolder ::instance()->getTagDictionary();
 
@@ -128,12 +128,12 @@
 
     $this->TreeBuilder->setCursor($ComponentRoot);
 
-    $this->ComponentParsingState =& new WactComponentParsingState($this, $this->TreeBuilder, $NodeBuilder, $TagDictionary);
-    $this->LiteralParsingState =& new WactLiteralParsingState($this, $this->TreeBuilder);
+    $this->ComponentParsingState = new WactComponentParsingState($this, $this->TreeBuilder, $NodeBuilder, $TagDictionary);
+    $this->LiteralParsingState = new WactLiteralParsingState($this, $this->TreeBuilder);
     $this->changeToComponentParsingState();
 
     $Chain =& $this->buildFilterChain($this->config->getSaxFilters());
-    $parser =& new HTMLParser($Chain);
+    $parser = new HTMLParser($Chain);
     $template = $this->locator->readTemplateFile($this->SourceFile);
     $parser->parse($template, $this->SourceFile);
 
@@ -152,7 +152,7 @@
   * @access public
   */
   function changeToComponentParsingState() {
-      $this->State =& $this->ComponentParsingState;
+      $this->State = $this->ComponentParsingState;
   }
 
   /**
@@ -162,7 +162,7 @@
   * @access public
   */
   function changeToLiteralParsingState($tag) {
-      $this->State = & $this->LiteralParsingState;
+      $this->State = $this->LiteralParsingState;
       $this->State->literalTag = $tag;
   }
 
@@ -172,7 +172,7 @@
   * @return void
   * @access private
   */
-  function setDocumentLocator(&$locator) {
+  function setDocumentLocator($locator) {
       $this->LiteralParsingState->setDocumentLocator($locator);
       $this->ComponentParsingState->setDocumentLocator($locator);
   }

Copied: 3.x/packages/wact/trunk/framework/template/compiler/WactNodeBuilder.class.php (from rev 4159, 3.x/packages/wact/trunk/framework/template/compiler/nodebuilder.inc.php)
===================================================================
--- 3.x/packages/wact/trunk/framework/template/compiler/WactNodeBuilder.class.php	                        (rev 0)
+++ 3.x/packages/wact/trunk/framework/template/compiler/WactNodeBuilder.class.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -0,0 +1,220 @@
+<?php
+/**
+* Responsible for building nodes of the component tree.
+* WactTreeBuilder is responsible for the tree structure.
+*/
+class WactNodeBuilder
+{
+  const BEFORE_CONTENT = 1;
+  const EXPRESSION = 2;
+  const AFTER_CONTENT = 5;
+
+  protected $variable_reference_pattern;
+
+  var $locator;
+
+  function __construct()
+  {
+    $this->variableReferencePattern =
+      // start at the beginning
+      '/^' .
+      // Pick up the portion of the string before the variable reference
+      '((?s).*?)' .
+      // Beginning of a variable reference
+      preg_quote('{$', '/') .
+      // Collect the entire variable reference into one subexpression
+      '(' .
+          // capture the contents of one or more fragments.
+          '(' .
+              // Anything thats not a quote or the end of the variable
+              // reference can be in a fragment
+              '[^"\'}]+' .
+              // OR
+              '|' .
+              // A string inside quotes is also a fragment
+              '(\'|").*?\4' .
+          ')+' .
+      ')' .
+      // end of a variable reference
+      preg_quote('}', '/') .
+      // Pick up the portion of the string after the variable reference
+      // This portion may contain additional references; we only match
+      // one at a time.
+      '((?s).*)' .
+      // Match until the end of the string
+      '$/';
+  }
+
+  function setDocumentLocator($locator)
+  {
+    $this->locator = $locator;
+  }
+
+  /**
+  * Builds a component, adding attributes
+  * @param TagInfo
+  * @param string XML tag name of component
+  * @param array attributes for tag
+  * @param boolean whether the tag has contents
+  * @return CompilerComponent
+  * @access public
+  */
+  function buildTagNode($TagInfo, $tag, $attrs, $isEmpty)
+  {
+    $component = $this->_initTagNode($TagInfo, $tag);
+    $component->emptyClosedTag = $isEmpty;
+    $this->addAttributesToComponent($component, $attrs);
+    return $component;
+  }
+
+  /**
+  * Builds content node(s), adding it (them) to the component tree.
+  * A single piece of content may actually be a mix of terminal nodes
+  * (TextNodes and Expressions)
+  * @param WactTreeBuilder
+  * @param string content of tag
+  * @return void
+  * @access public
+  */
+  function addContent(&$TreeBuilder, $text) {
+      // if there is no expression (common case), shortcut this process
+      if (strpos($text, '{$') === FALSE) {
+          $TreeBuilder->addTextNode($text);
+          return;
+      }
+
+      while (preg_match($this->variableReferencePattern, $text, $match)) {
+          if (strlen($match[self :: BEFORE_CONTENT]) > 0) {
+              $TreeBuilder->addTextNode($match[self :: BEFORE_CONTENT]);
+          }
+
+          $expression =& new OutputExpression($match[self :: EXPRESSION]);
+          $expression->SourceLocation =& new WactSourceLocation(
+              $this->locator->getPublicId(),
+              $this->locator->getLineNumber());
+
+          $TreeBuilder->addNode($expression);
+
+          $text = $match[self :: AFTER_CONTENT];
+      }
+      if (strlen($text) > 0) {
+          $TreeBuilder->addTextNode($text);
+      }
+  }
+
+  /**
+  * Builds XML processing instructions and adds them to the component tree.
+  * PHP instructions are ignored.
+  * @param WactTreeBuilder
+  * @param string target processor
+  * @param string instruction
+  * @return void
+  * @access public
+  */
+  function addProcessingInstruction(&$TreeBuilder, $target, $instruction) {
+      // we can optimize here by not loading PHP node until we need it
+      // It will probably be rarely used in templates.
+      require_once WACT_ROOT . '/template/compiler/PHPNode.class.php';
+
+      // Pass through any PI's except PHP PI's
+      $php_targets = array('php','PHP','=','');
+      if(in_array($target, $php_targets))
+      {
+        $TreeBuilder->addNode(new PHPNode($instruction));
+      }
+      else
+      {
+        $php = 'echo "<?'.$target.' '; // Whitespace assumption
+        $php.= str_replace('"','\"',$instruction);
+        $php.= '?>\n";'; // Newline assumption
+        $TreeBuilder->addNode(new PHPNode($php));
+      }
+  }
+
+  /**
+  * Create a new tag component
+  * @param TagInfo
+  * @param string XML tag name of component
+  * @return void
+  * @access private
+  */
+  protected function _initTagNode(&$TagInfo, $tag) {
+      $class = $TagInfo->TagClass;
+      $component =& new $class();
+      $component->SourceLocation =& new WactSourceLocation(
+          $this->locator->getPublicId(),
+          $this->locator->getLineNumber());
+      $component->tag = $tag;
+      $component->TagInfo =& $TagInfo;
+      $dict = WactDictionaryHolder :: instance()->getPropertyDictionary();;
+      $properties = $dict->getPropertyList($tag);
+      foreach ($properties as $property) {
+          $property->load();
+          $PropertyClass = $property->PropertyClass;
+          $component->registerProperty(
+              $property->Property, new $PropertyClass($component));
+      }
+      return $component;
+  }
+
+  /**
+  * Create AttributeNodes or ArtributeVariableReferences to a component
+  * @param CompilerCOmponent
+  * @param array attributes found in tag
+  * @return void
+  * @access private
+  */
+  function addAttributesToComponent(&$component, $attrs) {
+      foreach ( $attrs as $name => $value ) {
+          // if there is no expression (common case), shortcut this process
+          if (strpos($value, '{$') === FALSE) {
+              $attribute =& new AttributeNode($name, $value);
+          } else {
+              if (preg_match($this->variableReferencePattern, $value, $match)) {
+                  if (strlen($match[self :: AFTER_CONTENT]) == 0 &&
+                      strlen($match[self :: BEFORE_CONTENT]) == 0) {
+                      $attribute =& new AttributeExpression(
+                          $name, $match[self :: EXPRESSION], $component);
+                  } else {
+                      $attribute =& $this->createCompoundAttribute(
+                          $component, $name, $value);
+                  }
+              } else {
+                  $attribute =& new AttributeNode($name, $value);
+              }
+          }
+
+          $component->addChildAttribute($attribute);
+      }
+  }
+
+  /**
+  * Creates a compound attribute
+  * @param CompilerComponent component to which the attribute belongs
+  * @param string name
+  * @param string value
+  * @return CompoundAttribute
+  * @access private
+  */
+  function &createCompoundAttribute(&$component, $name, $value) {
+      $attribute =& new CompoundAttribute($name);
+
+      while (preg_match($this->variableReferencePattern, $value, $match)) {
+          if (strlen($match[self :: BEFORE_CONTENT]) > 0) {
+              $attribute->addAttributeFragment(
+                  new AttributeNode($name, $match[self :: BEFORE_CONTENT]));
+          }
+
+          $attribute->addAttributeFragment(new AttributeExpression($name,
+              $match[self :: EXPRESSION], $component));
+
+          $value = $match[self :: AFTER_CONTENT];
+      }
+      if (strlen($value) > 0) {
+          $attribute->addAttributeFragment(new AttributeNode($name, $value));
+      }
+
+      return $attribute;
+  }
+}
+?>
\ No newline at end of file

Added: 3.x/packages/wact/trunk/framework/template/compiler/WactSourceLocation.class.php
===================================================================
--- 3.x/packages/wact/trunk/framework/template/compiler/WactSourceLocation.class.php	                        (rev 0)
+++ 3.x/packages/wact/trunk/framework/template/compiler/WactSourceLocation.class.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -0,0 +1,13 @@
+<?php
+class WactSourceLocation
+{
+  public $file;
+  public $line;
+
+  function __construct($file, $line)
+  {
+    $this->file = $file;
+    $this->line = $line;
+  }
+}
+?>
\ No newline at end of file

Deleted: 3.x/packages/wact/trunk/framework/template/compiler/htmlparser.inc.php
===================================================================
--- 3.x/packages/wact/trunk/framework/template/compiler/htmlparser.inc.php	2006-10-13 21:27:19 UTC (rev 4159)
+++ 3.x/packages/wact/trunk/framework/template/compiler/htmlparser.inc.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -1,380 +0,0 @@
-<?php
-//--------------------------------------------------------------------------------
-// Copyright 2003 Procata, Inc.
-// Released under the LGPL license (http://www.gnu.org/copyleft/lesser.html)
-//--------------------------------------------------------------------------------
-
-/**
-* HTML/XHTML/XML parser
-* fast parser robustly handles malformed input.
-* All events are triggered by valid markup.
-* If markup is invalid, it is treated as data event.
-* @access protected
-* @abstract
-*/
-class HTMLParser
-{
-    var $publicId;
-    var $Observer;
-    /**
-    * XML document being parsed
-    * @var string
-    * @access private
-    */
-    var $rawtext;
-    /**
-    * Position in XML document relative to start (0)
-    * @var int
-    * @access private
-    */
-    var $position;
-    /**
-    * Length of the XML document in characters
-    * @var int
-    * @access private
-    */
-    var $length;
-
-    /**
-    * @var Observer event handler
-    * @access protected
-    */
-    function HTMLParser(&$Observer) {
-        $this->Observer =& $Observer;
-    }
-
-    /*
-    * Calculates the line number from the byte index
-    * @return int the current line number
-    * @access private
-    */
-    function getLineNumber() {
-        return 1 + substr_count(substr($this->rawtext, 0, $this->position), "\n");
-    }
-
-    function getPublicId() {
-        return $this->publicId;
-    }
-
-    /*
-    * Calculates the column number from the byte index
-    * @return int the current line number
-    * @access private
-    */
-    function getColumnNumber() {
-        // Not implemented yet.
-    }
-
-    /**
-    * Moves the position forward past any whitespace characters
-    * @access protected
-    * @return void
-    */
-    function ignoreWhitespace() {
-        while ($this->position < $this->length &&
-            strpos(" \n\r\t", $this->rawtext{$this->position}) !== FALSE) {
-            $this->position++;
-        }
-    }
-
-    /**
-    * Begins the parsing operation, setting up any decorators, depending on
-    * parse options invoking _parse() to execute parsing
-    * @param string XML document to parse
-    * @access protected
-    * @return void
-    */
-    function parse($data, $publicId = NULL) {
-        $this->rawtext = $data;
-        $this->length = strlen($data);
-        $this->position = 0;
-        $this->publicId = $publicId;
-
-        $this->Observer->setDocumentLocator($this);
-
-        do {
-            $start = $this->position;
-            $this->position = strpos($this->rawtext, '<', $start);
-            if ($this->position === FALSE) {
-                if ($start < $this->length) {
-                    $this->Observer->characters(substr($this->rawtext, $start));
-                }
-                return;
-            }
-
-            if ($this->position > $start) {
-                $this->Observer->characters(substr($this->rawtext, $start, $this->position - $start));
-            }
-
-            $this->position += 1;   // ignore '<' character
-            if ($this->position >= $this->length) {
-                $this->Observer->unexpectedEOF('<');
-                return;
-            }
-
-            $element_pos = $this->position;
-            $this->position += 1;
-
-            switch($this->rawtext{$element_pos}) {
-            case '/':
-                $start = $this->position;
-                while ($this->position < $this->length && $this->rawtext{$this->position} != '>') {
-                    $this->position++;
-                }
-                if ($this->position >= $this->length) {
-                    $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
-                    return;
-                }
-
-                $tag = substr($this->rawtext, $start, $this->position - $start);
-
-                $this->Observer->endElement($tag);
-                $this->position += 1;   // ignore '>' string
-                break;
-            case '?':
-                $start = $this->position;
-                while ($this->position < $this->length && strpos(" \n\r\t", $this->rawtext{$this->position}) === FALSE) {
-                    $this->position++;
-                }
-                if ($this->position >= $this->length) {
-                    $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
-                    return;
-                }
-
-                $target = substr($this->rawtext, $start, $this->position - $start);
-
-                $this->ignoreWhitespace();
-
-                $start = $this->position;
-                $this->position = strpos($this->rawtext, '?>', $start);
-                if ($this->position === FALSE) {
-                    $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
-                    return;
-                }
-
-                $this->Observer->processingInstruction($target,
-                    substr($this->rawtext, $start, $this->position - $start));
-
-                $this->position += 2;   // ignore '? >' string
-                break;
-            case '%':
-                if ($this->position >= $this->length) {
-                    $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
-                    return;
-                }
-
-                $start = $this->position;
-                $this->position = strpos($this->rawtext, '%>', $start);
-                if ($this->position === FALSE) {
-                    $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
-                    return;
-                }
-
-                $this->Observer->jasp(substr($this->rawtext, $start, $this->position - $start));
-
-        /* changed to multi line comment per request on list
-        http://sourceforge.net/mailarchive/forum.php?thread_id=5925242&forum_id=35579
-        ignore '%>' string
-        */
-                $this->position += 2;
-                break;
-            case '!':
-                if ($this->position >= $this->length) {
-                    $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
-                    return;
-                }
-
-                $start = $this->position;
-                if (substr($this->rawtext, $start, 2) == "--") {
-                    $this->position = strpos($this->rawtext, '-->', $start);
-                    if ($this->position === FALSE) {
-                        $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
-                        return;
-                    }
-                    $this->Observer->comment(
-                        substr($this->rawtext, $start + 2, $this->position - $start - 2));
-                    $this->position += 3;
-                } else if (strcasecmp(substr($this->rawtext, $start, 7), 'DOCTYPE') == 0) {
-                    while ($this->position < $this->length && $this->rawtext{$this->position} != '>') {
-                        $this->position++;
-                    }
-                    if ($this->position >= $this->length) {
-                        $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
-                        return;
-                    }
-                    $this->Observer->doctype(
-                        substr($this->rawtext, $start, $this->position - $start));
-                    $this->position += 1;
-                } else if (substr($this->rawtext, $start, 7) == '[CDATA[') {
-                    $this->position = strpos($this->rawtext, ']]>', $start + 7);
-                    if ($this->position === FALSE) {
-                        $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
-                        return;
-                    }
-                    $this->Observer->cdata(
-                        substr($this->rawtext, $start + 7 , $this->position - $start - 7));
-                    $this->position += 3;
-                } else {
-                    while ($this->position < $this->length && $this->rawtext{$this->position} != '>') {
-                        $this->position++;
-                    }
-                    if ($this->position >= $this->length) {
-                        $this->Observer->unexpectedEOF(
-                            substr($this->rawtext, $element_pos - 1));
-                        return;
-                    }
-                    $this->position += 1;
-                    $this->Observer->escape(
-                        substr($this->rawtext, $start, $this->position - $start));
-                }
-                break;
-            default:
-                while ($this->position < $this->length && strpos("/> \n\r\t", $this->rawtext{$this->position}) === FALSE) {
-                    $this->position++;
-                }
-                if ($this->position >= $this->length) {
-                    $this->Observer->unexpectedEOF(
-                        substr($this->rawtext, $element_pos - 1));
-                    return;
-                }
-
-                $tag = substr($this->rawtext, $element_pos, $this->position - $element_pos);
-                $Attributes = array();
-
-                $this->ignoreWhitespace();
-
-                while ( $this->position < $this->length &&
-                        $this->rawtext{$this->position} != '/' &&
-                        $this->rawtext{$this->position} != '>') {
-
-                    $start = $this->position;
-                    while ($this->position < $this->length && strpos("/>= \n\r\t", $this->rawtext{$this->position}) === FALSE) {
-                        $this->position++;
-                    }
-                    if ($this->position >= $this->length) {
-                        $this->Observer->unexpectedEOF(
-                            substr($this->rawtext, $element_pos - 1));
-                        return;
-                    }
-
-                    $attributename = substr($this->rawtext, $start, $this->position - $start);
-                    $attributevalue = NULL;
-
-                    $this->ignoreWhitespace();
-                    if ($this->position >= $this->length) {
-                        $this->Observer->unexpectedEOF(
-                            substr($this->rawtext, $element_pos - 1));
-                        return;
-                    }
-
-                    if ( $this->rawtext{$this->position} == '=') {
-                        $this->position++;
-                        $this->ignoreWhitespace();
-                        if ($this->position >= $this->length) {
-                            $this->Observer->unexpectedEOF(
-                                substr($this->rawtext, $element_pos - 1));
-                            return;
-                        }
-
-                        $quote = $this->rawtext{$this->position};
-                        if ($quote == '"' || $quote == "'") {
-                            $start = $this->position + 1;
-                            $this->position = strpos($this->rawtext, $quote, $start);
-                            if ($this->position === FALSE) {
-                                $this->Observer->unexpectedEOF(
-                                    substr($this->rawtext, $element_pos - 1));
-                                return;
-                            }
-                            $attributevalue = substr($this->rawtext, $start, $this->position - $start);
-
-                            $this->position++;
-                            if ($this->position >= $this->length) {
-                                $this->Observer->unexpectedEOF(
-                                    substr($this->rawtext, $element_pos - 1));
-                                return;
-                            }
-
-                            if (strpos("/> \n\r\t", $this->rawtext{$this->position}) === FALSE) {
-                                $this->Observer->invalidAttributeSyntax();
-                            }
-
-                        } else {
-                            $start = $this->position;
-                            while ($this->position < $this->length && strpos("/> \n\r\t", $this->rawtext{$this->position}) === FALSE) {
-                                $this->position++;
-                            }
-                            if ($this->position >= $this->length) {
-                                $this->Observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
-                                return;
-                            }
-                            $attributevalue = substr($this->rawtext, $start, $this->position - $start);
-                        }
-                    }
-
-                    $Attributes[$attributename] = $attributevalue;
-
-                    $this->ignoreWhitespace();
-                }
-
-                if ($this->position >= $this->length) {
-                    $this->Observer->unexpectedEOF(
-                        substr($this->rawtext, $element_pos - 1));
-                    return;
-                }
-
-                if ($this->rawtext{$this->position} == '/') {
-                    $this->position += 1;
-                    if ($this->position >= $this->length) {
-                        $this->Observer->unexpectedEOF(
-                            substr($this->rawtext, $element_pos - 1));
-                        return;
-                    }
-
-                    if ($this->rawtext{$this->position} != '>') {
-                        $start = $this->position;
-                        while ($this->position < $this->length && $this->rawtext{$this->position} != '>') {
-                            $this->position++;
-                        }
-                        if ($this->position >= $this->length) {
-                            $this->Observer->invalidEntitySyntax(
-                                substr($this->rawtext, $element_pos - 1));
-                            break;
-                        }
-
-                        $this->Observer->invalidEntitySyntax(
-                            substr($this->rawtext, $element_pos - 1, $this->position - $element_pos + 2));
-                        $this->position += 1;
-                        break;
-                    }
-
-                    $this->Observer->emptyElement($tag, $Attributes);
-                } else {
-                    $this->Observer->startElement($tag, $Attributes);
-                }
-                $this->position += 1;
-
-                /*
-                removed due to bug #1000806
-                see http://www.w3.org/TR/REC-html40/appendix/notes.html#notes-specifying-data
-                if (strcasecmp($tag, 'script') == 0 || strcasecmp($tag, 'style') == 0) {
-                    $start = $this->position;
-                    $this->position = strpos($this->rawtext, '</', $start);
-                    if ($this->position == FALSE) {
-                        $this->Observer->unexpectedEOF(
-                            substr($this->rawtext, $start));
-                        return;
-                    }
-
-                    $this->Observer->characters(
-                        substr($this->rawtext, $start, $this->position - $start));
-
-                }
-                */
-
-                break;
-            }
-        } while ($this->position < $this->length);
-    }
-
-}
-?>

Deleted: 3.x/packages/wact/trunk/framework/template/compiler/nodebuilder.inc.php
===================================================================
--- 3.x/packages/wact/trunk/framework/template/compiler/nodebuilder.inc.php	2006-10-13 21:27:19 UTC (rev 4159)
+++ 3.x/packages/wact/trunk/framework/template/compiler/nodebuilder.inc.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -1,278 +0,0 @@
-<?php
-//------------------------------------------------------------------------------
-// Copyright 2003 Procata, Inc.
-// Released under the LGPL license (http://www.gnu.org/copyleft/lesser.html)
-//------------------------------------------------------------------------------
-/**
-* @package WACT_TEMPLATE
-* @version $Id: nodebuilder.inc.php,v 1.2 2005/12/30 10:35:47 ian_w_white Exp $
-*/
-//------------------------------------------------------------------------------
-
-/**#@+
- *      ExpresionParser capture constants
- */
-define('WACT_EXPPARSER_BEFORE_CONTENT', 1);
-define('WACT_EXPPARSER_EXPRESSION', 2);
-define('WACT_EXPPARSER_AFTER_CONTENT',  5);
-/**#@-*/
-
-class WactSourceLocation {
-
-    /**
-    * Used to identify the source template file, when generating compile time
-    * error messages.
-    * @var string source template filename
-    * @access private
-    */
-    var $file;
-
-    /**
-    * Used to indentify the line number where a compile time error occurred.
-    * @var int line number
-    * @access private
-    */
-    var $line;
-
-    function WactSourceLocation($file, $line) {
-        $this->file = $file;
-        $this->line = $line;
-    }
-
-}
-
-
-/**
-* Responsible for building nodes of the component tree.
-* WactTreeBuilder is responsible for the tree structure.
-*
-* @see http://wact.sourceforge.net/index.php/NodeBuilder
-* @access public
-* @package WACT_TEMPLATE
-*/
-class WactNodeBuilder {
-
-    /**
-    * Regular expression for mathcing wact variable expressions
-    * @access private
-    */
-    var $variableReferencePattern;
-
-    /**
-    * Used to locate position within document
-    * @access private
-    */
-    var $locator;
-
-    /**
-    * Constructs WactTreeBuilder, setting up expression parsers
-    * @access public
-    */
-    function WactNodeBuilder() {
-        $this->variableReferencePattern =
-            // start at the beginning
-            '/^' .
-            // Pick up the portion of the string before the variable reference
-            '((?s).*?)' .
-            // Beginning of a variable reference
-            preg_quote('{$', '/') .
-            // Collect the entire variable reference into one subexpression
-            '(' .
-                // capture the contents of one or more fragments.
-                '(' .
-                    // Anything thats not a quote or the end of the variable
-                    // reference can be in a fragment
-                    '[^"\'}]+' .
-                    // OR
-                    '|' .
-                    // A string inside quotes is also a fragment
-                    '(\'|").*?\4' .
-                ')+' .
-            ')' .
-            // end of a variable reference
-            preg_quote('}', '/') .
-            // Pick up the portion of the string after the variable reference
-            // This portion may contain additional references; we only match
-            // one at a time.
-            '((?s).*)' .
-            // Match until the end of the string
-            '$/';
-    }
-
-    /**
-    * Sets the Document Locator
-    * @param DocumentLocator
-    * @return void
-    * @access public
-    */
-    function setDocumentLocator(&$locator) {
-        $this->locator =& $locator;
-    }
-
-    /**
-    * Builds a component, adding attributes
-    * @param TagInfo
-    * @param string XML tag name of component
-    * @param array attributes for tag
-    * @param boolean whether the tag has contents
-    * @return CompilerComponent
-    * @access public
-    */
-    function &buildComponent(&$TagInfo, $tag, $attrs, $isEmpty) {
-        $component =& $this->createComponent($TagInfo, $tag);
-        $component->emptyClosedTag = $isEmpty;
-        $this->addAttributesToComponent($component, $attrs);
-        return $component;
-    }
-
-    /**
-    * Builds content node(s), adding it (them) to the component tree.
-    * A single piece of content may actually be a mix of terminal nodes
-    * (TextNodes and Expressions)
-    * @param WactTreeBuilder
-    * @param string content of tag
-    * @return void
-    * @access public
-    */
-    function addContent(&$TreeBuilder, $text) {
-        // if there is no expression (common case), shortcut this process
-        if (strpos($text, '{$') === FALSE) {
-            $TreeBuilder->addTextNode($text);
-            return;
-        }
-
-        while (preg_match($this->variableReferencePattern, $text, $match)) {
-            if (strlen($match[WACT_EXPPARSER_BEFORE_CONTENT]) > 0) {
-                $TreeBuilder->addTextNode($match[WACT_EXPPARSER_BEFORE_CONTENT]);
-            }
-
-            $expression =& new OutputExpression($match[WACT_EXPPARSER_EXPRESSION]);
-            $expression->SourceLocation =& new WactSourceLocation(
-                $this->locator->getPublicId(),
-                $this->locator->getLineNumber());
-
-            $TreeBuilder->addNode($expression);
-
-            $text = $match[WACT_EXPPARSER_AFTER_CONTENT];
-        }
-        if (strlen($text) > 0) {
-            $TreeBuilder->addTextNode($text);
-        }
-    }
-
-    /**
-    * Builds XML processing instructions and adds them to the component tree.
-    * PHP instructions are ignored.
-    * @param WactTreeBuilder
-    * @param string target processor
-    * @param string instruction
-    * @return void
-    * @access public
-    */
-    function addProcessingInstruction(&$TreeBuilder, $target, $instruction) {
-        // we can optimize here by not loading PHP node until we need it
-        // It will probably be rarely used in templates.
-        require_once WACT_ROOT . '/template/compiler/PHPNode.class.php';
-
-        // Pass through any PI's except PHP PI's
-        $php_targets = array('php','PHP','=','');
-        if(in_array($target, $php_targets))
-        {
-          $TreeBuilder->addNode(new PHPNode($instruction));
-        }
-        else
-        {
-          $php = 'echo "<?'.$target.' '; // Whitespace assumption
-          $php.= str_replace('"','\"',$instruction);
-          $php.= '?>\n";'; // Newline assumption
-          $TreeBuilder->addNode(new PHPNode($php));
-        }
-    }
-
-    /**
-    * Create a new tag component
-    * @param TagInfo
-    * @param string XML tag name of component
-    * @return void
-    * @access private
-    */
-    function &createComponent(&$TagInfo, $tag) {
-        $class = $TagInfo->TagClass;
-        $component =& new $class();
-        $component->SourceLocation =& new WactSourceLocation(
-            $this->locator->getPublicId(),
-            $this->locator->getLineNumber());
-        $component->tag = $tag;
-        $component->TagInfo =& $TagInfo;
-        $dict = WactDictionaryHolder :: instance()->getPropertyDictionary();;
-        $properties = $dict->getPropertyList($tag);
-        foreach ($properties as $property) {
-            $property->load();
-            $PropertyClass = $property->PropertyClass;
-            $component->registerProperty(
-                $property->Property, new $PropertyClass($component));
-        }
-        return $component;
-    }
-
-    /**
-    * Create AttributeNodes or ArtributeVariableReferences to a component
-    * @param CompilerCOmponent
-    * @param array attributes found in tag
-    * @return void
-    * @access private
-    */
-    function addAttributesToComponent(&$component, $attrs) {
-        foreach ( $attrs as $name => $value ) {
-            // if there is no expression (common case), shortcut this process
-            if (strpos($value, '{$') === FALSE) {
-                $attribute =& new AttributeNode($name, $value);
-            } else {
-                if (preg_match($this->variableReferencePattern, $value, $match)) {
-                    if (strlen($match[WACT_EXPPARSER_AFTER_CONTENT]) == 0 &&
-                        strlen($match[WACT_EXPPARSER_BEFORE_CONTENT]) == 0) {
-                        $attribute =& new AttributeExpression(
-                            $name, $match[WACT_EXPPARSER_EXPRESSION], $component);
-                    } else {
-                        $attribute =& $this->createCompoundAttribute(
-                            $component, $name, $value);
-                    }
-                } else {
-                    $attribute =& new AttributeNode($name, $value);
-                }
-            }
-
-            $component->addChildAttribute($attribute);
-        }
-    }
-
-    /**
-    * Creates a compound attribute
-    * @param CompilerComponent component to which the attribute belongs
-    * @param string name
-    * @param string value
-    * @return CompoundAttribute
-    * @access private
-    */
-    function &createCompoundAttribute(&$component, $name, $value) {
-        $attribute =& new CompoundAttribute($name);
-
-        while (preg_match($this->variableReferencePattern, $value, $match)) {
-            if (strlen($match[WACT_EXPPARSER_BEFORE_CONTENT]) > 0) {
-                $attribute->addAttributeFragment(
-                    new AttributeNode($name, $match[WACT_EXPPARSER_BEFORE_CONTENT]));
-            }
-
-            $attribute->addAttributeFragment(new AttributeExpression($name,
-                $match[WACT_EXPPARSER_EXPRESSION], $component));
-
-            $value = $match[WACT_EXPPARSER_AFTER_CONTENT];
-        }
-        if (strlen($value) > 0) {
-            $attribute->addAttributeFragment(new AttributeNode($name, $value));
-        }
-
-        return $attribute;
-    }
-}
-?>
\ No newline at end of file

Deleted: 3.x/packages/wact/trunk/framework/template/compiler/outputexpression.inc.php
===================================================================
--- 3.x/packages/wact/trunk/framework/template/compiler/outputexpression.inc.php	2006-10-13 21:27:19 UTC (rev 4159)
+++ 3.x/packages/wact/trunk/framework/template/compiler/outputexpression.inc.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -1,42 +0,0 @@
-<?php
-//--------------------------------------------------------------------------------
-// Copyright 2003 Procata, Inc.
-// Released under the LGPL license (http://www.gnu.org/copyleft/lesser.html)
-//--------------------------------------------------------------------------------
-/**
-* @package WACT_TAG
-* @version $Id: outputexpression.inc.php,v 1.4 2005/08/16 12:46:05 pachanga Exp $
-*/
-//--------------------------------------------------------------------------------
-/**
-* Outputs the resultof an expression
-* @see CoreOutputTag
-* @see http://wact.sourceforge.net/index.php/CoreOutputTag
-* @access protected
-* @package WACT_TAG
-*/
-class OutputExpression extends CompilerComponent {
-    var $expression;
-
-    function OutputExpression($expression) {
-        $this->expression =& new Expression($expression, $this, 'html');
-    }
-
-    function prepare() {
-        $this->expression->prepare();
-        parent::prepare();
-    }
-
-    function generate(&$code) {
-        if ($this->expression->isConstant()) {
-            $code->writeHTML($this->expression->getValue());
-        } else {
-            $this->expression->generatePreStatement($code);
-            $code->writePHP('echo ');
-            $this->expression->generateExpression($code);
-            $code->writePHP(';');
-            $this->expression->generatePostStatement($code);
-        }
-    }
-}
-?>
\ No newline at end of file

Added: 3.x/packages/wact/trunk/framework/template/compiler/parser/ParserListener.interface.php
===================================================================
--- 3.x/packages/wact/trunk/framework/template/compiler/parser/ParserListener.interface.php	                        (rev 0)
+++ 3.x/packages/wact/trunk/framework/template/compiler/parser/ParserListener.interface.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -0,0 +1,19 @@
+<?php
+interface ParserListener
+{
+  function startElement($tag_name, $attrs);
+  function endElement($tag_name);
+  function emptyElement($tag_name, $attrs);
+  function characters($data);
+  function cdata($data);
+  function processingInstruction($type, $data);
+  function escape($data);
+  function comment($data);
+  function doctype($data);
+  function jasp($data);
+  function unexpectedEOF($data);
+  function invalidEntitySyntax($data);
+  function invalidAttributeSyntax();
+  function setDocumentLocator($locator);
+}
+?>
\ No newline at end of file

Modified: 3.x/packages/wact/trunk/framework/template/compiler/parserstate.inc.php
===================================================================
--- 3.x/packages/wact/trunk/framework/template/compiler/parserstate.inc.php	2006-10-13 21:27:19 UTC (rev 4159)
+++ 3.x/packages/wact/trunk/framework/template/compiler/parserstate.inc.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -1,27 +1,9 @@
 <?php
-//--------------------------------------------------------------------------------
-// Copyright 2003 Procata, Inc.
-// Released under the LGPL license (http://www.gnu.org/copyleft/lesser.html)
-//--------------------------------------------------------------------------------
 /**
-* @package WACT_TEMPLATE
-* @version $Id: parserstate.inc.php,v 1.49 2005/12/30 10:35:47 ian_w_white Exp $
-*/
-//--------------------------------------------------------------------------------
-/**
-* Load array_change_key_case as needed
-*/
-if (!function_exists('array_change_key_case')) {
-    require_once WACT_ROOT . '/util/phpcompat/array_change_key_case.php';
-}
-
-/**
 * Base state handler for the SourceFileParser. Handles plain text
-* @see http://wact.sourceforge.net/index.php/BaseParsingState
-* @access public
-* @package WACT_TEMPLATE
 */
-class WactBaseParsingState {
+abstract class WactBaseParsingState
+{
     /**
     * Instance of SourceFileParser
     * @var SourceFileParser
@@ -42,32 +24,20 @@
     */
     var $TreeBuilder;
 
-    /**
-    * @param SourceFileParser
-    * @access protected
-    */
-    function WactBaseParsingState(&$Parser, &$TreeBuilder) {
-        $this->Parser = & $Parser;
-        $this->TreeBuilder = & $TreeBuilder;
-        $this->Locator = NULL; // to suppress 4.1.2 warnings
+    function __construct($Parser, $TreeBuilder)
+    {
+      $this->Parser = $Parser;
+      $this->TreeBuilder = $TreeBuilder;
     }
 
-    /**
-    * @param Locator
-    * @access protected
-    */
-    function setDocumentLocator(&$locator) {
-        $this->Locator =& $locator;
-        $this->TreeBuilder->setDocumentLocator($locator);
+    function setDocumentLocator($locator)
+    {
+      $this->Locator = $locator;
+      $this->TreeBuilder->setDocumentLocator($locator);
     }
 
-    /**
-  * Builds an attribute string based on passed array of attributes
-  * @param array
-  * @return string
-  * @access protected
-  */
-    function getAttributeString($attrs) {
+    function getAttributeString($attrs)
+    {
         $attrib_str = '';
         foreach ( $attrs as $key => $value ) {
             $attrib_str .= ' ' . $key;
@@ -87,10 +57,11 @@
     * @param string tag content
     * @access public
     */
-    function invalidAttributeSyntax() {
-        throw new WactException('compiler', 'INVALID_ATTRIBUTE_SYNTAX', array(
-            'file' => $this->Locator->getPublicId(),
-            'line' => $this->Locator->getLineNumber()));
+    function invalidAttributeSyntax()
+    {
+      throw new WactException('compiler', 'INVALID_ATTRIBUTE_SYNTAX', array(
+          'file' => $this->Locator->getPublicId(),
+          'line' => $this->Locator->getLineNumber()));
     }
 
 }
@@ -107,7 +78,8 @@
 * @access public
 * @access WACT_TEMPLATE
 */
-class WactComponentParsingState extends WactBaseParsingState {
+class WactComponentParsingState extends WactBaseParsingState implements ParserListener
+{
     /**
     * Instance of TagDictionary
     * @var TagDictionary
@@ -125,19 +97,22 @@
     * @param SourceFileParser
     * @access public
     */
-    function WactComponentParsingState(&$Parser, &$TreeBuilder, &$NodeBuilder, &$TagDictionary) {
-        parent::WactBaseParsingState($Parser, $TreeBuilder);
-        $this->TagDictionary =& $TagDictionary;
-        $this->NodeBuilder =& $NodeBuilder;
+    function __construct($Parser, $TreeBuilder, $NodeBuilder, $TagDictionary)
+    {
+      parent :: __construct($Parser, $TreeBuilder);
+      $this->TagDictionary =& $TagDictionary;
+      $this->NodeBuilder =& $NodeBuilder;
     }
 
     /**
     * @param Locator
     * @access protected
     */
-    function setDocumentLocator(&$locator) {
-        parent::setDocumentLocator($locator);
-        $this->NodeBuilder->setDocumentLocator($locator);
+    function setDocumentLocator($locator)
+    {
+      parent::setDocumentLocator($locator);
+
+      $this->NodeBuilder->setDocumentLocator($locator);
     }
 
     /**
@@ -185,7 +160,7 @@
         if ( $TagInfo->EndTag != ENDTAG_FORBIDDEN )
           $this->TreeBuilder->pushExpectedTag($tag, PARSER_TAG_IS_COMPONENT, $this->_getPositionInfo());
 
-        $component =& $this->NodeBuilder->buildComponent($TagInfo, $tag, $attrs, FALSE);
+        $component =& $this->NodeBuilder->buildTagNode($TagInfo, $tag, $attrs, FALSE);
         if ( $this->TreeBuilder->pushNode($component) == PARSER_FORBID_PARSING)
           $this->Parser->changeToLiteralParsingState($tag);
 
@@ -242,14 +217,13 @@
     */
     function emptyElement($tag, $attrs)
     {
-
       $lower_attributes = $this->checkAttributes($attrs);
 
         $TagInfo =& $this->TagDictionary->findComponent($tag, $lower_attributes, TRUE, $this->TreeBuilder->getCursor());
         if (is_object($TagInfo)) {
             $TagInfo->load();
 
-            $component =& $this->NodeBuilder->buildComponent($TagInfo, $tag, $attrs, TRUE);
+            $component =& $this->NodeBuilder->buildTagNode($TagInfo, $tag, $attrs, TRUE);
             if ( $this->TreeBuilder->pushNode($component) == PARSER_FORBID_PARSING) {
                 $this->Parser->changeToLiteralParsingState($tag);
       }
@@ -381,28 +355,10 @@
 * @access public
 * @access WACT_TEMPLATE
 */
-class WactLiteralParsingState extends WactBaseParsingState {
-    /**
-    * Name of the literal tag
-    * @var string
-    * @access public
-    */
+class WactLiteralParsingState extends WactBaseParsingState  implements ParserListener
+{
     var $literalTag;
 
-    /**
-    * @param SourceFileParser
-    * @access public
-    */
-    function WactLiteralParsingState(&$Parser, &$TreeBuilder) {
-        parent::WactBaseParsingState($Parser, $TreeBuilder);
-    }
-
-    /**
-    * Handle opening tags
-    * @param string tag name
-    * @param array tag attributes
-    * @access public
-    */
     function startElement($tag, $attrs) {
         $this->TreeBuilder->addTextNode('<' . $tag . $this->getAttributeString($attrs) . '>');
     }
@@ -519,6 +475,5 @@
         // Ignore the error and treat the rest of the file like data
         $this->TreeBuilder->addTextNode($text);
     }
-
 }
 ?>
\ No newline at end of file

Modified: 3.x/packages/wact/trunk/framework/template/compiler/tags/tagdictionary.inc.php
===================================================================
--- 3.x/packages/wact/trunk/framework/template/compiler/tags/tagdictionary.inc.php	2006-10-13 21:27:19 UTC (rev 4159)
+++ 3.x/packages/wact/trunk/framework/template/compiler/tags/tagdictionary.inc.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -110,7 +110,7 @@
   * @return boolean TRUE if it's a component
   * @access private
   */
-  function findComponent($tag, $attrs, $isEmpty, &$Component) {
+  function findComponent($tag, $attrs, $isEmpty, $Component) {
       $tag = strtolower($tag);
       // Does the tag have a wact:id attribute? if so, set 'runat = server'
       if ( isset ( $attrs['wact:id'] ) ) {

Modified: 3.x/packages/wact/trunk/framework/template/compiler/templatecompiler.inc.php
===================================================================
--- 3.x/packages/wact/trunk/framework/template/compiler/templatecompiler.inc.php	2006-10-13 21:27:19 UTC (rev 4159)
+++ 3.x/packages/wact/trunk/framework/template/compiler/templatecompiler.inc.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -31,7 +31,7 @@
 
 require_once WACT_ROOT . '/template/compiler/TextNode.class.php';
 require_once WACT_ROOT . '/template/compiler/PHPNode.class.php';
-require_once WACT_ROOT . '/template/compiler/outputexpression.inc.php';
+require_once WACT_ROOT . '/template/compiler/OutputExpression.class.php';
 
 require_once WACT_ROOT . '/template/compiler/attribute/AttributeNode.class.php';
 require_once WACT_ROOT . '/template/compiler/filter/CompilerFilter.class.php';
@@ -42,6 +42,9 @@
 require_once WACT_ROOT . '/template/compiler/property/property.inc.php';
 require_once WACT_ROOT . '/template/compiler/property/constant.inc.php';
 
+require_once WACT_ROOT . '/template/compiler/parser/ParserListener.interface.php';
+
+require_once WACT_ROOT . '/template/compiler/WactSourceLocation.class.php';
 require_once WACT_ROOT . '/template/compiler/WactTreeBuilder.class.php';
 require_once WACT_ROOT . '/template/compiler/SourceFileParser.class.php';
 require_once WACT_ROOT . '/template/compiler/CodeWriter.class.php';

Added: 3.x/packages/wact/trunk/tests/cases/template/compiler/HtmlParserMalformedTest.class.php
===================================================================
--- 3.x/packages/wact/trunk/tests/cases/template/compiler/HtmlParserMalformedTest.class.php	                        (rev 0)
+++ 3.x/packages/wact/trunk/tests/cases/template/compiler/HtmlParserMalformedTest.class.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -0,0 +1,92 @@
+<?php
+require_once(WACT_TEST_CASES . '/template/compiler/HtmlParserTest.class.php');
+
+class HtmlParserMalformedTest extends UnitTestCase
+{
+  protected $parser;
+  protected $listener;
+
+  function setUp()
+  {
+    $this->listener = new MockParserListener();
+    $this->parser = new HTMLParser($this->listener);
+  }
+
+  function testMalformedJasp()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<%>'));
+    $this->listener->expectNever('jasp');
+    $this->parser->parse('stuff<%>');
+  }
+
+  function testMalformedProcessingInstruction()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<?>'));
+    $this->listener->expectNever('processingInstruction');
+    $this->parser->parse('stuff<?>');
+  }
+
+  function testMalformedProcessingInstruction2()
+  {
+    $this->listener->expectOnce('unexpectedEOF', array('<??>'));
+    $this->listener->expectNever('processingInstruction');
+    $this->listener->expectNever('characters');
+    $this->parser->parse('<??>');
+  }
+
+  function testMalformedComment()
+  {
+    $this->listener->expectOnce('unexpectedEOF', array('<!--x->'));
+    $this->listener->expectNever('escape');
+    $this->listener->expectNever('comment');
+    $this->listener->expectNever('characters');
+    $this->parser->parse('<!--x->');
+  }
+
+  function testOpenElementMalformedClose()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('invalidEntitySyntax', array('<tag attribute=\'value\'/morestuff'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute=\'value\'/morestuff');
+  }
+
+  function testOpenElementMalformedClose2()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('invalidEntitySyntax', array('<tag attribute=\'value\'/morestuff>'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute=\'value\'/morestuff>');
+  }
+
+  function testElementNestedSingleQuote()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array('attribute' => '', "'" => NULL)));
+    $this->listener->expectOnce('invalidAttributeSyntax');
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('<tag attribute=\'\'\'>');
+  }
+
+  function testElementNestedDoubleQuote()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array('attribute' => '', '"' => NULL)));
+    $this->listener->expectOnce('invalidAttributeSyntax');
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('<tag attribute=""">');
+  }
+
+  function testElementMalformedAttribute()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array('attribute' => 'test', 'extra' => NULL)));
+    $this->listener->expectOnce('invalidAttributeSyntax');
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('<tag attribute="test"extra>');
+  }
+}
+
+?>
\ No newline at end of file

Copied: 3.x/packages/wact/trunk/tests/cases/template/compiler/HtmlParserTest.class.php (from rev 4159, 3.x/packages/wact/trunk/tests/cases/template/compiler/htmlparser.test.php)
===================================================================
--- 3.x/packages/wact/trunk/tests/cases/template/compiler/HtmlParserTest.class.php	                        (rev 0)
+++ 3.x/packages/wact/trunk/tests/cases/template/compiler/HtmlParserTest.class.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -0,0 +1,299 @@
+<?php
+require_once(WACT_ROOT . '/template/compiler/templatecompiler.inc.php');
+
+Mock::generate('ParserListener', 'MockParserListener');
+
+class HtmlParserTest extends UnitTestCase
+{
+  var $parser;
+  var $listener;
+
+  function setUp()
+  {
+    $this->listener = new MockParserListener();
+    $this->parser = new HTMLParser($this->listener);
+  }
+
+  function testEmpty()
+  {
+    $this->listener->expectNever('jasp');
+    $this->listener->expectNever('processingInstruction');
+    $this->listener->expectNever('escape');
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('startElement');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('');
+  }
+
+  function testSimpledata()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->parser->parse('stuff');
+  }
+
+  function testPreservingWhiteSpace()
+  {
+    $this->listener->expectOnce('characters', array(" stuff\t\r\n "));
+    $this->parser->parse(" stuff\t\r\n ");
+  }
+
+  function testEmptyElement()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array()));
+    $this->listener->expectOnce('endElement', array('tag'));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<tag></tag>');
+  }
+
+  function testEmptyElementSelfClose()
+  {
+    $this->listener->expectOnce('emptyElement', array('br', array()));
+    $this->listener->expectNever('startElement');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('<br/>');
+  }
+
+  function testElementWithContent()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array()));
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('endElement', array('tag'));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<tag>stuff</tag>');
+  }
+
+  function testEmptyComment()
+  {
+    $this->listener->expectOnce('comment', array(''));
+    $this->listener->expectNever('characters');
+    $this->parser->parse('<!---->');
+  }
+
+  function testElementNestedSingleQuote()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array('attribute' => '\'')));
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('endElement');
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<tag attribute="\'">');
+  }
+
+  function testElementNestedDoubleQuote()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array('attribute' => '"')));
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('endElement');
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<tag attribute=\'"\'>');
+  }
+
+  function testEmptyDocType()
+  {
+    $this->listener->expectOnce('doctype', array('DOCTYPE'));
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('escape');
+    $this->parser->parse('<!DOCTYPE>');
+  }
+
+  function testEmptyClose()
+  {
+    $this->listener->expectOnce('endElement', array(''));
+    $this->listener->expectNever('characters');
+    $this->parser->parse('</>');
+  }
+
+  function testEmptyJasp()
+  {
+    $this->listener->expectOnce('jasp', array(''));
+    $this->listener->expectNever('characters');
+    $this->parser->parse('<%%>');
+  }
+
+  function testTargetOnlyProcessingInstruction()
+  {
+    $this->listener->expectOnce('processingInstruction', array('php', ''));
+    $this->listener->expectNever('characters');
+    $this->parser->parse('<?php ?>');
+  }
+
+  function testElementWithPreContent()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('startElement', array('br', array()));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('stuff<br>');
+  }
+
+  function testElementWithPostContent()
+  {
+    $this->listener->expectOnce('startElement', array('br', array()));
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<br>stuff');
+  }
+
+  function testExpressionAfterTag()
+  {
+    $this->listener->expectOnce('emptyElement', array('br', array()));
+    $this->listener->expectOnce('characters', array('{$str|clip:5}'));
+    $this->parser->parse('<br/>{$str|clip:5}');
+  }
+
+  function testExpressionAfterTagWithArguments()
+  {
+    $this->listener->expectOnce('emptyElement', array('core:set', array('str' => 'abcdefgh')));
+    $this->listener->expectOnce('characters', array('{$str|clip:5}'));
+    $this->parser->parse('<core:set str="abcdefgh" />{$str|clip:5}');
+  }
+
+  function testMismatchedElements()
+  {
+    $this->listener->expectArgumentsAt(0, 'startElement', array('b', array()));
+    $this->listener->expectArgumentsAt(1, 'startElement', array('i', array()));
+    $this->listener->expectArgumentsAt(0, 'endElement', array('b'));
+    $this->listener->expectArgumentsAt(1, 'endElement', array('i'));
+    $this->listener->expectCallCount('startElement', 2);
+    $this->listener->expectCallCount('endElement', 2);
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<b><i>stuff</b></i>');
+  }
+
+  function testAttributes()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array("a" => "A", "b" => "B", "c" => "C")));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<tag a="A" b=\'B\' c = "C">');
+  }
+
+  function testEmptyAttributes()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array("a" => NULL, "b" => NULL, "c" => NULL)));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<tag a b c>');
+  }
+
+  function testNastyAttributes()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array("a" => "&%$'?<>",
+                                                                   "b" => "\r\n\t\"",
+                                                                   "c" => "")));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse("<tag a=\"&%$'?<>\" b='\r\n\t\"' c = ''>");
+  }
+
+  function testAttributesPadding()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array("a" => "A", "b" => "B", "c" => "C")));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse("<tag\ta=\"A\"\rb='B'\nc = \"C\"\n>");
+  }
+
+  function testAllProcessingInstruction()
+  {
+    $this->listener->expectOnce('processingInstruction', array('php', 'print "Hello"; '));
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('startElement');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('<?php print "Hello"; ?>');
+  }
+
+  function testNestedProcessingInstruction()
+  {
+    $this->listener->expectOnce('processingInstruction', array('php', 'print "Hello"; '));
+    $this->listener->expectArgumentsAt(0, 'characters', array('a'));
+    $this->listener->expectArgumentsAt(1, 'characters', array('b'));
+    $this->listener->expectCallCount('characters', 2);
+    $this->listener->expectNever('startElement');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('a<?php print "Hello"; ?>b');
+  }
+
+  function testEscapeDocTypeHandler()
+  {
+    $this->listener->expectOnce('doctype', array('doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN"'));
+    $this->parser->parse('<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">');
+  }
+
+  function testNestedDoctypeHandler()
+  {
+    $this->listener->expectOnce('doctype', array('doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN"'));
+    $this->listener->expectArgumentsAt(0, 'characters', array('a'));
+    $this->listener->expectArgumentsAt(1, 'characters', array('b'));
+    $this->listener->expectCallCount('characters', 2);
+    $this->parser->parse('a<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">b');
+  }
+
+  function testEscapeCData()
+  {
+    $this->listener->expectOnce('cdata', array('string = \'A CDATA block\';'));
+    $this->parser->parse('<![CDATA[string = \'A CDATA block\';]]>');
+  }
+
+  function testSimpleComment()
+  {
+    $this->listener->expectOnce('comment', array(' A comment '));
+    $this->parser->parse('<!-- A comment -->');
+  }
+
+  function testNastyComment()
+  {
+    $this->listener->expectOnce('comment', array(' <tag></tag><?php ?><' . '% %> '));
+    $this->parser->parse('<tag><!-- <tag></tag><?php ?><' . '% %> --></tag>');
+  }
+
+  function testSimpleJasp()
+  {
+    $this->listener->expectOnce('jasp', array(' document.write("Hello World");'));
+    $this->listener->expectNever('processingInstruction');
+    $this->listener->expectNever('comment');
+    $this->listener->expectNever('escape');
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('startElement');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('<' . '% document.write("Hello World");%>');
+  }
+
+  function testNastyJasp()
+  {
+    $this->listener->expectOnce('jasp', array(' <tag a="A"><?php ?></tag><!-- comment --> '));
+    $this->listener->expectNever('processingInstruction');
+    $this->listener->expectNever('comment');
+    $this->listener->expectNever('escape');
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('startElement');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('<' . '% <tag a="A"><?php ?></tag><!-- comment --> %>');
+  }
+
+  function testJaspInTag()
+  {
+    $this->listener->expectOnce( 'jasp', array(' document.write("Hello World");'));
+    $this->listener->expectNever('processingInstruction');
+    $this->listener->expectNever('escape');
+    $this->listener->expectNever('characters');
+    $this->listener->expectOnce('startElement');
+    $this->listener->expectOnce('endElement');
+    $this->parser->parse('<tag><' . '% document.write("Hello World");%></tag>');
+  }
+
+  /*
+//  removed due to bug #1000806
+//  see http://www.w3.org/TR/REC-html40/appendix/notes.html#notes-specifying-data
+  function testScriptElement() {
+      $this->listener->expectOnce('startElement', array('script', array('language'=>'Javascript')));
+      $this->listener->expectOnce('endElement', array('script'));
+      $this->listener->expectOnce('characters', array("document.write('<B>Test<\/B>');"));
+      $this->listener->expectNever('invalidAttributeSyntax');
+      $this->parser->parse('<script language="Javascript">document.write(\'<B>Test<\/B>\');</script>');
+  }
+  */
+
+  function testScriptElementEmbedComment() {
+      $this->listener->expectOnce('startElement', array('script', array('language'=>'Javascript')));
+      $this->listener->expectOnce('endElement', array('script'));
+      $this->listener->expectOnce('comment', array(" document.write('<B>Test<\/B>'); "));
+      $this->listener->expectNever('invalidAttributeSyntax');
+      $this->parser->parse('<script language="Javascript"><!-- document.write(\'<B>Test<\/B>\'); --></script>');
+  }
+}
+?>
\ No newline at end of file

Added: 3.x/packages/wact/trunk/tests/cases/template/compiler/HtmlParserTruncatedTest.class.php
===================================================================
--- 3.x/packages/wact/trunk/tests/cases/template/compiler/HtmlParserTruncatedTest.class.php	                        (rev 0)
+++ 3.x/packages/wact/trunk/tests/cases/template/compiler/HtmlParserTruncatedTest.class.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -0,0 +1,233 @@
+<?php
+require_once(WACT_TEST_CASES . '/template/compiler/HtmlParserTest.class.php');
+
+class HtmlParserTruncatedTest extends UnitTestCase
+{
+  var $parser;
+  var $listener;
+
+  function setUp()
+  {
+    $this->listener = new MockParserListener();
+    $this->parser = new HTMLParser($this->listener);
+  }
+
+  function testTruncatedOpen()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<');
+  }
+
+  function testTruncatedEmptyClose()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('</'));
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('stuff</');
+  }
+
+  function testTruncatedClose()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('</a'));
+    $this->parser->parse('stuff</a');
+    $this->listener->expectNever('endElement');
+  }
+
+  function testTruncatedProcessingInstruction()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<?'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<?');
+  }
+
+  function testTruncatedProcessingInstructionTarget()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<?php'));
+    $this->listener->expectNever('processingInstruction');
+    $this->parser->parse('stuff<?php');
+  }
+
+  function testTruncatedProcessingInstructionNoClose()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<?php '));
+    $this->listener->expectNever('processingInstruction');
+    $this->parser->parse('stuff<?php ');
+  }
+
+  function testTruncatedJasp()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<%'));
+    $this->listener->expectNever('jasp');
+    $this->parser->parse('stuff<%');
+  }
+
+  function testTruncatedJaspNoClose()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<% more stuff'));
+    $this->listener->expectNever('jasp');
+    $this->parser->parse('stuff<% more stuff');
+  }
+
+  function testTruncatedComment()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<!--'));
+    $this->listener->expectNever('escape');
+    $this->listener->expectNever('comment');
+    $this->parser->parse('stuff<!--');
+  }
+
+  function testTruncatedCommentNoClose()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<!-- blah'));
+    $this->listener->expectNever('escape');
+    $this->listener->expectNever('comment');
+    $this->parser->parse('stuff<!-- blah');
+  }
+
+  function testTruncatedDocType()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<!doctype'));
+    $this->listener->expectNever('escape');
+    $this->parser->parse('stuff<!doctype');
+  }
+
+  function testTruncatedDocTypetNoClose()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<!doctype blah'));
+    $this->listener->expectNever('escape');
+    $this->parser->parse('stuff<!doctype blah');
+  }
+
+  function testTruncatedOpenElementChar()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<a'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<a');
+  }
+
+  function testTruncatedOpenElement()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag');
+  }
+
+  function testTruncatedOpenElementSpace()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag '));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag ');
+  }
+
+  function testTruncatedOpenElementMinimizedAttribute()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag attribute'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute');
+  }
+
+  function testTruncatedOpenElementMinimizedAttributeSpace()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag attribute '));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute ');
+  }
+
+  function testTruncatedOpenElementAttribute()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag attribute='));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute=');
+  }
+
+  function testTruncatedOpenElementAttributeSpace()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag attribute= '));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute= ');
+  }
+
+  function testTruncatedOpenElementAttributeNoQuote()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag attribute=value'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute=value');
+  }
+
+  function testTruncatedOpenElementAttributeDoubleQuote()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag attribute="'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute="');
+  }
+
+  function testTruncatedOpenElementAttributeDoubleQuoteNoClose()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag attribute="value'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute="value');
+  }
+
+  function testTruncatedOpenElementAttributeDoubleQuoteValue()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag attribute="value"'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute="value"');
+  }
+
+  function testTruncatedOpenElementAttributeSingleQuote()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag attribute=\''));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute=\'');
+  }
+
+  function testTruncatedOpenElementAttributeSingleQuoteNoClose()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag attribute=\'value'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute=\'value');
+  }
+
+  function testTruncatedOpenElementAttributeSingleQuoteValue()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag attribute=\'value\''));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute=\'value\'');
+  }
+
+  function testTruncatedOpenElementClose()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('unexpectedEOF', array('<tag attribute=\'value\'/'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute=\'value\'/');
+  }
+}
+?>
\ No newline at end of file

Copied: 3.x/packages/wact/trunk/tests/cases/template/compiler/SourceFileParserTest.class.php (from rev 4159, 3.x/packages/wact/trunk/tests/cases/template/compiler/sourcefileparser.test.php)
===================================================================
--- 3.x/packages/wact/trunk/tests/cases/template/compiler/SourceFileParserTest.class.php	                        (rev 0)
+++ 3.x/packages/wact/trunk/tests/cases/template/compiler/SourceFileParserTest.class.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -0,0 +1,134 @@
+<?php
+require_once WACT_ROOT . '/template/compiler/templatecompiler.inc.php';
+
+Mock::generate('WactLiteralParsingState','MockLiteralParsingState');
+Mock::generate('WactComponentParsingState','MockComponentParsingState');
+Mock::generatePartial('SourceFileParser','SourceFileParserTestVersion', array('SourceFileParser'));
+
+class SourceFileParserTest extends UnitTestCase {
+
+  function setUp()
+  {
+    $this->SFP = new SourceFileParserTestVersion();
+    $this->SFP->ComponentParsingState = new MockComponentParsingState();
+    $this->SFP->LiteralParsingState = new MockLiteralParsingState();
+    $this->SFP->State = $this->SFP->ComponentParsingState;
+  }
+
+  function testBuildFilterChain() {
+      $Parser =& $this->SFP->buildFilterChain('tagstolower');
+      $this->SFP->ComponentParsingState->expectOnce('startElement', array('test', '*'));
+      $Parser->startElement('TEST', array());
+  }
+
+  function testChangeToComponentParsingState() {
+    $this->SFP->changeToComponentParsingState();
+    $this->assertIsA($this->SFP->State, 'MockComponentParsingState');
+  }
+
+  function testChangeToLiteralParsingState() {
+    $this->SFP->changeToLiteralParsingState('test');
+    $this->assertIsA($this->SFP->State,'MockLiteralParsingState');
+    $this->assertEqual($this->SFP->LiteralParsingState->literalTag, 'test');
+  }
+
+  function testChangeStates() {
+    $this->SFP->changeToComponentParsingState();
+    $this->assertIsA($this->SFP->State, 'MockComponentParsingState');
+    $this->SFP->changeToLiteralParsingState('test');
+    $this->assertIsA($this->SFP->State, 'MockLiteralParsingState');
+    $this->SFP->changeToComponentParsingState();
+    $this->assertIsA($this->SFP->State, 'MockComponentParsingState');
+  }
+
+
+  function testSetDocumentLocator() {
+    $this->SFP->ComponentParsingState->expectOnce('setDocumentLocator');
+    $this->SFP->LiteralParsingState->expectOnce('setDocumentLocator');
+    $Locator = NULL;
+
+    $this->SFP->setDocumentLocator($Locator);
+    }
+
+    function testStartElement() {
+        $tag = 'test';
+        $attributes = array('foo' => 'bar');
+    $this->SFP->State->expectOnce('startElement', array($tag, $attributes));
+    $this->SFP->startElement($tag, $attributes);
+    }
+
+    function testEndElement() {
+        $tag = 'test';
+    $this->SFP->State->expectOnce('endElement', array($tag));
+    $this->SFP->endElement($tag);
+    }
+
+    function testEmptyElement() {
+        $tag = 'test';
+        $attributes = array('foo' => 'bar');
+    $this->SFP->State->expectOnce('emptyElement', array($tag, $attributes));
+    $this->SFP->emptyElement($tag, $attributes);
+    }
+
+    function testCharacters() {
+        $data = 'test';
+    $this->SFP->State->expectOnce('characters', array($data));
+    $this->SFP->characters($data);
+    }
+
+    function testCdata() {
+        $data = 'test';
+    $this->SFP->State->expectOnce('cdata', array($data));
+    $this->SFP->cdata($data);
+    }
+
+    function testProcessingInstruction() {
+        $target = 'test';
+        $instruction = 'hi';
+    $this->SFP->State->expectOnce('processingInstruction', array($target, $instruction));
+    $this->SFP->processingInstruction($target, $instruction);
+    }
+
+    function testEscape() {
+        $text = 'test';
+    $this->SFP->State->expectOnce('escape', array($text));
+    $this->SFP->escape($text);
+    }
+
+    function testComment() {
+        $text = 'test';
+    $this->SFP->State->expectOnce('comment', array($text));
+    $this->SFP->comment($text);
+    }
+
+    function testDoctype() {
+        $text = 'test';
+    $this->SFP->State->expectOnce('doctype', array($text));
+    $this->SFP->doctype($text);
+    }
+
+    function testJasp() {
+        $text = 'test';
+    $this->SFP->State->expectOnce('jasp', array($text));
+    $this->SFP->jasp($text);
+    }
+
+    function testUnexpectedEOF() {
+        $text = 'test';
+    $this->SFP->State->expectOnce('unexpectedEOF', array($text));
+    $this->SFP->unexpectedEOF($text);
+    }
+
+    function testInvalidEntitySyntax() {
+        $text = 'test';
+    $this->SFP->State->expectOnce('invalidEntitySyntax', array($text));
+    $this->SFP->invalidEntitySyntax($text);
+    }
+
+    function testInvalidAttributeSyntax() {
+    $this->SFP->State->expectOnce('invalidAttributeSyntax', array());
+    $this->SFP->invalidAttributeSyntax();
+    }
+
+}
+?>
\ No newline at end of file

Deleted: 3.x/packages/wact/trunk/tests/cases/template/compiler/htmlparser.test.php
===================================================================
--- 3.x/packages/wact/trunk/tests/cases/template/compiler/htmlparser.test.php	2006-10-13 21:27:19 UTC (rev 4159)
+++ 3.x/packages/wact/trunk/tests/cases/template/compiler/htmlparser.test.php	2006-10-15 19:52:26 UTC (rev 4160)
@@ -1,606 +0,0 @@
-<?php
-/**
-* @package WACT_TESTS
-* @version $Id: htmlparser.test.php 3142 2006-04-24 15:47:48Z wiliam $
-*/
-
-require_once WACT_ROOT . 'template/compiler/htmlparser.inc.php';
-
-class ListenerInterface {
-    function ListenerInterface() {
-    }
-
-    function startElement($name, $attrs) {
-    }
-
-    function endElement($name) {
-    }
-
-    function emptyElement($name, $attrs) {
-    }
-
-    function characters($data) {
-    }
-
-    function cdata($data) {
-    }
-
-    function processingInstruction($target, $data) {
-    }
-
-    function escape($data) {
-    }
-
-    function comment($data) {
-    }
-
-    function doctype($data) {
-    }
-
-    function jasp($data) {
-    }
-
-    function unexpectedEOF($data) {
-    }
-
-    function invalidEntitySyntax($data) {
-    }
-
-    function invalidAttributeSyntax() {
-    }
-
-    function setDocumentLocator(&$locator) {
-    }
-}
-Mock::generate('ListenerInterface', 'MockListener');
-
-class HtmlParserTestCase extends UnitTestCase {
-    var $parser;
-    var $listener;
-
-  function setUp() {
-        $this->listener = new MockListener();
-        $this->parser = new HTMLParser($this->listener);
-  }
-
-  function testEmpty() {
-        $this->listener->expectNever('jasp');
-        $this->listener->expectNever('processingInstruction');
-        $this->listener->expectNever('escape');
-        $this->listener->expectNever('characters');
-        $this->listener->expectNever('startElement');
-        $this->listener->expectNever('endElement');
-        $this->parser->parse('');
-  }
-
-    function testSimpledata() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->parser->parse('stuff');
-    }
-
-    function testPreservingWhiteSpace() {
-        $this->listener->expectOnce('characters', array(" stuff\t\r\n "));
-        $this->parser->parse(" stuff\t\r\n ");
-    }
-
-    function testEmptyElement() {
-        $this->listener->expectOnce('startElement', array('tag', array()));
-        $this->listener->expectOnce('endElement', array('tag'));
-        $this->listener->expectNever('invalidAttributeSyntax');
-        $this->parser->parse('<tag></tag>');
-    }
-
-    function testEmptyElementSelfClose() {
-        $this->listener->expectOnce('emptyElement', array('br', array()));
-        $this->listener->expectNever('startElement');
-        $this->listener->expectNever('endElement');
-        $this->parser->parse('<br/>');
-    }
-
-    function testElementWithContent() {
-        $this->listener->expectOnce('startElement', array('tag', array()));
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('endElement', array('tag'));
-        $this->listener->expectNever('invalidAttributeSyntax');
-        $this->parser->parse('<tag>stuff</tag>');
-    }
-
-  function testEmptyComment() {
-        $this->listener->expectOnce('comment', array(''));
-        $this->listener->expectNever('characters');
-        $this->parser->parse('<!---->');
-  }
-
-    function testElementNestedSingleQuote() {
-        $this->listener->expectOnce('startElement', array('tag', array('attribute' => '\'')));
-        $this->listener->expectNever('characters');
-        $this->listener->expectNever('endElement');
-        $this->listener->expectNever('invalidAttributeSyntax');
-        $this->parser->parse('<tag attribute="\'">');
-    }
-
-    function testElementNestedDoubleQuote() {
-        $this->listener->expectOnce('startElement', array('tag', array('attribute' => '"')));
-        $this->listener->expectNever('characters');
-        $this->listener->expectNever('endElement');
-        $this->listener->expectNever('invalidAttributeSyntax');
-        $this->parser->parse('<tag attribute=\'"\'>');
-    }
-
-  function testEmptyDocType() {
-        $this->listener->expectOnce('doctype', array('DOCTYPE'));
-        $this->listener->expectNever('characters');
-        $this->listener->expectNever('escape');
-        $this->parser->parse('<!DOCTYPE>');
-  }
-
-  function testEmptyClose() {
-        $this->listener->expectOnce('endElement', array(''));
-        $this->listener->expectNever('characters');
-        $this->parser->parse('</>');
-  }
-
-  function testEmptyJasp() {
-        $this->listener->expectOnce('jasp', array(''));
-        $this->listener->expectNever('characters');
-        $this->parser->parse('<%%>');
-  }
-
-  function testTargetOnlyProcessingInstruction() {
-        $this->listener->expectOnce('processingInstruction', array('php', ''));
-        $this->listener->expectNever('characters');
-        $this->parser->parse('<?php ?>');
-  }
-
-    function testElementWithPreContent() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('startElement', array('br', array()));
-        $this->listener->expectNever('invalidAttributeSyntax');
-        $this->parser->parse('stuff<br>');
-    }
-
-    function testElementWithPostContent() {
-        $this->listener->expectOnce('startElement', array('br', array()));
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectNever('invalidAttributeSyntax');
-        $this->parser->parse('<br>stuff');
-    }
-
-    function testMismatchedElements() {
-        $this->listener->expectArgumentsAt(0, 'startElement', array('b', array()));
-        $this->listener->expectArgumentsAt(1, 'startElement', array('i', array()));
-        $this->listener->expectArgumentsAt(0, 'endElement', array('b'));
-        $this->listener->expectArgumentsAt(1, 'endElement', array('i'));
-        $this->listener->expectCallCount('startElement', 2);
-        $this->listener->expectCallCount('endElement', 2);
-        $this->listener->expectNever('invalidAttributeSyntax');
-        $this->parser->parse('<b><i>stuff</b></i>');
-    }
-
-    function testAttributes() {
-        $this->listener->expectOnce(
-                'startElement',
-                array('tag', array("a" => "A", "b" => "B", "c" => "C")));
-        $this->listener->expectNever('invalidAttributeSyntax');
-        $this->parser->parse('<tag a="A" b=\'B\' c = "C">');
-    }
-
-    function testEmptyAttributes() {
-        $this->listener->expectOnce(
-                'startElement',
-                array('tag', array("a" => NULL, "b" => NULL, "c" => NULL)));
-        $this->listener->expectNever('invalidAttributeSyntax');
-        $this->parser->parse('<tag a b c>');
-    }
-
-    function testNastyAttributes() {
-        $this->listener->expectOnce(
-                'startElement',
-                array('tag', array("a" => "&%$'?<>", "b" => "\r\n\t\"", "c" => "")));
-        $this->listener->expectNever('invalidAttributeSyntax');
-        $this->parser->parse("<tag a=\"&%$'?<>\" b='\r\n\t\"' c = ''>");
-    }
-
-    function testAttributesPadding() {
-        $this->listener->expectOnce(
-                'startElement',
-                array('tag', array("a" => "A", "b" => "B", "c" => "C")));
-        $this->listener->expectNever('invalidAttributeSyntax');
-        $this->parser->parse("<tag\ta=\"A\"\rb='B'\nc = \"C\"\n>");
-    }
-
-    function testAllprocessingInstruction() {
-        $this->listener->expectOnce('processingInstruction', array('php', 'print "Hello"; '));
-        $this->listener->expectNever('characters');
-        $this->listener->expectNever('startElement');
-        $this->listener->expectNever('endElement');
-        $this->parser->parse('<?php print "Hello"; ?>');
-    }
-
-    function testNestedprocessingInstruction() {
-        $this->listener->expectOnce('processingInstruction', array('php', 'print "Hello"; '));
-        $this->listener->expectArgumentsAt(0, 'characters', array('a'));
-        $this->listener->expectArgumentsAt(1, 'characters', array('b'));
-        $this->listener->expectCallCount('characters', 2);
-        $this->listener->expectNever('startElement');
-        $this->listener->expectNever('endElement');
-        $this->parser->parse('a<?php print "Hello"; ?>b');
-    }
-
-
-    function testEscapeDocTypeHandler() {
-        $this->listener->expectOnce(
-                'doctype',
-                array('doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN"'));
-        $this->parser->parse('<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">');
-    }
-
-    function testNestedDoctypeHandler() {
-        $this->listener->expectOnce(
-                'doctype',
-                array('doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN"'));
-        $this->listener->expectArgumentsAt(0, 'characters', array('a'));
-        $this->listener->expectArgumentsAt(1, 'characters', array('b'));
-        $this->listener->expectCallCount('characters', 2);
-        $this->parser->parse('a<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">b');
-    }
-
-    function testEscapeCData() {
-        $this->listener->expectOnce(
-            'cdata',
-            array('string = \'A CDATA block\';'));
-        $this->parser->parse('<![CDATA[string = \'A CDATA block\';]]>');
-    }
-
-    function testSimpleComment() {
-        $this->listener->expectOnce('comment', array(' A comment '));
-        $this->parser->parse('<!-- A comment -->');
-    }
-
-    function testNastyComment() {
-        $this->listener->expectOnce(
-                'comment',
-                array(' <tag></tag><?php ?><' . '% %> '));
-        $this->parser->parse('<tag><!-- <tag></tag><?php ?><' . '% %> --></tag>');
-    }
-
-    function testSimpleJasp() {
-        $this->listener->expectOnce(
-                'jasp',
-                array(' document.write("Hello World");'));
-        $this->listener->expectNever('processingInstruction');
-        $this->listener->expectNever('comment');
-        $this->listener->expectNever('escape');
-        $this->listener->expectNever('characters');
-        $this->listener->expectNever('startElement');
-        $this->listener->expectNever('endElement');
-        $this->parser->parse('<' . '% document.write("Hello World");%>');
-    }
-
-    function testNastyJasp() {
-        $this->listener->expectOnce(
-                'jasp',
-                array(' <tag a="A"><?php ?></tag><!-- comment --> '));
-        $this->listener->expectNever('processingInstruction');
-        $this->listener->expectNever('comment');
-        $this->listener->expectNever('escape');
-        $this->listener->expectNever('characters');
-        $this->listener->expectNever('startElement');
-        $this->listener->expectNever('endElement');
-        $this->parser->parse('<' . '% <tag a="A"><?php ?></tag><!-- comment --> %>');
-    }
-
-    function testJaspInTag() {
-        $this->listener->expectOnce(
-                'jasp',
-                array(' document.write("Hello World");'));
-        $this->listener->expectNever('processingInstruction');
-        $this->listener->expectNever('escape');
-        $this->listener->expectNever('characters');
-        $this->listener->expectOnce('startElement');
-        $this->listener->expectOnce('endElement');
-        $this->parser->parse('<tag><' . '% document.write("Hello World");%></tag>');
-    }
-
-    /*
-    removed due to bug #1000806
-    see http://www.w3.org/TR/REC-html40/appendix/notes.html#notes-specifying-data
-    function testScriptElement() {
-        $this->listener->expectOnce('startElement', array('script', array('language'=>'Javascript')));
-        $this->listener->expectOnce('endElement', array('script'));
-        $this->listener->expectOnce('characters', array("document.write('<B>Test<\/B>');"));
-        $this->listener->expectNever('invalidAttributeSyntax');
-        $this->parser->parse('<script language="Javascript">document.write(\'<B>Test<\/B>\');</script>');
-    }
-
-    function testScriptElementEmbedComment() {
-        $this->listener->expectOnce('startElement', array('script', array('language'=>'Javascript')));
-        $this->listener->expectOnce('endElement', array('script'));
-        $this->listener->expectOnce('characters', array("<!-- document.write('<B>Test<\/B>'); -->"));
-        $this->listener->expectNever('invalidAttributeSyntax');
-        $this->parser->parse('<script language="Javascript"><!-- document.write(\'<B>Test<\/B>\'); --></script>');
-    }
-    */
-}
-
-class HtmlParserTruncatedTestCase extends UnitTestCase {
-    var $parser;
-    var $listener;
-
-  function setUp() {
-        $this->listener = new MockListener();
-        $this->parser = new HTMLParser($this->listener);
-  }
-
-  function testTruncatedOpen() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<'));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<');
-  }
-
-  function testTruncatedEmptyClose() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('</'));
-        $this->listener->expectNever('endElement');
-        $this->parser->parse('stuff</');
-  }
-
-  function testTruncatedClose() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('</a'));
-        $this->parser->parse('stuff</a');
-        $this->listener->expectNever('endElement');
-  }
-
-  function testTruncatedProcessingInstruction() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<?'));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<?');
-  }
-
-  function testTruncatedProcessingInstructionTarget() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<?php'));
-        $this->listener->expectNever('processingInstruction');
-        $this->parser->parse('stuff<?php');
-  }
-
-  function testTruncatedProcessingInstructionNoClose() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<?php '));
-        $this->listener->expectNever('processingInstruction');
-        $this->parser->parse('stuff<?php ');
-  }
-
-  function testTruncatedJasp() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<%'));
-        $this->listener->expectNever('jasp');
-        $this->parser->parse('stuff<%');
-  }
-
-  function testTruncatedJaspNoClose() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<% more stuff'));
-        $this->listener->expectNever('jasp');
-        $this->parser->parse('stuff<% more stuff');
-  }
-
-  function testTruncatedComment() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<!--'));
-        $this->listener->expectNever('escape');
-        $this->listener->expectNever('comment');
-        $this->parser->parse('stuff<!--');
-  }
-
-  function testTruncatedCommentNoClose() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<!-- blah'));
-        $this->listener->expectNever('escape');
-        $this->listener->expectNever('comment');
-        $this->parser->parse('stuff<!-- blah');
-  }
-
-  function testTruncatedDocType() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<!doctype'));
-        $this->listener->expectNever('escape');
-        $this->parser->parse('stuff<!doctype');
-  }
-
-  function testTruncatedDocTypetNoClose() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<!doctype blah'));
-        $this->listener->expectNever('escape');
-        $this->parser->parse('stuff<!doctype blah');
-  }
-
-  function testTruncatedOpenElementChar() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<a'));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<a');
-  }
-
-  function testTruncatedOpenElement() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<tag'));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<tag');
-  }
-
-  function testTruncatedOpenElementSpace() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<tag '));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<tag ');
-  }
-
-  function testTruncatedOpenElementMinimizedAttribute() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<tag attribute'));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<tag attribute');
-  }
-
-  function testTruncatedOpenElementMinimizedAttributeSpace() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<tag attribute '));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<tag attribute ');
-  }
-
-  function testTruncatedOpenElementAttribute() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<tag attribute='));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<tag attribute=');
-  }
-
-  function testTruncatedOpenElementAttributeSpace() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<tag attribute= '));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<tag attribute= ');
-  }
-
-  function testTruncatedOpenElementAttributeNoQuote() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<tag attribute=value'));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<tag attribute=value');
-  }
-
-  function testTruncatedOpenElementAttributeDoubleQuote() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<tag attribute="'));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<tag attribute="');
-  }
-
-  function testTruncatedOpenElementAttributeDoubleQuoteNoClose() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<tag attribute="value'));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<tag attribute="value');
-  }
-
-  function testTruncatedOpenElementAttributeDoubleQuoteValue() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<tag attribute="value"'));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<tag attribute="value"');
-  }
-
-  function testTruncatedOpenElementAttributeSingleQuote() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<tag attribute=\''));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<tag attribute=\'');
-  }
-
-  function testTruncatedOpenElementAttributeSingleQuoteNoClose() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<tag attribute=\'value'));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<tag attribute=\'value');
-  }
-
-  function testTruncatedOpenElementAttributeSingleQuoteValue() {
-        $this->listener->expectOnce('characters', array('stuff'));
-        $this->listener->expectOnce('unexpectedEOF', array('<tag attribute=\'value\''));
-        $this->listener->expectNever('startElement');
-        $this->parser->parse('stuff<