egroupware/phpgwapi/inc/horde/XML/WBXML/Encoder.php
2005-06-19 19:00:58 +00:00

487 lines
14 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';
/**
* $Horde: framework/XML_WBXML/WBXML/Encoder.php,v 1.27 2005/01/03 13:09:25 jan Exp $
*
* Copyright 2003-2005 Anthony Mills <amills@pyramid6.com>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* From Binary XML Content Format Specification Version 1.3, 25 July
* 2001 found at http://www.wapforum.org
*
* @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;
var $_subParserStack = 0;
/**
* These will store the startElement params to see if we should
* call startElementImp or startEndElementImp.
*/
var $_storeURI;
var $_storeName;
var $_storeAttributes;
/**
* The XML parser.
* @var resource $_parser
*/
var $_parser;
/**
* The DTD Manager.
* @var object XML_WBXML_DTDManager $dtdManager
*/
var $_dtdManager;
var $_indent = 0;
/**
* Use wbxml2xml from libwbxml.
* @var string $_xml2wbxml
*/
var $_xml2wbxml = '/usr/bin/xml2wbxml';
/**
* Arguments to pass to xml2wbxml.
* @var string $_xml2wbxml_args
*/
var $_xml2wbxml_args = '-k -n -v 1.2 -o - -';
/**
* Constructor.
*/
function XML_WBXML_Encoder()
{
if (empty($this->_xml2wbxml) || !is_executable($this->_xml2wbxml)) {
$this->_stringTable = &new XML_WBXML_HashTable();
$this->_dtdManager = &new XML_WBXML_DTDManager();
}
}
/**
* Take the input $xml and turn it into WBXML.
*/
function encode($xml)
{
if (!empty($this->_xml2wbxml) && is_executable($this->_xml2wbxml)) {
$descriptorspec = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
);
$xml2wbxml = proc_open($this->_xml2wbxml . ' ' . $this->_xml2wbxml_args,
$descriptorspec, $pipes);
if (is_resource($xml2wbxml)) {
fwrite($pipes[0], $xml);
fclose($pipes[0]);
// Grab the output of xml2wbxml.
$wbxml = '';
while (!feof($pipes[1])) {
$wbxml .= fread($pipes[1], 8192);
}
fclose($pipes[1]);
$rv = proc_close($xml2wbxml);
return $wbxml;
} else {
return PEAR::raiseError('xml2wbxml failed');
}
} else {
// 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_default_handler($this->_parser, 'defaultHandler');
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);
$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 = 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);
}
/**
* Has no content, 64.
*/
function startEndElementImp($uri, $name, $attributes)
{
if (!$this->_hasWrittenHeader) {
$this->writeHeader($uri);
}
$this->writeTag($name, $attributes, false, $this->_charset);
}
function startElementImp($uri, $name, $attributes)
{
if (!$this->_hasWrittenHeader) {
$this->writeHeader($uri);
}
if ($this->_currentURI != $uri) {
$this->changecodepage($uri);
$this->_currentURI != $uri;
}
$this->writeTag($name, $attributes, true, $this->_charset);
}
function writeStartElement($isEnd)
{
if ($this->_storeName != null) {
if ($isEnd) {
$this->startEndElementImp($this->_storeURI, $this->_storeName, $this->_storeAttributes);
} else {
$this->startElementImp($this->_storeURI, $this->_storeName, $this->_storeAttributes);
}
$this->_storeURI = null;
$this->_storeName = null;
$this->_storeAttributes = null;
}
}
function startElement($uri, $name, $attributes)
{
if ($this->_subParser == null) {
$this->writeStartElement(false);
$this->_storeURI = $uri;
$this->_storeName = $name;
$this->_storeAttributes = $attributes;
} else {
$this->_subParserStack++;
}
}
function _startElement($parser, $tag, $attributes)
{
list($uri, $name) = $this->_splitURI($tag);
$this->startElement($uri, $name, $attributes);
}
function opaque($bytes)
{
if ($this->_subParser == null) {
$this->writeStartElement(false);
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_OPAQUE);
XML_WBXML::intToMBUInt32($this->_output, count($bytes));
$this->_output .= $bytes;
}
}
function characters($chars)
{
$chars = trim($chars);
if (strlen($chars)) {
/* We definitely don't want any whitespace. */
if ($this->_subParser == null) {
$this->writeStartElement(false);
$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);
}
}
}
}
function _characters($parser, $chars)
{
$this->characters($chars);
}
function defaultHandler($parser, $data)
{
}
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) {
$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->writeStartElement(false);
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_END);
} else {
$this->_subParserStack--;
if ($this->_subParserStack == 0) {
unset($this->_subParser);
}
}
}
function _endElement($parser, $tag)
{
list($uri, $name) = $this->_splitURI($tag);
$this->endElement($uri, $name);
}
function changecodepage($uri)
{
$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);
} else {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_OPAQUE);
$this->_subParser = &new XML_WBXML_Encoder($this->_output);
$this->startElement($this->_storeURI, $this->_storeName, $this->_storeAttributes);
$this->_subParserStack = 2;
$this->_storeURI = null;
$this->_storeName = null;
$this->_storeAttributes = null;
}
}
/**
* Getter for property output.
*/
function getOutput($output)
{
return $this->_output;
}
}