MRUBucketMap.java :  » Content-Management-System » apache-lenya-2.0 » org » apache » cocoon » util » Java Open Source

Java Open Source » Content Management System » apache lenya 2.0 
apache lenya 2.0 » org » apache » cocoon » util » MRUBucketMap.java
/*
 * 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: MRUBucketMap.java 433543 2006-08-22 06:22:54Z crossley $
 */
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;
        }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.