source: trunk/prototype/plugins/davicalCliente/XMLDocument.php @ 7655

Revision 7655, 10.0 KB checked in by douglasz, 11 years ago (diff)

Ticket #3236 - Melhorias de performance no codigo do Expresso.

Line 
1<?php
2/**
3* Handling of namespacing for XML documents
4*
5* @package awl
6* @subpackage XMLDocument
7* @author Andrew McMillan <andrew@morphoss.com>
8* @copyright Morphoss Ltd - http://www.morphoss.com/
9* @license   http://www.gnu.org/licenses/lgpl-3.0.txt  GNU LGPL version 3 or later
10*
11*/
12require_once ROOTPATH.'/plugins/davicalCliente/XMLElement.php';
13
14/**
15* A class for XML Documents which will contain namespaced XML elements
16*
17* @package   awl
18*/
19class XMLDocument {
20
21  /**#@+
22  * @access private
23  */
24  /**
25  * holds the namespaces which this document has been configured for.
26  * @var namespaces
27  */
28  var $namespaces;
29
30  /**
31  * holds the prefixes which are shorthand for the namespaces.
32  * @var prefixes
33  */
34  var $prefixes;
35
36  /**
37  * Holds the root document for the tree
38  * @var root
39  */
40  var $root;
41
42  /**
43  * Simple XMLDocument constructor
44  *
45  * @param array $namespaces An array of 'namespace' => 'prefix' pairs, where the prefix is used as a short form for the namespace.
46  */
47  function __construct( $namespaces = null ) {
48    $this->namespaces = array();
49    $this->prefixes = array();
50    if ( $namespaces != null ) {
51      foreach( $namespaces AS $ns => $prefix ) {
52        $this->namespaces[$ns] = $prefix;
53        $this->prefixes[$prefix] = $prefix;
54      }
55    }
56    $this->next_prefix = 0;
57  }
58
59  /**
60  * Add a new namespace to the document, optionally specifying it's short prefix
61  *
62  * @param string $namespace The full namespace name to be added
63  * @param string $prefix An optional short form for the namespace.
64  */
65  function AddNamespace( $namespace, $prefix = null ) {
66    if ( !isset($this->namespaces[$namespace]) ) {
67      if ( isset($prefix) && ($prefix == "" || isset($this->prefixes[$prefix])) ) $prefix = null;
68      if ( $prefix == null ) {
69        //  Try and build a prefix based on the first alphabetic character of the last element of the namespace
70        if ( preg_match('/^(.*):([^:]+)$/', $namespace, $matches) ) {
71          $alpha = preg_replace( '/[^a-z]/i', '', $matches[2] );
72          $prefix = strtoupper(substr($alpha,0,1));
73        }
74        else {
75          $prefix = 'X';
76        }
77        $i = "";
78        if ( isset($this->prefixes[$prefix]) ) {
79          for ( $i=1; $i<10 && isset($this->prefixes["$prefix$i"]); ++$i ) {
80          }
81        }
82        if ( isset($this->prefixes["$prefix$i"]) ) {
83          dbg_error_log("ERROR", "Cannot find a free prefix for this namespace");
84          exit;
85        }
86        $prefix = "$prefix$i";
87        dbg_error_log("XMLDocument", "auto-assigning prefix of '%s' for ns of '%s'", $prefix, $namespace );
88      }
89      else if ( $prefix == "" || isset($this->prefixes[$prefix]) ) {
90        dbg_error_log("ERROR", "Cannot assign the same prefix to two different namespaces");
91        exit;
92      }
93
94      $this->prefixes[$prefix] = $prefix;
95      $this->namespaces[$namespace] = $prefix;
96    }
97    else {
98      if ( isset($this->namespaces[$namespace]) && $this->namespaces[$namespace] != $prefix ) {
99        dbg_error_log("ERROR", "Cannot use the same namespace with two different prefixes");
100        exit;
101      }
102      $this->prefixes[$prefix] = $prefix;
103      $this->namespaces[$namespace] = $prefix;
104    }
105  }
106
107
108  /**
109  * Return a tag with namespace stripped and replaced with a short form, and the ns added to the document.
110  *
111  */
112  function GetXmlNsArray() {
113
114    $ns = array();
115    foreach( $this->namespaces AS $n => $p ) {
116      if ( $p == "" ) $ns["xmlns"] = $n; else $ns["xmlns:$p"] = $n;
117    }
118
119    return $ns;
120  }
121
122
123  /**
124  * Return a tag with namespace stripped and replaced with a short form, and the ns added to the document.
125  *
126  * @param string $in_tag The tag we want a namespace prefix on.
127  * @param string $namespace The namespace we want it in (which will be parsed from $in_tag if not present
128  * @param string $prefix The prefix we would like to use.  Leave it out and one will be assigned.
129  *
130  * @return string The tag with a namespace prefix consistent with previous tags in this namespace.
131  */
132  function Tag( $in_tag, $namespace=null, $prefix=null ) {
133
134    if ( $namespace == null ) {
135      // Attempt to split out from namespace:tag
136      if ( preg_match('/^(.*):([^:]+)$/', $in_tag, $matches) ) {
137        $namespace = $matches[1];
138        $tag = $matches[2];
139      }
140      else {
141        // There is nothing we can do here
142        return $in_tag;
143      }
144    }
145    else {
146      $tag = $in_tag;
147    }
148
149    if ( !isset($this->namespaces[$namespace]) ) {
150      $this->AddNamespace( $namespace, $prefix );
151    }
152    $prefix = $this->namespaces[$namespace];
153
154    return $prefix . ($prefix == "" ? "" : ":") . $tag;
155  }
156
157
158  /**
159  * Special helper for namespaced tags.
160  *
161  * @param object $element The tag are adding a new namespaced element to
162  * @param string $tag the tag name, possibly prefixed with the namespace
163  * @param mixed  $content The content of the tag
164  * @param array  $attributes An array of key/value pairs of attributes.
165  * @param string $namespace The namespace for the tag
166  *
167  */
168  function NSElement( &$element, $in_tag, $content=false, $attributes=false, $namespace=null ) {
169    if ( $namespace == null && preg_match('/^(.*):([^:]+)$/', $in_tag, $matches) ) {
170      $namespace = $matches[1];
171      $tag = $matches[2];
172    }
173    else {
174      $tag = $in_tag;
175    }
176
177    if ( isset($namespace) && !isset($this->namespaces[$namespace]) ) $this->AddNamespace( $namespace );
178    return $element->NewElement( $tag, $content, $attributes, $namespace );
179  }
180
181
182  /**
183  * Special helper for tags in the DAV: namespace.
184  *
185  * @param object $element The tag are adding a new namespaced element to
186  * @param string $tag the tag name
187  * @param mixed  $content The content of the tag
188  * @param array  $attributes An array of key/value pairs of attributes.
189  */
190  function DAVElement( &$element, $tag, $content=false, $attributes=false ) {
191    return $this->NSElement( $element, $tag, $content, $attributes, 'DAV:' );
192  }
193
194
195  /**
196  * Special helper for tags in the urn:ietf:params:xml:ns:caldav namespace.
197  *
198  * @param object $element The tag are adding a new namespaced element to
199  * @param string $tag the tag name
200  * @param mixed  $content The content of the tag
201  * @param array  $attributes An array of key/value pairs of attributes.
202  */
203  function CalDAVElement( &$element, $tag, $content=false, $attributes=false ) {
204    if ( !isset($this->namespaces['urn:ietf:params:xml:ns:caldav']) ) $this->AddNamespace( 'urn:ietf:params:xml:ns:caldav', 'C' );
205    return $this->NSElement( $element, $tag, $content, $attributes, 'urn:ietf:params:xml:ns:caldav' );
206  }
207
208
209  /**
210  * Special helper for tags in the urn:ietf:params:xml:ns:carddav namespace.
211  *
212  * @param object $element The tag are adding a new namespaced element to
213  * @param string $tag the tag name
214  * @param mixed  $content The content of the tag
215  * @param array  $attributes An array of key/value pairs of attributes.
216  */
217  function CardDAVElement( &$element, $tag, $content=false, $attributes=false ) {
218    if ( !isset($this->namespaces['urn:ietf:params:xml:ns:carddav']) ) $this->AddNamespace( 'urn:ietf:params:xml:ns:carddav', 'VC' );
219    return $this->NSElement( $element, $tag, $content, $attributes, 'urn:ietf:params:xml:ns:carddav' );
220  }
221
222
223  /**
224  * Special helper for tags in the urn:ietf:params:xml:ns:caldav namespace.
225  *
226  * @param object $element The tag are adding a new namespaced element to
227  * @param string $tag the tag name
228  * @param mixed  $content The content of the tag
229  * @param array  $attributes An array of key/value pairs of attributes.
230  */
231  function CalendarserverElement( &$element, $tag, $content=false, $attributes=false ) {
232    if ( !isset($this->namespaces['http://calendarserver.org/ns/']) ) $this->AddNamespace( 'http://calendarserver.org/ns/', 'A' );
233    return $this->NSElement( $element, $tag, $content, $attributes, 'http://calendarserver.org/ns/' );
234  }
235
236
237  /**
238  * @param string $in_tag The tag name of the new element, possibly namespaced
239  * @param mixed $content Either a string of content, or an array of sub-elements
240  * @param array $attributes An array of attribute name/value pairs
241  * @param array $xmlns An XML namespace specifier
242  */
243  function NewXMLElement( $in_tag, $content=false, $attributes=false, $xmlns=null ) {
244    if ( $xmlns == null && preg_match('/^(.*):([^:]+)$/', $in_tag, $matches) ) {
245      $xmlns = $matches[1];
246      $tagname = $matches[2];
247    }
248    else {
249      $tagname = $in_tag;
250    }
251
252    if ( isset($xmlns) && !isset($this->namespaces[$xmlns]) ) $this->AddNamespace( $xmlns );
253    return new XMLElement($tagname, $content, $attributes, $xmlns );
254  }
255
256  /**
257  * Render the document tree into (nicely formatted) XML
258  *
259  * @param mixed $root A root XMLElement or a tagname to create one with the remaining parameters.
260  * @param mixed $content Either a string of content, or an array of sub-elements
261  * @param array $attributes An array of attribute name/value pairs
262  * @param array $xmlns An XML namespace specifier
263  *
264  * @return A rendered namespaced XML document.
265  */
266  function Render( $root, $content=false, $attributes=false, $xmlns=null ) {
267    if ( is_object($root) ) {
268      /** They handed us a pre-existing object.  We'll just use it... */
269      $this->root = $root;
270    }
271    else {
272      /** We got a tag name, so we need to create the root element */
273      $this->root = $this->NewXMLElement( $root, $content, $attributes, $xmlns );
274    }
275
276    /**
277    * Add our namespace attributes here.
278    */
279    foreach( $this->namespaces AS $n => $p ) {
280      $this->root->SetAttribute( 'xmlns'.($p == '' ? '' : ':') . $p, $n);
281    }
282
283    /** And render... */
284    return $this->root->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
285  }
286
287  /**
288  * Return a DAV::href XML element, or an array of them
289  * @param mixed $url The URL (or array of URLs) to be wrapped in DAV::href tags
290  *
291  * @return XMLElement The newly created XMLElement object.
292  */
293  function href($url) {
294    if ( is_array($url) ) {
295      $set = array();
296      foreach( $url AS $href ) {
297        $set[] = $this->href( $href );
298      }
299      return $set;
300    }
301    return $this->NewXMLElement('href', $url, false, 'DAV:');
302  }
303
304}
305
306
Note: See TracBrowser for help on using the repository browser.