src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/output/NamespaceContextImpl.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
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 java.io.IOException;
    29 import java.util.Collections;
    30 import java.util.Iterator;
    32 import javax.xml.XMLConstants;
    33 import javax.xml.stream.XMLStreamException;
    35 import com.sun.istack.internal.NotNull;
    36 import com.sun.istack.internal.Nullable;
    37 import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper;
    38 import com.sun.xml.internal.bind.v2.WellKnownNamespace;
    39 import com.sun.xml.internal.bind.v2.runtime.Name;
    40 import com.sun.xml.internal.bind.v2.runtime.NamespaceContext2;
    41 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
    43 import org.xml.sax.SAXException;
    45 /**
    46  * Keeps track of in-scope namespace bindings for the marshaller.
    47  *
    48  * <p>
    49  * This class is also used to keep track of tag names for each element
    50  * for the marshaller (for the performance reason.)
    51  *
    52  * @author Kohsuke Kawaguchi
    53  */
    54 public final class NamespaceContextImpl implements NamespaceContext2 {
    55     private final XMLSerializer owner;
    57     private String[] prefixes = new String[4];
    58     private String[] nsUris = new String[4];
    59 //    /**
    60 //     * True if the correponding namespace declaration is an authentic one that should be printed.
    61 //     *
    62 //     * False if it's a re-discovered in-scope namespace binding available at the ancestor elements
    63 //     * outside this marhsalling. The false value is used to incorporate the in-scope namespace binding
    64 //     * information from {@link #inscopeNamespaceContext}. When false, such a declaration does not need
    65 //     * to be printed, as it's already available in ancestors.
    66 //     */
    67 //    private boolean[] visible = new boolean[4];
    68 //
    69 //    /**
    70 //     * {@link NamespaceContext} that informs this {@link XMLSerializer} about the
    71 //     * in-scope namespace bindings of the ancestor elements outside this marshalling.
    72 //     *
    73 //     * <p>
    74 //     * This is used when the marshaller is marshalling into a subtree that has ancestor
    75 //     * elements created outside the JAXB marshaller.
    76 //     *
    77 //     * Its {@link NamespaceContext#getPrefix(String)} is used to discover in-scope namespace
    78 //     * binding,
    79 //     */
    80 //    private final NamespaceContext inscopeNamespaceContext;
    82     /**
    83      * Number of URIs declared. Identifies the valid portion of
    84      * the {@link #prefixes} and {@link #nsUris} arrays.
    85      */
    86     private int size;
    88     private Element current;
    90     /**
    91      * This is the {@link Element} whose prev==null.
    92      * This element is used to hold the contextual namespace bindings
    93      * that are assumed to be outside of the document we are marshalling.
    94      * Specifically the xml prefix and any other user-specified bindings.
    95      *
    96      * @see NamespacePrefixMapper#getPreDeclaredNamespaceUris()
    97      */
    98     private final Element top;
   100     /**
   101      * Never null.
   102      */
   103     private NamespacePrefixMapper prefixMapper = defaultNamespacePrefixMapper;
   105     /**
   106      * True to allow new URIs to be declared. False otherwise.
   107      */
   108     public boolean collectionMode;
   111     public NamespaceContextImpl(XMLSerializer owner) {
   112         this.owner = owner;
   114         current = top = new Element(this,null);
   115         // register namespace URIs that are implicitly bound
   116         put(XMLConstants.XML_NS_URI,XMLConstants.XML_NS_PREFIX);
   117     }
   119     public void setPrefixMapper( NamespacePrefixMapper mapper ) {
   120         if(mapper==null)
   121             mapper = defaultNamespacePrefixMapper;
   122         this.prefixMapper = mapper;
   123     }
   125     public NamespacePrefixMapper getPrefixMapper() {
   126         return prefixMapper;
   127     }
   129     public void reset() {
   130         current = top;
   131         size = 1;
   132         collectionMode = false;
   133     }
   135     /**
   136      * Returns the prefix index to the specified URI.
   137      * This method allocates a new URI if necessary.
   138      */
   139     public int declareNsUri( String uri, String preferedPrefix, boolean requirePrefix ) {
   140         preferedPrefix = prefixMapper.getPreferredPrefix(uri,preferedPrefix,requirePrefix);
   142         if(uri.length()==0) {
   143             for( int i=size-1; i>=0; i-- ) {
   144                 if(nsUris[i].length()==0)
   145                     return i; // already declared
   146                 if(prefixes[i].length()==0) {
   147                     // the default prefix is already taken.
   148                     // move that URI to another prefix, then assign "" to the default prefix.
   149                     assert current.defaultPrefixIndex==-1 && current.oldDefaultNamespaceUriIndex==-1;
   151                     String oldUri = nsUris[i];
   152                     String[] knownURIs = owner.nameList.namespaceURIs;
   154                     if(current.baseIndex<=i) {
   155                         // this default prefix is declared in this context. just reassign it
   157                         nsUris[i] = "";
   159                         int subst = put(oldUri,null);
   161                         // update uri->prefix table if necessary
   162                         for( int j=knownURIs.length-1; j>=0; j-- ) {
   163                             if(knownURIs[j].equals(oldUri)) {
   164                                 owner.knownUri2prefixIndexMap[j] = subst;
   165                                 break;
   166                             }
   167                         }
   168                         if (current.elementLocalName != null) {
   169                             current.setTagName(subst, current.elementLocalName, current.getOuterPeer());
   170                         }
   171                         return i;
   172                     } else {
   173                         // first, if the previous URI assigned to "" is
   174                         // a "known URI", remember what we've reallocated
   175                         // so that we can fix it when this context pops.
   176                         for( int j=knownURIs.length-1; j>=0; j-- ) {
   177                             if(knownURIs[j].equals(oldUri)) {
   178                                 current.defaultPrefixIndex = i;
   179                                 current.oldDefaultNamespaceUriIndex = j;
   180                                 // assert commented out; too strict/not valid any more
   181                                 // assert owner.knownUri2prefixIndexMap[j]==current.defaultPrefixIndex;
   182                                 // update the table to point to the prefix we'll declare
   183                                 owner.knownUri2prefixIndexMap[j] = size;
   184                                 break;
   185                             }
   186                         }
   187                         if (current.elementLocalName!=null) {
   188                                                 current.setTagName(size, current.elementLocalName, current.getOuterPeer());
   189                         }
   191                         put(nsUris[i],null);
   192                         return put("", "");
   193                     }
   194                 }
   195             }
   197             // "" isn't in use
   198             return put("", "");
   199         } else {
   200             // check for the existing binding
   201             for( int i=size-1; i>=0; i-- ) {
   202                 String p = prefixes[i];
   203                 if(nsUris[i].equals(uri)) {
   204                     if (!requirePrefix || p.length()>0)
   205                         return i;
   206                     // declared but this URI is bound to empty. Look further
   207                 }
   208                 if(p.equals(preferedPrefix)) {
   209                     // the suggested prefix is already taken. can't use it
   210                     preferedPrefix = null;
   211                 }
   212             }
   214             if(preferedPrefix==null && requirePrefix)
   215                 // we know we can't bind to "", but we don't have any possible name at hand.
   216                 // generate it here to avoid this namespace to be bound to "".
   217                 preferedPrefix = makeUniquePrefix();
   219             // haven't been declared. allocate a new one
   220             // if the preferred prefix is already in use, it should have been set to null by this time
   221             return put(uri, preferedPrefix);
   222         }
   223     }
   225     public int force(@NotNull String uri, @NotNull String prefix) {
   226         // check for the existing binding
   228         for( int i=size-1; i>=0; i-- ) {
   229             if(prefixes[i].equals(prefix)) {
   230                 if(nsUris[i].equals(uri))
   231                     return i;   // found duplicate
   232                 else
   233                     // the prefix is used for another namespace. we need to declare it
   234                     break;
   235             }
   236         }
   238         return put(uri, prefix);
   239     }
   241     /**
   242      * Puts this new binding into the declared prefixes list
   243      * without doing any duplicate check.
   244      *
   245      * This can be used to forcibly set namespace declarations.
   246      *
   247      * <p>
   248      * Most of the time {@link #declareNamespace(String, String, boolean)} shall be used.
   249      *
   250      * @return
   251      *      the index of this new binding.
   252      */
   253     public int put(@NotNull String uri, @Nullable String prefix) {
   254         if(size==nsUris.length) {
   255             // reallocate
   256             String[] u = new String[nsUris.length*2];
   257             String[] p = new String[prefixes.length*2];
   258             System.arraycopy(nsUris,0,u,0,nsUris.length);
   259             System.arraycopy(prefixes,0,p,0,prefixes.length);
   260             nsUris = u;
   261             prefixes = p;
   262         }
   263         if(prefix==null) {
   264             if(size==1)
   265                 prefix = "";    // if this is the first user namespace URI we see, use "".
   266             else {
   267                 // otherwise make up an unique name
   268                 prefix = makeUniquePrefix();
   269             }
   270         }
   271         nsUris[size] = uri;
   272         prefixes[size] = prefix;
   274         return size++;
   275     }
   277     private String makeUniquePrefix() {
   278         String prefix;
   279         prefix = new StringBuilder(5).append("ns").append(size).toString();
   280         while(getNamespaceURI(prefix)!=null) {
   281             prefix += '_';  // under a rare circumstance there might be existing 'nsNNN', so rename them
   282         }
   283         return prefix;
   284     }
   287     public Element getCurrent() {
   288         return current;
   289     }
   291     /**
   292      * Returns the prefix index of the specified URI.
   293      * It is an error if the URI is not declared.
   294      */
   295     public int getPrefixIndex( String uri ) {
   296         for( int i=size-1; i>=0; i-- ) {
   297                 if(nsUris[i].equals(uri))
   298                     return i;
   299         }
   300         throw new IllegalStateException();
   301     }
   303     /**
   304      * Gets the prefix from a prefix index.
   305      *
   306      * The behavior is undefined if the index is out of range.
   307      */
   308     public String getPrefix(int prefixIndex) {
   309         return prefixes[prefixIndex];
   310     }
   312     public String getNamespaceURI(int prefixIndex) {
   313         return nsUris[prefixIndex];
   314     }
   316     /**
   317      * Gets the namespace URI that is bound to the specified prefix.
   318      *
   319      * @return null
   320      *      if the prefix is unbound.
   321      */
   322     public String getNamespaceURI(String prefix) {
   323         for( int i=size-1; i>=0; i-- )
   324             if(prefixes[i].equals(prefix))
   325                 return nsUris[i];
   326         return null;
   327     }
   329     /**
   330      * Returns the prefix of the specified URI,
   331      * or null if none exists.
   332      */
   333     public String getPrefix( String uri ) {
   334         if(collectionMode) {
   335             return declareNamespace(uri,null,false);
   336         } else {
   337             for( int i=size-1; i>=0; i-- )
   338                 if(nsUris[i].equals(uri))
   339                     return prefixes[i];
   340             return null;
   341         }
   342     }
   344     public Iterator<String> getPrefixes(String uri) {
   345         String prefix = getPrefix(uri);
   346         if(prefix==null)
   347             return Collections.<String>emptySet().iterator();
   348         else
   349             return Collections.singleton(uri).iterator();
   350     }
   352     public String declareNamespace(String namespaceUri, String preferedPrefix, boolean requirePrefix) {
   353         int idx = declareNsUri(namespaceUri,preferedPrefix,requirePrefix);
   354         return getPrefix(idx);
   355     }
   357     /**
   358      * Number of total bindings declared.
   359      */
   360     public int count() {
   361         return size;
   362     }
   365     /**
   366      * This model of namespace declarations maintain the following invariants.
   367      *
   368      * <ul>
   369      *  <li>If a non-empty prefix is declared, it will never be reassigned to different namespace URIs.
   370      *  <li>
   371      */
   372     public final class Element {
   374         public final NamespaceContextImpl context;
   376         /**
   377          * {@link Element}s form a doubly-linked list.
   378          */
   379         private final Element prev;
   380         private Element next;
   382         private int oldDefaultNamespaceUriIndex;
   383         private int defaultPrefixIndex;
   386         /**
   387          * The numbe of prefixes declared by ancestor {@link Element}s.
   388          */
   389         private int baseIndex;
   391         /**
   392          * The depth of the {@link Element}.
   393          *
   394          * This value is equivalent as the result of the following computation.
   395          *
   396          * <pre>
   397          * int depth() {
   398          *   int i=-1;
   399          *   for(Element e=this; e!=null;e=e.prev)
   400          *     i++;
   401          *   return i;
   402          * }
   403          * </pre>
   404          */
   405         private final int depth;
   409         private int elementNamePrefix;
   410         private String elementLocalName;
   412         /**
   413          * Tag name of this element.
   414          * Either this field is used or the {@link #elementNamePrefix} and {@link #elementLocalName} pair.
   415          */
   416         private Name elementName;
   418         /**
   419          * Used for the binder. The JAXB object that corresponds to this element.
   420          */
   421         private Object outerPeer;
   422         private Object innerPeer;
   425         private Element(NamespaceContextImpl context,Element prev) {
   426             this.context = context;
   427             this.prev = prev;
   428             this.depth = (prev==null) ? 0 : prev.depth+1;
   429         }
   431         /**
   432          * Returns true if this {@link Element} represents the root element that
   433          * we are marshalling.
   434          */
   435         public boolean isRootElement() {
   436             return depth==1;
   437         }
   439         public Element push() {
   440             if(next==null)
   441                 next = new Element(context,this);
   442             next.onPushed();
   443             return next;
   444         }
   446         public Element pop() {
   447             if(oldDefaultNamespaceUriIndex>=0) {
   448                 // restore the old default namespace URI binding
   449                 context.owner.knownUri2prefixIndexMap[oldDefaultNamespaceUriIndex] = defaultPrefixIndex;
   450             }
   451             context.size = baseIndex;
   452             context.current = prev;
   453             // release references to user objects
   454             outerPeer = innerPeer = null;
   455             return prev;
   456         }
   458         private void onPushed() {
   459             oldDefaultNamespaceUriIndex = defaultPrefixIndex = -1;
   460             baseIndex = context.size;
   461             context.current = this;
   462         }
   464         public void setTagName( int prefix, String localName, Object outerPeer ) {
   465             assert localName!=null;
   466             this.elementNamePrefix = prefix;
   467             this.elementLocalName = localName;
   468             this.elementName = null;
   469             this.outerPeer = outerPeer;
   470         }
   472         public void setTagName( Name tagName, Object outerPeer ) {
   473             assert tagName!=null;
   474             this.elementName = tagName;
   475             this.outerPeer = outerPeer;
   476         }
   478         public void startElement(XmlOutput out, Object innerPeer) throws IOException, XMLStreamException {
   479             this.innerPeer = innerPeer;
   480             if(elementName!=null) {
   481                 out.beginStartTag(elementName);
   482             } else {
   483                 out.beginStartTag(elementNamePrefix,elementLocalName);
   484             }
   485         }
   487         public void endElement(XmlOutput out) throws IOException, SAXException, XMLStreamException {
   488             if(elementName!=null) {
   489                 out.endTag(elementName);
   490                 elementName = null;
   491             } else {
   492                 out.endTag(elementNamePrefix,elementLocalName);
   493             }
   494         }
   496         /**
   497          * Gets the number of bindings declared on this element.
   498          */
   499         public final int count() {
   500             return context.size-baseIndex;
   501         }
   503         /**
   504          * Gets the prefix declared in this context.
   505          *
   506          * @param idx
   507          *      between 0 and {@link #count()}
   508          */
   509         public final String getPrefix(int idx) {
   510             return context.prefixes[baseIndex+idx];
   511         }
   513         /**
   514          * Gets the namespace URI declared in this context.
   515          *
   516          * @param idx
   517          *      between 0 and {@link #count()}
   518          */
   519         public final String getNsUri(int idx) {
   520             return context.nsUris[baseIndex+idx];
   521         }
   523         public int getBase() {
   524             return baseIndex;
   525         }
   527         public Object getOuterPeer() {
   528             return outerPeer;
   529         }
   531         public Object getInnerPeer() {
   532             return innerPeer;
   533         }
   535         /**
   536          * Gets the parent {@link Element}.
   537          */
   538         public Element getParent() {
   539             return prev;
   540         }
   541     }
   544     /**
   545      * Default {@link NamespacePrefixMapper} implementation used when
   546      * it is not specified by the user.
   547      */
   548     private static final NamespacePrefixMapper defaultNamespacePrefixMapper = new NamespacePrefixMapper() {
   549         public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
   550             if( namespaceUri.equals(WellKnownNamespace.XML_SCHEMA_INSTANCE) )
   551                 return "xsi";
   552             if( namespaceUri.equals(WellKnownNamespace.XML_SCHEMA) )
   553                 return "xs";
   554             if( namespaceUri.equals(WellKnownNamespace.XML_MIME_URI) )
   555                 return "xmime";
   556             return suggestion;
   557         }
   558     };
   559 }

mercurial