diff -r 000000000000 -r 373ffda63c9a src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/output/NamespaceContextImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/output/NamespaceContextImpl.java Wed Apr 27 01:27:09 2016 +0800 @@ -0,0 +1,559 @@ +/* + * 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 java.io.IOException; +import java.util.Collections; +import java.util.Iterator; + +import javax.xml.XMLConstants; +import javax.xml.stream.XMLStreamException; + +import com.sun.istack.internal.NotNull; +import com.sun.istack.internal.Nullable; +import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper; +import com.sun.xml.internal.bind.v2.WellKnownNamespace; +import com.sun.xml.internal.bind.v2.runtime.Name; +import com.sun.xml.internal.bind.v2.runtime.NamespaceContext2; +import com.sun.xml.internal.bind.v2.runtime.XMLSerializer; + +import org.xml.sax.SAXException; + +/** + * Keeps track of in-scope namespace bindings for the marshaller. + * + *
+ * This class is also used to keep track of tag names for each element + * for the marshaller (for the performance reason.) + * + * @author Kohsuke Kawaguchi + */ +public final class NamespaceContextImpl implements NamespaceContext2 { + private final XMLSerializer owner; + + private String[] prefixes = new String[4]; + private String[] nsUris = new String[4]; +// /** +// * True if the correponding namespace declaration is an authentic one that should be printed. +// * +// * False if it's a re-discovered in-scope namespace binding available at the ancestor elements +// * outside this marhsalling. The false value is used to incorporate the in-scope namespace binding +// * information from {@link #inscopeNamespaceContext}. When false, such a declaration does not need +// * to be printed, as it's already available in ancestors. +// */ +// private boolean[] visible = new boolean[4]; +// +// /** +// * {@link NamespaceContext} that informs this {@link XMLSerializer} about the +// * in-scope namespace bindings of the ancestor elements outside this marshalling. +// * +// *
+// * This is used when the marshaller is marshalling into a subtree that has ancestor +// * elements created outside the JAXB marshaller. +// * +// * Its {@link NamespaceContext#getPrefix(String)} is used to discover in-scope namespace +// * binding, +// */ +// private final NamespaceContext inscopeNamespaceContext; + + /** + * Number of URIs declared. Identifies the valid portion of + * the {@link #prefixes} and {@link #nsUris} arrays. + */ + private int size; + + private Element current; + + /** + * This is the {@link Element} whose prev==null. + * This element is used to hold the contextual namespace bindings + * that are assumed to be outside of the document we are marshalling. + * Specifically the xml prefix and any other user-specified bindings. + * + * @see NamespacePrefixMapper#getPreDeclaredNamespaceUris() + */ + private final Element top; + + /** + * Never null. + */ + private NamespacePrefixMapper prefixMapper = defaultNamespacePrefixMapper; + + /** + * True to allow new URIs to be declared. False otherwise. + */ + public boolean collectionMode; + + + public NamespaceContextImpl(XMLSerializer owner) { + this.owner = owner; + + current = top = new Element(this,null); + // register namespace URIs that are implicitly bound + put(XMLConstants.XML_NS_URI,XMLConstants.XML_NS_PREFIX); + } + + public void setPrefixMapper( NamespacePrefixMapper mapper ) { + if(mapper==null) + mapper = defaultNamespacePrefixMapper; + this.prefixMapper = mapper; + } + + public NamespacePrefixMapper getPrefixMapper() { + return prefixMapper; + } + + public void reset() { + current = top; + size = 1; + collectionMode = false; + } + + /** + * Returns the prefix index to the specified URI. + * This method allocates a new URI if necessary. + */ + public int declareNsUri( String uri, String preferedPrefix, boolean requirePrefix ) { + preferedPrefix = prefixMapper.getPreferredPrefix(uri,preferedPrefix,requirePrefix); + + if(uri.length()==0) { + for( int i=size-1; i>=0; i-- ) { + if(nsUris[i].length()==0) + return i; // already declared + if(prefixes[i].length()==0) { + // the default prefix is already taken. + // move that URI to another prefix, then assign "" to the default prefix. + assert current.defaultPrefixIndex==-1 && current.oldDefaultNamespaceUriIndex==-1; + + String oldUri = nsUris[i]; + String[] knownURIs = owner.nameList.namespaceURIs; + + if(current.baseIndex<=i) { + // this default prefix is declared in this context. just reassign it + + nsUris[i] = ""; + + int subst = put(oldUri,null); + + // update uri->prefix table if necessary + for( int j=knownURIs.length-1; j>=0; j-- ) { + if(knownURIs[j].equals(oldUri)) { + owner.knownUri2prefixIndexMap[j] = subst; + break; + } + } + if (current.elementLocalName != null) { + current.setTagName(subst, current.elementLocalName, current.getOuterPeer()); + } + return i; + } else { + // first, if the previous URI assigned to "" is + // a "known URI", remember what we've reallocated + // so that we can fix it when this context pops. + for( int j=knownURIs.length-1; j>=0; j-- ) { + if(knownURIs[j].equals(oldUri)) { + current.defaultPrefixIndex = i; + current.oldDefaultNamespaceUriIndex = j; + // assert commented out; too strict/not valid any more + // assert owner.knownUri2prefixIndexMap[j]==current.defaultPrefixIndex; + // update the table to point to the prefix we'll declare + owner.knownUri2prefixIndexMap[j] = size; + break; + } + } + if (current.elementLocalName!=null) { + current.setTagName(size, current.elementLocalName, current.getOuterPeer()); + } + + put(nsUris[i],null); + return put("", ""); + } + } + } + + // "" isn't in use + return put("", ""); + } else { + // check for the existing binding + for( int i=size-1; i>=0; i-- ) { + String p = prefixes[i]; + if(nsUris[i].equals(uri)) { + if (!requirePrefix || p.length()>0) + return i; + // declared but this URI is bound to empty. Look further + } + if(p.equals(preferedPrefix)) { + // the suggested prefix is already taken. can't use it + preferedPrefix = null; + } + } + + if(preferedPrefix==null && requirePrefix) + // we know we can't bind to "", but we don't have any possible name at hand. + // generate it here to avoid this namespace to be bound to "". + preferedPrefix = makeUniquePrefix(); + + // haven't been declared. allocate a new one + // if the preferred prefix is already in use, it should have been set to null by this time + return put(uri, preferedPrefix); + } + } + + public int force(@NotNull String uri, @NotNull String prefix) { + // check for the existing binding + + for( int i=size-1; i>=0; i-- ) { + if(prefixes[i].equals(prefix)) { + if(nsUris[i].equals(uri)) + return i; // found duplicate + else + // the prefix is used for another namespace. we need to declare it + break; + } + } + + return put(uri, prefix); + } + + /** + * Puts this new binding into the declared prefixes list + * without doing any duplicate check. + * + * This can be used to forcibly set namespace declarations. + * + *
+ * Most of the time {@link #declareNamespace(String, String, boolean)} shall be used.
+ *
+ * @return
+ * the index of this new binding.
+ */
+ public int put(@NotNull String uri, @Nullable String prefix) {
+ if(size==nsUris.length) {
+ // reallocate
+ String[] u = new String[nsUris.length*2];
+ String[] p = new String[prefixes.length*2];
+ System.arraycopy(nsUris,0,u,0,nsUris.length);
+ System.arraycopy(prefixes,0,p,0,prefixes.length);
+ nsUris = u;
+ prefixes = p;
+ }
+ if(prefix==null) {
+ if(size==1)
+ prefix = ""; // if this is the first user namespace URI we see, use "".
+ else {
+ // otherwise make up an unique name
+ prefix = makeUniquePrefix();
+ }
+ }
+ nsUris[size] = uri;
+ prefixes[size] = prefix;
+
+ return size++;
+ }
+
+ private String makeUniquePrefix() {
+ String prefix;
+ prefix = new StringBuilder(5).append("ns").append(size).toString();
+ while(getNamespaceURI(prefix)!=null) {
+ prefix += '_'; // under a rare circumstance there might be existing 'nsNNN', so rename them
+ }
+ return prefix;
+ }
+
+
+ public Element getCurrent() {
+ return current;
+ }
+
+ /**
+ * Returns the prefix index of the specified URI.
+ * It is an error if the URI is not declared.
+ */
+ public int getPrefixIndex( String uri ) {
+ for( int i=size-1; i>=0; i-- ) {
+ if(nsUris[i].equals(uri))
+ return i;
+ }
+ throw new IllegalStateException();
+ }
+
+ /**
+ * Gets the prefix from a prefix index.
+ *
+ * The behavior is undefined if the index is out of range.
+ */
+ public String getPrefix(int prefixIndex) {
+ return prefixes[prefixIndex];
+ }
+
+ public String getNamespaceURI(int prefixIndex) {
+ return nsUris[prefixIndex];
+ }
+
+ /**
+ * Gets the namespace URI that is bound to the specified prefix.
+ *
+ * @return null
+ * if the prefix is unbound.
+ */
+ public String getNamespaceURI(String prefix) {
+ for( int i=size-1; i>=0; i-- )
+ if(prefixes[i].equals(prefix))
+ return nsUris[i];
+ return null;
+ }
+
+ /**
+ * Returns the prefix of the specified URI,
+ * or null if none exists.
+ */
+ public String getPrefix( String uri ) {
+ if(collectionMode) {
+ return declareNamespace(uri,null,false);
+ } else {
+ for( int i=size-1; i>=0; i-- )
+ if(nsUris[i].equals(uri))
+ return prefixes[i];
+ return null;
+ }
+ }
+
+ public Iterator
+ *
+ * int depth() {
+ * int i=-1;
+ * for(Element e=this; e!=null;e=e.prev)
+ * i++;
+ * return i;
+ * }
+ *
+ */
+ private final int depth;
+
+
+
+ private int elementNamePrefix;
+ private String elementLocalName;
+
+ /**
+ * Tag name of this element.
+ * Either this field is used or the {@link #elementNamePrefix} and {@link #elementLocalName} pair.
+ */
+ private Name elementName;
+
+ /**
+ * Used for the binder. The JAXB object that corresponds to this element.
+ */
+ private Object outerPeer;
+ private Object innerPeer;
+
+
+ private Element(NamespaceContextImpl context,Element prev) {
+ this.context = context;
+ this.prev = prev;
+ this.depth = (prev==null) ? 0 : prev.depth+1;
+ }
+
+ /**
+ * Returns true if this {@link Element} represents the root element that
+ * we are marshalling.
+ */
+ public boolean isRootElement() {
+ return depth==1;
+ }
+
+ public Element push() {
+ if(next==null)
+ next = new Element(context,this);
+ next.onPushed();
+ return next;
+ }
+
+ public Element pop() {
+ if(oldDefaultNamespaceUriIndex>=0) {
+ // restore the old default namespace URI binding
+ context.owner.knownUri2prefixIndexMap[oldDefaultNamespaceUriIndex] = defaultPrefixIndex;
+ }
+ context.size = baseIndex;
+ context.current = prev;
+ // release references to user objects
+ outerPeer = innerPeer = null;
+ return prev;
+ }
+
+ private void onPushed() {
+ oldDefaultNamespaceUriIndex = defaultPrefixIndex = -1;
+ baseIndex = context.size;
+ context.current = this;
+ }
+
+ public void setTagName( int prefix, String localName, Object outerPeer ) {
+ assert localName!=null;
+ this.elementNamePrefix = prefix;
+ this.elementLocalName = localName;
+ this.elementName = null;
+ this.outerPeer = outerPeer;
+ }
+
+ public void setTagName( Name tagName, Object outerPeer ) {
+ assert tagName!=null;
+ this.elementName = tagName;
+ this.outerPeer = outerPeer;
+ }
+
+ public void startElement(XmlOutput out, Object innerPeer) throws IOException, XMLStreamException {
+ this.innerPeer = innerPeer;
+ if(elementName!=null) {
+ out.beginStartTag(elementName);
+ } else {
+ out.beginStartTag(elementNamePrefix,elementLocalName);
+ }
+ }
+
+ public void endElement(XmlOutput out) throws IOException, SAXException, XMLStreamException {
+ if(elementName!=null) {
+ out.endTag(elementName);
+ elementName = null;
+ } else {
+ out.endTag(elementNamePrefix,elementLocalName);
+ }
+ }
+
+ /**
+ * Gets the number of bindings declared on this element.
+ */
+ public final int count() {
+ return context.size-baseIndex;
+ }
+
+ /**
+ * Gets the prefix declared in this context.
+ *
+ * @param idx
+ * between 0 and {@link #count()}
+ */
+ public final String getPrefix(int idx) {
+ return context.prefixes[baseIndex+idx];
+ }
+
+ /**
+ * Gets the namespace URI declared in this context.
+ *
+ * @param idx
+ * between 0 and {@link #count()}
+ */
+ public final String getNsUri(int idx) {
+ return context.nsUris[baseIndex+idx];
+ }
+
+ public int getBase() {
+ return baseIndex;
+ }
+
+ public Object getOuterPeer() {
+ return outerPeer;
+ }
+
+ public Object getInnerPeer() {
+ return innerPeer;
+ }
+
+ /**
+ * Gets the parent {@link Element}.
+ */
+ public Element getParent() {
+ return prev;
+ }
+ }
+
+
+ /**
+ * Default {@link NamespacePrefixMapper} implementation used when
+ * it is not specified by the user.
+ */
+ private static final NamespacePrefixMapper defaultNamespacePrefixMapper = new NamespacePrefixMapper() {
+ public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
+ if( namespaceUri.equals(WellKnownNamespace.XML_SCHEMA_INSTANCE) )
+ return "xsi";
+ if( namespaceUri.equals(WellKnownNamespace.XML_SCHEMA) )
+ return "xs";
+ if( namespaceUri.equals(WellKnownNamespace.XML_MIME_URI) )
+ return "xmime";
+ return suggestion;
+ }
+ };
+}