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