#ifndef ARABICA_NAMESPACE_SUPPORT_H #define ARABICA_NAMESPACE_SUPPORT_H /* * $Id$ */ #include #include #include #include namespace SAX { template struct NamespaceConstants { typedef string_type stringT; typedef string_adaptor_type string_adaptorT; const stringT xml; const stringT xmlns; const stringT xml_uri; const stringT xmlns_uri; const stringT xmlns11_uri; const stringT colon; NamespaceConstants() : xml(string_adaptorT::construct_from_utf8("xml")), xmlns(string_adaptorT::construct_from_utf8("xmlns")), xml_uri(string_adaptorT::construct_from_utf8("http://www.w3.org/XML/1998/namespace")), xmlns_uri(), xmlns11_uri(string_adaptorT::construct_from_utf8("http://www.w3.org/2000/xmlns/")), colon(string_adaptorT::construct_from_utf8(":")) { } // NamespaceConstants }; // struct NamespaceContants /** * Encapsulate Namespace logic for use by SAX drivers. * *

This class encapsulates the logic of Namespace processing: * it tracks the declarations currently in force for each context * and automatically processes qualified XML 1.0 names into their * Namespace parts; it can also be used in reverse for generating * XML 1.0 from Namespaces.

* *

Namespace support objects are reusable, but the reset method * must be invoked between each session.

* *

Here is a simple session:

* *
 * NamespaceSupport support;
 *
 * support.pushContext();
 * support.declarePrefix("", "http://www.w3.org/1999/xhtml");
 * support.declarePrefix("dc", "http://www.purl.org/dc#");
 *
 * NamespaceSupport parts = support.processName("p", parts, false);
 * std::cout << "Namespace URI: " << parts.URI << std::endl;
 * std::cout << "Local name: " << parts.localName << std::endl;
 * std::cout << "Raw name: " << parts.rawName << std::endl;

 * parts = support.processName("dc:title", parts, false);
 * std::cout << "Namespace URI: " << parts.URI << std::endl;
 * std::cout << "Local name: " << parts.localName << std::endl;
 * std::cout << "Raw name: " << parts.rawName << std::endl;

 * support.popContext();
 * 
* *

Note that this class is optimized for the use case where most * elements do not contain Namespace declarations: if the same * prefix/URI mapping is repeated for each context (for example), this * class will be somewhat less efficient.

