diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/NamespaceResolver.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/NamespaceResolver.java index 91f8ce8db4e..2ab48d40780 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/NamespaceResolver.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/NamespaceResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,9 +14,10 @@ // Oracle - initial API and implementation from Oracle TopLink package org.eclipse.persistence.internal.oxm; +import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; -import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Vector; @@ -58,14 +59,15 @@ */ public class NamespaceResolver implements XMLNamespaceResolver { private static final String BASE_PREFIX = "ns"; + private static final Vector EMPTY_VECTOR = VectorUtils.emptyVector(); private String defaultNamespaceURI; - private Map prefixesToNamespaces; - int prefixCounter; + private NamespaceResolverStorage prefixesToNamespaces; + private int prefixCounter; private Node dom; /** - * Default constructor, creates a new NamespaceResolver. + * Default constructor, creates a new NamespaceResolver. */ public NamespaceResolver() { super(); @@ -77,31 +79,36 @@ public NamespaceResolver() { */ public NamespaceResolver(NamespaceResolver namespaceResolver) { this.defaultNamespaceURI = namespaceResolver.defaultNamespaceURI; - Map namespaceResolverPrefixesToNamespaces = namespaceResolver.prefixesToNamespaces; - if(namespaceResolverPrefixesToNamespaces != null) { - this.prefixesToNamespaces = new LinkedHashMap(namespaceResolverPrefixesToNamespaces.size()); - this.prefixesToNamespaces.putAll(namespaceResolver.prefixesToNamespaces); - } + setPrefixesToNamespaces(namespaceResolver.prefixesToNamespaces); + this.prefixCounter = namespaceResolver.prefixCounter; this.dom = namespaceResolver.dom; } + private void setPrefixesToNamespaces(Map input) { + if (input == null) { + return; + } + prefixesToNamespaces = new NamespaceResolverStorage(input.size()); + prefixesToNamespaces.putAll(input); + } + public Map getPrefixesToNamespaces() { - if(null == prefixesToNamespaces) { - prefixesToNamespaces = new LinkedHashMap(); + if (null == prefixesToNamespaces) { + prefixesToNamespaces = new NamespaceResolverStorage(); } return prefixesToNamespaces; } public boolean hasPrefixesToNamespaces() { - return null != prefixesToNamespaces; + return null != prefixesToNamespaces; } - /** Indicates whether given {@code prefix} is assigned to a name-space. - * + /** + * Indicates whether given {@code prefix} is assigned to a name-space. * @param prefix name-space prefix * @return {@code true} if {@code prefix} is present in prefix to name-space map - * ({@link #getPrefixesToNamespaces()} + * ({@link #getPrefixesToNamespaces()} */ public boolean hasPrefix(String prefix) { return null != prefixesToNamespaces ? prefixesToNamespaces.containsKey(prefix) : false; @@ -112,9 +119,8 @@ public void setDOM(Node dom) { } /** - * Returns the namespace URI associated with a specified namespace prefix - * - * @param prefix The prefix to lookup a namespace URI for + * Returns the namespace URI associated with a specified namespace prefix + * @param prefix The prefix to lookup a namespace URI for * @return The namespace URI associated with the specified prefix */ @Override @@ -123,17 +129,17 @@ public String resolveNamespacePrefix(String prefix) { return defaultNamespaceURI; } String uri = null; - if(null != prefixesToNamespaces) { + if (null != prefixesToNamespaces) { uri = prefixesToNamespaces.get(prefix); } - if(null != uri) { + if (null != uri) { return uri; } else if (javax.xml.XMLConstants.XML_NS_PREFIX.equals(prefix)) { return javax.xml.XMLConstants.XML_NS_URI; } else if (javax.xml.XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) { return javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI; } - if(dom != null) { + if (dom != null) { return XMLPlatformFactory.getInstance().getXMLPlatform().resolveNamespacePrefix(dom, prefix); } return null; @@ -145,12 +151,12 @@ public String resolveNamespacePrefix(String prefix) { * @return The prefix associated with the namespace URI. */ public String resolveNamespaceURI(String uri) { - if(null == uri) { + if (null == uri) { return null; } - if(null != prefixesToNamespaces) { - for(Entry entry : prefixesToNamespaces.entrySet()) { - if(uri.equals(entry.getValue())) { + if (null != prefixesToNamespaces) { + for (Entry entry : prefixesToNamespaces.entrySet()) { + if (uri.equals(entry.getValue())) { return entry.getKey(); } } @@ -164,24 +170,25 @@ public String resolveNamespaceURI(String uri) { } private String resolveNamespaceURI(Node node, String uri) { - if(null == node) { + if (null == node) { return null; } // If the element is of the same namespace URI, then return the prefix. - if(uri.equals(node.getNamespaceURI())) { + if (uri.equals(node.getNamespaceURI())) { return node.getPrefix(); } // Check the namespace URI declarations. NamedNodeMap namedNodeMap = node.getAttributes(); - if(null != namedNodeMap) { + if (null != namedNodeMap) { int namedNodeMapSize = namedNodeMap.getLength(); - for(int x=0; x removedItems = null; - if (getPrefixesToNamespaces().containsValue(namespaceURI.intern())) { - removedItems = new LinkedHashMap(); - for (Map.Entry prefixEntry : getPrefixesToNamespaces().entrySet()) { - if (namespaceURI.intern().equals(prefixEntry.getValue())) { - removedItems.put(prefixEntry.getKey(), prefixEntry.getValue()); + List removedKeys = null; + final String cachedJvmValue = namespaceURI.intern(); + if (getPrefixesToNamespaces().containsValue(cachedJvmValue)) { + removedKeys = new ArrayList<>(); + for (Map.Entry prefixEntry : prefixesToNamespaces.entrySet()) { + if (cachedJvmValue.equals(prefixEntry.getValue())) { + removedKeys.add(prefixEntry.getKey()); } } } - if (null != removedItems) { - for (String key : removedItems.keySet()) { - getPrefixesToNamespaces().remove(key); + if (null != removedKeys) { + for (String key : removedKeys) { + prefixesToNamespaces.remove(key); } } - getPrefixesToNamespaces().put(prefix, namespaceURI.intern()); - if (null != removedItems) { - for (Map.Entry removedEntry : removedItems.entrySet()) { - getPrefixesToNamespaces().put(removedEntry.getKey(), removedEntry.getValue()); + prefixesToNamespaces.put(prefix, cachedJvmValue); + if (null != removedKeys) { + for (String key : removedKeys) { + prefixesToNamespaces.put(key, cachedJvmValue); } } } } /** - * Returns the list of prefixes in the NamespaceResolver - * + * Returns the list of prefixes in the NamespaceResolver * @return An Enumeration containing the prefixes in the NamespaceResolver */ public Enumeration getPrefixes() { - if(hasPrefixesToNamespaces()) { + if (hasPrefixesToNamespaces()) { return new IteratorEnumeration(getPrefixesToNamespaces().keySet().iterator()); } else { return new IteratorEnumeration(null); @@ -251,36 +257,27 @@ public Enumeration getPrefixes() { } /** - * INTERNAL: + * INTERNAL: * Returns a Vector of of Namespace objects in the current Namespace Resolver * Used for deployment XML - * @return A Vector containing the namespace URIs in the namespace resolver + * @return A Vector containing the namespace URIs in the namespace resolver */ public Vector getNamespaces() { - if(!hasPrefixesToNamespaces()) { - return new Vector(0); - } - Vector names = new Vector(prefixesToNamespaces.size()); - for(Entry entry: prefixesToNamespaces.entrySet()) { - Namespace namespace = new Namespace(entry.getKey(), entry.getValue()); - names.addElement(namespace); + if (!hasPrefixesToNamespaces()) { + return EMPTY_VECTOR; } - return names; + return prefixesToNamespaces.getNamespaces(); } /** - * INTERNAL: + * INTERNAL: * Set the namespaces on the namespace resolver based on the specified Vector of Namespace objects * Used for deployment XML - * @param names A Vector of namespace URIs + * @param names A Vector of namespace URIs */ public void setNamespaces(Vector names) { - prefixesToNamespaces = new LinkedHashMap(names.size()); - for(Namespace namespace : (Vector) names) { - if ((namespace.getPrefix() != null) && (namespace.getNamespaceURI() != null)) { - prefixesToNamespaces.put(namespace.getPrefix(), namespace.getNamespaceURI()); - } - } + prefixesToNamespaces = new NamespaceResolverStorage(names.size()); + prefixesToNamespaces.setNamespaces(names); } public String generatePrefix() { @@ -301,23 +298,23 @@ public String generatePrefix(String defaultPrefix) { } public void removeNamespace(String prefix) { - if(null != prefixesToNamespaces) { + if (null != prefixesToNamespaces) { prefixesToNamespaces.remove(prefix); } } public void setDefaultNamespaceURI(String namespaceUri) { - if(namespaceUri == null){ + if (namespaceUri == null) { defaultNamespaceURI = null; - }else{ + } else { defaultNamespaceURI = namespaceUri.intern(); } } public String getDefaultNamespaceURI() { - if(null != defaultNamespaceURI) { + if (null != defaultNamespaceURI) { return defaultNamespaceURI; - } else if(dom != null) { + } else if (dom != null) { return XMLPlatformFactory.getInstance().getXMLPlatform().resolveNamespacePrefix(dom, null); } return null; @@ -333,7 +330,7 @@ public IteratorEnumeration(Iterator iterator) { @Override public boolean hasMoreElements() { - if(null == iterator) { + if (null == iterator) { return false; } return iterator.hasNext(); diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/NamespaceResolverStorage.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/NamespaceResolverStorage.java new file mode 100644 index 00000000000..3321005094d --- /dev/null +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/NamespaceResolverStorage.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package org.eclipse.persistence.internal.oxm; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.Vector; +import java.util.function.BiFunction; + +import static org.eclipse.persistence.internal.oxm.VectorUtils.emptyVector; +import static org.eclipse.persistence.internal.oxm.VectorUtils.unmodifiableVector; + +public class NamespaceResolverStorage extends LinkedHashMap { + private static final long serialVersionUID = -4697397620139076774L; + private transient Vector namespaces = emptyVector(); + private transient boolean modified = false; + + public NamespaceResolverStorage() { + super(); + } + + public NamespaceResolverStorage(int initialCapacity) { + super(initialCapacity); + } + + @Override + public String put(String key, String value) { + String response = super.put(key, value); + setModified(); + return response; + } + + private void setModified() { + setModified(true); + } + + @Override + public void putAll(Map m) { + super.putAll(m); + setModified(); + } + + @Override + public String remove(Object key) { + if (containsKey(key)) { + setModified(); + } + + return super.remove(key); + } + + @Override + public String putIfAbsent(String key, String value) { + String response = super.putIfAbsent(key, value); + setModified(); + return response; + } + + @Override + public boolean remove(Object key, Object value) { + boolean response = super.remove(key, value); + if (response) { + setModified(); + } + return response; + } + + @Override + public boolean replace(String key, String oldValue, String newValue) { + boolean response = super.replace(key, oldValue, newValue); + if (response) { + setModified(response); + } + return response; + } + + @Override + public String replace(String key, String value) { + String response = super.replace(key, value); + setModified(); + return response; + } + + public Vector getNamespaces() { + if (isModified()) { + namespaces = buildNamespacesUnmodifiable(); + setModified(false); + } + return namespaces; + } + + public void setNamespaces(Vector namespaces) { + super.clear(); + for (Namespace namespace : (Vector) namespaces) { + if ((namespace.getPrefix() != null) && (namespace.getNamespaceURI() != null)) { + super.put(namespace.getPrefix(), namespace.getNamespaceURI()); + } + } + setModified(); + } + + private boolean isModified() { + return modified; + } + + private void setModified(boolean modified) { + this.modified = modified; + } + + private Vector buildNamespacesUnmodifiable() { + Vector names = new Vector(size()); + for (Map.Entry entry : entrySet()) { + Namespace namespace = new Namespace(entry.getKey(), entry.getValue()); + names.addElement(namespace); + } + return unmodifiableVector(names); + } + + /** + * Unmodifiable set of keys + */ + @Override + public Set keySet() { + return Collections.unmodifiableSet(super.keySet()); + } + + /** + * Unmodifiable collection of values + */ + @Override + public Collection values() { + return Collections.unmodifiableCollection(super.values()); + } + + /** + * Unmodifiable set of entries + */ + @Override + public Set> entrySet() { + return Collections.unmodifiableSet(super.entrySet()); + } + + @Override + public void replaceAll(BiFunction function) { + super.replaceAll(function); + setModified(); + } +} diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/VectorUtils.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/VectorUtils.java new file mode 100644 index 00000000000..f265ac012cc --- /dev/null +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/oxm/VectorUtils.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package org.eclipse.persistence.internal.oxm; + +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Objects; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.Vector; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +import static java.util.Collections.emptyIterator; +import static java.util.Collections.emptyListIterator; + +/** + * This class provides "empty" and "unmodifiable" wrappers for the Vector class. + * The functionality is similar the Collections class and mostly copy-paste from the class. + * Unfortunately, the standard Collections class does not provides wrappers for the outdated Vector class. + * + * @see Collections + */ +public class VectorUtils { + /** + * The empty vector (immutable). + * @see #emptyVector() + */ + public static final Vector EMPTY_VECTOR = new EmptyVector(); + + /** + * Returns an unmodifiable view of the specified vector. This method allows + * modules to provide users with "read-only" access to internal + * vectors. Query operations on the returned vector "read through" to the + * specified vector, and attempts to modify the returned vector, whether + * direct or via its iterator, result in an + * UnsupportedOperationException.

