*/
protected void serializeElement( Element elem )
throws IOException
{
Attr attr;
NamedNodeMap attrMap;
int i;
Node child;
ElementState state;
String name;
String value;
String tagName;
String prefix, localUri;
String uri;
if (fNamespaces) {
// local binder stores namespace declaration
// that has been printed out during namespace fixup of
// the current element
fLocalNSBinder.reset();
// add new namespace context
fNSBinder.pushContext();
}
if (DEBUG) {
System.out.println("==>startElement: " +elem.getNodeName() +" ns="+elem.getNamespaceURI());
}
tagName = elem.getTagName();
state = getElementState();
if (isDocumentState()) {
// If this is the root element handle it differently.
// If the first root element in the document, serialize
// the document's DOCTYPE. Space preserving defaults
// to that of the output format.
if (! _started) {
startDocument( tagName);
}
} else {
// For any other element, if first in parent, then
// close parent's opening tag and use the parent's
// space preserving.
if (state.empty)
_printer.printText( '>' );
// Must leave CData section first
if (state.inCData) {
_printer.printText( "]]>" );
state.inCData = false;
}
// Indent this element on a new line if the first
// content of the parent element or immediately
// following an element.
if (_indenting && ! state.preserveSpace &&
( state.empty || state.afterElement || state.afterComment))
_printer.breakLine();
}
// Do not change the current element state yet.
// This only happens in endElement().
fPreserveSpace = state.preserveSpace;
int length = 0;
attrMap = null;
// retrieve attributes
if (elem.hasAttributes()) {
attrMap = elem.getAttributes();
length = attrMap.getLength();
}
if (!fNamespaces) { // no namespace fixup should be performed
// serialize element name
_printer.printText( '<' );
_printer.printText( tagName );
_printer.indent();
// For each attribute print it's name and value as one part,
// separated with a space so the element can be broken on
// multiple lines.
for ( i = 0 ; i < length ; ++i ) {
attr = (Attr) attrMap.item( i );
name = attr.getName();
value = attr.getValue();
if ( value == null )
value = "";
printAttribute (name, value, attr.getSpecified(), attr);
}
} else { // do namespace fixup
// REVISIT: some optimization could probably be done to avoid traversing
// attributes twice.
//
// ---------------------------------------
// record all valid namespace declarations
// before attempting to fix element's namespace
// ---------------------------------------
for (i = 0;i < length;i++) {
attr = (Attr) attrMap.item( i );
uri = attr.getNamespaceURI();
// check if attribute is a namespace decl
if (uri != null && uri.equals(NamespaceContext.XMLNS_URI)) {
value = attr.getNodeValue();
if (value == null) {
value=XMLSymbols.EMPTY_STRING;
}
if (value.equals(NamespaceContext.XMLNS_URI)) {
if (fDOMErrorHandler != null) {
String msg = DOMMessageFormatter.formatMessage(
DOMMessageFormatter.XML_DOMAIN,"CantBindXMLNS",null );
modifyDOMError(msg, DOMError.SEVERITY_ERROR, null, attr);
boolean continueProcess = fDOMErrorHandler.handleError(fDOMError);
if (!continueProcess) {
// stop the namespace fixup and validation
throw new RuntimeException(
DOMMessageFormatter.formatMessage(
DOMMessageFormatter.SERIALIZER_DOMAIN,
"SerializationStopped", null));
}
}
} else {
prefix = attr.getPrefix();
prefix = (prefix == null ||
prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix);
String localpart = fSymbolTable.addSymbol( attr.getLocalName());
if (prefix == XMLSymbols.PREFIX_XMLNS) { //xmlns:prefix
value = fSymbolTable.addSymbol(value);
// record valid decl
if (value.length() != 0) {
fNSBinder.declarePrefix(localpart, value);
} else {
// REVISIT: issue error on invalid declarations
// xmlns:foo = ""
}
continue;
}
// xmlns --- empty prefix is always bound ("" or some string)
value = fSymbolTable.addSymbol(value);
fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, value);
continue;
} // end-else: valid declaration
} // end-if: namespace declaration
} // end-for
//-----------------------
// get element uri/prefix
//-----------------------
uri = elem.getNamespaceURI();
prefix = elem.getPrefix();
//----------------------
// output element name
//----------------------
// REVISIT: this could be removed if we always convert empty string to null
// for the namespaces.
if ((uri !=null && prefix !=null ) && uri.length() == 0 && prefix.length()!=0) {
// uri is an empty string and element has some prefix
// the namespace alg later will fix up the namespace attributes
// remove element prefix
prefix = null;
_printer.printText( '<' );
_printer.printText( elem.getLocalName() );
_printer.indent();
} else {
_printer.printText( '<' );
_printer.printText( tagName );
_printer.indent();
}
// ---------------------------------------------------------
// Fix up namespaces for element: per DOM L3
// Need to consider the following cases:
//
// case 1: <foo:elem xmlns:ns1="myURI" xmlns="default"/>
// Assume "foo", "ns1" are declared on the parent. We should not miss
// redeclaration for both "ns1" and default namespace. To solve this
// we add a local binder that stores declaration only for current element.
// This way we avoid outputing duplicate declarations for the same element
// as well as we are not omitting redeclarations.
//
// case 2: <elem xmlns="" xmlns="default"/>
// We need to bind default namespace to empty string, to be able to
// omit duplicate declarations for the same element
//
// case 3: <xsl:stylesheet xmlns:xsl="http://xsl">
// We create another element body bound to the "http://xsl" namespace
// as well as namespace attribute rebounding xsl to another namespace.
// <xsl:body xmlns:xsl="http://another">
// Need to make sure that the new namespace decl value is changed to
// "http://xsl"
//
// ---------------------------------------------------------
// check if prefix/namespace is correct for current element
// ---------------------------------------------------------
if (uri != null) { // Element has a namespace
uri = fSymbolTable.addSymbol(uri);
prefix = (prefix == null ||
prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix);
if (fNSBinder.getURI(prefix) == uri) {
// The xmlns:prefix=namespace or xmlns="default" was declared at parent.
// The binder always stores mapping of empty prefix to "".
// (NOTE: local binder does not store this kind of binding!)
// Thus the case where element was declared with uri="" (with or without a prefix)
// will be covered here.
} else {
// the prefix is either undeclared
// or
// conflict: the prefix is bound to another URI
if (fNamespacePrefixes) {
printNamespaceAttr(prefix, uri);
}
fLocalNSBinder.declarePrefix(prefix, uri);
fNSBinder.declarePrefix(prefix, uri);
}
} else { // Element has no namespace
if (elem.getLocalName() == null) {
// DOM Level 1 node!
if (fDOMErrorHandler != null) {
String msg = DOMMessageFormatter.formatMessage(
DOMMessageFormatter.DOM_DOMAIN, "NullLocalElementName",
new Object[]{elem.getNodeName()});
modifyDOMError(msg,DOMError.SEVERITY_ERROR, null, elem);
boolean continueProcess = fDOMErrorHandler.handleError(fDOMError);
// REVISIT: should we terminate upon request?
if (!continueProcess) {
throw new RuntimeException(
DOMMessageFormatter.formatMessage(
DOMMessageFormatter.SERIALIZER_DOMAIN,
"SerializationStopped", null));
}
}
} else { // uri=null and no colon (DOM L2 node)
uri = fNSBinder.getURI(XMLSymbols.EMPTY_STRING);
if (uri !=null && uri.length() > 0) {
// there is a default namespace decl that is bound to
// non-zero length uri, output xmlns=""
if (fNamespacePrefixes) {
printNamespaceAttr(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
}
fLocalNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
}
}
}
// -----------------------------------------
// Fix up namespaces for attributes: per DOM L3
// check if prefix/namespace is correct the attributes
// -----------------------------------------
for (i = 0; i < length; i++) {
attr = (Attr) attrMap.item( i );
value = attr.getValue();
name = attr.getNodeName();
uri = attr.getNamespaceURI();