You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by mr...@apache.org on 2008/09/03 15:10:25 UTC
svn commit: r691603 - in
/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core:
state/ChildNodeEntries.java state/NameSet.java state/NodeState.java
util/EmptyLinkedMap.java
Author: mreutegg
Date: Wed Sep 3 06:10:23 2008
New Revision: 691603
URL: http://svn.apache.org/viewvc?rev=691603&view=rev
Log:
JCR-1705: Reduce memory usage of transient nodes
Added:
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NameSet.java (with props)
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/EmptyLinkedMap.java (with props)
Modified:
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java?rev=691603&r1=691602&r2=691603&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ChildNodeEntries.java Wed Sep 3 06:10:23 2008
@@ -20,6 +20,7 @@
import org.apache.commons.collections.MapIterator;
import org.apache.commons.collections.OrderedMapIterator;
import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.util.EmptyLinkedMap;
import org.apache.jackrabbit.spi.Name;
import java.util.List;
@@ -29,6 +30,7 @@
import java.util.Iterator;
import java.util.Collection;
import java.util.ListIterator;
+import java.util.Map;
/**
* <code>ChildNodeEntries</code> represents an insertion-ordered
@@ -40,15 +42,26 @@
*/
class ChildNodeEntries implements List, Cloneable {
- // insertion-ordered map of entries (key=NodeId, value=entry)
+ /**
+ * Insertion-ordered map of entries
+ * (key=NodeId, value=entry)
+ */
private LinkedMap entries;
- // map used for lookup by name
- // (key=name, value=either a single entry or a list of sns entries)
- private HashMap nameMap;
+
+ /**
+ * Map used for lookup by name
+ * (key=name, value=either a single entry or a list of sns entries)
+ */
+ private Map nameMap;
+
+ /**
+ * Indicates whether the entries and nameMap are shared with another
+ * ChildNodeEntries instance.
+ */
+ private boolean shared;
ChildNodeEntries() {
- entries = new LinkedMap();
- nameMap = new HashMap();
+ init();
}
ChildNodeEntry get(NodeId id) {
@@ -94,6 +107,7 @@
}
ChildNodeEntry add(Name nodeName, NodeId id) {
+ ensureModifiable();
List siblings = null;
int index = 0;
Object obj = nameMap.get(nodeName);
@@ -143,6 +157,7 @@
throw new IllegalArgumentException("index is 1-based");
}
+ ensureModifiable();
Object obj = nameMap.get(nodeName);
if (obj == null) {
return null;
@@ -223,8 +238,7 @@
* Removes all child node entries
*/
public void removeAll() {
- nameMap.clear();
- entries.clear();
+ init();
}
/**
@@ -434,6 +448,7 @@
}
//------------------------------------------------< Cloneable support >
+
/**
* Returns a shallow copy of this <code>ChildNodeEntries</code> instance;
* the entries themselves are not cloned.
@@ -441,19 +456,50 @@
* @return a shallow copy of this instance.
*/
protected Object clone() {
- ChildNodeEntries clone = new ChildNodeEntries();
- clone.entries = (LinkedMap) entries.clone();
- clone.nameMap = new HashMap(nameMap.size());
- for (Iterator it = nameMap.keySet().iterator(); it.hasNext();) {
- Object key = it.next();
- Object obj = nameMap.get(key);
- if (obj instanceof ArrayList) {
- // clone List
- obj = ((ArrayList) obj).clone();
+ try {
+ ChildNodeEntries clone = (ChildNodeEntries) super.clone();
+ if (nameMap != Collections.EMPTY_MAP) {
+ clone.shared = true;
+ shared = true;
+ }
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ // never happens, this class is cloneable
+ throw new InternalError();
+ }
+ }
+
+ //-------------------------------------------------------------< internal >
+
+ /**
+ * Initializes the name and entries map with unmodifiable empty instances.
+ */
+ private void init() {
+ nameMap = Collections.EMPTY_MAP;
+ entries = EmptyLinkedMap.INSTANCE;
+ shared = false;
+ }
+
+ /**
+ * Ensures that the {@link #nameMap} and {@link #entries} map are
+ * modifiable.
+ */
+ private void ensureModifiable() {
+ if (nameMap == Collections.EMPTY_MAP) {
+ nameMap = new HashMap();
+ entries = new LinkedMap();
+ } else if (shared) {
+ entries = (LinkedMap) entries.clone();
+ nameMap = (Map) ((HashMap) nameMap).clone();
+ for (Iterator it = nameMap.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry entry = (Map.Entry) it.next();
+ Object value = entry.getValue();
+ if (value instanceof ArrayList) {
+ entry.setValue(((ArrayList) value).clone());
+ }
}
- clone.nameMap.put(key, obj);
+ shared = false;
}
- return clone;
}
//----------------------------------------------------< inner classes >
Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NameSet.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NameSet.java?rev=691603&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NameSet.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NameSet.java Wed Sep 3 06:10:23 2008
@@ -0,0 +1,352 @@
+/*
+ * 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.state;
+
+import org.apache.jackrabbit.spi.Name;
+
+import java.util.Set;
+import java.util.Iterator;
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * <code>NameSet</code> implements a collection of unique {@link Name}s. The
+ * methods exposed via the {@link Set} interface are for read only access, which
+ * means this implementation will throw a {@link UnsupportedOperationException}
+ * for all modifying methods specified by the {@link Set} interface.
+ */
+final class NameSet implements Set, Cloneable {
+
+ /**
+ * The name set cache instance.
+ */
+ private static final NameSetCache CACHE = new NameSetCache();
+
+ /**
+ * The maximum number of names in a set that are cached.
+ */
+ private static final int NUM_NAMES_THRESHOLD = 5;
+
+ /**
+ * The set of property {@link Name}s.
+ */
+ private HashSet names = CACHE.getEmptySet();
+
+ /**
+ * Flag indicating whether the {@link #names} set is shared with another
+ * {@link NameSet} instance. The initial value is <code>true</code> because
+ * {@link #names} is initialized with an empty set from the cache.
+ */
+ private boolean shared = true;
+
+ /**
+ * Adds a <code>name</code>.
+ *
+ * @param name the name to add.
+ * @return <code>true</code> if the name is already present,
+ * <code>false</code> otherwise.
+ */
+ boolean add(Name name) {
+ if (names.size() > NUM_NAMES_THRESHOLD) {
+ ensureModifiable();
+ return names.add(name);
+ } else {
+ int size = names.size();
+ // get a cached set
+ names = CACHE.get(names, name, !shared);
+ // a set from the cache is always shared
+ shared = true;
+ return names.size() != size;
+ }
+ }
+
+ /**
+ * Removes a <code>name</code>.
+ *
+ * @param name the name to remove.
+ * @return <code>true</code> if the name was removed, <code>false</code>
+ * if the name was unknown.
+ */
+ boolean remove(Name name) {
+ ensureModifiable();
+ return names.remove(name);
+ }
+
+ /**
+ * Removes all names from this {@link NameSet}.
+ */
+ void removeAll() {
+ ensureModifiable();
+ names.clear();
+ }
+
+ /**
+ * Removes all names currently present and adds all names from
+ * <code>c</code>.
+ *
+ * @param c the {@link Name}s to add.
+ */
+ void replaceAll(Collection c) {
+ if (c instanceof NameSet) {
+ NameSet propNames = (NameSet) c;
+ names = propNames.names;
+ shared = true;
+ propNames.shared = true;
+ } else if (c instanceof HashSet) {
+ names = CACHE.get((HashSet) c);
+ shared = true;
+ } else {
+ ensureModifiable();
+ names.clear();
+ names.addAll(c);
+ }
+ }
+
+ //------------------------------------------------< unmodifiable Set view >
+
+ /**
+ * {@inheritDoc}
+ */
+ public int size() {
+ return names.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isEmpty() {
+ return names.isEmpty();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean contains(Object o) {
+ return names.contains(o);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * The returned iterator will throw a {@link UnsupportedOperationException}
+ * on {@link Iterator#remove()}.
+ */
+ public Iterator iterator() {
+ return new Iterator() {
+ Iterator i = names.iterator();
+
+ public boolean hasNext() {
+ return i.hasNext();
+ }
+
+ public Object next() {
+ return i.next();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object[] toArray() {
+ return names.toArray();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object[] toArray(Object[] a) {
+ return names.toArray(a);
+ }
+
+ /**
+ * @throws UnsupportedOperationException always.
+ */
+ public boolean add(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @throws UnsupportedOperationException always.
+ */
+ public boolean remove(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean containsAll(Collection c) {
+ return names.containsAll(c);
+ }
+
+ /**
+ * @throws UnsupportedOperationException always.
+ */
+ public boolean addAll(Collection c) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @throws UnsupportedOperationException always.
+ */
+ public boolean retainAll(Collection c) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @throws UnsupportedOperationException always.
+ */
+ public boolean removeAll(Collection c) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @throws UnsupportedOperationException always.
+ */
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ //----------------------------------------------------< Cloneable support >
+
+ /**
+ * Returns a clone of this <code>PropertyNames</code> instance.
+ *
+ * @return a clone of this <code>PropertyNames</code> instance.
+ */
+ public Object clone() {
+ try {
+ NameSet propNames = (NameSet) super.clone();
+ shared = true;
+ propNames.shared = true;
+ return propNames;
+ } catch (CloneNotSupportedException e) {
+ // will never happen
+ throw new InternalError();
+ }
+ }
+
+ //-------------------------------------------------------------< internal >
+
+ /**
+ * Ensures that {@link #names} can be modified (-> not shared).
+ */
+ private void ensureModifiable() {
+ if (shared) {
+ names = (HashSet) names.clone();
+ shared = false;
+ }
+ }
+
+ /**
+ * Implements a simple <code>HashSet<Name></code> cache.
+ * <p/>
+ * Please note that this cache does not ensures that the sets are immutable!
+ * It is the responsibility of the caller to make sure that sets passed to
+ * {@link #get} are not modified by multiple threads. Modifying a cached
+ * set is not a problem in general because it will only cause cache misses.
+ */
+ private static final class NameSetCache {
+
+ /**
+ * Size of the cache (must be a power of two). Note that this is the
+ * maximum number of objects kept in the cache, but due to hashing it
+ * can well be that only a part of the cache array is filled even if
+ * many more distinct objects are being accessed.
+ */
+ private static final int SIZE_POWER_OF_2 = 1024;
+
+ /**
+ * Array of cached hash sets, indexed by their hash codes
+ * (module size of the array).
+ */
+ private final HashSet[] array = new HashSet[SIZE_POWER_OF_2];
+
+ /**
+ * Returns a set that contains all elements from <code>set</code> and
+ * <code>obj</code>. If a cached copy of the set already exists, then
+ * this method returns that copy. Otherwise <code>obj</code> is added
+ * to the given <code>set</code>, the <code>set</code> is cached and
+ * then returned.
+ *
+ * @param set the initial set.
+ * @param obj the object to add to <code>set</code>.
+ * @param modifiable <code>true</code> if <code>set</code> may be modified.
+ * @return a cached set that contains all elements from <code>set</code>
+ * and <code>obj</code>.
+ */
+ public HashSet get(HashSet set, Object obj, boolean modifiable) {
+ if (set.contains(obj)) {
+ return set;
+ }
+ int position = (set.hashCode() + obj.hashCode()) & (SIZE_POWER_OF_2 - 1);
+ HashSet previous = array[position];
+ if (previous != null &&
+ previous.size() == set.size() + 1 &&
+ previous.containsAll(set) &&
+ previous.contains(obj)) {
+ return previous;
+ } else {
+ if (modifiable) {
+ set.add(obj);
+ } else {
+ set = (HashSet) set.clone();
+ set.add(obj);
+ }
+ array[position] = set;
+ return set;
+ }
+ }
+
+ /**
+ * If a cached copy of the given set already exists, then this method
+ * returns that copy. Otherwise the given set is cached and returned.
+ *
+ * @param set set to return from the cache
+ * @return the given set or a previously cached copy
+ */
+ public HashSet get(HashSet set) {
+ int position = set.hashCode() & (SIZE_POWER_OF_2 - 1);
+ HashSet previous = array[position];
+ if (set.equals(previous)) {
+ return previous;
+ } else {
+ array[position] = set;
+ return set;
+ }
+ }
+
+ /**
+ * Returns a cached copy of an empty set.
+ *
+ * @return a cached copy of an empty set.
+ */
+ public HashSet getEmptySet() {
+ HashSet set = array[0];
+ if (set == null || !set.isEmpty()) {
+ set = new HashSet();
+ array[0] = set;
+ }
+ return set;
+ }
+ }
+}
Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NameSet.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java?rev=691603&r1=691602&r2=691603&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java Wed Sep 3 06:10:23 2008
@@ -42,7 +42,7 @@
/**
* the names of this node's mixin types
*/
- private Set mixinTypeNames = Collections.EMPTY_SET;
+ private NameSet mixinTypeNames = new NameSet();
/**
* the id of this node.
@@ -66,21 +66,9 @@
private ChildNodeEntries childNodeEntries = new ChildNodeEntries();
/**
- * Set to <code>true</code> if {@link #childNodeEntries} are shared between
- * different <code>NodeState</code> instance.
- */
- private boolean sharedChildNodeEntries = false;
-
- /**
* set of property names (Name objects)
*/
- private HashSet propertyNames = new HashSet();
-
- /**
- * Set to <code>true</code> if {@link #propertyNames} is shared between
- * different <code>NodeState</code> instances.
- */
- private boolean sharedPropertyNames = false;
+ private NameSet propertyNames = new NameSet();
/**
* Shared set, consisting of the parent ids of this shareable node. This
@@ -138,14 +126,10 @@
id = nodeState.id;
parentId = nodeState.parentId;
nodeTypeName = nodeState.nodeTypeName;
- mixinTypeNames = nodeState.mixinTypeNames;
+ mixinTypeNames = (NameSet) nodeState.mixinTypeNames.clone();
defId = nodeState.defId;
- propertyNames = nodeState.propertyNames;
- sharedPropertyNames = true;
- nodeState.sharedPropertyNames = true;
- childNodeEntries = nodeState.childNodeEntries;
- sharedChildNodeEntries = true;
- nodeState.sharedChildNodeEntries = true;
+ propertyNames = (NameSet) nodeState.propertyNames.clone();
+ childNodeEntries = (ChildNodeEntries) nodeState.childNodeEntries.clone();
if (syncModCount) {
setModCount(state.getModCount());
}
@@ -214,7 +198,7 @@
* @return a set of the names of this node's mixin types.
*/
public synchronized Set getMixinTypeNames() {
- return Collections.unmodifiableSet(mixinTypeNames);
+ return mixinTypeNames;
}
/**
@@ -223,11 +207,7 @@
* @param names set of names of mixin types
*/
public synchronized void setMixinTypeNames(Set names) {
- if (names instanceof HashSet) {
- mixinTypeNames = (Set) ((HashSet) names).clone();
- } else {
- mixinTypeNames = new HashSet(names);
- }
+ mixinTypeNames.replaceAll(names);
}
/**
@@ -367,10 +347,6 @@
*/
public synchronized ChildNodeEntry addChildNodeEntry(Name nodeName,
NodeId id) {
- if (sharedChildNodeEntries) {
- childNodeEntries = (ChildNodeEntries) childNodeEntries.clone();
- sharedChildNodeEntries = false;
- }
ChildNodeEntry entry = childNodeEntries.add(nodeName, id);
notifyNodeAdded(entry);
return entry;
@@ -387,10 +363,6 @@
*/
public synchronized boolean renameChildNodeEntry(Name oldName, int index,
Name newName) {
- if (sharedChildNodeEntries) {
- childNodeEntries = (ChildNodeEntries) childNodeEntries.clone();
- sharedChildNodeEntries = false;
- }
ChildNodeEntry oldEntry = childNodeEntries.remove(oldName, index);
if (oldEntry != null) {
ChildNodeEntry newEntry =
@@ -411,10 +383,6 @@
* in the list of child node entries and could be removed.
*/
public synchronized boolean removeChildNodeEntry(Name nodeName, int index) {
- if (sharedChildNodeEntries) {
- childNodeEntries = (ChildNodeEntries) childNodeEntries.clone();
- sharedChildNodeEntries = false;
- }
ChildNodeEntry entry = childNodeEntries.remove(nodeName, index);
if (entry != null) {
notifyNodeRemoved(entry);
@@ -430,10 +398,6 @@
* in the list of child node entries and could be removed.
*/
public synchronized boolean removeChildNodeEntry(NodeId id) {
- if (sharedChildNodeEntries) {
- childNodeEntries = (ChildNodeEntries) childNodeEntries.clone();
- sharedChildNodeEntries = false;
- }
ChildNodeEntry entry = childNodeEntries.remove(id);
if (entry != null) {
notifyNodeRemoved(entry);
@@ -445,10 +409,6 @@
* Removes all <code>ChildNodeEntry</code>s.
*/
public synchronized void removeAllChildNodeEntries() {
- if (sharedChildNodeEntries) {
- childNodeEntries = (ChildNodeEntries) childNodeEntries.clone();
- sharedChildNodeEntries = false;
- }
childNodeEntries.removeAll();
notifyNodesReplaced();
}
@@ -464,14 +424,8 @@
// optimization
ChildNodeEntries entries = (ChildNodeEntries) nodeEntries;
childNodeEntries = (ChildNodeEntries) entries.clone();
- sharedChildNodeEntries = false;
} else {
- if (sharedChildNodeEntries) {
- childNodeEntries = new ChildNodeEntries();
- sharedChildNodeEntries = false;
- } else {
- childNodeEntries.removeAll();
- }
+ childNodeEntries.removeAll();
childNodeEntries.addAll(nodeEntries);
}
@@ -487,7 +441,7 @@
* @see #removePropertyName
*/
public synchronized Set getPropertyNames() {
- return Collections.unmodifiableSet(propertyNames);
+ return propertyNames;
}
/**
@@ -496,10 +450,6 @@
* @param propName <code>Name</code> object specifying the property name
*/
public synchronized void addPropertyName(Name propName) {
- if (sharedPropertyNames) {
- propertyNames = (HashSet) propertyNames.clone();
- sharedPropertyNames = false;
- }
propertyNames.add(propName);
}
@@ -511,10 +461,6 @@
* in the list of property name entries and could be removed.
*/
public synchronized boolean removePropertyName(Name propName) {
- if (sharedPropertyNames) {
- propertyNames = (HashSet) propertyNames.clone();
- sharedPropertyNames = false;
- }
return propertyNames.remove(propName);
}
@@ -522,12 +468,7 @@
* Removes all property name entries.
*/
public synchronized void removeAllPropertyNames() {
- if (sharedPropertyNames) {
- propertyNames = new HashSet();
- sharedPropertyNames = false;
- } else {
- propertyNames.clear();
- }
+ propertyNames.removeAll();
}
/**
@@ -536,19 +477,7 @@
* @param propNames set of {@link Name}s.
*/
public synchronized void setPropertyNames(Set propNames) {
- if (propNames instanceof HashSet) {
- HashSet names = (HashSet) propNames;
- propertyNames = (HashSet) names.clone();
- sharedPropertyNames = false;
- } else {
- if (sharedPropertyNames) {
- propertyNames = new HashSet();
- sharedPropertyNames = false;
- } else {
- propertyNames.clear();
- }
- propertyNames.addAll(propNames);
- }
+ propertyNames.replaceAll(propNames);
}
/**
@@ -667,7 +596,7 @@
*/
public synchronized Set getAddedPropertyNames() {
if (!hasOverlayedState()) {
- return Collections.unmodifiableSet(propertyNames);
+ return propertyNames;
}
NodeState other = (NodeState) getOverlayedState();
Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/EmptyLinkedMap.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/EmptyLinkedMap.java?rev=691603&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/EmptyLinkedMap.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/EmptyLinkedMap.java Wed Sep 3 06:10:23 2008
@@ -0,0 +1,125 @@
+/*
+ * 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 org.apache.commons.collections.map.LinkedMap;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * <code>EmptyLinkedMap</code> implements an empty unmodifiable {@link LinkedMap}.
+ */
+public class EmptyLinkedMap extends LinkedMap {
+
+ private static final long serialVersionUID = -9165910643562370800L;
+
+ /**
+ * The only instance of this class.
+ */
+ public static final LinkedMap INSTANCE = new EmptyLinkedMap();
+
+ private EmptyLinkedMap() {
+ super();
+ }
+
+ /**
+ * @throws UnsupportedOperationException always.
+ */
+ public Object remove(int i) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @throws UnsupportedOperationException always.
+ */
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @throws UnsupportedOperationException always.
+ */
+ public Object put(Object o, Object o1) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @throws UnsupportedOperationException always.
+ */
+ public void putAll(Map map) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @throws UnsupportedOperationException always.
+ */
+ public Object remove(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns an unmodifiable empty set.
+ *
+ * @return an unmodifiable empty set.
+ */
+ public Set entrySet() {
+ return Collections.EMPTY_SET;
+ }
+
+ /**
+ * Returns an unmodifiable empty set.
+ *
+ * @return an unmodifiable empty set.
+ */
+ public Set keySet() {
+ return Collections.EMPTY_SET;
+ }
+
+ /**
+ * Returns an unmodifiable empty collection.
+ *
+ * @return an unmodifiable empty collection.
+ */
+ public Collection values() {
+ return Collections.EMPTY_LIST;
+ }
+
+ //----------------------------------------------------< Cloneable support >
+
+ /**
+ * Returns the single instance of this class.
+ *
+ * @return {@link #INSTANCE}.
+ */
+ public Object clone() {
+ return INSTANCE;
+ }
+
+ //-------------------------------------------------< Serializable support >
+
+ /**
+ * Returns the single instance of this class.
+ *
+ * @return {@link #INSTANCE}.
+ */
+ private Object readResolve() {
+ return INSTANCE;
+ }
+}
Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/EmptyLinkedMap.java
------------------------------------------------------------------------------
svn:eol-style = native