/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is XSL:P XSLT processor.
*
* The Initial Developer of the Original Code is Keith Visco.
* Portions created by Keith Visco (C) 1999 Keith Visco.
* All Rights Reserved.
*
* Contributor(s):
* Keith Visco, kvisco@ziplink.net
* -- original author
* Majkel Kretschmar
* -- UTF-8 changes
* Bob Miller, kbob@oblix.com
* -- plugged core leak.
*
* $Id: XMLPrinter.cpp,v 1.3 1999-11-25 03:03:05 kvisco%ziplink.net Exp $
*/
#include "printers.h"
//--------------------------------/
//- Implementation of XMLPrinter -/
//--------------------------------/
/**
* A class for printing XML nodes.
* This class was ported from XSL:P Java source
* @author Keith Visco
* @version $Revision: 1.3 $ $Date: 1999-11-25 03:03:05 $
**/
/**
* The default indent size
**/
const int XMLPrinter::DEFAULT_INDENT = 2;
const String XMLPrinter::AMP_ENTITY = "&";
const String XMLPrinter::GT_ENTITY = ">";
const String XMLPrinter::LT_ENTITY = "<";
const String XMLPrinter::HEX_ENTITY = "";
const String XMLPrinter::CDATA_END = "]]>";
const String XMLPrinter::CDATA_START = "";
const String XMLPrinter::DOCTYPE_START = "";
const String XMLPrinter::DOUBLE_QUOTE = "\"";
const String XMLPrinter::EQUALS = "=";
const String XMLPrinter::FORWARD_SLASH = "/";
const String XMLPrinter::L_ANGLE_BRACKET = "<";
const String XMLPrinter::PI_START = "";
const String XMLPrinter::PI_END = "?>";
const String XMLPrinter::PUBLIC = "PUBLIC";
const String XMLPrinter::R_ANGLE_BRACKET = ">";
const String XMLPrinter::SEMICOLON = ";";
const String XMLPrinter::SPACE = " ";
const String XMLPrinter::SYSTEM = "SYSTEM";
const String XMLPrinter::XML_DECL = "xml version=";
// chars
const char XMLPrinter::AMPERSAND = '&';
const char XMLPrinter::GT = '>';
const char XMLPrinter::LT = '<';
const char XMLPrinter::DASH = '-';
const char XMLPrinter::CR = '\r';
const char XMLPrinter::LF = '\n';
//---------------/
//- Contructors -/
//---------------/
/**
* Default Constructor. Creates a new XMLPrinter using cout as the ostream.
**/
XMLPrinter::XMLPrinter() {
initialize(cout, DEFAULT_INDENT);
} //-- XMLPrinter
/**
* Creates a new XML Printer using the given ostream for output
* @param os the out stream to use for output
**/
XMLPrinter::XMLPrinter(ostream& os) {
initialize(os, DEFAULT_INDENT);
} //-- XMLPrinter
/**
* Creates a new XML Printer using the given ostream
* for output, and nodes are indenting using the specified
* indent size
* @param os the out stream to use for output
* @param indent the number of spaces to indent
**/
XMLPrinter::XMLPrinter (ostream& os, int indent) {
initialize(os, indent);
} //-- XMLPrinter
void XMLPrinter::initialize(ostream& os, int indentSize) {
ostreamPtr = &os;
indentChar = ' ';
version = "1.0";
entityTokens = "&<>";
setIndentSize(indentSize);
unescapeCDATA = MB_FALSE;
useEmptyElementShorthand = MB_TRUE;
useFormat = MB_FALSE;
} //-- initialize
// destructor is needed so that subclasses are destroyed.
XMLPrinter::~XMLPrinter()
{ }
/**
* Prints the given Node
* @param node the Node to print
**/
void XMLPrinter::print(Node* node) {
String currentIndent;
print(node,currentIndent);
*ostreamPtr<indentSize = indentSize;
indent.clear();
for (int i = 0; i < indentSize; i++) {
indent.append(indentChar);
}
} //-- setIndentSize
/**
* Sets whether or not to "unwrap" CDATA Sections
* when printing. By Default CDATA Sections are left as is.
* @param unescape the boolean indicating whether or not
* to unescape CDATA Sections
**/
void XMLPrinter::setUnescapeCDATA(MBool unescape) {
unescapeCDATA = unescape;
} //-- setUnescapeCDATA
void XMLPrinter::setUseEmptyElementShorthand(MBool useShorthand) {
useEmptyElementShorthand = useShorthand;
} //-- setUseEmptyElementShorthand
/**
* Sets whether or not this XMLPrinter should add whitespace
* to pretty print the XML tree
* @param useFormat a boolean to indicate whether to allow the
* XMLPrinter to add whitespace to the XML tree. (false by default)
**/
void XMLPrinter::setUseFormat(MBool useFormat) {
this->useFormat = useFormat;
} //-- setUseFormat
//---------------------/
//- Protected Methods -/
//---------------------/
/**
* prints the given node to this XMLPrinter's Writer. If the
* useFormat flag has been set, the node will be printed with
* indentation equal to currentIndent + indentSize
* @param node the Node to print
* @param currentIndent the current indent String
* @return true, if and only if a new line was printed at
* the end of printing the given node
**/
MBool XMLPrinter::print(Node* node, String& currentIndent) {
ostream& out = *this->ostreamPtr;
//-- if (node == null) return false;
NodeList* nl;
switch(node->getNodeType()) {
//-- print Document Node
case Node::DOCUMENT_NODE:
{
Document* doc = (Document*)node;
out << PI_START << XML_DECL << DOUBLE_QUOTE;
out << version;
out << DOUBLE_QUOTE << PI_END << endl;
//-- printDoctype(doc.getDoctype());
nl = doc->getChildNodes();
for (int i = 0; i < nl->getLength(); i++) {
print(nl->item(i),currentIndent);
}
break;
}
//-- print Attribute Node
case Node::ATTRIBUTE_NODE:
{
Attr* attr = (Attr*)node;
//out << attr->getName();
out << attr->getNodeName();
const DOMString& data = attr->getNodeValue();
if (&data != &NULL_STRING) {
out << EQUALS << DOUBLE_QUOTE;
out << data;
out << DOUBLE_QUOTE;
}
break;
}
//-- print Element
case Node::ELEMENT_NODE:
{
Element* element = (Element*)node;
out << L_ANGLE_BRACKET;
out << element->getNodeName();
NamedNodeMap* attList = element->getAttributes();
if (attList) {
//-- print attribute nodes
Attr* att;
for (int i = 0; i < attList->getLength(); i++) {
att = (Attr*)attList->item(i);
//const DOMString& data = att->getValue();
const DOMString& data = att->getNodeValue(); //-- remove
//out << SPACE << * (att->getName());
out << SPACE << att->getNodeName();
if (&data != &NULL_STRING) {
out << EQUALS << DOUBLE_QUOTE;
out << data;
out << DOUBLE_QUOTE;
}
}
}
NodeList* childList = element->getChildNodes();
int size = childList->getLength();
if ((size == 0) && (useEmptyElementShorthand))
{
out << FORWARD_SLASH << R_ANGLE_BRACKET;
if (useFormat) {
out << endl;
return MB_TRUE;
}
}
else {
// Either children, or no shorthand
MBool newLine = MB_FALSE;
out << R_ANGLE_BRACKET;
if ((useFormat) && (size > 0)) {
// Fix formatting of PCDATA elements by Peter Marks and
// David King Lassman
// -- add if statement to check for text node before
// adding line break
if (childList->item(0)->getNodeType() != Node::TEXT_NODE) {
out << endl;
newLine = MB_TRUE;
}
}
Node* child = 0;
String newIndent(indent);
newIndent.append(currentIndent);
for (int i = 0; i < size; i++) {
child = childList->item(i);
if ((useFormat) && newLine)
{
out << newIndent;
}
newLine = print(child,newIndent);
}
if (useFormat) {
// Fix formatting of PCDATA elements by Peter Marks and
// David King Lassman
// -- add if statement to check for text node before
// adding line break
if (child) {
if (child->getNodeType() != Node::TEXT_NODE) {
out << currentIndent;
}
}
}
out << L_ANGLE_BRACKET << FORWARD_SLASH;
out << element->getNodeName();
out << R_ANGLE_BRACKET;
if (useFormat) {
Node* sibling = node->getNextSibling();
if ((!sibling) ||
(sibling->getNodeType() != Node::TEXT_NODE))
{
out<getData();
printWithXMLEntities(data);
break;
}
case Node::CDATA_SECTION_NODE:
if (unescapeCDATA)
printWithXMLEntities( ((Text*)node)->getData() );
else {
const DOMString& data = ((Text*)node)->getData();
out << CDATA_START;
printUTF8Chars(data);
out << CDATA_END;
}
break;
case Node::COMMENT_NODE:
out << COMMENT_START;
printComment(((CharacterData*)node)->getData());
out << COMMENT_END;
if (useFormat) {
out <getNodeName() << SEMICOLON;
break;
case Node::PROCESSING_INSTRUCTION_NODE:
{
ProcessingInstruction* pi = (ProcessingInstruction*)node;
out << PI_START;
out << pi->getTarget();
out << SPACE;
out << pi->getData();
out << PI_END;
if (useFormat) {
out <ostreamPtr;
if (ch >= 128) {
out << HEX_ENTITY;
if ( ch >= 256 ) out << (ch % 256);
else out << ch;
out << SEMICOLON;
}
else out << (char)ch;
} //-- printUTF8Char
/**
* Print the proper UTF8 characters
* based on code submitted by Majkel Kretschmar
**/
void XMLPrinter::printUTF8Chars(const DOMString& data) {
int i = 0;
while(i < data.length()) printUTF8Char(data.charAt(i++));
} //-- printUTF8Chars
//-------------------/
//- Private Methods -/
//-------------------/
void XMLPrinter::printWithXMLEntities(const DOMString& data) {
DOM_CHAR currChar;
if (&data == &NULL_STRING) return;
for (int i = 0; i < data.length(); i++) {
currChar = data.charAt(i);
switch (currChar) {
case AMPERSAND:
*ostreamPtr << AMP_ENTITY;
break;
case LT:
*ostreamPtr << LT_ENTITY;
break;
case GT:
*ostreamPtr << GT_ENTITY;
break;
default:
printUTF8Char(currChar);
break;
}
}
*ostreamPtr << flush;
} // -- printWithXMLEntities
/**
* Replaces any occurances of -- inside comment data with - -
* @param data the comment data (does not include start and end tags)
**/
void XMLPrinter::printComment(const DOMString& data) {
DOM_CHAR prevChar;
DOM_CHAR currChar;
if (&data == &NULL_STRING) return;
prevChar = '\0';
for (int i = 0; i < data.length(); i++) {
currChar = data.charAt(i);
if ((currChar == DASH) && (prevChar == DASH))
*ostreamPtr << SPACE << DASH;
else
printUTF8Char(currChar);
prevChar = currChar;
}
} //-- formatComment