diff -r 000000000000 -r 373ffda63c9a src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/output/FastInfosetStreamWriterOutput.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/output/FastInfosetStreamWriterOutput.java Wed Apr 27 01:27:09 2016 +0800 @@ -0,0 +1,463 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.xml.internal.bind.v2.runtime.output; + +import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; +import com.sun.xml.internal.bind.v2.runtime.Name; +import com.sun.xml.internal.bind.v2.runtime.XMLSerializer; +import javax.xml.stream.XMLStreamException; + +import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data; +import com.sun.xml.internal.fastinfoset.EncodingConstants; +import com.sun.xml.internal.fastinfoset.stax.StAXDocumentSerializer; +import java.io.IOException; +import java.util.Collection; +import java.util.Map; +import java.util.WeakHashMap; +import javax.xml.bind.JAXBContext; +import com.sun.xml.internal.org.jvnet.fastinfoset.VocabularyApplicationData; +import org.xml.sax.SAXException; + +/** + * {@link XmlOutput} for {@link LowLevelStAXDocumentSerializer}. + *

+ * This class is responsible for managing the indexing of elements, attributes + * and local names that are known to JAXB by way of the JAXBContext (generated + * from JAXB beans or schema). The pre-encoded UTF-8 representations of known + * local names are also utilized. + *

+ * The lookup of elements, attributes and local names with respect to a context + * is very efficient. It relies on an incrementing base line so that look up is + * performed in O(1) time and only uses static memory. When the base line reaches + * a point where integer overflow will occur the arrays and base line are reset + * (such an event is rare and will have little impact on performance). + *

+ * A weak map of JAXB contexts to optimized tables for attributes, elements and + * local names is utilized and stored on the LowLevel StAX serializer. Thus, + * optimized serializing can work other multiple serializing of JAXB beans using + * the same LowLevel StAX serializer instance. This approach works best when JAXB + * contexts are only created once per schema or JAXB beans (which is the recommended + * practice as the creation JAXB contexts are expensive, they are thread safe and + * can be reused). + * + * @author Paul.Sandoz@Sun.Com + */ +public final class FastInfosetStreamWriterOutput extends XMLStreamWriterOutput { + private final StAXDocumentSerializer fiout; + private final Encoded[] localNames; + private final TablesPerJAXBContext tables; + + /** + * Holder for the optimzed element, attribute and + * local name tables. + */ + final static class TablesPerJAXBContext { + final int[] elementIndexes; + final int[] elementIndexPrefixes; + + final int[] attributeIndexes; + final int[] localNameIndexes; + + /** + * The offset of the index + */ + int indexOffset; + + /** + * The the maximum known value of an index + */ + int maxIndex; + + /** + * True if the tables require clearing + */ + boolean requiresClear; + + /** + * Create a new set of tables for a JAXB context. + *

+ * @param content the JAXB context. + * @param initialIndexOffset the initial index offset to calculate + * the maximum possible index + * + */ + TablesPerJAXBContext(JAXBContextImpl context, int initialIndexOffset) { + elementIndexes = new int[context.getNumberOfElementNames()]; + elementIndexPrefixes = new int[context.getNumberOfElementNames()]; + attributeIndexes = new int[context.getNumberOfAttributeNames()]; + localNameIndexes = new int[context.getNumberOfLocalNames()]; + + indexOffset = 1; + maxIndex = initialIndexOffset + elementIndexes.length + attributeIndexes.length; + } + + /** + * Require that tables are cleared. + */ + public void requireClearTables() { + requiresClear = true; + } + + /** + * Clear or reset the tables. + *

+ * @param initialIndexOffset the initial index offset to calculate + * the maximum possible index + */ + public void clearOrResetTables(int intialIndexOffset) { + if (requiresClear) { + requiresClear = false; + + // Increment offset to new position + indexOffset += maxIndex; + // Reset the maximum known value of an index + maxIndex = intialIndexOffset + elementIndexes.length + attributeIndexes.length; + // Check if there is enough free space + // If overflow + if ((indexOffset + maxIndex) < 0) { + clearAll(); + } + } else { + // Reset the maximum known value of an index + maxIndex = intialIndexOffset + elementIndexes.length + attributeIndexes.length; + // Check if there is enough free space + // If overflow + if ((indexOffset + maxIndex) < 0) { + resetAll(); + } + } + } + + private void clearAll() { + clear(elementIndexes); + clear(attributeIndexes); + clear(localNameIndexes); + indexOffset = 1; + } + + private void clear(int[] array) { + for (int i = 0; i < array.length; i++) { + array[i] = 0; + } + } + + /** + * Increment the maximum know index value + *

+ * The indexes are preserved. + */ + public void incrementMaxIndexValue() { + // Increment the maximum value of an index + maxIndex++; + // Check if there is enough free space + // If overflow + if ((indexOffset + maxIndex) < 0) { + resetAll(); + } + } + + private void resetAll() { + clear(elementIndexes); + clear(attributeIndexes); + clear(localNameIndexes); + indexOffset = 1; + } + + private void reset(int[] array) { + for (int i = 0; i < array.length; i++) { + if (array[i] > indexOffset) { + array[i] = array[i] - indexOffset + 1; + } else { + array[i] = 0; + } + } + } + + } + + /** + * Holder of JAXB contexts -> tables. + *

+ * An instance will be registered with the + * {@link LowLevelStAXDocumentSerializer}. + */ + final static class AppData implements VocabularyApplicationData { + final Map contexts = + new WeakHashMap(); + final Collection collectionOfContexts = contexts.values(); + + /** + * Clear all the tables. + */ + public void clear() { + for(TablesPerJAXBContext c : collectionOfContexts) + c.requireClearTables(); + } + } + + public FastInfosetStreamWriterOutput(StAXDocumentSerializer out, + JAXBContextImpl context) { + super(out); + + this.fiout = out; + this.localNames = context.getUTF8NameTable(); + + final VocabularyApplicationData vocabAppData = fiout.getVocabularyApplicationData(); + AppData appData = null; + if (vocabAppData == null || !(vocabAppData instanceof AppData)) { + appData = new AppData(); + fiout.setVocabularyApplicationData(appData); + } else { + appData = (AppData)vocabAppData; + } + + final TablesPerJAXBContext tablesPerContext = appData.contexts.get(context); + if (tablesPerContext != null) { + tables = tablesPerContext; + /** + * Obtain the current local name index. Thus will be used to + * calculate the maximum index value when serializing for this context + */ + tables.clearOrResetTables(out.getLocalNameIndex()); + } else { + tables = new TablesPerJAXBContext(context, out.getLocalNameIndex()); + appData.contexts.put(context, tables); + } + } + + @Override + public void startDocument(XMLSerializer serializer, boolean fragment, + int[] nsUriIndex2prefixIndex, NamespaceContextImpl nsContext) + throws IOException, SAXException, XMLStreamException { + super.startDocument(serializer, fragment, nsUriIndex2prefixIndex, nsContext); + + if (fragment) + fiout.initiateLowLevelWriting(); + } + + @Override + public void endDocument(boolean fragment) throws IOException, SAXException, XMLStreamException { + super.endDocument(fragment); + } + + @Override + public void beginStartTag(Name name) throws IOException { + fiout.writeLowLevelTerminationAndMark(); + + if (nsContext.getCurrent().count() == 0) { + final int qNameIndex = tables.elementIndexes[name.qNameIndex] - tables.indexOffset; + final int prefixIndex = nsUriIndex2prefixIndex[name.nsUriIndex]; + + if (qNameIndex >= 0 && + tables.elementIndexPrefixes[name.qNameIndex] == prefixIndex) { + fiout.writeLowLevelStartElementIndexed(EncodingConstants.ELEMENT, qNameIndex); + } else { + tables.elementIndexes[name.qNameIndex] = fiout.getNextElementIndex() + tables.indexOffset; + tables.elementIndexPrefixes[name.qNameIndex] = prefixIndex; + writeLiteral(EncodingConstants.ELEMENT | EncodingConstants.ELEMENT_LITERAL_QNAME_FLAG, + name, + nsContext.getPrefix(prefixIndex), + nsContext.getNamespaceURI(prefixIndex)); + } + } else { + beginStartTagWithNamespaces(name); + } + } + + public void beginStartTagWithNamespaces(Name name) throws IOException { + final NamespaceContextImpl.Element nse = nsContext.getCurrent(); + + fiout.writeLowLevelStartNamespaces(); + for (int i = nse.count() - 1; i >= 0; i--) { + final String uri = nse.getNsUri(i); + if (uri.length() == 0 && nse.getBase() == 1) + continue; // no point in definint xmlns='' on the root + fiout.writeLowLevelNamespace(nse.getPrefix(i), uri); + } + fiout.writeLowLevelEndNamespaces(); + + final int qNameIndex = tables.elementIndexes[name.qNameIndex] - tables.indexOffset; + final int prefixIndex = nsUriIndex2prefixIndex[name.nsUriIndex]; + + if (qNameIndex >= 0 && + tables.elementIndexPrefixes[name.qNameIndex] == prefixIndex) { + fiout.writeLowLevelStartElementIndexed(0, qNameIndex); + } else { + tables.elementIndexes[name.qNameIndex] = fiout.getNextElementIndex() + tables.indexOffset; + tables.elementIndexPrefixes[name.qNameIndex] = prefixIndex; + writeLiteral(EncodingConstants.ELEMENT_LITERAL_QNAME_FLAG, + name, + nsContext.getPrefix(prefixIndex), + nsContext.getNamespaceURI(prefixIndex)); + } + } + + @Override + public void attribute(Name name, String value) throws IOException { + fiout.writeLowLevelStartAttributes(); + + final int qNameIndex = tables.attributeIndexes[name.qNameIndex] - tables.indexOffset; + if (qNameIndex >= 0) { + fiout.writeLowLevelAttributeIndexed(qNameIndex); + } else { + tables.attributeIndexes[name.qNameIndex] = fiout.getNextAttributeIndex() + tables.indexOffset; + + final int namespaceURIId = name.nsUriIndex; + if (namespaceURIId == -1) { + writeLiteral(EncodingConstants.ATTRIBUTE_LITERAL_QNAME_FLAG, + name, + "", + ""); + } else { + final int prefix = nsUriIndex2prefixIndex[namespaceURIId]; + writeLiteral(EncodingConstants.ATTRIBUTE_LITERAL_QNAME_FLAG, + name, + nsContext.getPrefix(prefix), + nsContext.getNamespaceURI(prefix)); + } + } + + fiout.writeLowLevelAttributeValue(value); + } + + private void writeLiteral(int type, Name name, String prefix, String namespaceURI) throws IOException { + final int localNameIndex = tables.localNameIndexes[name.localNameIndex] - tables.indexOffset; + + if (localNameIndex < 0) { + tables.localNameIndexes[name.localNameIndex] = fiout.getNextLocalNameIndex() + tables.indexOffset; + + fiout.writeLowLevelStartNameLiteral( + type, + prefix, + localNames[name.localNameIndex].buf, + namespaceURI); + } else { + fiout.writeLowLevelStartNameLiteral( + type, + prefix, + localNameIndex, + namespaceURI); + } + } + + @Override + public void endStartTag() throws IOException { + fiout.writeLowLevelEndStartElement(); + } + + @Override + public void endTag(Name name) throws IOException { + fiout.writeLowLevelEndElement(); + } + + @Override + public void endTag(int prefix, String localName) throws IOException { + fiout.writeLowLevelEndElement(); + } + + + @Override + public void text(Pcdata value, boolean needsSeparatingWhitespace) throws IOException { + if (needsSeparatingWhitespace) + fiout.writeLowLevelText(" "); + + /* + * Check if the CharSequence is from a base64Binary data type + */ + if (!(value instanceof Base64Data)) { + final int len = value.length(); + if(len 0) { + final NamespaceContextImpl.Element nse = nsContext.getCurrent(); + + fiout.writeLowLevelStartNamespaces(); + for (int i = nse.count() - 1; i >= 0; i--) { + final String uri = nse.getNsUri(i); + if (uri.length() == 0 && nse.getBase() == 1) + continue; // no point in definint xmlns='' on the root + fiout.writeLowLevelNamespace(nse.getPrefix(i), uri); + } + fiout.writeLowLevelEndNamespaces(); + + type= 0; + } + + final boolean isIndexed = fiout.writeLowLevelStartElement( + type, + nsContext.getPrefix(prefix), + localName, + nsContext.getNamespaceURI(prefix)); + + if (!isIndexed) + tables.incrementMaxIndexValue(); + } + + @Override + public void attribute(int prefix, String localName, String value) throws IOException { + fiout.writeLowLevelStartAttributes(); + + boolean isIndexed; + if (prefix == -1) + isIndexed = fiout.writeLowLevelAttribute("", "", localName); + else + isIndexed = fiout.writeLowLevelAttribute( + nsContext.getPrefix(prefix), + nsContext.getNamespaceURI(prefix), + localName); + + if (!isIndexed) + tables.incrementMaxIndexValue(); + + fiout.writeLowLevelAttributeValue(value); + } +}