egroupware_official/phpgwapi/inc/horde/XML/WBXML/Encoder.php
2009-11-16 08:04:18 +00:00

426 lines
12 KiB
PHP

<?php
include_once 'XML/WBXML.php';
include_once 'XML/WBXML/ContentHandler.php';
include_once 'XML/WBXML/DTDManager.php';
include_once 'Horde/String.php';
/**
* From Binary XML Content Format Specification Version 1.3, 25 July 2001
* found at http://www.wapforum.org
*
* $Horde: framework/XML_WBXML/WBXML/Encoder.php,v 1.25.10.17 2008/08/26 15:41:21 jan Exp $
*
* Copyright 2003-2008 The Horde Project (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @package XML_WBXML
*/
class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
var $_strings = array();
var $_stringTable;
var $_hasWrittenHeader = false;
var $_dtd;
var $_output = '';
var $_uris = array();
var $_uriNums = array();
var $_currentURI;
var $_subParser = null;
var $_subParserStack = 0;
/**
* The XML parser.
*
* @var resource
*/
var $_parser;
/**
* The DTD Manager.
*
* @var XML_WBXML_DTDManager
*/
var $_dtdManager;
/**
* Constructor.
*/
function XML_WBXML_Encoder()
{
$this->_dtdManager = new XML_WBXML_DTDManager();
$this->_stringTable = new XML_WBXML_HashTable();
}
/**
* Take the input $xml and turn it into WBXML. This is _not_ the
* intended way of using this class. It is derived from
* Contenthandler and one should use it as a ContentHandler and
* produce the XML-structure with startElement(), endElement(),
* and characters().
*/
function encode($xml)
{
// Create the XML parser and set method references.
$this->_parser = xml_parser_create_ns($this->_charset);
xml_set_object($this->_parser, $this);
xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
xml_set_element_handler($this->_parser, '_startElement', '_endElement');
xml_set_character_data_handler($this->_parser, '_characters');
xml_set_processing_instruction_handler($this->_parser, '');
xml_set_external_entity_ref_handler($this->_parser, '');
if (!xml_parse($this->_parser, $xml)) {
return $this->raiseError(sprintf('XML error: %s at line %d',
xml_error_string(xml_get_error_code($this->_parser)),
xml_get_current_line_number($this->_parser)));
}
xml_parser_free($this->_parser);
return $this->_output;
}
/**
* This will write the correct headers.
*/
function writeHeader($uri)
{
$this->_dtd = &$this->_dtdManager->getInstanceURI($uri);
if (!$this->_dtd) {
// TODO: proper error handling
die('Unable to find dtd for ' . $uri);
}
$dpiString = $this->_dtd->getDPI();
// Set Version Number from Section 5.4
// version = u_int8
// currently 1, 2 or 3
$this->writeVersionNumber($this->_wbxmlVersion);
// Set Document Public Idetifier from Section 5.5
// publicid = mb_u_int32 | ( zero index )
// zero = u_int8
// containing the value zero (0)
// The actual DPI is determined after the String Table is read.
$this->writeDocumentPublicIdentifier($dpiString, $this->_strings);
// Set Charset from 5.6
// charset = mb_u_int32
$this->writeCharset($this->_charset);
// Set String Table from 5.7
// strb1 = length *byte
$this->writeStringTable($this->_strings, $this->_charset, $this->_stringTable);
$this->_currentURI = $uri;
$this->_hasWrittenHeader = true;
}
function writeVersionNumber($version)
{
$this->_output .= chr($version);
}
function writeDocumentPublicIdentifier($dpiString, &$strings)
{
$i = 0;
// The OMA test suite doesn't like DPI as integer code.
// So don't try lookup and always send full DPI string.
// $i = XML_WBXML::getDPIInt($dpiString);
if ($i == 0) {
$strings[0] = $dpiString;
$this->_output .= chr(0);
$this->_output .= chr(0);
} else {
XML_WBXML::intToMBUInt32($this->_output, $i);
}
}
function writeCharset($charset)
{
$cs = XML_WBXML::getCharsetInt($charset);
if ($cs == 0) {
return $this->raiseError('Unsupported Charset: ' . $charset);
} else {
XML_WBXML::intToMBUInt32($this->_output, $cs);
}
}
function writeStringTable($strings, $charset, $stringTable)
{
$stringBytes = array();
$count = 0;
foreach ($strings as $str) {
$bytes = $this->_getBytes($str, $charset);
$stringBytes = array_merge($stringBytes, $bytes);
$nullLength = $this->_addNullByte($bytes);
$this->_stringTable->set($str, $count);
$count += count($bytes) + $nullLength;
}
XML_WBXML::intToMBUInt32($this->_output, count($stringBytes));
$this->_output .= implode('', $stringBytes);
}
function writeString($str, $cs)
{
$bytes = $this->_getBytes($str, $cs);
$this->_output .= implode('', $bytes);
$this->writeNull($cs);
}
function writeNull($charset)
{
$this->_output .= chr(0);
return 1;
}
function _addNullByte(&$bytes)
{
$bytes[] = chr(0);
return 1;
}
function _getBytes($string, $cs)
{
$string = String::convertCharset($string, $cs, 'utf-8');
$nbytes = strlen($string);
$bytes = array();
for ($i = 0; $i < $nbytes; $i++) {
$bytes[] = $string{$i};
}
return $bytes;
}
function _splitURI($tag)
{
$parts = explode(':', $tag);
$name = array_pop($parts);
$uri = implode(':', $parts);
return array($uri, $name);
}
function startElement($uri, $name, $attributes = array())
{
#Horde::logMessage("WBXML Encoder $uri, " . ($this->_hasWrittenHeader ? 'true' : 'false'), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($this->_subParser == null) {
if (!$this->_hasWrittenHeader) {
$this->writeHeader($uri);
}
if ($this->_currentURI != $uri) {
$this->changecodepage($uri);
}
if ($this->_subParser == null) {
$this->writeTag($name, $attributes, true, $this->_charset);
} else {
$this->_subParser->startElement($uri, $name, $attributes);
}
} else {
$this->_subParserStack++;
$this->_subParser->startElement($uri, $name, $attributes);
}
}
function _startElement($parser, $tag, $attributes)
{
list($uri, $name) = $this->_splitURI($tag);
$this->startElement($uri, $name, $attributes);
}
function opaque($o)
{
if ($this->_subParser == null) {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_OPAQUE);
XML_WBXML::intToMBUInt32($this->_output, strlen($o));
$this->_output .= $o;
}
}
function characters($chars)
{
$chars = trim($chars);
if (strlen($chars)) {
/* We definitely don't want any whitespace. */
if ($this->_subParser == null) {
$i = $this->_stringTable->get($chars);
if ($i != null) {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_STR_T);
XML_WBXML::intToMBUInt32($this->_output, $i);
} else {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_STR_I);
$this->writeString($chars, $this->_charset);
}
} else {
$this->_subParser->characters($chars);
}
}
}
function _characters($parser, $chars)
{
$this->characters($chars);
}
function writeTag($name, $attrs, $hasContent, $cs)
{
if ($attrs != null && !count($attrs)) {
$attrs = null;
}
$t = $this->_dtd->toTagInt($name);
if ($t == -1) {
$i = $this->_stringTable->get($name);
if ($i == null) {
return $this->raiseError($name . ' is not found in String Table or DTD');
} else {
if ($attrs == null && !$hasContent) {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_LITERAL);
} elseif ($attrs == null && $hasContent) {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_LITERAL_A);
} elseif ($attrs != null && $hasContent) {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_LITERAL_C);
} elseif ($attrs != null && !$hasContent) {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_LITERAL_AC);
}
XML_WBXML::intToMBUInt32($this->_output, $i);
}
} else {
if ($attrs == null && !$hasContent) {
$this->_output .= chr($t);
} elseif ($attrs == null && $hasContent) {
$this->_output .= chr($t | 64);
} elseif ($attrs != null && $hasContent) {
$this->_output .= chr($t | 128);
} elseif ($attrs != null && !$hasContent) {
$this->_output .= chr($t | 192);
}
}
if ($attrs != null && is_array($attrs) && count($attrs) > 0) {
$this->writeAttributes($attrs, $cs);
}
}
function writeAttributes($attrs, $cs)
{
foreach ($attrs as $name => $value) {
$this->writeAttribute($name, $value, $cs);
}
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_END);
}
function writeAttribute($name, $value, $cs)
{
$a = $this->_dtd->toAttribute($name);
if ($a == -1) {
$i = $this->_stringTable->get($name);
if ($i == null) {
return $this->raiseError($name . ' is not found in String Table or DTD');
} else {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_LITERAL);
XML_WBXML::intToMBUInt32($this->_output, $i);
}
} else {
$this->_output .= $a;
}
$i = $this->_stringTable->get($name);
if ($i != null) {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_STR_T);
XML_WBXML::intToMBUInt32($this->_output, $i);
} else {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_STR_I);
$this->writeString($value, $cs);
}
}
function endElement($uri, $name)
{
if ($this->_subParser == null) {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_END);
} else {
$this->_subParser->endElement($uri, $name);
$this->_subParserStack--;
if ($this->_subParserStack == 0) {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_OPAQUE);
XML_WBXML::intToMBUInt32($this->_output,
strlen($this->_subParser->getOutput()));
$this->_output .= $this->_subParser->getOutput();
$this->_subParser = null;
}
}
}
function _endElement($parser, $tag)
{
list($uri, $name) = $this->_splitURI($tag);
$this->endElement($uri, $name);
}
function changecodepage($uri)
{
if ($this->_dtd->getVersion() == 2 && !preg_match('/1\.2$/', $uri)) {
$uri .= '1.2';
}
if ($this->_dtd->getVersion() == 1 && !preg_match('/1\.1$/', $uri)) {
$uri .= '1.1';
}
if ($this->_dtd->getVersion() == 0 && !preg_match('/1\.0$/', $uri)) {
$uri .= '1.0';
}
$cp = $this->_dtd->toCodePageURI($uri);
if (strlen($cp)) {
$this->_dtd = &$this->_dtdManager->getInstanceURI($uri);
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_SWITCH_PAGE);
$this->_output .= chr($cp);
$this->_currentURI = $uri;
} else {
$this->_subParser = new XML_WBXML_Encoder(true);
$this->_subParserStack = 1;
}
}
/**
* Getter for property output.
*/
function getOutput()
{
return $this->_output;
}
function getOutputSize()
{
return strlen($this->_output);
}
}