1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/output/FastInfosetStreamWriterOutput.java Wed Apr 27 01:27:09 2016 +0800 1.3 @@ -0,0 +1,463 @@ 1.4 +/* 1.5 + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +package com.sun.xml.internal.bind.v2.runtime.output; 1.30 + 1.31 +import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; 1.32 +import com.sun.xml.internal.bind.v2.runtime.Name; 1.33 +import com.sun.xml.internal.bind.v2.runtime.XMLSerializer; 1.34 +import javax.xml.stream.XMLStreamException; 1.35 + 1.36 +import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data; 1.37 +import com.sun.xml.internal.fastinfoset.EncodingConstants; 1.38 +import com.sun.xml.internal.fastinfoset.stax.StAXDocumentSerializer; 1.39 +import java.io.IOException; 1.40 +import java.util.Collection; 1.41 +import java.util.Map; 1.42 +import java.util.WeakHashMap; 1.43 +import javax.xml.bind.JAXBContext; 1.44 +import com.sun.xml.internal.org.jvnet.fastinfoset.VocabularyApplicationData; 1.45 +import org.xml.sax.SAXException; 1.46 + 1.47 +/** 1.48 + * {@link XmlOutput} for {@link LowLevelStAXDocumentSerializer}. 1.49 + * <p> 1.50 + * This class is responsible for managing the indexing of elements, attributes 1.51 + * and local names that are known to JAXB by way of the JAXBContext (generated 1.52 + * from JAXB beans or schema). The pre-encoded UTF-8 representations of known 1.53 + * local names are also utilized. 1.54 + * <p> 1.55 + * The lookup of elements, attributes and local names with respect to a context 1.56 + * is very efficient. It relies on an incrementing base line so that look up is 1.57 + * performed in O(1) time and only uses static memory. When the base line reaches 1.58 + * a point where integer overflow will occur the arrays and base line are reset 1.59 + * (such an event is rare and will have little impact on performance). 1.60 + * <p> 1.61 + * A weak map of JAXB contexts to optimized tables for attributes, elements and 1.62 + * local names is utilized and stored on the LowLevel StAX serializer. Thus, 1.63 + * optimized serializing can work other multiple serializing of JAXB beans using 1.64 + * the same LowLevel StAX serializer instance. This approach works best when JAXB 1.65 + * contexts are only created once per schema or JAXB beans (which is the recommended 1.66 + * practice as the creation JAXB contexts are expensive, they are thread safe and 1.67 + * can be reused). 1.68 + * 1.69 + * @author Paul.Sandoz@Sun.Com 1.70 + */ 1.71 +public final class FastInfosetStreamWriterOutput extends XMLStreamWriterOutput { 1.72 + private final StAXDocumentSerializer fiout; 1.73 + private final Encoded[] localNames; 1.74 + private final TablesPerJAXBContext tables; 1.75 + 1.76 + /** 1.77 + * Holder for the optimzed element, attribute and 1.78 + * local name tables. 1.79 + */ 1.80 + final static class TablesPerJAXBContext { 1.81 + final int[] elementIndexes; 1.82 + final int[] elementIndexPrefixes; 1.83 + 1.84 + final int[] attributeIndexes; 1.85 + final int[] localNameIndexes; 1.86 + 1.87 + /** 1.88 + * The offset of the index 1.89 + */ 1.90 + int indexOffset; 1.91 + 1.92 + /** 1.93 + * The the maximum known value of an index 1.94 + */ 1.95 + int maxIndex; 1.96 + 1.97 + /** 1.98 + * True if the tables require clearing 1.99 + */ 1.100 + boolean requiresClear; 1.101 + 1.102 + /** 1.103 + * Create a new set of tables for a JAXB context. 1.104 + * <p> 1.105 + * @param content the JAXB context. 1.106 + * @param initialIndexOffset the initial index offset to calculate 1.107 + * the maximum possible index 1.108 + * 1.109 + */ 1.110 + TablesPerJAXBContext(JAXBContextImpl context, int initialIndexOffset) { 1.111 + elementIndexes = new int[context.getNumberOfElementNames()]; 1.112 + elementIndexPrefixes = new int[context.getNumberOfElementNames()]; 1.113 + attributeIndexes = new int[context.getNumberOfAttributeNames()]; 1.114 + localNameIndexes = new int[context.getNumberOfLocalNames()]; 1.115 + 1.116 + indexOffset = 1; 1.117 + maxIndex = initialIndexOffset + elementIndexes.length + attributeIndexes.length; 1.118 + } 1.119 + 1.120 + /** 1.121 + * Require that tables are cleared. 1.122 + */ 1.123 + public void requireClearTables() { 1.124 + requiresClear = true; 1.125 + } 1.126 + 1.127 + /** 1.128 + * Clear or reset the tables. 1.129 + * <p> 1.130 + * @param initialIndexOffset the initial index offset to calculate 1.131 + * the maximum possible index 1.132 + */ 1.133 + public void clearOrResetTables(int intialIndexOffset) { 1.134 + if (requiresClear) { 1.135 + requiresClear = false; 1.136 + 1.137 + // Increment offset to new position 1.138 + indexOffset += maxIndex; 1.139 + // Reset the maximum known value of an index 1.140 + maxIndex = intialIndexOffset + elementIndexes.length + attributeIndexes.length; 1.141 + // Check if there is enough free space 1.142 + // If overflow 1.143 + if ((indexOffset + maxIndex) < 0) { 1.144 + clearAll(); 1.145 + } 1.146 + } else { 1.147 + // Reset the maximum known value of an index 1.148 + maxIndex = intialIndexOffset + elementIndexes.length + attributeIndexes.length; 1.149 + // Check if there is enough free space 1.150 + // If overflow 1.151 + if ((indexOffset + maxIndex) < 0) { 1.152 + resetAll(); 1.153 + } 1.154 + } 1.155 + } 1.156 + 1.157 + private void clearAll() { 1.158 + clear(elementIndexes); 1.159 + clear(attributeIndexes); 1.160 + clear(localNameIndexes); 1.161 + indexOffset = 1; 1.162 + } 1.163 + 1.164 + private void clear(int[] array) { 1.165 + for (int i = 0; i < array.length; i++) { 1.166 + array[i] = 0; 1.167 + } 1.168 + } 1.169 + 1.170 + /** 1.171 + * Increment the maximum know index value 1.172 + * <p> 1.173 + * The indexes are preserved. 1.174 + */ 1.175 + public void incrementMaxIndexValue() { 1.176 + // Increment the maximum value of an index 1.177 + maxIndex++; 1.178 + // Check if there is enough free space 1.179 + // If overflow 1.180 + if ((indexOffset + maxIndex) < 0) { 1.181 + resetAll(); 1.182 + } 1.183 + } 1.184 + 1.185 + private void resetAll() { 1.186 + clear(elementIndexes); 1.187 + clear(attributeIndexes); 1.188 + clear(localNameIndexes); 1.189 + indexOffset = 1; 1.190 + } 1.191 + 1.192 + private void reset(int[] array) { 1.193 + for (int i = 0; i < array.length; i++) { 1.194 + if (array[i] > indexOffset) { 1.195 + array[i] = array[i] - indexOffset + 1; 1.196 + } else { 1.197 + array[i] = 0; 1.198 + } 1.199 + } 1.200 + } 1.201 + 1.202 + } 1.203 + 1.204 + /** 1.205 + * Holder of JAXB contexts -> tables. 1.206 + * <p> 1.207 + * An instance will be registered with the 1.208 + * {@link LowLevelStAXDocumentSerializer}. 1.209 + */ 1.210 + final static class AppData implements VocabularyApplicationData { 1.211 + final Map<JAXBContext, TablesPerJAXBContext> contexts = 1.212 + new WeakHashMap<JAXBContext, TablesPerJAXBContext>(); 1.213 + final Collection<TablesPerJAXBContext> collectionOfContexts = contexts.values(); 1.214 + 1.215 + /** 1.216 + * Clear all the tables. 1.217 + */ 1.218 + public void clear() { 1.219 + for(TablesPerJAXBContext c : collectionOfContexts) 1.220 + c.requireClearTables(); 1.221 + } 1.222 + } 1.223 + 1.224 + public FastInfosetStreamWriterOutput(StAXDocumentSerializer out, 1.225 + JAXBContextImpl context) { 1.226 + super(out); 1.227 + 1.228 + this.fiout = out; 1.229 + this.localNames = context.getUTF8NameTable(); 1.230 + 1.231 + final VocabularyApplicationData vocabAppData = fiout.getVocabularyApplicationData(); 1.232 + AppData appData = null; 1.233 + if (vocabAppData == null || !(vocabAppData instanceof AppData)) { 1.234 + appData = new AppData(); 1.235 + fiout.setVocabularyApplicationData(appData); 1.236 + } else { 1.237 + appData = (AppData)vocabAppData; 1.238 + } 1.239 + 1.240 + final TablesPerJAXBContext tablesPerContext = appData.contexts.get(context); 1.241 + if (tablesPerContext != null) { 1.242 + tables = tablesPerContext; 1.243 + /** 1.244 + * Obtain the current local name index. Thus will be used to 1.245 + * calculate the maximum index value when serializing for this context 1.246 + */ 1.247 + tables.clearOrResetTables(out.getLocalNameIndex()); 1.248 + } else { 1.249 + tables = new TablesPerJAXBContext(context, out.getLocalNameIndex()); 1.250 + appData.contexts.put(context, tables); 1.251 + } 1.252 + } 1.253 + 1.254 + @Override 1.255 + public void startDocument(XMLSerializer serializer, boolean fragment, 1.256 + int[] nsUriIndex2prefixIndex, NamespaceContextImpl nsContext) 1.257 + throws IOException, SAXException, XMLStreamException { 1.258 + super.startDocument(serializer, fragment, nsUriIndex2prefixIndex, nsContext); 1.259 + 1.260 + if (fragment) 1.261 + fiout.initiateLowLevelWriting(); 1.262 + } 1.263 + 1.264 + @Override 1.265 + public void endDocument(boolean fragment) throws IOException, SAXException, XMLStreamException { 1.266 + super.endDocument(fragment); 1.267 + } 1.268 + 1.269 + @Override 1.270 + public void beginStartTag(Name name) throws IOException { 1.271 + fiout.writeLowLevelTerminationAndMark(); 1.272 + 1.273 + if (nsContext.getCurrent().count() == 0) { 1.274 + final int qNameIndex = tables.elementIndexes[name.qNameIndex] - tables.indexOffset; 1.275 + final int prefixIndex = nsUriIndex2prefixIndex[name.nsUriIndex]; 1.276 + 1.277 + if (qNameIndex >= 0 && 1.278 + tables.elementIndexPrefixes[name.qNameIndex] == prefixIndex) { 1.279 + fiout.writeLowLevelStartElementIndexed(EncodingConstants.ELEMENT, qNameIndex); 1.280 + } else { 1.281 + tables.elementIndexes[name.qNameIndex] = fiout.getNextElementIndex() + tables.indexOffset; 1.282 + tables.elementIndexPrefixes[name.qNameIndex] = prefixIndex; 1.283 + writeLiteral(EncodingConstants.ELEMENT | EncodingConstants.ELEMENT_LITERAL_QNAME_FLAG, 1.284 + name, 1.285 + nsContext.getPrefix(prefixIndex), 1.286 + nsContext.getNamespaceURI(prefixIndex)); 1.287 + } 1.288 + } else { 1.289 + beginStartTagWithNamespaces(name); 1.290 + } 1.291 + } 1.292 + 1.293 + public void beginStartTagWithNamespaces(Name name) throws IOException { 1.294 + final NamespaceContextImpl.Element nse = nsContext.getCurrent(); 1.295 + 1.296 + fiout.writeLowLevelStartNamespaces(); 1.297 + for (int i = nse.count() - 1; i >= 0; i--) { 1.298 + final String uri = nse.getNsUri(i); 1.299 + if (uri.length() == 0 && nse.getBase() == 1) 1.300 + continue; // no point in definint xmlns='' on the root 1.301 + fiout.writeLowLevelNamespace(nse.getPrefix(i), uri); 1.302 + } 1.303 + fiout.writeLowLevelEndNamespaces(); 1.304 + 1.305 + final int qNameIndex = tables.elementIndexes[name.qNameIndex] - tables.indexOffset; 1.306 + final int prefixIndex = nsUriIndex2prefixIndex[name.nsUriIndex]; 1.307 + 1.308 + if (qNameIndex >= 0 && 1.309 + tables.elementIndexPrefixes[name.qNameIndex] == prefixIndex) { 1.310 + fiout.writeLowLevelStartElementIndexed(0, qNameIndex); 1.311 + } else { 1.312 + tables.elementIndexes[name.qNameIndex] = fiout.getNextElementIndex() + tables.indexOffset; 1.313 + tables.elementIndexPrefixes[name.qNameIndex] = prefixIndex; 1.314 + writeLiteral(EncodingConstants.ELEMENT_LITERAL_QNAME_FLAG, 1.315 + name, 1.316 + nsContext.getPrefix(prefixIndex), 1.317 + nsContext.getNamespaceURI(prefixIndex)); 1.318 + } 1.319 + } 1.320 + 1.321 + @Override 1.322 + public void attribute(Name name, String value) throws IOException { 1.323 + fiout.writeLowLevelStartAttributes(); 1.324 + 1.325 + final int qNameIndex = tables.attributeIndexes[name.qNameIndex] - tables.indexOffset; 1.326 + if (qNameIndex >= 0) { 1.327 + fiout.writeLowLevelAttributeIndexed(qNameIndex); 1.328 + } else { 1.329 + tables.attributeIndexes[name.qNameIndex] = fiout.getNextAttributeIndex() + tables.indexOffset; 1.330 + 1.331 + final int namespaceURIId = name.nsUriIndex; 1.332 + if (namespaceURIId == -1) { 1.333 + writeLiteral(EncodingConstants.ATTRIBUTE_LITERAL_QNAME_FLAG, 1.334 + name, 1.335 + "", 1.336 + ""); 1.337 + } else { 1.338 + final int prefix = nsUriIndex2prefixIndex[namespaceURIId]; 1.339 + writeLiteral(EncodingConstants.ATTRIBUTE_LITERAL_QNAME_FLAG, 1.340 + name, 1.341 + nsContext.getPrefix(prefix), 1.342 + nsContext.getNamespaceURI(prefix)); 1.343 + } 1.344 + } 1.345 + 1.346 + fiout.writeLowLevelAttributeValue(value); 1.347 + } 1.348 + 1.349 + private void writeLiteral(int type, Name name, String prefix, String namespaceURI) throws IOException { 1.350 + final int localNameIndex = tables.localNameIndexes[name.localNameIndex] - tables.indexOffset; 1.351 + 1.352 + if (localNameIndex < 0) { 1.353 + tables.localNameIndexes[name.localNameIndex] = fiout.getNextLocalNameIndex() + tables.indexOffset; 1.354 + 1.355 + fiout.writeLowLevelStartNameLiteral( 1.356 + type, 1.357 + prefix, 1.358 + localNames[name.localNameIndex].buf, 1.359 + namespaceURI); 1.360 + } else { 1.361 + fiout.writeLowLevelStartNameLiteral( 1.362 + type, 1.363 + prefix, 1.364 + localNameIndex, 1.365 + namespaceURI); 1.366 + } 1.367 + } 1.368 + 1.369 + @Override 1.370 + public void endStartTag() throws IOException { 1.371 + fiout.writeLowLevelEndStartElement(); 1.372 + } 1.373 + 1.374 + @Override 1.375 + public void endTag(Name name) throws IOException { 1.376 + fiout.writeLowLevelEndElement(); 1.377 + } 1.378 + 1.379 + @Override 1.380 + public void endTag(int prefix, String localName) throws IOException { 1.381 + fiout.writeLowLevelEndElement(); 1.382 + } 1.383 + 1.384 + 1.385 + @Override 1.386 + public void text(Pcdata value, boolean needsSeparatingWhitespace) throws IOException { 1.387 + if (needsSeparatingWhitespace) 1.388 + fiout.writeLowLevelText(" "); 1.389 + 1.390 + /* 1.391 + * Check if the CharSequence is from a base64Binary data type 1.392 + */ 1.393 + if (!(value instanceof Base64Data)) { 1.394 + final int len = value.length(); 1.395 + if(len <buf.length) { 1.396 + value.writeTo(buf, 0); 1.397 + fiout.writeLowLevelText(buf, len); 1.398 + } else { 1.399 + fiout.writeLowLevelText(value.toString()); 1.400 + } 1.401 + } else { 1.402 + final Base64Data dataValue = (Base64Data)value; 1.403 + // Write out the octets using the base64 encoding algorithm 1.404 + fiout.writeLowLevelOctets(dataValue.get(), dataValue.getDataLen()); 1.405 + } 1.406 + } 1.407 + 1.408 + 1.409 + @Override 1.410 + public void text(String value, boolean needsSeparatingWhitespace) throws IOException { 1.411 + if (needsSeparatingWhitespace) 1.412 + fiout.writeLowLevelText(" "); 1.413 + 1.414 + fiout.writeLowLevelText(value); 1.415 + } 1.416 + 1.417 + 1.418 + @Override 1.419 + public void beginStartTag(int prefix, String localName) throws IOException { 1.420 + fiout.writeLowLevelTerminationAndMark(); 1.421 + 1.422 + int type = EncodingConstants.ELEMENT; 1.423 + if (nsContext.getCurrent().count() > 0) { 1.424 + final NamespaceContextImpl.Element nse = nsContext.getCurrent(); 1.425 + 1.426 + fiout.writeLowLevelStartNamespaces(); 1.427 + for (int i = nse.count() - 1; i >= 0; i--) { 1.428 + final String uri = nse.getNsUri(i); 1.429 + if (uri.length() == 0 && nse.getBase() == 1) 1.430 + continue; // no point in definint xmlns='' on the root 1.431 + fiout.writeLowLevelNamespace(nse.getPrefix(i), uri); 1.432 + } 1.433 + fiout.writeLowLevelEndNamespaces(); 1.434 + 1.435 + type= 0; 1.436 + } 1.437 + 1.438 + final boolean isIndexed = fiout.writeLowLevelStartElement( 1.439 + type, 1.440 + nsContext.getPrefix(prefix), 1.441 + localName, 1.442 + nsContext.getNamespaceURI(prefix)); 1.443 + 1.444 + if (!isIndexed) 1.445 + tables.incrementMaxIndexValue(); 1.446 + } 1.447 + 1.448 + @Override 1.449 + public void attribute(int prefix, String localName, String value) throws IOException { 1.450 + fiout.writeLowLevelStartAttributes(); 1.451 + 1.452 + boolean isIndexed; 1.453 + if (prefix == -1) 1.454 + isIndexed = fiout.writeLowLevelAttribute("", "", localName); 1.455 + else 1.456 + isIndexed = fiout.writeLowLevelAttribute( 1.457 + nsContext.getPrefix(prefix), 1.458 + nsContext.getNamespaceURI(prefix), 1.459 + localName); 1.460 + 1.461 + if (!isIndexed) 1.462 + tables.incrementMaxIndexValue(); 1.463 + 1.464 + fiout.writeLowLevelAttributeValue(value); 1.465 + } 1.466 +}