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

changeset 0
373ffda63c9a
equal deleted inserted replaced
-1:000000000000 0:373ffda63c9a
1 /*
2 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.xml.internal.txw2;
27
28 import com.sun.xml.internal.txw2.output.XmlSerializer;
29
30 import java.util.Map;
31 import java.util.HashMap;
32
33 /**
34 * Coordinates the entire writing process.
35 *
36 * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
37 */
38 public final class Document {
39
40 private final XmlSerializer out;
41
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;
49
50 /**
51 * Currently active writer.
52 *
53 * <p>
54 * This points to the last written token.
55 */
56 private Content current = null;
57
58 private final Map<Class,DatatypeWriter> datatypeWriters = new HashMap<Class,DatatypeWriter>();
59
60 /**
61 * Used to generate unique namespace prefix.
62 */
63 private int iota = 1;
64
65 /**
66 * Used to keep track of in-scope namespace bindings declared in ancestors.
67 */
68 private final NamespaceSupport inscopeNamespace = new NamespaceSupport();
69
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;
75
76
77 Document(XmlSerializer out) {
78 this.out = out;
79 for( DatatypeWriter dw : DatatypeWriter.BUILTIN )
80 datatypeWriters.put(dw.getType(),dw);
81 }
82
83 void flush() {
84 out.flush();
85 }
86
87 void setFirstContent(Content c) {
88 assert current==null;
89 current = new StartDocument();
90 current.setNext(this,c);
91 }
92
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 }
107
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 }
121
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");
131
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 }
142
143 if(buf.length()>0)
144 buf.append(' ');
145
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 }
155
156 // if nothing applies, just use toString
157 buf.append(obj);
158 }
159
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 }
168
169 public void onEndDocument() {
170 out.endDocument();
171 }
172
173 public void onEndTag() {
174 out.endTag();
175 inscopeNamespace.popContext();
176 activeNamespaces = null;
177 }
178
179 public void onPcdata(StringBuilder buffer) {
180 if(activeNamespaces!=null)
181 buffer = fixPrefix(buffer);
182 out.text(buffer);
183 }
184
185 public void onCdata(StringBuilder buffer) {
186 if(activeNamespaces!=null)
187 buffer = fixPrefix(buffer);
188 out.cdata(buffer);
189 }
190
191 public void onComment(StringBuilder buffer) {
192 if(activeNamespaces!=null)
193 buffer = fixPrefix(buffer);
194 out.comment(buffer);
195 }
196
197 public void onStartTag(String nsUri, String localName, Attribute attributes, NamespaceDecl namespaces) {
198 assert nsUri!=null;
199 assert localName!=null;
200
201 activeNamespaces = namespaces;
202
203 if(!started) {
204 started = true;
205 out.startDocument();
206 }
207
208 inscopeNamespace.pushContext();
209
210 // declare the explicitly bound namespaces
211 for( NamespaceDecl ns=namespaces; ns!=null; ns=ns.next ) {
212 ns.declared = false; // reset this flag
213
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 }
225
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 }
244
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);
249
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 }
255
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 }
263
264 out.endStartTag(nsUri,localName,namespaces.prefix);
265 }
266 };
267
268 /**
269 * Used by {@link #newPrefix()}.
270 */
271 private final StringBuilder prefixSeed = new StringBuilder("ns");
272
273 private int prefixIota = 0;
274
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 }
283
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;
293
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;
303
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);
311
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 }
319
320 buf.replace(i,i+length,prefix);
321 len += prefix.length()-length;
322
323 while(i<len && buf.charAt(i)!=MAGIC)
324 i++;
325 }
326
327 return buf;
328 }
329
330 /**
331 * The first char of the dummy prefix.
332 */
333 static final char MAGIC = '\u0000';
334
335 char assignNewId() {
336 return (char)iota++;
337 }
338 }

mercurial