* * @since SAX 2.0 * @author Jez Higgins, * jez@jezuk.co.uk * @version 2.0 */ template class basic_NamespaceSupport { public: typedef std::vector stringListT; struct Parts { stringT URI; stringT localName; stringT rawName; stringT prefix; }; // struct Parts // functions basic_NamespaceSupport() { reset(); } // basic_NamespaceSupport /** * Reset this Namespace support object for reuse. * *

It is necessary to invoke this method before reusing the * Namespace support object for a new session.

*/ void reset() { contexts_.clear(); contexts_.push_back(Context()); contexts_.back().insert(std::make_pair(nsc_.xml, nsc_.xml_uri)); } // reset //////////////////////////////////////////////////////////////////// // Context management. //////////////////////////////////////////////////////////////////// /** * Start a new Namespace context. * *

Normally, you should push a new context at the beginning * of each XML element: the new context will automatically inherit * the declarations of its parent context, but it will also keep * track of which declarations were made within this context.

* *

The Namespace support object always starts with a base context * already in force: in this context, only the "xml" prefix is * declared.

* * @see #popContext */ void pushContext() { contexts_.push_back(Context()); } // pushContext /** * Revert to the previous Namespace context. * *

Normally, you should pop the context at the end of each * XML element. After popping the context, all Namespace prefix * mappings that were previously in force are restored.

* *

You must not attempt to declare additional Namespace * prefixes after popping a context, unless you push another * context first.

* * @see #pushContext */ void popContext() { contexts_.pop_back(); } // popContext //////////////////////////////////////////////////////////////////// // Operations within a context. //////////////////////////////////////////////////////////////////// /** * Declare a Namespace prefix. * *

This method declares a prefix in the current Namespace * context; the prefix will remain in force until this context * is popped, unless it is shadowed in a descendant context.

* *

To declare a default Namespace, use the empty string. The * prefix must not be "xml" or "xmlns".

* *

Note that you must not declare a prefix after * you've pushed and popped another Namespace.

* *

Note that there is an asymmetry in this library: while {@link * #getPrefix getPrefix} will not return the default "" prefix, * even if you have declared one; to check for a default prefix, * you have to look it up explicitly using {@link #getURI getURI}. * This asymmetry exists to make it easier to look up prefixes * for attribute names, where the default prefix is not allowed.

* * @param prefix The prefix to declare, or the empty string. * @param uri The Namespace URI to associate with the prefix. * @return true if the prefix was legal, false otherwise * @see #processName * @see #getURI * @see #getPrefix */ bool declarePrefix(const stringT& prefix, const stringT& uri) { if((prefix == nsc_.xml) || (prefix == nsc_.xmlns)) return false; contexts_.back().insert(std::make_pair(prefix, uri)); return true; } // declarePrefix /** * Process a raw XML 1.0 name. * *

This method processes a raw XML 1.0 name in the current * context by removing the prefix and looking it up among the * prefixes currently declared. * *

If * the raw name has a prefix that has not been declared, then * the return value will be empty.

* *

Note that attribute names are processed differently than * element names: an unprefixed element name will received the * default Namespace (if any), while an unprefixed element name * will not.

* * @param qName The raw XML 1.0 name to be processed. * @param isAttribute A flag indicating whether this is an * attribute name (true) or an element name (false). * @return A Parts holding three strings representing the * Namespace URI (or empty string), the local name, and the raw XML * 1.0 name. * @see #declarePrefix */ Parts processName(const stringT& qName, bool isAttribute) const { Parts name; typename string_adaptorT::size_type index = string_adaptorT::find(qName, nsc_.colon); if(index == string_adaptorT::npos()) { // no prefix name.URI = isAttribute ? stringT() : getURI(stringT()); name.localName = qName; } else { // prefix stringT prefix = string_adaptorT::substr(qName, 0, index); name.URI = getURI(prefix); name.localName = string_adaptorT::substr(qName, index + 1); name.prefix = prefix; } // if ... name.rawName = qName; return name; } // processName /** * Look up a prefix and get the currently-mapped Namespace URI. * *

This method looks up the prefix in the current context. * Use the empty string ("") for the default Namespace.

* * @param prefix The prefix to look up. * @return The associated Namespace URI, or empty string if the prefix * is undeclared in this context. * @see #getPrefix * @see #getPrefixes */ stringT getURI(const stringT& prefix) const { for(typename contextListT::const_reverse_iterator i = contexts_.rbegin(); i != contexts_.rend(); ++i) { typename stringMapT::const_iterator u = i->find(prefix); if(u != i->end()) return u->second; } // for ... return stringT(); } // getURI /** * Return one of the prefixes mapped to a Namespace URI. * *

If more than one prefix is currently mapped to the same * URI, this method will make an arbitrary selection; if you * want all of the prefixes, use the {@link #getPrefixes} * method instead.

* *

Note: this will never return the empty (default) prefix; * to check for a default prefix, use the {@link #getURI getURI} * method with an argument of "".

* * @param uri The Namespace URI. * @return One of the prefixes currently mapped to the URI supplied, * or an empty string if none is mapped or if the URI is assigned to * the default Namespace. * @see #getPrefixes(const stringT&) * @see #getURI */ stringT getPrefix(const stringT& uri) const { for(typename contextListT::const_reverse_iterator i = contexts_.rbegin(); i != contexts_.rend(); ++i) { for(typename stringMapT::const_iterator u = i->begin(); u != i->end(); ++u) if(u->second == uri) return u->first; } // for ... return stringT(); } // getPrefix /** * Returns all prefixes currently declared. * *

Note: if there is a default prefix, it will not be * returned in this enumeration; check for the default prefix * using the {@link #getURI getURI} with an argument of "".

* * @return A list of all prefixes declared in the * current context except for the empty (default) * prefix. * @see #getDeclaredPrefixes * @see #getURI */ stringListT getPrefixes() const { stringListT prefixes; for(typename contextListT::const_reverse_iterator i = contexts_.rbegin(); i != contexts_.rend(); ++i) { for(typename stringMapT::const_iterator u = i->begin(); u != i->end(); ++u) if(!u->first.empty()) prefixes.push_back(u->first); } // for ... return prefixes; } // getPrefixes /** * Returns a list of all prefixes currently declared for a URI. * *

This method returns prefixes mapped to a specific Namespace * URI. The xml: prefix will be included. If you want only one * prefix that's mapped to the Namespace URI, and you don't care * which one you get, use the {@link #getPrefix getPrefix} * method instead.

* *

Note: the empty (default) prefix is never included * in this enumeration; to check for the presence of a default * Namespace, use the {@link #getURI getURI} method with an * argument of "".

* * @param uri The Namespace URI. * @return A list of all prefixes declared in the * current context. * @see #getPrefix * @see #getDeclaredPrefixes * @see #getURI */ stringListT getPrefixes(const stringT& uri) const { stringListT prefixes; for(typename contextListT::const_reverse_iterator i = contexts_.rbegin(); i != contexts_.rend(); ++i) { for(typename stringMapT::const_iterator u = i->begin(); u != i->end(); ++u) if(u->second == uri) prefixes.push_back(u->first); } // for ... return prefixes; } // getPrefixes /** * Return an enumeration of all prefixes declared in this context. * *

The empty (default) prefix will be included in this * enumeration; note that this behaviour differs from that of * {@link #getPrefix} and {@link #getPrefixes}.

* * @return An enumeration of all prefixes declared in this * context. * @see #getPrefixes * @see #getURI */ stringListT getDeclaredPrefixes() const { stringListT prefixes; for(typename stringMapT::const_iterator u = contexts_.back().begin(); u != contexts_.back().end(); ++u) prefixes.push_back(u->first); return prefixes; } // getDeclaredPrefixes private: typedef typename std::multimap stringMapT; typedef stringMapT Context; typedef typename std::vector contextListT; // member variables contextListT contexts_; const NamespaceConstants nsc_; // no impl basic_NamespaceSupport(const basic_NamespaceSupport&); basic_NamespaceSupport& operator=(const basic_NamespaceSupport&); bool operator==(const basic_NamespaceSupport&) const; }; // class basic_NamespaceSupport }; // namespace SAX #endif // NamespaceSupportH