protected function parse($input)
{
$input = $this->preprocess($input);
$tpl =& $this->_tpl;
$n = preg_match_all(self::REGEX_RULES, $input, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
$expectPropEnd = false;
$textStart = 0;
$stack = array();
$container = -1;
$matchEnd = 0;
$c = 0;
$this->_directive = null;
for ($i = 0; $i < $n; ++$i) {
$match =& $matches[$i];
$str = $match[0][0];
$matchStart = $match[0][1];
$matchEnd = $matchStart + strlen($str) - 1;
if (strpos($str, '<com:') === 0) {
if ($expectPropEnd) {
continue;
}
if ($matchStart > $textStart) {
$tpl[$c++] = array($container, substr($input, $textStart, $matchStart - $textStart));
}
$textStart = $matchEnd + 1;
$type = $match[1][0];
$attributes = $this->parseAttributes($match[2][0], $match[2][1]);
$this->validateAttributes($type, $attributes);
$tpl[$c++] = array($container, $type, $attributes);
if ($str[strlen($str) - 2] !== '/') {
$stack[] = $type;
$container = $c - 1;
}
} else {
if (strpos($str, '</com:') === 0) {
if ($expectPropEnd) {
continue;
}
if ($matchStart > $textStart) {
$tpl[$c++] = array($container, substr($input, $textStart, $matchStart - $textStart));
}
$textStart = $matchEnd + 1;
$type = $match[1][0];
if (empty($stack)) {
throw new TConfigurationException('template_closingtag_unexpected', "</com:{$type}>");
}
$name = array_pop($stack);
if ($name !== $type) {
$tag = $name[0] === '@' ? '</prop:' . substr($name, 1) . '>' : "</com:{$name}>";
throw new TConfigurationException('template_closingtag_expected', $tag);
}
$container = $tpl[$container][0];
} else {
if (strpos($str, '<%@') === 0) {
if ($expectPropEnd) {
continue;
}
if ($matchStart > $textStart) {
$tpl[$c++] = array($container, substr($input, $textStart, $matchStart - $textStart));
}
$textStart = $matchEnd + 1;
if (isset($tpl[0]) || $this->_directive !== null) {
throw new TConfigurationException('template_directive_nonunique');
}
$this->_directive = $this->parseAttributes($match[4][0], $match[4][1]);
} else {
if (strpos($str, '<%') === 0) {
if ($expectPropEnd) {
continue;
}
if ($matchStart > $textStart) {
$tpl[$c++] = array($container, substr($input, $textStart, $matchStart - $textStart));
}
$textStart = $matchEnd + 1;
$literal = trim($match[5][0]);
if ($str[2] === '=') {
// expression
$tpl[$c++] = array($container, array(TCompositeLiteral::TYPE_EXPRESSION, $literal));
} else {
if ($str[2] === '%') {
// statements
$tpl[$c++] = array($container, array(TCompositeLiteral::TYPE_STATEMENTS, $literal));
} else {
if ($str[2] === '#') {
$tpl[$c++] = array($container, array(TCompositeLiteral::TYPE_DATABINDING, $literal));
} else {
if ($str[2] === '$') {
$tpl[$c++] = array($container, array(TCompositeLiteral::TYPE_EXPRESSION, "\$this->getApplication()->getParameters()->itemAt('{$literal}')"));
} else {
if ($str[2] === '~') {
$tpl[$c++] = array($container, array(TCompositeLiteral::TYPE_EXPRESSION, "\$this->publishFilePath('{$this->_contextPath}/{$literal}')"));
} else {
if ($str[2] === '/') {
$tpl[$c++] = array($container, array(TCompositeLiteral::TYPE_EXPRESSION, "rtrim(dirname(\$this->getApplication()->getRequest()->getApplicationUrl()), '/').'/{$literal}'"));
} else {
if ($str[2] === '[') {
$literal = strtr(trim(substr($literal, 0, strlen($literal) - 1)), array("'" => "\\'", "\\" => "\\\\"));
$tpl[$c++] = array($container, array(TCompositeLiteral::TYPE_EXPRESSION, "Prado::localize('{$literal}')"));
}
}
}
}
}
}
}
} else {
if (strpos($str, '<prop:') === 0) {
if (strrpos($str, '/>') === strlen($str) - 2) {
if ($expectPropEnd) {
continue;
}
if ($matchStart > $textStart) {
$tpl[$c++] = array($container, substr($input, $textStart, $matchStart - $textStart));
}
$textStart = $matchEnd + 1;
$prop = strtolower($match[6][0]);
$attrs = $this->parseAttributes($match[7][0], $match[7][1]);
$attributes = array();
foreach ($attrs as $name => $value) {
$attributes[$prop . '.' . $name] = $value;
}
$type = $tpl[$container][1];
$this->validateAttributes($type, $attributes);
foreach ($attributes as $name => $value) {
if (isset($tpl[$container][2][$name])) {
throw new TConfigurationException('template_property_duplicated', $name);
}
$tpl[$container][2][$name] = $value;
}
} else {
$prop = strtolower($match[3][0]);
$stack[] = '@' . $prop;
if (!$expectPropEnd) {
if ($matchStart > $textStart) {
$tpl[$c++] = array($container, substr($input, $textStart, $matchStart - $textStart));
}
$textStart = $matchEnd + 1;
$expectPropEnd = true;
}
}
} else {
if (strpos($str, '</prop:') === 0) {
$prop = strtolower($match[3][0]);
if (empty($stack)) {
throw new TConfigurationException('template_closingtag_unexpected', "</prop:{$prop}>");
}
$name = array_pop($stack);
if ($name !== '@' . $prop) {
$tag = $name[0] === '@' ? '</prop:' . substr($name, 1) . '>' : "</com:{$name}>";
throw new TConfigurationException('template_closingtag_expected', $tag);
}
if (($last = count($stack)) < 1 || $stack[$last - 1][0] !== '@') {
if ($matchStart > $textStart) {
$value = substr($input, $textStart, $matchStart - $textStart);
if (substr($prop, -8, 8) === 'template') {
$value = $this->parseTemplateProperty($value, $textStart);
} else {
$value = $this->parseAttribute($value);
}
if ($container >= 0) {
$type = $tpl[$container][1];
$this->validateAttributes($type, array($prop => $value));
if (isset($tpl[$container][2][$prop])) {
throw new TConfigurationException('template_property_duplicated', $prop);
}
$tpl[$container][2][$prop] = $value;
} else {
// a property for the template control
$this->_directive[$prop] = $value;
}
$textStart = $matchEnd + 1;
}
$expectPropEnd = false;
}
} else {
if (strpos($str, '<!--') === 0) {
if ($expectPropEnd) {
throw new TConfigurationException('template_comments_forbidden');
}
if ($matchStart > $textStart) {
$tpl[$c++] = array($container, substr($input, $textStart, $matchStart - $textStart));
}
$textStart = $matchEnd + 1;
} else {
throw new TConfigurationException('template_matching_unexpected', $match);
}
}
}
}
}
}
}
}
if (!empty($stack)) {
$name = array_pop($stack);
$tag = $name[0] === '@' ? '</prop:' . substr($name, 1) . '>' : "</com:{$name}>";
throw new TConfigurationException('template_closingtag_expected', $tag);
}
if ($textStart < strlen($input)) {
$tpl[$c++] = array($container, substr($input, $textStart));
}
/*
catch(\Exception $e)
{
if(($e instanceof TException) && ($e instanceof TTemplateException))
throw $e;
if($matchEnd===0)
$line=$this->_startingLine+1;
else
$line=$this->_startingLine+count(explode("\n",substr($input,0,$matchEnd+1)));
$this->handleException($e,$line,$input);
} */
if ($this->_directive === null) {
$this->_directive = array();
}
// optimization by merging consecutive strings, expressions, statements and bindings
$objects = array();
$parent = null;
$merged = array();
foreach ($tpl as $id => $object) {
if (isset($object[2]) || $object[0] !== $parent) {
if ($parent !== null) {
if (count($merged[1]) === 1 && is_string($merged[1][0])) {
$objects[$id - 1] = array($merged[0], $merged[1][0]);
} else {
$objects[$id - 1] = array($merged[0], new TCompositeLiteral($merged[1]));
}
}
if (isset($object[2])) {
$parent = null;
$objects[$id] = $object;
} else {
$parent = $object[0];
$merged = array($parent, array($object[1]));
}
} else {
$merged[1][] = $object[1];
}
}
if ($parent !== null) {
if (count($merged[1]) === 1 && is_string($merged[1][0])) {
$objects[$id] = array($merged[0], $merged[1][0]);
} else {
$objects[$id] = array($merged[0], new TCompositeLiteral($merged[1]));
}
}
$tpl = $objects;
return $objects;
}