diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java index 0eeaddfbd95..aedddef5497 100644 --- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java +++ b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java @@ -16,15 +16,15 @@ */ package org.apache.jackrabbit.core.state; -import org.apache.commons.collections.map.LinkedMap; import org.apache.jackrabbit.core.id.NodeId; -import org.apache.jackrabbit.core.util.EmptyLinkedMap; +import org.apache.jackrabbit.core.util.EmptyLinkedHashMap; import org.apache.jackrabbit.spi.Name; import java.util.List; import java.util.HashMap; import java.util.Collections; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -34,11 +34,21 @@ */ class ChildNodeEntries implements Cloneable { + @SuppressWarnings("unchecked") + private static final LinkedHashMap EMPTY_ENTRIES = EmptyLinkedHashMap.INSTANCE; + + // The JavaDoc for Collections.emptyMap() states: + // "Implementations of this method need not create a separate Map object + // for each call." + // https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#emptyMap-- + // This indicates that an implementation *might* return a separate Map object. + private static final Map EMPTY_NAME_MAP = Collections.emptyMap(); + /** * Insertion-ordered map of entries * (key=NodeId, value=entry) */ - private LinkedMap entries; + private LinkedHashMap entries; /** * Map used for lookup by name @@ -57,7 +67,7 @@ class ChildNodeEntries implements Cloneable { } ChildNodeEntry get(NodeId id) { - return (ChildNodeEntry) entries.get(id); + return entries.get(id); } @SuppressWarnings("unchecked") @@ -193,7 +203,7 @@ public ChildNodeEntry remove(Name nodeName, int index) { } // clean up name lookup map if necessary - if (siblings.size() == 0) { + if (siblings.isEmpty()) { // no more entries with that name left: // remove from name lookup map as well nameMap.remove(nodeName); @@ -208,13 +218,13 @@ public ChildNodeEntry remove(Name nodeName, int index) { } /** - * Removes the child node entry refering to the node with the given id. + * Removes the child node entry referring to the node with the given id. * * @param id id of node whose entry is to be removed. * @return the removed entry or null if there is no such entry. */ ChildNodeEntry remove(NodeId id) { - ChildNodeEntry entry = (ChildNodeEntry) entries.get(id); + ChildNodeEntry entry = entries.get(id); if (entry != null) { return remove(entry.getName(), entry.getIndex()); } @@ -260,8 +270,7 @@ List removeAll(ChildNodeEntries other) { } List result = new ArrayList(); - for (Object e : entries.values()) { - ChildNodeEntry entry = (ChildNodeEntry) e; + for (ChildNodeEntry entry : entries.values()) { ChildNodeEntry otherEntry = other.get(entry.getId()); if (entry == otherEntry) { continue; @@ -294,8 +303,7 @@ List retainAll(ChildNodeEntries other) { } List result = new ArrayList(); - for (Object e : entries.values()) { - ChildNodeEntry entry = (ChildNodeEntry) e; + for (ChildNodeEntry entry : entries.values()) { ChildNodeEntry otherEntry = other.get(entry.getId()); if (entry == otherEntry) { result.add(entry); @@ -313,15 +321,13 @@ public boolean isEmpty() { return entries.isEmpty(); } - @SuppressWarnings("unchecked") public List list() { return new ArrayList(entries.values()); } public List getRenamedEntries(ChildNodeEntries that) { List renamed = Collections.emptyList(); - for (Object e : entries.values()) { - ChildNodeEntry entry = (ChildNodeEntry) e; + for (ChildNodeEntry entry : entries.values()) { ChildNodeEntry other = that.get(entry.getId()); if (other != null && !entry.getName().equals(other.getName())) { // child node entry with same id but different name exists in @@ -375,7 +381,7 @@ public int hashCode() { protected Object clone() { try { ChildNodeEntries clone = (ChildNodeEntries) super.clone(); - if (nameMap != Collections.EMPTY_MAP) { + if (nameMap != EMPTY_NAME_MAP) { clone.shared = true; shared = true; } @@ -392,8 +398,8 @@ protected Object clone() { * Initializes the name and entries map with unmodifiable empty instances. */ private void init() { - nameMap = Collections.emptyMap(); - entries = EmptyLinkedMap.INSTANCE; + nameMap = EMPTY_NAME_MAP; + entries = EMPTY_ENTRIES; shared = false; } @@ -403,11 +409,11 @@ private void init() { */ @SuppressWarnings("unchecked") private void ensureModifiable() { - if (nameMap == Collections.EMPTY_MAP) { + if (nameMap == EMPTY_NAME_MAP) { nameMap = new HashMap(); - entries = new LinkedMap(); + entries = new LinkedHashMap(); } else if (shared) { - entries = (LinkedMap) entries.clone(); + entries = (LinkedHashMap) entries.clone(); nameMap = new HashMap(nameMap); for (Map.Entry entry : nameMap.entrySet()) { Object value = entry.getValue(); diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/EmptyLinkedHashMap.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/EmptyLinkedHashMap.java new file mode 100644 index 00000000000..4a82ead8879 --- /dev/null +++ b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/EmptyLinkedHashMap.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.core.util; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +public class EmptyLinkedHashMap extends LinkedHashMap { + + public static final EmptyLinkedHashMap INSTANCE = new EmptyLinkedHashMap(); + + private EmptyLinkedHashMap() { } + + @Override + public V put(K key, V value) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(Map m) { + throw new UnsupportedOperationException(); + } + + @Override + public V remove(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public Set keySet() { + return Collections.emptySet(); + } + + @Override + public Set> entrySet() { + return Collections.emptySet(); + } + + @Override + public Collection values() { + return Collections.emptySet(); + } + + @Override + public Object clone() { + return this; + } +}