blob: 9e3148ed7c4d14c53ba5302f90901b094506bce6 [file] [log] [blame]
/*
* 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.cocoon.util;
import java.util.Set;
import java.util.HashSet;
import java.util.NoSuchElementException;
import java.util.Map;
import java.util.Collection;
import java.util.Iterator;
/**
* A MRUBucketMap is an efficient ThreadSafe implementation of a Map with
* addition of the removeLast method implementing LRU removal policy.
* <br />
* MRUBucketMap is based on the Avalon's BucketMap.
*
* @author <a href="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
* @deprecated Will be removed in Cocoon 2.2
* @version CVS $Id$
*/
public final class MRUBucketMap implements Map
{
private static final int DEFAULT_BUCKETS = 255;
private final Node[] m_buckets;
private final Object[] m_locks;
private final Node m_header = new Node();
private int m_size = 0;
/**
* Creates map with default number of buckets.
*/
public MRUBucketMap()
{
this( DEFAULT_BUCKETS );
}
/**
* Creates map with specified number of buckets.
*/
public MRUBucketMap( int numBuckets )
{
int size = Math.max( 17, numBuckets );
// Ensure that bucketSize is never a power of 2 (to ensure maximal distribution)
if( size % 2 == 0 )
{
size--;
}
m_buckets = new Node[ size ];
m_locks = new Object[ size ];
for( int i = 0; i < size; i++ )
{
m_locks[ i ] = new Object();
}
m_header.mru_next = m_header.mru_previous = m_header;
}
private final int getHash( Object key )
{
final int hash = key.hashCode() % m_buckets.length;
return ( hash < 0 ) ? hash * -1 : hash;
}
public Set keySet()
{
Set keySet = new HashSet();
for( int i = 0; i < m_buckets.length; i++ )
{
synchronized( m_locks[ i ] )
{
Node n = m_buckets[ i ];
while( n != null )
{
keySet.add( n.key );
n = n.next;
}
}
}
return keySet;
}
/**
* Returns the current number of key, value pairs.
*/
public int size()
{
return m_size;
}
/**
* Put a reference in the Map.
*/
public Object put( final Object key, final Object value )
{
if( null == key || null == value )
{
return null;
}
int isNew = 0;
Node node;
Object oldValue = null;
int hash = getHash( key );
synchronized( m_locks[ hash ] )
{
Node n = m_buckets[ hash ];
if( n == null )
{
node = new Node();
node.key = key;
node.value = value;
m_buckets[ hash ] = node;
isNew = 1;
}
else
{
// Set n to the last node in the linked list. Check each key along the way
// If the key is found, then change the value of that node and return
// the old value.
for( Node next = n; next != null; next = next.next )
{
n = next;
if( n.key.equals( key ) )
{
oldValue = n.value;
n.value = value;
break;
}
}
if (oldValue == null) {
// The key was not found in the current list of nodes,
// add it to the end in a new node.
node = new Node();
node.key = key;
node.value = value;
n.next = node;
isNew = 1;
} else {
// The key was found in the list. Move it to the head.
node = n;
}
}
}
synchronized ( m_header )
{
if (isNew == 0) {
// Remove
node.mru_previous.mru_next = node.mru_next;
node.mru_next.mru_previous = node.mru_previous;
}
// Move node to the head.
node.mru_previous = m_header;
node.mru_next = m_header.mru_next;
node.mru_previous.mru_next = node;
node.mru_next.mru_previous = node;
m_size += isNew;
}
return oldValue;
}
public Object get( final Object key )
{
if( null == key )
{
return null;
}
Node n;
int hash = getHash( key );
synchronized( m_locks[ hash ] )
{
n = m_buckets[ hash ];
while( n != null )
{
if( n.key.equals( key ) )
{
break;
}
n = n.next;
}
}
if( n != null ) {
synchronized( m_header ) {
// Remove
n.mru_previous.mru_next = n.mru_next;
n.mru_next.mru_previous = n.mru_previous;
// Add first
n.mru_previous = m_header;
n.mru_next = m_header.mru_next;
n.mru_previous.mru_next = n;
n.mru_next.mru_previous = n;
}
return n.value;
}
return null;
}
public boolean containsKey( final Object key )
{
if( null == key )
{
return false;
}
int hash = getHash( key );
synchronized( m_locks[ hash ] )
{
Node n = m_buckets[ hash ];
while( n != null )
{
if( n.key.equals( key ) )
{
return true;
}
n = n.next;
}
}
return false;
}
public boolean containsValue( final Object value )
{
if( null == value )
{
return false;
}
synchronized( m_header )
{
for( Node n = m_header.mru_next; n != m_header; n = n.mru_next )
{
if( n.value.equals( value ) )
{
return true;
}
}
}
return false;
}
public Collection values()
{
Set valueSet = new HashSet();
synchronized( m_header )
{
for( Node n = m_header.mru_next; n != m_header; n = n.mru_next )
{
valueSet.add( n.value );
}
}
return valueSet;
}
public Set entrySet()
{
Set entrySet = new HashSet();
synchronized( m_header )
{
for( Node n = m_header.mru_next; n != m_header; n = n.mru_next )
{
entrySet.add( n );
}
}
return entrySet;
}
/**
* Add all the contents of one Map into this one.
*/
public void putAll( Map other )
{
for (Iterator i = other.entrySet().iterator(); i.hasNext(); ) {
Map.Entry me = (Map.Entry)i.next();
put(me.getKey(), me.getValue());
}
}
public Object remove( Object key )
{
if( null == key )
{
return null;
}
Node n;
int hash = getHash( key );
synchronized( m_locks[ hash ] )
{
n = m_buckets[ hash ];
Node prev = null;
while( n != null )
{
if( n.key.equals( key ) )
{
// Remove this node from the linked list of nodes.
if( null == prev )
{
// This node was the head, set the next node to be the new head.
m_buckets[ hash ] = n.next;
}
else
{
// Set the next node of the previous node to be the node after this one.
prev.next = n.next;
}
break;
}
prev = n;
n = n.next;
}
}
if (n != null) {
synchronized(m_header) {
// Remove
n.mru_previous.mru_next = n.mru_next;
n.mru_next.mru_previous = n.mru_previous;
m_size--;
}
return n.value;
}
return null;
}
public final boolean isEmpty()
{
return m_size == 0;
}
public final void clear()
{
synchronized ( m_header ) {
for( int i = 0; i < m_buckets.length; i++ )
{
m_buckets[ i ] = null;
}
m_header.mru_next = m_header.mru_previous = m_header;
m_size = 0;
}
}
public Map.Entry removeLast()
{
Node node = m_header.mru_previous;
if (node == m_header) {
throw new NoSuchElementException("MRUBucketMap is empty");
}
remove(node.key);
node.mru_next = null;
node.mru_previous = null;
return node;
}
private static final class Node implements Map.Entry
{
protected Object key;
protected Object value;
protected Node next;
protected Node mru_previous;
protected Node mru_next;
public Object getKey()
{
return key;
}
public Object getValue()
{
return value;
}
public Object setValue( Object val )
{
Object retVal = value;
value = val;
return retVal;
}
}
static class X {
String x;
public X (String s) {
x = s;
}
public int hashCode() {
return 1;
}
public boolean equals(Object obj) {
return this == obj;
}
public String toString() {
return x;
}
}
}