Generated docs for classEditor.
[awl.git] / inc / XMLDocument.php
blob7bbf673f2e1b865367fcf330e8880205c8725c8a
1 <?php
2 /**
3 * Handling of namespacing for XML documents
5 * @package awl
6 * @subpackage XMLDocument
7 * @author Andrew McMillan <andrew@morphoss.com>
8 * @copyright Morphoss Ltd - http://www.morphoss.com/
9 * @license http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
13 require_once("XMLElement.php");
15 /**
16 * A class for XML Documents which will contain namespaced XML elements
18 * @package awl
20 class XMLDocument {
22 /**#@+
23 * @access private
25 /**
26 * holds the namespaces which this document has been configured for.
27 * @var namespaces
29 var $namespaces;
31 /**
32 * holds the prefixes which are shorthand for the namespaces.
33 * @var prefixes
35 var $prefixes;
37 /**
38 * Holds the root document for the tree
39 * @var root
41 var $root;
43 /**
44 * Simple XMLDocument constructor
46 * @param array $namespaces An array of 'namespace' => 'prefix' pairs, where the prefix is used as a short form for the namespace.
48 function XMLDocument( $namespaces = null ) {
49 $this->namespaces = array();
50 $this->prefixes = array();
51 if ( $namespaces != null ) {
52 foreach( $namespaces AS $ns => $prefix ) {
53 $this->namespaces[$ns] = $prefix;
54 $this->prefixes[$prefix] = $prefix;
57 $this->next_prefix = 0;
60 /**
61 * Add a new namespace to the document, optionally specifying it's short prefix
63 * @param string $namespace The full namespace name to be added
64 * @param string $prefix An optional short form for the namespace.
66 function AddNamespace( $namespace, $prefix = null ) {
67 if ( !isset($this->namespaces[$namespace]) ) {
68 if ( isset($prefix) && ($prefix == "" || isset($this->prefixes[$prefix])) ) $prefix = null;
69 if ( $prefix == null ) {
70 // Try and build a prefix based on the first alphabetic character of the last element of the namespace
71 if ( preg_match('/^(.*):([^:]+)$/', $namespace, $matches) ) {
72 $alpha = preg_replace( '/[^a-z]/i', '', $matches[2] );
73 $prefix = strtoupper(substr($alpha,0,1));
75 else {
76 $prefix = 'X';
78 $i = "";
79 if ( isset($this->prefixes[$prefix]) ) {
80 for ( $i=1; $i<10 && isset($this->prefixes["$prefix$i"]); $i++ ) {
83 if ( isset($this->prefixes["$prefix$i"]) ) {
84 dbg_error_log("ERROR", "Cannot find a free prefix for this namespace");
85 exit;
87 $prefix = "$prefix$i";
88 dbg_error_log("XMLDocument", "auto-assigning prefix of '%s' for ns of '%s'", $prefix, $namespace );
90 else if ( $prefix == "" || isset($this->prefixes[$prefix]) ) {
91 dbg_error_log("ERROR", "Cannot assign the same prefix to two different namespaces");
92 exit;
95 $this->prefixes[$prefix] = $prefix;
96 $this->namespaces[$namespace] = $prefix;
98 else {
99 if ( isset($this->namespaces[$namespace]) && $this->namespaces[$namespace] != $prefix ) {
100 dbg_error_log("ERROR", "Cannot use the same namespace with two different prefixes");
101 exit;
103 $this->prefixes[$prefix] = $prefix;
104 $this->namespaces[$namespace] = $prefix;
110 * Return a tag with namespace stripped and replaced with a short form, and the ns added to the document.
113 function GetXmlNsArray() {
115 $ns = array();
116 foreach( $this->namespaces AS $n => $p ) {
117 if ( $p == "" ) $ns["xmlns"] = $n; else $ns["xmlns:$p"] = $n;
120 return $ns;
125 * Return a tag with namespace stripped and replaced with a short form, and the ns added to the document.
127 * @param string $in_tag The tag we want a namespace prefix on.
128 * @param string $namespace The namespace we want it in (which will be parsed from $in_tag if not present
129 * @param string $prefix The prefix we would like to use. Leave it out and one will be assigned.
131 * @return string The tag with a namespace prefix consistent with previous tags in this namespace.
133 function Tag( $in_tag, $namespace=null, $prefix=null ) {
135 if ( $namespace == null ) {
136 // Attempt to split out from namespace:tag
137 if ( preg_match('/^(.*):([^:]+)$/', $in_tag, $matches) ) {
138 $namespace = $matches[1];
139 $tag = $matches[2];
141 else {
142 // There is nothing we can do here
143 return $in_tag;
146 else {
147 $tag = $in_tag;
150 if ( !isset($this->namespaces[$namespace]) ) {
151 $this->AddNamespace( $namespace, $prefix );
153 $prefix = $this->namespaces[$namespace];
155 return $prefix . ($prefix == "" ? "" : ":") . $tag;
160 * Special helper for namespaced tags.
162 * @param object $element The tag are adding a new namespaced element to
163 * @param string $tag the tag name, possibly prefixed with the namespace
164 * @param mixed $content The content of the tag
165 * @param array $attributes An array of key/value pairs of attributes.
166 * @param string $namespace The namespace for the tag
169 function NSElement( &$element, $in_tag, $content=false, $attributes=false, $namespace=null ) {
170 if ( $namespace == null && preg_match('/^(.*):([^:]+)$/', $in_tag, $matches) ) {
171 $namespace = $matches[1];
172 $tag = $matches[2];
174 else {
175 $tag = $in_tag;
178 if ( isset($namespace) && !isset($this->namespaces[$namespace]) ) $this->AddNamespace( $namespace );
179 $element->NewElement( $tag, $content, $attributes, $namespace );
184 * Special helper for tags in the DAV: namespace.
186 * @param object $element The tag are adding a new namespaced element to
187 * @param string $tag the tag name
188 * @param mixed $content The content of the tag
189 * @param array $attributes An array of key/value pairs of attributes.
191 function DAVElement( &$element, $tag, $content=false, $attributes=false ) {
192 $this->NSElement( $element, $tag, $content, $attributes, 'DAV:' );
197 * Special helper for tags in the urn:ietf:params:xml:ns:caldav namespace.
199 * @param object $element The tag are adding a new namespaced element to
200 * @param string $tag the tag name
201 * @param mixed $content The content of the tag
202 * @param array $attributes An array of key/value pairs of attributes.
204 function CalDAVElement( &$element, $tag, $content=false, $attributes=false ) {
205 if ( !isset($this->namespaces['urn:ietf:params:xml:ns:caldav']) ) $this->AddNamespace( 'urn:ietf:params:xml:ns:caldav', 'C' );
206 $this->NSElement( $element, $tag, $content, $attributes, 'urn:ietf:params:xml:ns:caldav' );
211 * Special helper for tags in the urn:ietf:params:xml:ns:caldav namespace.
213 * @param object $element The tag are adding a new namespaced element to
214 * @param string $tag the tag name
215 * @param mixed $content The content of the tag
216 * @param array $attributes An array of key/value pairs of attributes.
218 function CalendarserverElement( &$element, $tag, $content=false, $attributes=false ) {
219 if ( !isset($this->namespaces['http://calendarserver.org/ns/']) ) $this->AddNamespace( 'http://calendarserver.org/ns/', 'A' );
220 $this->NSElement( $element, $tag, $content, $attributes, 'http://calendarserver.org/ns/' );
225 * @param string $tagname The tag name of the new element
226 * @param mixed $content Either a string of content, or an array of sub-elements
227 * @param array $attributes An array of attribute name/value pairs
228 * @param array $xmlns An XML namespace specifier
230 function NewXMLElement( $tagname, $content=false, $attributes=false, $xmlns=null ) {
231 if ( isset($xmlns) && !isset($this->namespaces[$xmlns]) ) $this->AddNamespace( $xmlns );
232 return new XMLElement($tagname, $content, $attributes, $xmlns );
236 * Render the document tree into (nicely formatted) XML
238 * @param mixed $root A root XMLElement or a tagname to create one with the remaining parameters.
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
243 * @return A rendered namespaced XML document.
245 function Render( $root, $content=false, $attributes=false, $xmlns=null ) {
246 if ( is_object($root) ) {
247 /** They handed us a pre-existing object. We'll just use it... */
248 $this->root = $root;
250 else {
251 /** We got a tag name, so we need to create the root element */
252 $this->root = $this->NewXMLElement( $root, $content, $attributes, $xmlns );
256 * Add our namespace attributes here.
258 foreach( $this->namespaces AS $n => $p ) {
259 $this->root->SetAttribute( 'xmlns'.($p == '' ? '' : ':') . $p, $n);
262 /** And render... */
263 return $this->root->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
267 * Return a DAV::href XML element, or an array of them
268 * @param mixed $url The URL (or array of URLs) to be wrapped in DAV::href tags
270 * @return XMLElement The newly created XMLElement object.
272 function href($url) {
273 if ( is_array($url) ) {
274 $set = array();
275 foreach( $url AS $href ) {
276 $set[] = $this->href( $href );
278 return $set;
280 return $this->NewXMLElement('href', $url, false, 'DAV:');