src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/output/FastInfosetStreamWriterOutput.java

changeset 0
373ffda63c9a
child 1443
dffc222439a1
equal deleted inserted replaced
-1:000000000000 0:373ffda63c9a
1 /*
2 * Copyright (c) 1997, 2011, 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.bind.v2.runtime.output;
27
28 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
29 import com.sun.xml.internal.bind.v2.runtime.Name;
30 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
31 import javax.xml.stream.XMLStreamException;
32
33 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data;
34 import com.sun.xml.internal.fastinfoset.EncodingConstants;
35 import com.sun.xml.internal.fastinfoset.stax.StAXDocumentSerializer;
36 import java.io.IOException;
37 import java.util.Collection;
38 import java.util.Map;
39 import java.util.WeakHashMap;
40 import javax.xml.bind.JAXBContext;
41 import com.sun.xml.internal.org.jvnet.fastinfoset.VocabularyApplicationData;
42 import org.xml.sax.SAXException;
43
44 /**
45 * {@link XmlOutput} for {@link LowLevelStAXDocumentSerializer}.
46 * <p>
47 * This class is responsible for managing the indexing of elements, attributes
48 * and local names that are known to JAXB by way of the JAXBContext (generated
49 * from JAXB beans or schema). The pre-encoded UTF-8 representations of known
50 * local names are also utilized.
51 * <p>
52 * The lookup of elements, attributes and local names with respect to a context
53 * is very efficient. It relies on an incrementing base line so that look up is
54 * performed in O(1) time and only uses static memory. When the base line reaches
55 * a point where integer overflow will occur the arrays and base line are reset
56 * (such an event is rare and will have little impact on performance).
57 * <p>
58 * A weak map of JAXB contexts to optimized tables for attributes, elements and
59 * local names is utilized and stored on the LowLevel StAX serializer. Thus,
60 * optimized serializing can work other multiple serializing of JAXB beans using
61 * the same LowLevel StAX serializer instance. This approach works best when JAXB
62 * contexts are only created once per schema or JAXB beans (which is the recommended
63 * practice as the creation JAXB contexts are expensive, they are thread safe and
64 * can be reused).
65 *
66 * @author Paul.Sandoz@Sun.Com
67 */
68 public final class FastInfosetStreamWriterOutput extends XMLStreamWriterOutput {
69 private final StAXDocumentSerializer fiout;
70 private final Encoded[] localNames;
71 private final TablesPerJAXBContext tables;
72
73 /**
74 * Holder for the optimzed element, attribute and
75 * local name tables.
76 */
77 final static class TablesPerJAXBContext {
78 final int[] elementIndexes;
79 final int[] elementIndexPrefixes;
80
81 final int[] attributeIndexes;
82 final int[] localNameIndexes;
83
84 /**
85 * The offset of the index
86 */
87 int indexOffset;
88
89 /**
90 * The the maximum known value of an index
91 */
92 int maxIndex;
93
94 /**
95 * True if the tables require clearing
96 */
97 boolean requiresClear;
98
99 /**
100 * Create a new set of tables for a JAXB context.
101 * <p>
102 * @param content the JAXB context.
103 * @param initialIndexOffset the initial index offset to calculate
104 * the maximum possible index
105 *
106 */
107 TablesPerJAXBContext(JAXBContextImpl context, int initialIndexOffset) {
108 elementIndexes = new int[context.getNumberOfElementNames()];
109 elementIndexPrefixes = new int[context.getNumberOfElementNames()];
110 attributeIndexes = new int[context.getNumberOfAttributeNames()];
111 localNameIndexes = new int[context.getNumberOfLocalNames()];
112
113 indexOffset = 1;
114 maxIndex = initialIndexOffset + elementIndexes.length + attributeIndexes.length;
115 }
116
117 /**
118 * Require that tables are cleared.
119 */
120 public void requireClearTables() {
121 requiresClear = true;
122 }
123
124 /**
125 * Clear or reset the tables.
126 * <p>
127 * @param initialIndexOffset the initial index offset to calculate
128 * the maximum possible index
129 */
130 public void clearOrResetTables(int intialIndexOffset) {
131 if (requiresClear) {
132 requiresClear = false;
133
134 // Increment offset to new position
135 indexOffset += maxIndex;
136 // Reset the maximum known value of an index
137 maxIndex = intialIndexOffset + elementIndexes.length + attributeIndexes.length;
138 // Check if there is enough free space
139 // If overflow
140 if ((indexOffset + maxIndex) < 0) {
141 clearAll();
142 }
143 } else {
144 // Reset the maximum known value of an index
145 maxIndex = intialIndexOffset + elementIndexes.length + attributeIndexes.length;
146 // Check if there is enough free space
147 // If overflow
148 if ((indexOffset + maxIndex) < 0) {
149 resetAll();
150 }
151 }
152 }
153
154 private void clearAll() {
155 clear(elementIndexes);
156 clear(attributeIndexes);
157 clear(localNameIndexes);
158 indexOffset = 1;
159 }
160
161 private void clear(int[] array) {
162 for (int i = 0; i < array.length; i++) {
163 array[i] = 0;
164 }
165 }
166
167 /**
168 * Increment the maximum know index value
169 * <p>
170 * The indexes are preserved.
171 */
172 public void incrementMaxIndexValue() {
173 // Increment the maximum value of an index
174 maxIndex++;
175 // Check if there is enough free space
176 // If overflow
177 if ((indexOffset + maxIndex) < 0) {
178 resetAll();
179 }
180 }
181
182 private void resetAll() {
183 clear(elementIndexes);
184 clear(attributeIndexes);
185 clear(localNameIndexes);
186 indexOffset = 1;
187 }
188
189 private void reset(int[] array) {
190 for (int i = 0; i < array.length; i++) {
191 if (array[i] > indexOffset) {
192 array[i] = array[i] - indexOffset + 1;
193 } else {
194 array[i] = 0;
195 }
196 }
197 }
198
199 }
200
201 /**
202 * Holder of JAXB contexts -> tables.
203 * <p>
204 * An instance will be registered with the
205 * {@link LowLevelStAXDocumentSerializer}.
206 */
207 final static class AppData implements VocabularyApplicationData {
208 final Map<JAXBContext, TablesPerJAXBContext> contexts =
209 new WeakHashMap<JAXBContext, TablesPerJAXBContext>();
210 final Collection<TablesPerJAXBContext> collectionOfContexts = contexts.values();
211
212 /**
213 * Clear all the tables.
214 */
215 public void clear() {
216 for(TablesPerJAXBContext c : collectionOfContexts)
217 c.requireClearTables();
218 }
219 }
220
221 public FastInfosetStreamWriterOutput(StAXDocumentSerializer out,
222 JAXBContextImpl context) {
223 super(out);
224
225 this.fiout = out;
226 this.localNames = context.getUTF8NameTable();
227
228 final VocabularyApplicationData vocabAppData = fiout.getVocabularyApplicationData();
229 AppData appData = null;
230 if (vocabAppData == null || !(vocabAppData instanceof AppData)) {
231 appData = new AppData();
232 fiout.setVocabularyApplicationData(appData);
233 } else {
234 appData = (AppData)vocabAppData;
235 }
236
237 final TablesPerJAXBContext tablesPerContext = appData.contexts.get(context);
238 if (tablesPerContext != null) {
239 tables = tablesPerContext;
240 /**
241 * Obtain the current local name index. Thus will be used to
242 * calculate the maximum index value when serializing for this context
243 */
244 tables.clearOrResetTables(out.getLocalNameIndex());
245 } else {
246 tables = new TablesPerJAXBContext(context, out.getLocalNameIndex());
247 appData.contexts.put(context, tables);
248 }
249 }
250
251 @Override
252 public void startDocument(XMLSerializer serializer, boolean fragment,
253 int[] nsUriIndex2prefixIndex, NamespaceContextImpl nsContext)
254 throws IOException, SAXException, XMLStreamException {
255 super.startDocument(serializer, fragment, nsUriIndex2prefixIndex, nsContext);
256
257 if (fragment)
258 fiout.initiateLowLevelWriting();
259 }
260
261 @Override
262 public void endDocument(boolean fragment) throws IOException, SAXException, XMLStreamException {
263 super.endDocument(fragment);
264 }
265
266 @Override
267 public void beginStartTag(Name name) throws IOException {
268 fiout.writeLowLevelTerminationAndMark();
269
270 if (nsContext.getCurrent().count() == 0) {
271 final int qNameIndex = tables.elementIndexes[name.qNameIndex] - tables.indexOffset;
272 final int prefixIndex = nsUriIndex2prefixIndex[name.nsUriIndex];
273
274 if (qNameIndex >= 0 &&
275 tables.elementIndexPrefixes[name.qNameIndex] == prefixIndex) {
276 fiout.writeLowLevelStartElementIndexed(EncodingConstants.ELEMENT, qNameIndex);
277 } else {
278 tables.elementIndexes[name.qNameIndex] = fiout.getNextElementIndex() + tables.indexOffset;
279 tables.elementIndexPrefixes[name.qNameIndex] = prefixIndex;
280 writeLiteral(EncodingConstants.ELEMENT | EncodingConstants.ELEMENT_LITERAL_QNAME_FLAG,
281 name,
282 nsContext.getPrefix(prefixIndex),
283 nsContext.getNamespaceURI(prefixIndex));
284 }
285 } else {
286 beginStartTagWithNamespaces(name);
287 }
288 }
289
290 public void beginStartTagWithNamespaces(Name name) throws IOException {
291 final NamespaceContextImpl.Element nse = nsContext.getCurrent();
292
293 fiout.writeLowLevelStartNamespaces();
294 for (int i = nse.count() - 1; i >= 0; i--) {
295 final String uri = nse.getNsUri(i);
296 if (uri.length() == 0 && nse.getBase() == 1)
297 continue; // no point in definint xmlns='' on the root
298 fiout.writeLowLevelNamespace(nse.getPrefix(i), uri);
299 }
300 fiout.writeLowLevelEndNamespaces();
301
302 final int qNameIndex = tables.elementIndexes[name.qNameIndex] - tables.indexOffset;
303 final int prefixIndex = nsUriIndex2prefixIndex[name.nsUriIndex];
304
305 if (qNameIndex >= 0 &&
306 tables.elementIndexPrefixes[name.qNameIndex] == prefixIndex) {
307 fiout.writeLowLevelStartElementIndexed(0, qNameIndex);
308 } else {
309 tables.elementIndexes[name.qNameIndex] = fiout.getNextElementIndex() + tables.indexOffset;
310 tables.elementIndexPrefixes[name.qNameIndex] = prefixIndex;
311 writeLiteral(EncodingConstants.ELEMENT_LITERAL_QNAME_FLAG,
312 name,
313 nsContext.getPrefix(prefixIndex),
314 nsContext.getNamespaceURI(prefixIndex));
315 }
316 }
317
318 @Override
319 public void attribute(Name name, String value) throws IOException {
320 fiout.writeLowLevelStartAttributes();
321
322 final int qNameIndex = tables.attributeIndexes[name.qNameIndex] - tables.indexOffset;
323 if (qNameIndex >= 0) {
324 fiout.writeLowLevelAttributeIndexed(qNameIndex);
325 } else {
326 tables.attributeIndexes[name.qNameIndex] = fiout.getNextAttributeIndex() + tables.indexOffset;
327
328 final int namespaceURIId = name.nsUriIndex;
329 if (namespaceURIId == -1) {
330 writeLiteral(EncodingConstants.ATTRIBUTE_LITERAL_QNAME_FLAG,
331 name,
332 "",
333 "");
334 } else {
335 final int prefix = nsUriIndex2prefixIndex[namespaceURIId];
336 writeLiteral(EncodingConstants.ATTRIBUTE_LITERAL_QNAME_FLAG,
337 name,
338 nsContext.getPrefix(prefix),
339 nsContext.getNamespaceURI(prefix));
340 }
341 }
342
343 fiout.writeLowLevelAttributeValue(value);
344 }
345
346 private void writeLiteral(int type, Name name, String prefix, String namespaceURI) throws IOException {
347 final int localNameIndex = tables.localNameIndexes[name.localNameIndex] - tables.indexOffset;
348
349 if (localNameIndex < 0) {
350 tables.localNameIndexes[name.localNameIndex] = fiout.getNextLocalNameIndex() + tables.indexOffset;
351
352 fiout.writeLowLevelStartNameLiteral(
353 type,
354 prefix,
355 localNames[name.localNameIndex].buf,
356 namespaceURI);
357 } else {
358 fiout.writeLowLevelStartNameLiteral(
359 type,
360 prefix,
361 localNameIndex,
362 namespaceURI);
363 }
364 }
365
366 @Override
367 public void endStartTag() throws IOException {
368 fiout.writeLowLevelEndStartElement();
369 }
370
371 @Override
372 public void endTag(Name name) throws IOException {
373 fiout.writeLowLevelEndElement();
374 }
375
376 @Override
377 public void endTag(int prefix, String localName) throws IOException {
378 fiout.writeLowLevelEndElement();
379 }
380
381
382 @Override
383 public void text(Pcdata value, boolean needsSeparatingWhitespace) throws IOException {
384 if (needsSeparatingWhitespace)
385 fiout.writeLowLevelText(" ");
386
387 /*
388 * Check if the CharSequence is from a base64Binary data type
389 */
390 if (!(value instanceof Base64Data)) {
391 final int len = value.length();
392 if(len <buf.length) {
393 value.writeTo(buf, 0);
394 fiout.writeLowLevelText(buf, len);
395 } else {
396 fiout.writeLowLevelText(value.toString());
397 }
398 } else {
399 final Base64Data dataValue = (Base64Data)value;
400 // Write out the octets using the base64 encoding algorithm
401 fiout.writeLowLevelOctets(dataValue.get(), dataValue.getDataLen());
402 }
403 }
404
405
406 @Override
407 public void text(String value, boolean needsSeparatingWhitespace) throws IOException {
408 if (needsSeparatingWhitespace)
409 fiout.writeLowLevelText(" ");
410
411 fiout.writeLowLevelText(value);
412 }
413
414
415 @Override
416 public void beginStartTag(int prefix, String localName) throws IOException {
417 fiout.writeLowLevelTerminationAndMark();
418
419 int type = EncodingConstants.ELEMENT;
420 if (nsContext.getCurrent().count() > 0) {
421 final NamespaceContextImpl.Element nse = nsContext.getCurrent();
422
423 fiout.writeLowLevelStartNamespaces();
424 for (int i = nse.count() - 1; i >= 0; i--) {
425 final String uri = nse.getNsUri(i);
426 if (uri.length() == 0 && nse.getBase() == 1)
427 continue; // no point in definint xmlns='' on the root
428 fiout.writeLowLevelNamespace(nse.getPrefix(i), uri);
429 }
430 fiout.writeLowLevelEndNamespaces();
431
432 type= 0;
433 }
434
435 final boolean isIndexed = fiout.writeLowLevelStartElement(
436 type,
437 nsContext.getPrefix(prefix),
438 localName,
439 nsContext.getNamespaceURI(prefix));
440
441 if (!isIndexed)
442 tables.incrementMaxIndexValue();
443 }
444
445 @Override
446 public void attribute(int prefix, String localName, String value) throws IOException {
447 fiout.writeLowLevelStartAttributes();
448
449 boolean isIndexed;
450 if (prefix == -1)
451 isIndexed = fiout.writeLowLevelAttribute("", "", localName);
452 else
453 isIndexed = fiout.writeLowLevelAttribute(
454 nsContext.getPrefix(prefix),
455 nsContext.getNamespaceURI(prefix),
456 localName);
457
458 if (!isIndexed)
459 tables.incrementMaxIndexValue();
460
461 fiout.writeLowLevelAttributeValue(value);
462 }
463 }

mercurial