* @license PHP License * @package wb * @subpackage Markup */ WBClass::load('WBMarkup_Handler'); /** * Markup Handler: Xml2Wxml * * Convert Wombat XML data to Wxml-HTML with Wombat attributes * * @version 0.1.0 * @package wb * @subpackage Markup */ class WBMarkup_Handler_Xml2Wxml implements WBMarkup_Handler { /** * current depth * @var int */ protected $depth = 0; /** * content * @var string */ protected $content = ''; /** * document's md5 sum * @var string */ protected $md5; /** * document nodes * @var array */ protected $doc = array(); /** * list of running converters (WBMarkup_Converter) * @var array */ protected $con = array(); /** * Map some tags * @var array */ protected $stdTagMap = array( 'strong' => 'b', 'em' => 'i', 'del' => 'strike', 'ins' => 'u' ); /** * List of Options 2 Pass 2 Converter * * @var array */ private $converterOptions = array(); /** * Set Converter Option List * * @param array */ public function setOption($opt = array()) { $this->converterOptions = $opt; } /** * handler on start beginning of scan * * * * @param string $content * @return bool usually true, false to stop the scanner */ public function onScanStart(&$content) { $this->con = array(); $this->depth = 0; $this->content =& $content; $this->md5 = md5($content); $this->doc = array(); $this->doc[0] = array( 'ns' => '', 'tag' => '', 'attributes' => array(), 'isEmpty' => false, 'cData' => array(), 'depth' => $this->depth ); // inform converter about document's md5 and content foreach (array_keys($this->con) as $con) { $this->con[$con]->preset($this, $content); } return true; } /** * handler on start element * * @param string $ns The used namespace, if set. otherwise null * @param string $tag the tag itself * @param array $attributes the attributes of the found element * @param bool $isEmpty true if it is a start- and closing-Element (e.g.
) * @return bool usually true, false to stop the scanner */ public function onStartElement($ns, $tag, $attributes, $isEmpty) { if (empty($ns) && isset($this->stdTagMap[$tag])) { $tag = $this->stdTagMap[$tag]; } ++$this->depth; $this->doc[$this->depth] = array( 'ns' => $ns, 'tag' => $tag, 'attributes' => $attributes, 'isEmpty' => $isEmpty, 'cData' => array(), 'depth' => $this->depth ); return true; } /** * handler fo cData * * Simply add cDate to document * * @uses replaceUrlString() * @param string $cData character data * @return bool usually true, false to stop the scanner */ public function onCharacterData($cData) { $this->doc[$this->depth]['cData'][] = $cData; return true; } /** * handler for entities * * Add entities to document's cData * * @param string $entity the entity element, e.g. nbsp for   * @param boolean $isUnicode true = the element is a unicode string */ public function onEntityElement($entity, $isUnicode) { $cData = '&'; if( $isUnicode ) { $cData .= '#'; } $cData .= $entity . ';'; $this->doc[$this->depth]['cData'][] = $cData; return true; } /** * test handler on end element * * @param string $ns The used namespace, if set. otherwise null * @param string $tag the tag itself * @param bool $empty defines if the tag is empty * @return bool usually true, false to stop the scanner */ public function onEndElement($ns, $tag, $empty) { // serialize node if( !$this->depth ) { return true; } $node = array_pop($this->doc); if ($node['ns']) { $this->convert($node['ns'], $node['tag'], $node); } --$this->depth; if( $this->depth < 0 ) { $this->depth = 0; } $this->doc[$this->depth]['cData'][] = $this->node2String($node); return true; } /** * called right after scan is complete * * @return bool usually true, false to stop the scanner */ public function onScanComplete() { $this->content = $this->node2String($this->doc[0]); // inform converter foreach (array_keys($this->con) as $con) { $this->con[$con]->reset(); } // flush listener $this->listener = array(); return true; } /** * receive translated content string * * @return string */ public function getParsedContent() { return $this->content; } /** * convert node * * transform namespaced tags to HTML * * @todo convert empty namespace (standard HTML) to known WB namespace, if possible * @param string $ns * @param string $tag * @param array $node */ protected function convert($ns, $tag, &$node) { $con = $this->getConverter($ns, $tag); if (!$con) { return ''; } return $con->toWxml($node); } /** * cet instance of matching converter * * See whether convert is already running, start it otherwise * * @param $ns * @param $tag * @return WBMarkup_Converter */ protected function getConverter($ns, $tag) { $con = ucfirst(strtolower($ns)) . '_' . ucfirst(strtolower($tag)); try{ if (!isset($this->con[$con])) { $this->con[$con] = WBClass::create('WBMarkup_Converter_' . $con); /** @var $this->con[$con] WBMarkup_Converter */ $this->con[$con]->preset($this, $this->content); } } catch(WBException_Class $e) { return null; } $this->con[$con]->setOption($this->converterOptions); return $this->con[$con]; } /** * serialize document node * * @param array $node * @return string serialized attributes */ protected function node2String($node) { $cData = implode('', $node['cData']); if (!$node['tag']) { return $cData; } $tag = ''; if ($node['ns']) { $tag .= $node['ns'] . ':'; } $tag .= $node['tag']; $element = '<' . $tag . $this->attributes2String($node['attributes']); if ($node['isEmpty']) { $element .= ' />'; return $element; } $element .= '>' . $cData . ''; return $element; } /** * serialize attribute list to HTML attribute string * * @param array $att attribute list * @return string serialized attributes */ protected function attributes2String($att) { if (empty($att)) { return ''; } $str = ''; foreach ($att as $k => $v) { $str .= ' ' . $k . '="' . $v . '"'; } return $str; } }