1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
14: class Horde_Xml_Wbxml_Encoder extends Horde_Xml_Wbxml_ContentHandler
15: {
16: protected $_strings = array();
17:
18: protected $_stringTable;
19:
20: protected = false;
21:
22: protected $_dtd;
23:
24: protected $_output = '';
25:
26: protected $_uris = array();
27:
28: protected $_uriNums = array();
29:
30: protected $_currentURI;
31:
32: protected $_subParser = null;
33: protected $_subParserStack = 0;
34:
35: 36: 37: 38: 39:
40: protected $_parser;
41:
42: 43: 44: 45: 46:
47: protected $_dtdManager;
48:
49: 50: 51:
52: public function Horde_Xml_Wbxml_Encoder()
53: {
54: $this->_dtdManager = new Horde_Xml_Wbxml_DtdManager();
55: $this->_stringTable = new Horde_Xml_Wbxml_HashTable();
56: }
57:
58: 59: 60: 61: 62: 63: 64: 65: 66:
67: public function encode($xml)
68: {
69:
70: $this->_parser = xml_parser_create_ns($this->_charset);
71: xml_set_object($this->_parser, $this);
72: xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
73: xml_set_element_handler($this->_parser, '_startElement', '_endElement');
74: xml_set_character_data_handler($this->_parser, '_characters');
75: xml_set_processing_instruction_handler($this->_parser, '');
76: xml_set_external_entity_ref_handler($this->_parser, '');
77:
78: if (!xml_parse($this->_parser, $xml)) {
79: throw new Horde_Xml_Wbxml_Exception(
80: sprintf('XML error: %s at line %d',
81: xml_error_string(xml_get_error_code($this->_parser)),
82: xml_get_current_line_number($this->_parser)));
83: }
84:
85: xml_parser_free($this->_parser);
86:
87: return $this->_output;
88: }
89:
90: 91: 92: 93: 94:
95: public function ($uri)
96: {
97:
98: if ($this->_wbxmlVersion == 2 && !preg_match('/1\.2$/', $uri)) {
99: $uri .= '1.2';
100: }
101: if ($this->_wbxmlVersion == 1 && !preg_match('/1\.1$/', $uri)) {
102: $uri .= '1.1';
103: }
104: if ($this->_wbxmlVersion == 0 && !preg_match('/1\.0$/', $uri)) {
105: $uri .= '1.0';
106: }
107:
108: $this->_dtd = $this->_dtdManager->getInstanceURI($uri);
109: if (!$this->_dtd) {
110: throw new Horde_Xml_Wbxml_Exception('Unable to find dtd for ' . $uri);
111: }
112: $dpiString = $this->_dtd->getDPI();
113:
114:
115:
116:
117: $this->writeVersionNumber($this->_wbxmlVersion);
118:
119:
120:
121:
122:
123:
124: $this->writeDocumentPublicIdentifier($dpiString, $this->_strings);
125:
126:
127:
128: $this->writeCharset($this->_charset);
129:
130:
131:
132: $this->writeStringTable($this->_strings, $this->_charset, $this->_stringTable);
133:
134: $this->_currentURI = $uri;
135:
136: $this->_hasWrittenHeader = true;
137: }
138:
139: public function writeVersionNumber($version)
140: {
141: $this->_output .= chr($version);
142: }
143:
144: public function writeDocumentPublicIdentifier($dpiString, &$strings)
145: {
146: $i = 0;
147:
148:
149:
150:
151:
152: if ($i == 0) {
153: $strings[0] = $dpiString;
154: $this->_output .= chr(0);
155: $this->_output .= chr(0);
156: } else {
157: Horde_Xml_Wbxml::intToMBUInt32($this->_output, $i);
158: }
159: }
160:
161: 162: 163:
164: public function writeCharset($charset)
165: {
166: $cs = Horde_Xml_Wbxml::getCharsetInt($charset);
167:
168: if ($cs == 0) {
169: throw new Horde_Xml_Wbxml_Exception('Unsupported Charset: ' . $charset);
170: } else {
171: Horde_Xml_Wbxml::intToMBUInt32($this->_output, $cs);
172: }
173: }
174:
175: public function writeStringTable($strings, $charset, $stringTable)
176: {
177: $stringBytes = array();
178: $count = 0;
179: foreach ($strings as $str) {
180: $bytes = $this->_getBytes($str, $charset);
181: $stringBytes = array_merge($stringBytes, $bytes);
182: $nullLength = $this->_addNullByte($bytes);
183: $this->_stringTable->set($str, $count);
184: $count += count($bytes) + $nullLength;
185: }
186:
187: Horde_Xml_Wbxml::intToMBUInt32($this->_output, count($stringBytes));
188: $this->_output .= implode('', $stringBytes);
189: }
190:
191: public function writeString($str, $cs)
192: {
193: $bytes = $this->_getBytes($str, $cs);
194: $this->_output .= implode('', $bytes);
195: $this->writeNull($cs);
196: }
197:
198: public function writeNull($charset)
199: {
200: $this->_output .= chr(0);
201: return 1;
202: }
203:
204: protected function _addNullByte(&$bytes)
205: {
206: $bytes[] = chr(0);
207: return 1;
208: }
209:
210: protected function _getBytes($string, $cs)
211: {
212: $nbytes = strlen($string);
213:
214: $bytes = array();
215: for ($i = 0; $i < $nbytes; $i++) {
216: $bytes[] = $string{$i};
217: }
218:
219: return $bytes;
220: }
221:
222: protected function _splitURI($tag)
223: {
224: $parts = explode(':', $tag);
225: $name = array_pop($parts);
226: $uri = implode(':', $parts);
227: return array($uri, $name);
228: }
229:
230: 231: 232:
233: public function startElement($uri, $name, $attributes = array())
234: {
235: if ($this->_subParser == null) {
236: if (!$this->_hasWrittenHeader) {
237: $this->writeHeader($uri);
238: }
239: if ($this->_currentURI != $uri) {
240: $this->changecodepage($uri);
241: $this->_currentURI = $uri;
242: }
243: if ($this->_subParser == null) {
244: $this->writeTag($name, $attributes, true, $this->_charset);
245: } else {
246: $this->_subParser->startElement($uri, $name, $attributes);
247: }
248: } else {
249: $this->_subParserStack++;
250: $this->_subParser->startElement($uri, $name, $attributes);
251: }
252: }
253:
254: protected function _startElement($parser, $tag, $attributes)
255: {
256: list($uri, $name) = $this->_splitURI($tag);
257: if (in_array(Horde_String::lower($uri), array('syncml:metinf', 'syncml:devinf'))) {
258: $uri .= '1.' . $this->getVersion();
259: }
260: $this->startElement($uri, $name, $attributes);
261: }
262:
263: public function opaque($o)
264: {
265: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_OPAQUE);
266: Horde_Xml_Wbxml::intToMBUInt32($this->_output, strlen($o));
267: $this->_output .= $o;
268: }
269:
270: public function characters($chars)
271: {
272: $chars = trim($chars);
273:
274: if (strlen($chars)) {
275:
276: if ($this->_subParser == null) {
277: $i = $this->_stringTable->get($chars);
278: if ($i != null) {
279: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_STR_T);
280: Horde_Xml_Wbxml::intToMBUInt32($this->_output, $i);
281: } else {
282: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_STR_I);
283: $this->writeString($chars, $this->_charset);
284: }
285: } else {
286: $this->_subParser->characters($chars);
287: }
288: }
289: }
290:
291: protected function _characters($parser, $chars)
292: {
293: $this->characters($chars);
294: }
295:
296: 297: 298:
299: public function writeTag($name, $attrs, $hasContent, $cs)
300: {
301: if ($attrs != null && !count($attrs)) {
302: $attrs = null;
303: }
304:
305: $t = $this->_dtd->toTagInt($name);
306: if ($t == -1) {
307: $i = $this->_stringTable->get($name);
308: if ($i == null) {
309: throw new Horde_Xml_Wbxml_Exception($name . ' is not found in String Table or DTD');
310: } else {
311: if ($attrs == null && !$hasContent) {
312: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_LITERAL);
313: } elseif ($attrs == null && $hasContent) {
314: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_LITERAL_A);
315: } elseif ($attrs != null && $hasContent) {
316: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_LITERAL_C);
317: } elseif ($attrs != null && !$hasContent) {
318: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_LITERAL_AC);
319: }
320:
321: Horde_Xml_Wbxml::intToMBUInt32($this->_output, $i);
322: }
323: } else {
324: if ($attrs == null && !$hasContent) {
325: $this->_output .= chr($t);
326: } elseif ($attrs == null && $hasContent) {
327: $this->_output .= chr($t | 64);
328: } elseif ($attrs != null && $hasContent) {
329: $this->_output .= chr($t | 128);
330: } elseif ($attrs != null && !$hasContent) {
331: $this->_output .= chr($t | 192);
332: }
333: }
334:
335: if ($attrs != null && is_array($attrs) && count($attrs) > 0) {
336: $this->writeAttributes($attrs, $cs);
337: }
338: }
339:
340: public function writeAttributes($attrs, $cs)
341: {
342: foreach ($attrs as $name => $value) {
343: $this->writeAttribute($name, $value, $cs);
344: }
345:
346: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_END);
347: }
348:
349: 350: 351:
352: public function writeAttribute($name, $value, $cs)
353: {
354: $a = $this->_dtd->toAttribute($name);
355: if ($a == -1) {
356: $i = $this->_stringTable->get($name);
357: if ($i == null) {
358: throw new Horde_Xml_Wbxml_Exception($name . ' is not found in String Table or DTD');
359: } else {
360: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_LITERAL);
361: Horde_Xml_Wbxml::intToMBUInt32($this->_output, $i);
362: }
363: } else {
364: $this->_output .= $a;
365: }
366:
367: $i = $this->_stringTable->get($name);
368: if ($i != null) {
369: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_STR_T);
370: Horde_Xml_Wbxml::intToMBUInt32($this->_output, $i);
371: } else {
372: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_STR_I);
373: $this->writeString($value, $cs);
374: }
375: }
376:
377: public function endElement($uri, $name)
378: {
379: if ($this->_subParser == null) {
380: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_END);
381: } else {
382: $this->_subParser->endElement($uri, $name);
383: $this->_subParserStack--;
384:
385: if ($this->_subParserStack == 0) {
386: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_OPAQUE);
387:
388: Horde_Xml_Wbxml::intToMBUInt32($this->_output,
389: strlen($this->_subParser->getOutput()));
390: $this->_output .= $this->_subParser->getOutput();
391:
392: $this->_subParser = null;
393: }
394: }
395: }
396:
397: protected function _endElement($parser, $tag)
398: {
399: list($uri, $name) = $this->_splitURI($tag);
400: $this->endElement($uri, $name);
401: }
402:
403: public function changecodepage($uri)
404: {
405:
406: if ($this->_dtd->getVersion() == 2 && !preg_match('/1\.2$/', $uri)) {
407: $uri .= '1.2';
408: }
409: if ($this->_dtd->getVersion() == 1 && !preg_match('/1\.1$/', $uri)) {
410: $uri .= '1.1';
411: }
412: if ($this->_dtd->getVersion() == 0 && !preg_match('/1\.0$/', $uri)) {
413: $uri .= '1.0';
414: }
415:
416: $cp = $this->_dtd->toCodePageURI($uri);
417: if (strlen($cp)) {
418: $this->_dtd = $this->_dtdManager->getInstanceURI($uri);
419: if (!$this->_dtd) {
420: throw new Horde_Xml_Wbxml_Exception('Unable to find dtd for ' . $uri);
421: }
422: $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_SWITCH_PAGE);
423: $this->_output .= chr($cp);
424: } else {
425: $this->_subParser = new Horde_Xml_Wbxml_Encoder(true);
426: $this->_subParserStack = 1;
427: }
428: }
429:
430: 431: 432:
433: public function getOutput()
434: {
435: return $this->_output;
436: }
437:
438: public function getOutputSize()
439: {
440: return strlen($this->_output);
441: }
442: }
443: