web/Zend/Gdata/App/Base.php
changeset 0 4eba9c11703f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/Zend/Gdata/App/Base.php	Mon Dec 13 18:29:26 2010 +0100
@@ -0,0 +1,572 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage App
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Base.php 22662 2010-07-24 17:37:36Z mabe $
+ */
+
+/**
+ * @see Zend_Gdata_App_Util
+ */
+require_once 'Zend/Gdata/App/Util.php';
+
+/**
+ * Abstract class for all XML elements
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage App
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Gdata_App_Base
+{
+
+    /**
+     * @var string The XML element name, including prefix if desired
+     */
+    protected $_rootElement = null;
+
+    /**
+     * @var string The XML namespace prefix
+     */
+    protected $_rootNamespace = 'atom';
+
+    /**
+     * @var string The XML namespace URI - takes precedence over lookup up the
+     * corresponding URI for $_rootNamespace
+     */
+    protected $_rootNamespaceURI = null;
+
+    /**
+     * @var array Leftover elements which were not handled
+     */
+    protected $_extensionElements = array();
+
+    /**
+     * @var array Leftover attributes which were not handled
+     */
+    protected $_extensionAttributes = array();
+
+    /**
+     * @var string XML child text node content
+     */
+    protected $_text = null;
+
+    /**
+     * @var array Memoized results from calls to lookupNamespace() to avoid
+     *      expensive calls to getGreatestBoundedValue(). The key is in the
+     *      form 'prefix-majorVersion-minorVersion', and the value is the
+     *      output from getGreatestBoundedValue().
+     */
+    protected static $_namespaceLookupCache = array();
+
+    /**
+     * List of namespaces, as a three-dimensional array. The first dimension
+     * represents the namespace prefix, the second dimension represents the
+     * minimum major protocol version, and the third dimension is the minimum
+     * minor protocol version. Null keys are NOT allowed.
+     *
+     * When looking up a namespace for a given prefix, the greatest version
+     * number (both major and minor) which is less than the effective version
+     * should be used.
+     *
+     * @see lookupNamespace()
+     * @see registerNamespace()
+     * @see registerAllNamespaces()
+     * @var array
+     */
+   protected $_namespaces = array(
+        'atom'      => array(
+            1 => array(
+                0 => 'http://www.w3.org/2005/Atom'
+                )
+            ),
+        'app'       => array(
+            1 => array(
+                0 => 'http://purl.org/atom/app#'
+                ),
+            2 => array(
+                0 => 'http://www.w3.org/2007/app'
+                )
+            )
+        );
+
+    public function __construct()
+    {
+    }
+
+    /**
+     * Returns the child text node of this element
+     * This represents any raw text contained within the XML element
+     *
+     * @return string Child text node
+     */
+    public function getText($trim = true)
+    {
+        if ($trim) {
+            return trim($this->_text);
+        } else {
+            return $this->_text;
+        }
+    }
+
+    /**
+     * Sets the child text node of this element
+     * This represents any raw text contained within the XML element
+     *
+     * @param string $value Child text node
+     * @return Zend_Gdata_App_Base Returns an object of the same type as 'this' to provide a fluent interface.
+     */
+    public function setText($value)
+    {
+        $this->_text = $value;
+        return $this;
+    }
+
+    /**
+     * Returns an array of all elements not matched to data model classes
+     * during the parsing of the XML
+     *
+     * @return array All elements not matched to data model classes during parsing
+     */
+    public function getExtensionElements()
+    {
+        return $this->_extensionElements;
+    }
+
+    /**
+     * Sets an array of all elements not matched to data model classes
+     * during the parsing of the XML.  This method can be used to add arbitrary
+     * child XML elements to any data model class.
+     *
+     * @param array $value All extension elements
+     * @return Zend_Gdata_App_Base Returns an object of the same type as 'this' to provide a fluent interface.
+     */
+    public function setExtensionElements($value)
+    {
+        $this->_extensionElements = $value;
+        return $this;
+    }
+
+    /**
+     * Returns an array of all extension attributes not transformed into data
+     * model properties during parsing of the XML.  Each element of the array
+     * is a hashed array of the format:
+     *     array('namespaceUri' => string, 'name' => string, 'value' => string);
+     *
+     * @return array All extension attributes
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->_extensionAttributes;
+    }
+
+    /**
+     * Sets an array of all extension attributes not transformed into data
+     * model properties during parsing of the XML.  Each element of the array
+     * is a hashed array of the format:
+     *     array('namespaceUri' => string, 'name' => string, 'value' => string);
+     * This can be used to add arbitrary attributes to any data model element
+     *
+     * @param array $value All extension attributes
+     * @return Zend_Gdata_App_Base Returns an object of the same type as 'this' to provide a fluent interface.
+     */
+    public function setExtensionAttributes($value)
+    {
+        $this->_extensionAttributes = $value;
+        return $this;
+    }
+
+    /**
+     * Retrieves a DOMElement which corresponds to this element and all
+     * child properties.  This is used to build an entry back into a DOM
+     * and eventually XML text for sending to the server upon updates, or
+     * for application storage/persistence.
+     *
+     * @param DOMDocument $doc The DOMDocument used to construct DOMElements
+     * @return DOMElement The DOMElement representing this element and all
+     * child properties.
+     */
+    public function getDOM($doc = null, $majorVersion = 1, $minorVersion = null)
+    {
+        if ($doc === null) {
+            $doc = new DOMDocument('1.0', 'utf-8');
+        }
+        if ($this->_rootNamespaceURI != null) {
+            $element = $doc->createElementNS($this->_rootNamespaceURI, $this->_rootElement);
+        } elseif ($this->_rootNamespace !== null) {
+            if (strpos($this->_rootElement, ':') === false) {
+                $elementName = $this->_rootNamespace . ':' . $this->_rootElement;
+            } else {
+                $elementName = $this->_rootElement;
+            }
+            $element = $doc->createElementNS($this->lookupNamespace($this->_rootNamespace), $elementName);
+        } else {
+            $element = $doc->createElement($this->_rootElement);
+        }
+        if ($this->_text != null) {
+            $element->appendChild($element->ownerDocument->createTextNode($this->_text));
+        }
+        foreach ($this->_extensionElements as $extensionElement) {
+            $element->appendChild($extensionElement->getDOM($element->ownerDocument));
+        }
+        foreach ($this->_extensionAttributes as $attribute) {
+            $element->setAttribute($attribute['name'], $attribute['value']);
+        }
+        return $element;
+    }
+
+    /**
+     * Given a child DOMNode, tries to determine how to map the data into
+     * object instance members.  If no mapping is defined, Extension_Element
+     * objects are created and stored in an array.
+     *
+     * @param DOMNode $child The DOMNode needed to be handled
+     */
+    protected function takeChildFromDOM($child)
+    {
+        if ($child->nodeType == XML_TEXT_NODE) {
+            $this->_text = $child->nodeValue;
+        } else {
+            $extensionElement = new Zend_Gdata_App_Extension_Element();
+            $extensionElement->transferFromDOM($child);
+            $this->_extensionElements[] = $extensionElement;
+        }
+    }
+
+    /**
+     * Given a DOMNode representing an attribute, tries to map the data into
+     * instance members.  If no mapping is defined, the name and value are
+     * stored in an array.
+     *
+     * @param DOMNode $attribute The DOMNode attribute needed to be handled
+     */
+    protected function takeAttributeFromDOM($attribute)
+    {
+        $arrayIndex = ($attribute->namespaceURI != '')?(
+                $attribute->namespaceURI . ':' . $attribute->name):
+                $attribute->name;
+        $this->_extensionAttributes[$arrayIndex] =
+                array('namespaceUri' => $attribute->namespaceURI,
+                      'name' => $attribute->localName,
+                      'value' => $attribute->nodeValue);
+    }
+
+    /**
+     * Transfers each child and attribute into member variables.
+     * This is called when XML is received over the wire and the data
+     * model needs to be built to represent this XML.
+     *
+     * @param DOMNode $node The DOMNode that represents this object's data
+     */
+    public function transferFromDOM($node)
+    {
+        foreach ($node->childNodes as $child) {
+            $this->takeChildFromDOM($child);
+        }
+        foreach ($node->attributes as $attribute) {
+            $this->takeAttributeFromDOM($attribute);
+        }
+    }
+
+    /**
+     * Parses the provided XML text and generates data model classes for
+     * each know element by turning the XML text into a DOM tree and calling
+     * transferFromDOM($element).  The first data model element with the same
+     * name as $this->_rootElement is used and the child elements are
+     * recursively parsed.
+     *
+     * @param string $xml The XML text to parse
+     */
+    public function transferFromXML($xml)
+    {
+        if ($xml) {
+            // Load the feed as an XML DOMDocument object
+            @ini_set('track_errors', 1);
+            $doc = new DOMDocument();
+            $success = @$doc->loadXML($xml);
+            @ini_restore('track_errors');
+            if (!$success) {
+                require_once 'Zend/Gdata/App/Exception.php';
+                throw new Zend_Gdata_App_Exception("DOMDocument cannot parse XML: $php_errormsg");
+            }
+            $element = $doc->getElementsByTagName($this->_rootElement)->item(0);
+            if (!$element) {
+                require_once 'Zend/Gdata/App/Exception.php';
+                throw new Zend_Gdata_App_Exception('No root <' . $this->_rootElement . '> element');
+            }
+            $this->transferFromDOM($element);
+        } else {
+            require_once 'Zend/Gdata/App/Exception.php';
+            throw new Zend_Gdata_App_Exception('XML passed to transferFromXML cannot be null');
+        }
+    }
+
+    /**
+     * Converts this element and all children into XML text using getDOM()
+     *
+     * @return string XML content
+     */
+    public function saveXML()
+    {
+        $element = $this->getDOM();
+        return $element->ownerDocument->saveXML($element);
+    }
+
+    /**
+     * Alias for saveXML() returns XML content for this element and all
+     * children
+     *
+     * @return string XML content
+     */
+    public function getXML()
+    {
+        return $this->saveXML();
+    }
+
+    /**
+     * Alias for saveXML()
+     *
+     * Can be overridden by children to provide more complex representations
+     * of entries.
+     *
+     * @return string Encoded string content
+     */
+    public function encode()
+    {
+        return $this->saveXML();
+    }
+
+    /**
+     * Get the full version of a namespace prefix
+     *
+     * Looks up a prefix (atom:, etc.) in the list of registered
+     * namespaces and returns the full namespace URI if
+     * available. Returns the prefix, unmodified, if it's not
+     * registered.
+     *
+     * @param string $prefix The namespace prefix to lookup.
+     * @param integer $majorVersion The major protocol version in effect.
+     *        Defaults to '1'.
+     * @param integer $minorVersion The minor protocol version in effect.
+     *        Defaults to null (use latest).
+     * @return string
+     */
+    public function lookupNamespace($prefix,
+                                    $majorVersion = 1,
+                                    $minorVersion = null)
+    {
+        // Check for a memoized result
+        $key = $prefix . ' ' .
+               ($majorVersion === null ? 'NULL' : $majorVersion) .
+               ' '. ($minorVersion === null ? 'NULL' : $minorVersion);
+        if (array_key_exists($key, self::$_namespaceLookupCache))
+          return self::$_namespaceLookupCache[$key];
+        // If no match, return the prefix by default
+        $result = $prefix;
+
+        // Find tuple of keys that correspond to the namespace we should use
+        if (isset($this->_namespaces[$prefix])) {
+            // Major version search
+            $nsData = $this->_namespaces[$prefix];
+            $foundMajorV = Zend_Gdata_App_Util::findGreatestBoundedValue(
+                    $majorVersion, $nsData);
+            // Minor version search
+            $nsData = $nsData[$foundMajorV];
+            $foundMinorV = Zend_Gdata_App_Util::findGreatestBoundedValue(
+                    $minorVersion, $nsData);
+            // Extract NS
+            $result = $nsData[$foundMinorV];
+        }
+
+        // Memoize result
+        self::$_namespaceLookupCache[$key] = $result;
+
+        return $result;
+    }
+
+    /**
+     * Add a namespace and prefix to the registered list
+     *
+     * Takes a prefix and a full namespace URI and adds them to the
+     * list of registered namespaces for use by
+     * $this->lookupNamespace().
+     *
+     * WARNING: Currently, registering a namespace will NOT invalidate any
+     *          memoized data stored in $_namespaceLookupCache. Under normal
+     *          use, this behavior is acceptable. If you are adding
+     *          contradictory data to the namespace lookup table, you must
+     *          call flushNamespaceLookupCache().
+     *
+     * @param  string $prefix The namespace prefix
+     * @param  string $namespaceUri The full namespace URI
+     * @param integer $majorVersion The major protocol version in effect.
+     *        Defaults to '1'.
+     * @param integer $minorVersion The minor protocol version in effect.
+     *        Defaults to null (use latest).
+     * @return void
+     */
+    public function registerNamespace($prefix,
+                                      $namespaceUri,
+                                      $majorVersion = 1,
+                                      $minorVersion = 0)
+    {
+        $this->_namespaces[$prefix][$majorVersion][$minorVersion] =
+        $namespaceUri;
+    }
+
+    /**
+     * Flush namespace lookup cache.
+     *
+     * Empties the namespace lookup cache. Call this function if you have
+     * added data to the namespace lookup table that contradicts values that
+     * may have been cached during a previous call to lookupNamespace().
+     */
+    public static function flushNamespaceLookupCache()
+    {
+        self::$_namespaceLookupCache = array();
+    }
+
+    /**
+     * Add an array of namespaces to the registered list.
+     *
+     * Takes an array in the format of:
+     * namespace prefix, namespace URI, major protocol version,
+     * minor protocol version and adds them with calls to ->registerNamespace()
+     *
+     * @param array $namespaceArray An array of namespaces.
+     * @return void
+     */
+    public function registerAllNamespaces($namespaceArray)
+    {
+        foreach($namespaceArray as $namespace) {
+                $this->registerNamespace(
+                    $namespace[0], $namespace[1], $namespace[2], $namespace[3]);
+        }
+    }
+
+
+    /**
+     * Magic getter to allow access like $entry->foo to call $entry->getFoo()
+     * Alternatively, if no getFoo() is defined, but a $_foo protected variable
+     * is defined, this is returned.
+     *
+     * TODO Remove ability to bypass getFoo() methods??
+     *
+     * @param string $name The variable name sought
+     */
+    public function __get($name)
+    {
+        $method = 'get'.ucfirst($name);
+        if (method_exists($this, $method)) {
+            return call_user_func(array(&$this, $method));
+        } else if (property_exists($this, "_${name}")) {
+            return $this->{'_' . $name};
+        } else {
+            require_once 'Zend/Gdata/App/InvalidArgumentException.php';
+            throw new Zend_Gdata_App_InvalidArgumentException(
+                    'Property ' . $name . ' does not exist');
+        }
+    }
+
+    /**
+     * Magic setter to allow acces like $entry->foo='bar' to call
+     * $entry->setFoo('bar') automatically.
+     *
+     * Alternatively, if no setFoo() is defined, but a $_foo protected variable
+     * is defined, this is returned.
+     *
+     * TODO Remove ability to bypass getFoo() methods??
+     *
+     * @param string $name
+     * @param string $value
+     */
+    public function __set($name, $val)
+    {
+        $method = 'set'.ucfirst($name);
+        if (method_exists($this, $method)) {
+            return call_user_func(array(&$this, $method), $val);
+        } else if (isset($this->{'_' . $name}) || ($this->{'_' . $name} === null)) {
+            $this->{'_' . $name} = $val;
+        } else {
+            require_once 'Zend/Gdata/App/InvalidArgumentException.php';
+            throw new Zend_Gdata_App_InvalidArgumentException(
+                    'Property ' . $name . '  does not exist');
+        }
+    }
+
+    /**
+     * Magic __isset method
+     *
+     * @param string $name
+     */
+    public function __isset($name)
+    {
+        $rc = new ReflectionClass(get_class($this));
+        $privName = '_' . $name;
+        if (!($rc->hasProperty($privName))) {
+            require_once 'Zend/Gdata/App/InvalidArgumentException.php';
+            throw new Zend_Gdata_App_InvalidArgumentException(
+                    'Property ' . $name . ' does not exist');
+        } else {
+            if (isset($this->{$privName})) {
+                if (is_array($this->{$privName})) {
+                    if (count($this->{$privName}) > 0) {
+                        return true;
+                    } else {
+                        return false;
+                    }
+                } else {
+                    return true;
+                }
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Magic __unset method
+     *
+     * @param string $name
+     */
+    public function __unset($name)
+    {
+        if (isset($this->{'_' . $name})) {
+            if (is_array($this->{'_' . $name})) {
+                $this->{'_' . $name} = array();
+            } else {
+                $this->{'_' . $name} = null;
+            }
+        }
+    }
+
+    /**
+     * Magic toString method allows using this directly via echo
+     * Works best in PHP >= 4.2.0
+     *
+     * @return string The text representation of this object
+     */
+    public function __toString()
+    {
+        return $this->getText();
+    }
+
+}