src/share/classes/com/sun/xml/internal/txw2/Document.java

Mon, 04 May 2009 21:10:41 -0700

author
tbell
date
Mon, 04 May 2009 21:10:41 -0700
changeset 50
42dfec6871f6
parent 45
31822b475baa
permissions
-rw-r--r--

6658158: Mutable statics in SAAJ (findbugs)
6658163: txw2.DatatypeWriter.BUILDIN is a mutable static (findbugs)
Reviewed-by: darcy

     1 /*
     2  * Copyright 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    26 package com.sun.xml.internal.txw2;
    28 import com.sun.xml.internal.txw2.output.XmlSerializer;
    30 import java.util.Map;
    31 import java.util.HashMap;
    33 /**
    34  * Coordinates the entire writing process.
    35  *
    36  * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
    37  */
    38 public final class Document {
    40     private final XmlSerializer out;
    42     /**
    43      * Set to true once we invoke {@link XmlSerializer#startDocument()}.
    44      *
    45      * <p>
    46      * This is so that we can defer the writing as much as possible.
    47      */
    48     private boolean started=false;
    50     /**
    51      * Currently active writer.
    52      *
    53      * <p>
    54      * This points to the last written token.
    55      */
    56     private Content current = null;
    58     private final Map<Class,DatatypeWriter> datatypeWriters = new HashMap<Class,DatatypeWriter>();
    60     /**
    61      * Used to generate unique namespace prefix.
    62      */
    63     private int iota = 1;
    65     /**
    66      * Used to keep track of in-scope namespace bindings declared in ancestors.
    67      */
    68     private final NamespaceSupport inscopeNamespace = new NamespaceSupport();
    70     /**
    71      * Remembers the namespace declarations of the last unclosed start tag,
    72      * so that we can fix up dummy prefixes in {@link Pcdata}.
    73      */
    74     private NamespaceDecl activeNamespaces;
    77     Document(XmlSerializer out) {
    78         this.out = out;
    79         for( DatatypeWriter dw : DatatypeWriter.BUILTIN )
    80             datatypeWriters.put(dw.getType(),dw);
    81     }
    83     void flush() {
    84         out.flush();
    85     }
    87     void setFirstContent(Content c) {
    88         assert current==null;
    89         current = new StartDocument();
    90         current.setNext(this,c);
    91     }
    93     /**
    94      * Defines additional user object -> string conversion logic.
    95      *
    96      * <p>
    97      * Applications can add their own {@link DatatypeWriter} so that
    98      * application-specific objects can be turned into {@link String}
    99      * for output.
   100      *
   101      * @param dw
   102      *      The {@link DatatypeWriter} to be added. Must not be null.
   103      */
   104     public void addDatatypeWriter( DatatypeWriter<?> dw ) {
   105         datatypeWriters.put(dw.getType(),dw);
   106     }
   108     /**
   109      * Performs the output as much as possible
   110      */
   111     void run() {
   112         while(true) {
   113             Content next = current.getNext();
   114             if(next==null || !next.isReadyToCommit())
   115                 return;
   116             next.accept(visitor);
   117             next.written();
   118             current = next;
   119         }
   120     }
   122     /**
   123      * Appends the given object to the end of the given buffer.
   124      *
   125      * @param nsResolver
   126      *      use
   127      */
   128     void writeValue( Object obj, NamespaceResolver nsResolver, StringBuilder buf ) {
   129         if(obj==null)
   130             throw new IllegalArgumentException("argument contains null");
   132         if(obj instanceof Object[]) {
   133             for( Object o : (Object[])obj )
   134                 writeValue(o,nsResolver,buf);
   135             return;
   136         }
   137         if(obj instanceof Iterable) {
   138             for( Object o : (Iterable<?>)obj )
   139                 writeValue(o,nsResolver,buf);
   140             return;
   141         }
   143         if(buf.length()>0)
   144             buf.append(' ');
   146         Class c = obj.getClass();
   147         while(c!=null) {
   148             DatatypeWriter dw = datatypeWriters.get(c);
   149             if(dw!=null) {
   150                 dw.print(obj,nsResolver,buf);
   151                 return;
   152             }
   153             c = c.getSuperclass();
   154         }
   156         // if nothing applies, just use toString
   157         buf.append(obj);
   158     }
   160     // I wanted to hide those write method from users
   161     private final ContentVisitor visitor = new ContentVisitor() {
   162         public void onStartDocument() {
   163             // the startDocument token is used as the sentry, so this method shall never
   164             // be called.
   165             // out.startDocument() is invoked when we write the start tag of the root element.
   166             throw new IllegalStateException();
   167         }
   169         public void onEndDocument() {
   170             out.endDocument();
   171         }
   173         public void onEndTag() {
   174             out.endTag();
   175             inscopeNamespace.popContext();
   176             activeNamespaces = null;
   177         }
   179         public void onPcdata(StringBuilder buffer) {
   180             if(activeNamespaces!=null)
   181                 buffer = fixPrefix(buffer);
   182             out.text(buffer);
   183         }
   185         public void onCdata(StringBuilder buffer) {
   186             if(activeNamespaces!=null)
   187                 buffer = fixPrefix(buffer);
   188             out.cdata(buffer);
   189         }
   191         public void onComment(StringBuilder buffer) {
   192             if(activeNamespaces!=null)
   193                 buffer = fixPrefix(buffer);
   194             out.comment(buffer);
   195         }
   197         public void onStartTag(String nsUri, String localName, Attribute attributes, NamespaceDecl namespaces) {
   198             assert nsUri!=null;
   199             assert localName!=null;
   201             activeNamespaces = namespaces;
   203             if(!started) {
   204                 started = true;
   205                 out.startDocument();
   206             }
   208             inscopeNamespace.pushContext();
   210             // declare the explicitly bound namespaces
   211             for( NamespaceDecl ns=namespaces; ns!=null; ns=ns.next ) {
   212                 ns.declared = false;    // reset this flag
   214                 if(ns.prefix!=null) {
   215                     String uri = inscopeNamespace.getURI(ns.prefix);
   216                     if(uri!=null && uri.equals(ns.uri))
   217                         ; // already declared
   218                     else {
   219                         // declare this new binding
   220                         inscopeNamespace.declarePrefix(ns.prefix,ns.uri);
   221                         ns.declared = true;
   222                     }
   223                 }
   224             }
   226             // then use in-scope namespace to assign prefixes to others
   227             for( NamespaceDecl ns=namespaces; ns!=null; ns=ns.next ) {
   228                 if(ns.prefix==null) {
   229                     if(inscopeNamespace.getURI("").equals(ns.uri))
   230                         ns.prefix="";
   231                     else {
   232                         String p = inscopeNamespace.getPrefix(ns.uri);
   233                         if(p==null) {
   234                             // assign a new one
   235                             while(inscopeNamespace.getURI(p=newPrefix())!=null)
   236                                 ;
   237                             ns.declared = true;
   238                             inscopeNamespace.declarePrefix(p,ns.uri);
   239                         }
   240                         ns.prefix = p;
   241                     }
   242                 }
   243             }
   245             // the first namespace decl must be the one for the element
   246             assert namespaces.uri.equals(nsUri);
   247             assert namespaces.prefix!=null : "a prefix must have been all allocated";
   248             out.beginStartTag(nsUri,localName,namespaces.prefix);
   250             // declare namespaces
   251             for( NamespaceDecl ns=namespaces; ns!=null; ns=ns.next ) {
   252                 if(ns.declared)
   253                     out.writeXmlns( ns.prefix, ns.uri );
   254             }
   256             // writeBody attributes
   257             for( Attribute a=attributes; a!=null; a=a.next) {
   258                 String prefix;
   259                 if(a.nsUri.length()==0) prefix="";
   260                 else                    prefix=inscopeNamespace.getPrefix(a.nsUri);
   261                 out.writeAttribute( a.nsUri, a.localName, prefix, fixPrefix(a.value) );
   262             }
   264             out.endStartTag(nsUri,localName,namespaces.prefix);
   265         }
   266     };
   268     /**
   269      * Used by {@link #newPrefix()}.
   270      */
   271     private final StringBuilder prefixSeed = new StringBuilder("ns");
   273     private int prefixIota = 0;
   275     /**
   276      * Allocates a new unique prefix.
   277      */
   278     private String newPrefix() {
   279         prefixSeed.setLength(2);
   280         prefixSeed.append(++prefixIota);
   281         return prefixSeed.toString();
   282     }
   284     /**
   285      * Replaces dummy prefixes in the value to the real ones
   286      * by using {@link #activeNamespaces}.
   287      *
   288      * @return
   289      *      the buffer passed as the <tt>buf</tt> parameter.
   290      */
   291     private StringBuilder fixPrefix(StringBuilder buf) {
   292         assert activeNamespaces!=null;
   294         int i;
   295         int len=buf.length();
   296         for(i=0;i<len;i++)
   297             if( buf.charAt(i)==MAGIC )
   298                 break;
   299         // typically it doens't contain any prefix.
   300         // just return the original buffer in that case
   301         if(i==len)
   302             return buf;
   304         while(i<len) {
   305             char uriIdx = buf.charAt(i+1);
   306             NamespaceDecl ns = activeNamespaces;
   307             while(ns!=null && ns.uniqueId!=uriIdx)
   308                 ns=ns.next;
   309             if(ns==null)
   310                 throw new IllegalStateException("Unexpected use of prefixes "+buf);
   312             int length = 2;
   313             String prefix = ns.prefix;
   314             if(prefix.length()==0) {
   315                 if(buf.length()<=i+2 || buf.charAt(i+2)!=':')
   316                     throw new IllegalStateException("Unexpected use of prefixes "+buf);
   317                 length=3;
   318             }
   320             buf.replace(i,i+length,prefix);
   321             len += prefix.length()-length;
   323             while(i<len && buf.charAt(i)!=MAGIC)
   324                 i++;
   325         }
   327         return buf;
   328     }
   330     /**
   331      * The first char of the dummy prefix.
   332      */
   333     static final char MAGIC = '\u0000';
   335     char assignNewId() {
   336         return (char)iota++;
   337     }
   338 }

mercurial