+ * + * @param the class of the objects in the vector + * @param vector the vector for which an unmodifiable view is to be returned. + * @return an unmodifiable view of the specified vector. + */ + public static Vector unmodifiableVector(Vector vector) { + if (vector == null) { + throw new IllegalArgumentException("Input value must not be NULL!"); + } + return new UnmodifiableVector<>(vector); + } + + /** + * Returns an empty vector (immutable). + * + *

This example illustrates the type-safe way to obtain an empty vector: + *

+     *     Vector<String> s = VectorUtils.emptyVector();
+     * 
+ * @param type of elements, if there were any, in the vector + * @return an empty immutable vector + * @implNote Implementations of this method need not create a separate Vector + * object for each call. Using this method is likely to have comparable + * cost to using the like-named field. (Unlike this method, the field does + * not provide type safety.) + * @see #EMPTY_VECTOR + */ + @SuppressWarnings("unchecked") + public static final Vector emptyVector() { + return (Vector) EMPTY_VECTOR; + } + + private VectorUtils() {} + + private static class EmptyVector extends Vector { + private static final long serialVersionUID = 5020332176914113951L; + + @Override + public int size() {return 0;} + + @Override + public boolean isEmpty() {return true;} + + @Override + public boolean contains(Object obj) {return false;} + + @Override + public Object[] toArray() { return new Object[0]; } + + @Override + public T[] toArray(T[] a) { + if (a.length > 0) { + a[0] = null; + } + return a; + } + + @Override + public E get(int index) { + throw new IndexOutOfBoundsException("Index: " + index); + } + + @Override + public void add(int index, E element) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(Collection c) { return c.isEmpty(); } + + @Override + public boolean equals(Object o) { + return (o instanceof Vector) && ((Vector) o).isEmpty(); + } + + @Override + public int hashCode() { return 1; } + + @Override + public ListIterator listIterator(int index) { + return emptyListIterator(); + } + + @Override + public ListIterator listIterator() { + return emptyListIterator(); + } + + @Override + public Iterator iterator() { + return emptyIterator(); + } + + // Override default methods in Collection + @Override + public void forEach(Consumer action) { + Objects.requireNonNull(action); + } + + @Override + public boolean removeIf(Predicate filter) { + Objects.requireNonNull(filter); + return false; + } + + @Override + public void replaceAll(UnaryOperator operator) { + Objects.requireNonNull(operator); + } + + @Override + public void sort(Comparator c) { + } + + @Override + public Spliterator spliterator() { return Spliterators.emptySpliterator(); } + + // Preserves singleton property + private Object readResolve() { + return EMPTY_VECTOR; + } + } + + /** + * @serial include + */ + static class UnmodifiableVector extends Vector { + private static final long serialVersionUID = -8378199697360550972L; + + UnmodifiableVector(Vector vector) { + super(vector); + } + + public E set(int index, E element) { + throw new UnsupportedOperationException(); + } + + public void add(int index, E element) { + throw new UnsupportedOperationException(); + } + + public E remove(int index) { + throw new UnsupportedOperationException(); + } + + public boolean addAll(int index, Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public List subList(int fromIndex, int toIndex) { + return Collections.unmodifiableList(super.subList(fromIndex, toIndex)); + } + + public ListIterator listIterator(final int index) { + return new ListIterator() { + private final ListIterator i + = listIterator(index); + + public boolean hasNext() {return i.hasNext();} + + public E next() {return i.next();} + + public boolean hasPrevious() {return i.hasPrevious();} + + public E previous() {return i.previous();} + + public int nextIndex() {return i.nextIndex();} + + public int previousIndex() {return i.previousIndex();} + + public void remove() { + throw new UnsupportedOperationException(); + } + + public void set(E e) { + throw new UnsupportedOperationException(); + } + + public void add(E e) { + throw new UnsupportedOperationException(); + } + + @Override + public void forEachRemaining(Consumer action) { + i.forEachRemaining(action); + } + }; + } + + public ListIterator listIterator() {return listIterator(0);} + + @Override + public void replaceAll(UnaryOperator operator) { + throw new UnsupportedOperationException(); + } + + @Override + public void sort(Comparator c) { + throw new UnsupportedOperationException(); + } + } +}