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