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

Sat, 07 Nov 2020 10:30:02 +0800

author
aoqi
date
Sat, 07 Nov 2020 10:30:02 +0800
changeset 1921
7166269ef0f1
parent 1443
dffc222439a1
permissions
-rw-r--r--

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 }

mercurial