mirror of
https://github.com/jezhiggins/arabica
synced 2024-11-17 07:48:50 +01:00
322 lines
10 KiB
C++
322 lines
10 KiB
C++
#ifndef ARABICA_DOM_UTILS_STREAM_H
|
|
#define ARABICA_DOM_UTILS_STREAM_H
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DOM/Utils/Stream.h
|
|
//
|
|
// Written by Jez Higgins <jez@jezuk.co.uk>
|
|
// Copyright 2003-2005 Jez UK Ltd, http://www.jezuk.co.uk
|
|
//
|
|
// Provides streaming operator<< for DOM::Nodes. Fully parameterised so
|
|
// will work with wide and narrow char types, so long as an operator<<
|
|
// exists for the DOM::Node's string_type.
|
|
//
|
|
// e.g.
|
|
// Arabica::SAX2DOM::Parser<std::wstring> domParser;
|
|
// SAX::wInputSource is;
|
|
// ... load instance from somewhere ...
|
|
// domParser.parse(is);
|
|
// DOM::Document<std::wstring> doc = domParser.getDocument();
|
|
// ... manipulate doc ...
|
|
// std::wcout << doc;
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
// $Id$
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
#include <iostream>
|
|
#include <algorithm>
|
|
#include <text/UnicodeCharacters.hpp>
|
|
#include <XML/escaper.hpp>
|
|
|
|
namespace Arabica
|
|
{
|
|
namespace DOM
|
|
{
|
|
|
|
namespace StreamImpl
|
|
{
|
|
template<class stringT, class string_adaptorT, class charT, class traitsT>
|
|
void streamChildren(std::basic_ostream<charT, traitsT>& stream, DOM::Node<stringT, string_adaptorT>& node)
|
|
{
|
|
DOM::Node<stringT> child = node.getFirstChild();
|
|
while(child != 0)
|
|
{
|
|
stream << child;
|
|
child = child.getNextSibling();
|
|
} // while
|
|
} // streamChildren
|
|
|
|
template<class stringT>
|
|
void prefix_mapper_callback(std::ios_base::event ev, std::ios_base& stream, int index)
|
|
{
|
|
if(ev == std::ios_base::erase_event)
|
|
delete static_cast<std::vector<std::map<stringT, stringT> >*>(stream.pword(index));
|
|
} // boing_callback
|
|
|
|
template<class stringT>
|
|
std::pair<bool, stringT> is_uri_declared(std::vector<std::map<stringT, stringT> >* prefix_stack, const stringT& namespaceURI)
|
|
{
|
|
stringT declared_prefix;
|
|
for(typename std::vector<std::map<stringT, stringT> >::reverse_iterator b = prefix_stack->rbegin(), e = prefix_stack->rend();
|
|
b != e;
|
|
++b)
|
|
{
|
|
typename std::map<stringT, stringT>::const_iterator p = b->find(namespaceURI);
|
|
if(p != b->end())
|
|
return std::make_pair(true, p->second);
|
|
} // for ...
|
|
|
|
return std::make_pair(false, stringT());
|
|
} // prefix_is_declared
|
|
|
|
template<class stringT, class string_adaptorT, class charT, class traitsT>
|
|
void check_and_output_node_name(std::basic_ostream<charT, traitsT>& stream,
|
|
DOM::Node<stringT, string_adaptorT>& node,
|
|
std::vector<std::map<stringT, stringT> >* prefix_stack)
|
|
{
|
|
std::map<stringT, stringT>& current = *(prefix_stack->rbegin());
|
|
|
|
stringT namespaceURI = node.getNamespaceURI();
|
|
if(!namespaceURI.empty())
|
|
{
|
|
std::pair<bool, stringT> prefix = is_uri_declared(prefix_stack, namespaceURI);
|
|
|
|
if(!prefix.first)
|
|
current[namespaceURI] = prefix.second = node.getPrefix();
|
|
|
|
if(!prefix.second.empty())
|
|
stream << prefix.second << Arabica::text::Unicode<charT>::COLON;
|
|
stream << node.getLocalName();
|
|
}
|
|
else
|
|
stream << node.getNodeName();
|
|
} // check_and_output_node_name
|
|
|
|
template<class stringT, class charT>
|
|
bool isXmlns(const stringT& str)
|
|
{
|
|
typedef Arabica::text::Unicode<charT> UnicodeT;
|
|
|
|
if(str.size() != 5)
|
|
return false;
|
|
|
|
if((str[0] == UnicodeT::LOWERCASE_X) &&
|
|
(str[1] == UnicodeT::LOWERCASE_M) &&
|
|
(str[2] == UnicodeT::LOWERCASE_L) &&
|
|
(str[3] == UnicodeT::LOWERCASE_N) &&
|
|
(str[4] == UnicodeT::LOWERCASE_S))
|
|
return true;
|
|
return false;
|
|
} // isXmlns
|
|
|
|
template<class stringT, class string_adaptorT, class charT, class traitsT>
|
|
int prefix_mapper(std::basic_ostream<charT, traitsT>& stream,
|
|
DOM::Node<stringT, string_adaptorT>& node)
|
|
{
|
|
typedef Arabica::text::Unicode<charT> UnicodeT;
|
|
|
|
static const int index = std::ios_base::xalloc();
|
|
std::vector<std::map<stringT, stringT> >* prefix_stack;
|
|
|
|
if(stream.pword(index) != 0)
|
|
{
|
|
prefix_stack = static_cast<std::vector<std::map<stringT, stringT> >*>(stream.pword(index));
|
|
prefix_stack->push_back(std::map<stringT, stringT>());
|
|
}
|
|
else
|
|
{
|
|
prefix_stack = new std::vector<std::map<stringT, stringT> >;
|
|
stream.pword(index) = prefix_stack;
|
|
|
|
std::map<stringT, stringT> prefixes;
|
|
for(DOM::Node<stringT> p = node.getParentNode(); p.getNodeType() == DOM::Node_base::ELEMENT_NODE; p = p.getParentNode())
|
|
{
|
|
if(p.getNamespaceURI().empty())
|
|
continue;
|
|
if(prefixes.find(p.getNamespaceURI()) == prefixes.end())
|
|
prefixes[p.getNamespaceURI()] = p.getPrefix();
|
|
} // for ...
|
|
|
|
prefix_stack->push_back(prefixes);
|
|
} // if ...
|
|
|
|
std::map<stringT, stringT>& current = *(prefix_stack->rbegin());
|
|
|
|
// is element namespace URI declared?
|
|
check_and_output_node_name(stream, node, prefix_stack);
|
|
|
|
DOM::NamedNodeMap<stringT> attrs = node.getAttributes();
|
|
std::vector<stringT> names;
|
|
for(unsigned int a = 0; a < attrs.getLength(); ++a)
|
|
names.push_back(attrs.item(a).getNodeName());
|
|
std::sort(names.begin(), names.end());
|
|
|
|
for(typename std::vector<stringT>::const_iterator a = names.begin(), ae = names.end(); a != ae; ++a)
|
|
{
|
|
DOM::Node<stringT> attr = attrs.getNamedItem(*a);
|
|
if(isXmlns<stringT, charT>(attr.getNodeName()) ||
|
|
isXmlns<stringT, charT>(attr.getPrefix()))
|
|
continue;
|
|
stream << UnicodeT::SPACE;
|
|
check_and_output_node_name(stream, attr, prefix_stack);
|
|
stream << UnicodeT::EQUALS_SIGN
|
|
<< UnicodeT::QUOTATION_MARK;
|
|
stringT value = attr.getNodeValue();
|
|
std::for_each(value.begin(), value.end(), Arabica::XML::attribute_escaper<charT, traitsT>(stream));
|
|
stream << UnicodeT::QUOTATION_MARK;
|
|
}
|
|
|
|
for(typename std::map<stringT, stringT>::const_iterator i = current.begin(), e = current.end();
|
|
i != e;
|
|
++i)
|
|
{
|
|
stream << UnicodeT::SPACE
|
|
<< UnicodeT::LOWERCASE_X
|
|
<< UnicodeT::LOWERCASE_M
|
|
<< UnicodeT::LOWERCASE_L
|
|
<< UnicodeT::LOWERCASE_N
|
|
<< UnicodeT::LOWERCASE_S;
|
|
if(!(i->second.empty()))
|
|
stream << UnicodeT::COLON << i->second;
|
|
stream << UnicodeT::EQUALS_SIGN << UnicodeT::QUOTATION_MARK;
|
|
std::for_each(i->first.begin(), i->first.end(), Arabica::XML::attribute_escaper<charT, traitsT>(stream));
|
|
stream << UnicodeT::QUOTATION_MARK;
|
|
} // for ...
|
|
|
|
return index;
|
|
} // prefix_mapper
|
|
|
|
template<class stringT, class string_adaptorT, class charT, class traitsT>
|
|
void prefix_mapper_pop(std::basic_ostream<charT, traitsT>& stream,
|
|
DOM::Node<stringT, string_adaptorT> node,
|
|
int index,
|
|
bool output)
|
|
{
|
|
std::vector<std::map<stringT, stringT> >* prefix_stack =
|
|
static_cast<std::vector<std::map<stringT, stringT> >*>(stream.pword(index));
|
|
|
|
if(output)
|
|
check_and_output_node_name(stream, node, prefix_stack);
|
|
|
|
prefix_stack->pop_back();
|
|
if(prefix_stack->empty())
|
|
{
|
|
delete static_cast<std::vector<std::map<stringT, stringT> >*>(stream.pword(index));
|
|
stream.pword(index) = 0;
|
|
} // if ...
|
|
} // prefix_mapper_pop
|
|
|
|
} // namespace StreamImpl
|
|
|
|
template<class stringT, class string_adaptorT, class charT, class traitsT>
|
|
std::basic_ostream<charT, traitsT>&
|
|
operator<<(std::basic_ostream<charT, traitsT>& stream,
|
|
DOM::Node<stringT, string_adaptorT>& node)
|
|
{
|
|
typedef Arabica::text::Unicode<charT> UnicodeT;
|
|
|
|
switch(node.getNodeType())
|
|
{
|
|
case DOM::Node<stringT>::DOCUMENT_NODE:
|
|
stream << UnicodeT::LESS_THAN_SIGN
|
|
<< UnicodeT::QUESTION_MARK
|
|
<< UnicodeT::LOWERCASE_X
|
|
<< UnicodeT::LOWERCASE_M
|
|
<< UnicodeT::LOWERCASE_L
|
|
<< UnicodeT::SPACE
|
|
<< UnicodeT::LOWERCASE_V
|
|
<< UnicodeT::LOWERCASE_E
|
|
<< UnicodeT::LOWERCASE_R
|
|
<< UnicodeT::LOWERCASE_S
|
|
<< UnicodeT::LOWERCASE_I
|
|
<< UnicodeT::LOWERCASE_O
|
|
<< UnicodeT::LOWERCASE_N
|
|
<< UnicodeT::EQUALS_SIGN
|
|
<< UnicodeT::QUOTATION_MARK
|
|
<< UnicodeT::NUMBER_1
|
|
<< UnicodeT::FULL_STOP
|
|
<< UnicodeT::NUMBER_0
|
|
<< UnicodeT::QUOTATION_MARK
|
|
<< UnicodeT::QUESTION_MARK
|
|
<< UnicodeT::GREATER_THAN_SIGN
|
|
<< std::endl;
|
|
case DOM::Node<stringT>::DOCUMENT_FRAGMENT_NODE:
|
|
StreamImpl::streamChildren(stream, node);
|
|
break;
|
|
case DOM::Node<stringT>::ELEMENT_NODE:
|
|
{
|
|
stream << UnicodeT::LESS_THAN_SIGN;
|
|
int index = StreamImpl::prefix_mapper(stream, node);
|
|
|
|
if(node.hasChildNodes())
|
|
{
|
|
stream << UnicodeT::GREATER_THAN_SIGN;
|
|
StreamImpl::streamChildren(stream, node);
|
|
stream << UnicodeT::LESS_THAN_SIGN << UnicodeT::SLASH;
|
|
StreamImpl::prefix_mapper_pop(stream, node, index, true);
|
|
stream << UnicodeT::GREATER_THAN_SIGN;
|
|
}
|
|
else
|
|
{
|
|
StreamImpl::prefix_mapper_pop(stream, node, index, false);
|
|
stream << UnicodeT::SLASH << UnicodeT::GREATER_THAN_SIGN;
|
|
}
|
|
}
|
|
break;
|
|
case DOM::Node<stringT>::TEXT_NODE:
|
|
{
|
|
stringT value = node.getNodeValue();
|
|
std::for_each(value.begin(), value.end(), Arabica::XML::text_escaper<charT, traitsT>(stream));
|
|
}
|
|
break;
|
|
case DOM::Node<stringT>::ENTITY_REFERENCE_NODE:
|
|
stream << UnicodeT::AMPERSAND
|
|
<< node.getNodeName()
|
|
<< UnicodeT::SEMI_COLON;
|
|
break;
|
|
case DOM::Node<stringT>::CDATA_SECTION_NODE:
|
|
stream << UnicodeT::LESS_THAN_SIGN
|
|
<< UnicodeT::EXCLAMATION_MARK
|
|
<< UnicodeT::LEFT_SQUARE_BRACKET
|
|
<< UnicodeT::CAPITAL_C
|
|
<< UnicodeT::CAPITAL_D
|
|
<< UnicodeT::CAPITAL_A
|
|
<< UnicodeT::CAPITAL_T
|
|
<< UnicodeT::CAPITAL_A
|
|
<< UnicodeT::LEFT_SQUARE_BRACKET
|
|
<< node.getNodeValue()
|
|
<< UnicodeT::RIGHT_SQUARE_BRACKET
|
|
<< UnicodeT::RIGHT_SQUARE_BRACKET
|
|
<< UnicodeT::GREATER_THAN_SIGN;
|
|
break;
|
|
case DOM::Node<stringT>::PROCESSING_INSTRUCTION_NODE:
|
|
stream << UnicodeT::LESS_THAN_SIGN
|
|
<< UnicodeT::QUESTION_MARK
|
|
<< node.getNodeName()
|
|
<< UnicodeT::SPACE
|
|
<< node.getNodeValue()
|
|
<< UnicodeT::QUESTION_MARK
|
|
<< UnicodeT::GREATER_THAN_SIGN;
|
|
break;
|
|
case DOM::Node<stringT>::COMMENT_NODE:
|
|
stream << UnicodeT::LESS_THAN_SIGN
|
|
<< UnicodeT::EXCLAMATION_MARK
|
|
<< UnicodeT::HYPHEN_MINUS
|
|
<< UnicodeT::HYPHEN_MINUS
|
|
<< node.getNodeValue()
|
|
<< UnicodeT::HYPHEN_MINUS
|
|
<< UnicodeT::HYPHEN_MINUS
|
|
<< UnicodeT::GREATER_THAN_SIGN;
|
|
break;
|
|
default: // keep GCC quiet
|
|
break;
|
|
} // switch
|
|
|
|
return stream;
|
|
} // operator<<
|
|
|
|
} // namespace DOM
|
|
} // namespace Arabica
|
|
|
|
#endif
|