arabica/include/SAX/helpers/NamespaceSupport.hpp

408 lines
13 KiB
C++
Raw Normal View History

2003-09-11 12:26:53 +02:00
#ifndef ARABICA_NAMESPACE_SUPPORT_H
#define ARABICA_NAMESPACE_SUPPORT_H
2002-06-21 13:16:28 +02:00
/*
* $Id$
*/
2007-09-05 00:55:47 +02:00
#include <SAX/ArabicaConfig.hpp>
2002-06-21 13:16:28 +02:00
#include <string>
#include <map>
#include <vector>
2007-09-05 11:49:18 +02:00
namespace Arabica
{
2002-06-21 13:16:28 +02:00
namespace SAX
{
template<class string_type, class string_adaptor>
2002-06-21 13:16:28 +02:00
struct NamespaceConstants
{
const string_type xml;
const string_type xmlns;
const string_type xml_uri;
const string_type xmlns_uri;
const string_type xmlns11_uri;
const string_type colon;
2002-06-21 13:16:28 +02:00
NamespaceConstants() :
xml(string_adaptor::construct_from_utf8("xml")),
xmlns(string_adaptor::construct_from_utf8("xmlns")),
xml_uri(string_adaptor::construct_from_utf8("http://www.w3.org/XML/1998/namespace")),
2003-12-07 16:27:43 +01:00
xmlns_uri(),
xmlns11_uri(string_adaptor::construct_from_utf8("http://www.w3.org/2000/xmlns/")),
colon(string_adaptor::construct_from_utf8(":"))
2002-06-21 13:16:28 +02:00
{
} // NamespaceConstants
}; // struct NamespaceContants
/**
* Encapsulate Namespace logic for use by SAX drivers.
*
* <p>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.</p>
*
* <p>Namespace support objects are reusable, but the reset method
* must be invoked between each session.</p>
*
* <p>Here is a simple session:</p>
*
* <pre>
* 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();
* </pre>
*
* <p>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.</p>
*
* @since SAX 2.0
* @author Jez Higgins,
* <a href="mailto:jez@jezuk.co.uk">jez@jezuk.co.uk</a>
* @version 2.0
*/
template<class string_type, class string_adaptor>
class NamespaceSupport
2002-06-21 13:16:28 +02:00
{
public:
typedef std::vector<string_type> stringListT;
2002-06-21 13:16:28 +02:00
struct Parts
{
string_type URI;
string_type localName;
string_type rawName;
string_type prefix;
2002-06-21 13:16:28 +02:00
}; // struct Parts
// functions
NamespaceSupport()
2002-06-21 13:16:28 +02:00
{
reset();
} // NamespaceSupport
2002-06-21 13:16:28 +02:00
/**
* Reset this Namespace support object for reuse.
*
* <p>It is necessary to invoke this method before reusing the
* Namespace support object for a new session.</p>
*/
void reset()
{
contexts_.clear();
2002-09-20 12:13:00 +02:00
contexts_.push_back(Context());
contexts_.back().insert(std::make_pair(nsc_.xml, nsc_.xml_uri));
2002-06-21 13:16:28 +02:00
} // reset
////////////////////////////////////////////////////////////////////
// Context management.
////////////////////////////////////////////////////////////////////
/**
* Start a new Namespace context.
*
* <p>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.</p>
*
* <p>The Namespace support object always starts with a base context
* already in force: in this context, only the "xml" prefix is
* declared.</p>
*
* @see #popContext
*/
void pushContext()
{
contexts_.push_back(Context());
} // pushContext
/**
* Revert to the previous Namespace context.
*
* <p>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.</p>
*
* <p>You must not attempt to declare additional Namespace
* prefixes after popping a context, unless you push another
* context first.</p>
*
* @see #pushContext
*/
void popContext()
{
contexts_.pop_back();
} // popContext
////////////////////////////////////////////////////////////////////
// Operations within a context.
////////////////////////////////////////////////////////////////////
/**
* Declare a Namespace prefix.
*
* <p>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.</p>
*
* <p>To declare a default Namespace, use the empty string. The
* prefix must not be "xml" or "xmlns".</p>
*
* <p>Note that you must <em>not</em> declare a prefix after
* you've pushed and popped another Namespace.</p>
*
* <p>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.</p>
*
* @param prefix The prefix to declare, or the empty string.
2002-06-21 13:16:28 +02:00
* @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 string_type& prefix, const string_type& uri)
2002-06-21 13:16:28 +02:00
{
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.
*
* <p>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.
*
* <p>If
* the raw name has a prefix that has not been declared, then
* the return value will be empty.</p>
*
* <p>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.</p>
*
* @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 <code>Parts</code> holding three strings representing the
* Namespace URI (or empty string), the local name, and the raw XML
* 1.0 name.
2002-06-21 13:16:28 +02:00
* @see #declarePrefix
*/
Parts processName(const string_type& qName, bool isAttribute) const
2002-06-21 13:16:28 +02:00
{
Parts name;
typename string_adaptor::size_type index = string_adaptor::find(qName, nsc_.colon);
2002-06-21 13:16:28 +02:00
if(index == string_adaptor::npos())
2002-06-21 13:16:28 +02:00
{
// no prefix
name.URI = isAttribute ? string_type() : getURI(string_type());
2002-06-21 13:16:28 +02:00
name.localName = qName;
}
else
{
// prefix
string_type prefix = string_adaptor::substr(qName, 0, index);
2002-06-21 13:16:28 +02:00
name.URI = getURI(prefix);
name.localName = string_adaptor::substr(qName, index + 1);
2002-06-21 13:16:28 +02:00
name.prefix = prefix;
2007-11-22 23:36:47 +01:00
if((string_adaptor::length(name.prefix) == 0) ||
(string_adaptor::length(name.localName) == 0) ||
(string_adaptor::find(name.localName, nsc_.colon) != string_adaptor::npos()))
throw SAX::SAXException("Bad qname");
2002-06-21 13:16:28 +02:00
} // if ...
name.rawName = qName;
return name;
} // processName
/**
* Look up a prefix and get the currently-mapped Namespace URI.
*
* <p>This method looks up the prefix in the current context.
* Use the empty string ("") for the default Namespace.</p>
*
* @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
*/
string_type getURI(const string_type& prefix) const
2002-06-21 13:16:28 +02:00
{
2002-09-20 12:06:31 +02:00
for(typename contextListT::const_reverse_iterator i = contexts_.rbegin(); i != contexts_.rend(); ++i)
2002-06-21 13:16:28 +02:00
{
2002-09-20 12:06:31 +02:00
typename stringMapT::const_iterator u = i->find(prefix);
2002-06-21 13:16:28 +02:00
if(u != i->end())
return u->second;
} // for ...
return string_type();
2002-06-21 13:16:28 +02:00
} // getURI
/**
* Return one of the prefixes mapped to a Namespace URI.
*
* <p>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.</p>
*
* <p><strong>Note:</strong> this will never return the empty (default) prefix;
* to check for a default prefix, use the {@link #getURI getURI}
* method with an argument of "".</p>
*
* @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
2002-06-21 13:16:28 +02:00
* the default Namespace.
* @see #getPrefixes(const string_type&)
2002-06-21 13:16:28 +02:00
* @see #getURI
*/
string_type getPrefix(const string_type& uri) const
2002-06-21 13:16:28 +02:00
{
2002-09-20 12:06:31 +02:00
for(typename contextListT::const_reverse_iterator i = contexts_.rbegin(); i != contexts_.rend(); ++i)
2002-06-21 13:16:28 +02:00
{
2002-09-20 12:06:31 +02:00
for(typename stringMapT::const_iterator u = i->begin(); u != i->end(); ++u)
2002-06-21 13:16:28 +02:00
if(u->second == uri)
return u->first;
} // for ...
return string_type();
2002-06-21 13:16:28 +02:00
} // getPrefix
/**
* Returns all prefixes currently declared.
*
* <p><strong>Note:</strong> 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 "".</p>
*
* @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;
2002-09-20 12:06:31 +02:00
for(typename contextListT::const_reverse_iterator i = contexts_.rbegin(); i != contexts_.rend(); ++i)
2002-06-21 13:16:28 +02:00
{
2002-09-20 12:06:31 +02:00
for(typename stringMapT::const_iterator u = i->begin(); u != i->end(); ++u)
2002-06-21 13:16:28 +02:00
if(!u->first.empty())
prefixes.push_back(u->first);
} // for ...
return prefixes;
} // getPrefixes
/**
* Returns a list of all prefixes currently declared for a URI.
*
* <p>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.</p>
*
* <p><strong>Note:</strong> the empty (default) prefix is <em>never</em> included
* in this enumeration; to check for the presence of a default
* Namespace, use the {@link #getURI getURI} method with an
* argument of "".</p>
*
* @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 string_type& uri) const
2002-06-21 13:16:28 +02:00
{
stringListT prefixes;
2002-09-20 12:06:31 +02:00
for(typename contextListT::const_reverse_iterator i = contexts_.rbegin(); i != contexts_.rend(); ++i)
2002-06-21 13:16:28 +02:00
{
2002-09-20 12:06:31 +02:00
for(typename stringMapT::const_iterator u = i->begin(); u != i->end(); ++u)
2002-06-21 13:16:28 +02:00
if(u->second == uri)
prefixes.push_back(u->first);
} // for ...
return prefixes;
} // getPrefixes
/**
* Return an enumeration of all prefixes declared in this context.
*
* <p>The empty (default) prefix will be included in this
* enumeration; note that this behaviour differs from that of
* {@link #getPrefix} and {@link #getPrefixes}.</p>
*
* @return An enumeration of all prefixes declared in this
* context.
* @see #getPrefixes
* @see #getURI
*/
stringListT getDeclaredPrefixes() const
{
stringListT prefixes;
2007-07-19 19:01:08 +02:00
for(typename stringMapT::const_iterator u = contexts_.back().begin(); u != contexts_.back().end(); ++u)
prefixes.push_back(u->first);
2002-06-21 13:16:28 +02:00
return prefixes;
} // getDeclaredPrefixes
private:
typedef typename std::multimap<string_type, string_type> stringMapT;
2002-06-21 13:16:28 +02:00
typedef stringMapT Context;
typedef typename std::vector<Context> contextListT;
// member variables
contextListT contexts_;
const NamespaceConstants<string_type, string_adaptor> nsc_;
2002-06-21 13:16:28 +02:00
// no impl
NamespaceSupport(const NamespaceSupport&);
NamespaceSupport& operator=(const NamespaceSupport&);
bool operator==(const NamespaceSupport&) const;
}; // class NamespaceSupport
2002-06-21 13:16:28 +02:00
2007-09-05 11:49:18 +02:00
} // namespace SAX
} // namespace Arabica
2002-06-21 13:16:28 +02:00
#endif // NamespaceSupportH