diff -r 000000000000 -r 0961a4a21176 src/share/classes/com/sun/xml/internal/txw2/Document.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/com/sun/xml/internal/txw2/Document.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,337 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.xml.internal.txw2; + +import com.sun.xml.internal.txw2.output.XmlSerializer; + +import java.util.Map; +import java.util.HashMap; + +/** + * Coordinates the entire writing process. + * + * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) + */ +public final class Document { + + private final XmlSerializer out; + + /** + * Set to true once we invoke {@link XmlSerializer#startDocument()}. + * + *
+ * This is so that we can defer the writing as much as possible. + */ + private boolean started=false; + + /** + * Currently active writer. + * + *
+ * This points to the last written token.
+ */
+ private Content current = null;
+
+ private final Map
+ * Applications can add their own {@link DatatypeWriter} so that
+ * application-specific objects can be turned into {@link String}
+ * for output.
+ *
+ * @param dw
+ * The {@link DatatypeWriter} to be added. Must not be null.
+ */
+ public void addDatatypeWriter( DatatypeWriter> dw ) {
+ datatypeWriters.put(dw.getType(),dw);
+ }
+
+ /**
+ * Performs the output as much as possible
+ */
+ void run() {
+ while(true) {
+ Content next = current.getNext();
+ if(next==null || !next.isReadyToCommit())
+ return;
+ next.accept(visitor);
+ next.written();
+ current = next;
+ }
+ }
+
+ /**
+ * Appends the given object to the end of the given buffer.
+ *
+ * @param nsResolver
+ * use
+ */
+ void writeValue( Object obj, NamespaceResolver nsResolver, StringBuilder buf ) {
+ if(obj==null)
+ throw new IllegalArgumentException("argument contains null");
+
+ if(obj instanceof Object[]) {
+ for( Object o : (Object[])obj )
+ writeValue(o,nsResolver,buf);
+ return;
+ }
+ if(obj instanceof Iterable) {
+ for( Object o : (Iterable>)obj )
+ writeValue(o,nsResolver,buf);
+ return;
+ }
+
+ if(buf.length()>0)
+ buf.append(' ');
+
+ Class c = obj.getClass();
+ while(c!=null) {
+ DatatypeWriter dw = datatypeWriters.get(c);
+ if(dw!=null) {
+ dw.print(obj,nsResolver,buf);
+ return;
+ }
+ c = c.getSuperclass();
+ }
+
+ // if nothing applies, just use toString
+ buf.append(obj);
+ }
+
+ // I wanted to hide those write method from users
+ private final ContentVisitor visitor = new ContentVisitor() {
+ public void onStartDocument() {
+ // the startDocument token is used as the sentry, so this method shall never
+ // be called.
+ // out.startDocument() is invoked when we write the start tag of the root element.
+ throw new IllegalStateException();
+ }
+
+ public void onEndDocument() {
+ out.endDocument();
+ }
+
+ public void onEndTag() {
+ out.endTag();
+ inscopeNamespace.popContext();
+ activeNamespaces = null;
+ }
+
+ public void onPcdata(StringBuilder buffer) {
+ if(activeNamespaces!=null)
+ buffer = fixPrefix(buffer);
+ out.text(buffer);
+ }
+
+ public void onCdata(StringBuilder buffer) {
+ if(activeNamespaces!=null)
+ buffer = fixPrefix(buffer);
+ out.cdata(buffer);
+ }
+
+ public void onComment(StringBuilder buffer) {
+ if(activeNamespaces!=null)
+ buffer = fixPrefix(buffer);
+ out.comment(buffer);
+ }
+
+ public void onStartTag(String nsUri, String localName, Attribute attributes, NamespaceDecl namespaces) {
+ assert nsUri!=null;
+ assert localName!=null;
+
+ activeNamespaces = namespaces;
+
+ if(!started) {
+ started = true;
+ out.startDocument();
+ }
+
+ inscopeNamespace.pushContext();
+
+ // declare the explicitly bound namespaces
+ for( NamespaceDecl ns=namespaces; ns!=null; ns=ns.next ) {
+ ns.declared = false; // reset this flag
+
+ if(ns.prefix!=null) {
+ String uri = inscopeNamespace.getURI(ns.prefix);
+ if(uri!=null && uri.equals(ns.uri))
+ ; // already declared
+ else {
+ // declare this new binding
+ inscopeNamespace.declarePrefix(ns.prefix,ns.uri);
+ ns.declared = true;
+ }
+ }
+ }
+
+ // then use in-scope namespace to assign prefixes to others
+ for( NamespaceDecl ns=namespaces; ns!=null; ns=ns.next ) {
+ if(ns.prefix==null) {
+ if(inscopeNamespace.getURI("").equals(ns.uri))
+ ns.prefix="";
+ else {
+ String p = inscopeNamespace.getPrefix(ns.uri);
+ if(p==null) {
+ // assign a new one
+ while(inscopeNamespace.getURI(p=newPrefix())!=null)
+ ;
+ ns.declared = true;
+ inscopeNamespace.declarePrefix(p,ns.uri);
+ }
+ ns.prefix = p;
+ }
+ }
+ }
+
+ // the first namespace decl must be the one for the element
+ assert namespaces.uri.equals(nsUri);
+ assert namespaces.prefix!=null : "a prefix must have been all allocated";
+ out.beginStartTag(nsUri,localName,namespaces.prefix);
+
+ // declare namespaces
+ for( NamespaceDecl ns=namespaces; ns!=null; ns=ns.next ) {
+ if(ns.declared)
+ out.writeXmlns( ns.prefix, ns.uri );
+ }
+
+ // writeBody attributes
+ for( Attribute a=attributes; a!=null; a=a.next) {
+ String prefix;
+ if(a.nsUri.length()==0) prefix="";
+ else prefix=inscopeNamespace.getPrefix(a.nsUri);
+ out.writeAttribute( a.nsUri, a.localName, prefix, fixPrefix(a.value) );
+ }
+
+ out.endStartTag(nsUri,localName,namespaces.prefix);
+ }
+ };
+
+ /**
+ * Used by {@link #newPrefix()}.
+ */
+ private final StringBuilder prefixSeed = new StringBuilder("ns");
+
+ private int prefixIota = 0;
+
+ /**
+ * Allocates a new unique prefix.
+ */
+ private String newPrefix() {
+ prefixSeed.setLength(2);
+ prefixSeed.append(++prefixIota);
+ return prefixSeed.toString();
+ }
+
+ /**
+ * Replaces dummy prefixes in the value to the real ones
+ * by using {@link #activeNamespaces}.
+ *
+ * @return
+ * the buffer passed as the buf parameter.
+ */
+ private StringBuilder fixPrefix(StringBuilder buf) {
+ assert activeNamespaces!=null;
+
+ int i;
+ int len=buf.length();
+ for(i=0;i