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

Thu, 12 Oct 2017 19:44:07 +0800

author
aoqi
date
Thu, 12 Oct 2017 19:44:07 +0800
changeset 760
e530533619ec
parent 0
373ffda63c9a
child 1443
dffc222439a1
permissions
-rw-r--r--

merge

     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  */
    26 package com.sun.xml.internal.bind.v2.runtime.output;
    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;
    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;
    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;
    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;
    81         final int[] attributeIndexes;
    82         final int[] localNameIndexes;
    84         /**
    85          * The offset of the index
    86          */
    87         int indexOffset;
    89         /**
    90          * The the maximum known value of an index
    91          */
    92         int maxIndex;
    94         /**
    95          * True if the tables require clearing
    96          */
    97         boolean requiresClear;
    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()];
   113             indexOffset = 1;
   114             maxIndex = initialIndexOffset + elementIndexes.length + attributeIndexes.length;
   115         }
   117         /**
   118          * Require that tables are cleared.
   119          */
   120         public void requireClearTables() {
   121             requiresClear = true;
   122         }
   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;
   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         }
   154         private void clearAll() {
   155             clear(elementIndexes);
   156             clear(attributeIndexes);
   157             clear(localNameIndexes);
   158             indexOffset = 1;
   159         }
   161         private void clear(int[] array) {
   162             for (int i = 0; i < array.length; i++) {
   163                 array[i] = 0;
   164             }
   165         }
   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         }
   182         private void resetAll() {
   183             clear(elementIndexes);
   184             clear(attributeIndexes);
   185             clear(localNameIndexes);
   186             indexOffset = 1;
   187         }
   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         }
   199     }
   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();
   212         /**
   213          * Clear all the tables.
   214          */
   215         public void clear() {
   216             for(TablesPerJAXBContext c : collectionOfContexts)
   217                 c.requireClearTables();
   218         }
   219     }
   221     public FastInfosetStreamWriterOutput(StAXDocumentSerializer out,
   222             JAXBContextImpl context) {
   223         super(out);
   225         this.fiout = out;
   226         this.localNames = context.getUTF8NameTable();
   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         }
   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     }
   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);
   257         if (fragment)
   258             fiout.initiateLowLevelWriting();
   259     }
   261     @Override
   262     public void endDocument(boolean fragment) throws IOException, SAXException, XMLStreamException {
   263         super.endDocument(fragment);
   264     }
   266     @Override
   267     public void beginStartTag(Name name) throws IOException {
   268         fiout.writeLowLevelTerminationAndMark();
   270         if (nsContext.getCurrent().count() == 0) {
   271             final int qNameIndex = tables.elementIndexes[name.qNameIndex] - tables.indexOffset;
   272             final int prefixIndex = nsUriIndex2prefixIndex[name.nsUriIndex];
   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     }
   290     public void beginStartTagWithNamespaces(Name name) throws IOException {
   291         final NamespaceContextImpl.Element nse = nsContext.getCurrent();
   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();
   302         final int qNameIndex = tables.elementIndexes[name.qNameIndex] - tables.indexOffset;
   303         final int prefixIndex = nsUriIndex2prefixIndex[name.nsUriIndex];
   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     }
   318     @Override
   319     public void attribute(Name name, String value) throws IOException {
   320         fiout.writeLowLevelStartAttributes();
   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;
   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         }
   343         fiout.writeLowLevelAttributeValue(value);
   344     }
   346     private void writeLiteral(int type, Name name, String prefix, String namespaceURI) throws IOException {
   347         final int localNameIndex = tables.localNameIndexes[name.localNameIndex] - tables.indexOffset;
   349         if (localNameIndex < 0) {
   350             tables.localNameIndexes[name.localNameIndex] = fiout.getNextLocalNameIndex() + tables.indexOffset;
   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     }
   366     @Override
   367     public void endStartTag() throws IOException {
   368         fiout.writeLowLevelEndStartElement();
   369     }
   371     @Override
   372     public void endTag(Name name) throws IOException {
   373         fiout.writeLowLevelEndElement();
   374     }
   376     @Override
   377     public void endTag(int prefix, String localName) throws IOException {
   378         fiout.writeLowLevelEndElement();
   379     }
   382     @Override
   383     public void text(Pcdata value, boolean needsSeparatingWhitespace) throws IOException {
   384         if (needsSeparatingWhitespace)
   385             fiout.writeLowLevelText(" ");
   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     }
   406     @Override
   407     public void text(String value, boolean needsSeparatingWhitespace) throws IOException {
   408         if (needsSeparatingWhitespace)
   409             fiout.writeLowLevelText(" ");
   411         fiout.writeLowLevelText(value);
   412     }
   415     @Override
   416     public void beginStartTag(int prefix, String localName) throws IOException {
   417         fiout.writeLowLevelTerminationAndMark();
   419         int type = EncodingConstants.ELEMENT;
   420         if (nsContext.getCurrent().count() > 0) {
   421             final NamespaceContextImpl.Element nse = nsContext.getCurrent();
   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();
   432             type= 0;
   433         }
   435         final boolean isIndexed = fiout.writeLowLevelStartElement(
   436                 type,
   437                 nsContext.getPrefix(prefix),
   438                 localName,
   439                 nsContext.getNamespaceURI(prefix));
   441         if (!isIndexed)
   442             tables.incrementMaxIndexValue();
   443     }
   445     @Override
   446     public void attribute(int prefix, String localName, String value) throws IOException {
   447         fiout.writeLowLevelStartAttributes();
   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);
   458         if (!isIndexed)
   459             tables.incrementMaxIndexValue();
   461         fiout.writeLowLevelAttributeValue(value);
   462     }
   463 }

mercurial