/*
 * This software is distributed under following license based on modified BSD
 * style license.
 * ----------------------------------------------------------------------
 * 
 * Copyright 2009 The Nimbus2 Project. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE NIMBUS PROJECT ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL THE NIMBUS PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the Nimbus2 Project.
 */
package jp.ossc.nimbus.service.cache;

import java.util.*;
import java.lang.reflect.Array;

import jp.ossc.nimbus.core.*;

/**
 * 抽象キャッシュマップサービス。<p>
 *
 * @author M.Takata
 */
public abstract class AbstractCacheMapService<K, V> extends ServiceBase
 implements CacheRemoveListener<V>, AbstractCacheMapServiceMBean<K, V>{
    
    private static final long serialVersionUID = 6779186037980520151L;
    
    /**
     * キャッシュのキーとキャッシュ参照のマップ。<p>
     */
    protected Map<K, KeyCachedReference<K, V>> references;
    
    /**
     * OverflowControllerサービスのサービス名配列。<p>
     */
    protected ServiceName[] overflowControllerServiceNames;
    
    /**
     * OverflowControllerサービスのリスト。<p>
     */
    protected List<OverflowController<V>> overflowControllers;
    
    /**
     * サービス停止時に、キャッシュをクリアするかどうかのフラグ。<p>
     * デフォルトは、false。<br>
     */
    protected boolean isClearOnStop;
    
    /**
     * サービス破棄時に、キャッシュをクリアするかどうかのフラグ。<p>
     * デフォルトは、true。<br>
     */
    protected boolean isClearOnDestroy = true;
    
    // AbstractCacheMapServiceMBeanのJavaDoc
    @Override
    public void setOverflowControllerServiceNames(ServiceName[] names){
        overflowControllerServiceNames = names;
    }
    // AbstractCacheMapServiceMBeanのJavaDoc
    @Override
    public ServiceName[] getOverflowControllerServiceNames(){
        return overflowControllerServiceNames;
    }
    
    // AbstractCacheMapServiceMBeanのJavaDoc
    @Override
    public void setClearOnStop(boolean isClear){
        isClearOnStop = isClear;
    }
    // AbstractCacheMapServiceMBeanのJavaDoc
    @Override
    public boolean isClearOnStop(){
        return isClearOnStop;
    }
    
    // AbstractCacheMapServiceMBeanのJavaDoc
    @Override
    public void setClearOnDestroy(boolean isClear){
        isClearOnDestroy = isClear;
    }
    // AbstractCacheMapServiceMBeanのJavaDoc
    @Override
    public boolean isClearOnDestroy(){
        return isClearOnDestroy;
    }
    
    /**
     * OverflowControllerを設定する。
     */
    public void setOverflowControllers(List<OverflowController<V>> overflowControllers) {
		this.overflowControllers = overflowControllers;
	}
    
	/**
     * サービスの生成前処理を行う。<p>
     * インスタンス変数の初期化を行う。<br>
     *
     * @exception 生成前処理に失敗した場合
     */
    @Override
    public void preCreateService() throws Exception{
        super.preCreateService();
        references = Collections.synchronizedMap(new HashMap<K, KeyCachedReference<K, V>>());
        overflowControllers = new ArrayList<OverflowController<V>>();
    }
    
    /**
     * サービスの開始前処理を行う。<p>
     * QverflowControllerサービスの取得を行う。<br>
     *
     * @exception 開始前処理に失敗した場合
     */
    @Override
    public void preStartService() throws Exception{
        super.preStartService();
        if(overflowControllerServiceNames != null){
            for(int i = 0; i < overflowControllerServiceNames.length; i++){
                overflowControllers.add(
                    ServiceManagerFactory
                        .<OverflowController<V>>getServiceObject(overflowControllerServiceNames[i])
                );
            }
        }
    }
    
    /**
     * サービスの停止後処理を行う。<p>
     * QverflowControllerサービスの参照を破棄する。<br>
     *
     * @exception 停止後処理に失敗した場合
     */
    @Override
    public void postStopService() throws Exception{
        if(isClearOnStop()){
            clear();
        }
        if(overflowControllers != null){
            overflowControllers.clear();
        }
        super.postStopService();
    }
    
    /**
     * サービスの破棄後処理を行う。<p>
     * インスタンス変数の参照を破棄する。<br>
     *
     * @exception 破棄後処理に失敗した場合
     */
    @Override
    public void postDestroyService() throws Exception{
        if(isClearOnDestroy()){
            clear();
        }
        references = null;
        overflowControllers = null;
        super.postDestroyService();
    }
    
    // CacheMapのJavaDoc
    @Override
    public KeyCachedReference<K, V> getCachedReference(Object key){
        if(references == null){
            return null;
        }
        return references.get(key);
    }
    
    // CacheMapのJavaDoc
    @Override
    public int size(){
        if(references == null){
            return 0;
        }
        return references.size();
    }
    
    // CacheMapのJavaDoc
    @Override
    public boolean isEmpty(){
        if(references == null){
            return true;
        }
        return references.isEmpty();
    }
    
    // CacheMapのJavaDoc
    @Override
    public boolean containsKey(Object key){
        if(references == null){
            return false;
        }
        return references.containsKey(key);
    }
    
    // CacheMapのJavaDoc
    @Override
    public boolean containsValue(Object value){
        if(references == null){
            return false;
        }
        synchronized(references){
            for(K key : references.keySet()){
                final Object obj = get(key, false);
                if(value == null){
                    if(obj == null){
                        return true;
                    }
                }else if(value.equals(obj)){
                    return true;
                }
            }
        }
        return false;
    }
    
    // CacheMapのJavaDoc
    @Override
    public V get(Object key){
        return get(key, true);
    }
    
    /**
     * 指定されたキーのキャッシュを取得する。<p>
     *
     * @param key キャッシュのキー
     * @param notify キャッシュにアクセスした事をリスナに通知するかどうかのフラグ。通知する場合、true
     */
    protected V get(Object key, boolean notify){
        if(references == null){
            return null;
        }
        synchronized(references){
            final CachedReference<V> ref = references.get(key);
            if(ref == null){
                return null;
            }
            return ref.get(this, notify);
        }
    }
    
    // CacheMapのJavaDoc
    @Override
    public V put(K key, V value){
        if(references == null){
            return null;
        }
        synchronized(references){
            V oldVal = null;
            if(references.containsKey(key)){
                oldVal = remove(key);
            }
            final KeyCachedReference<K, V> ref = createKeyCachedReference(key, value);
            put(key, ref);
            return oldVal;
        }
    }
    
    /**
     * 指定したキーのキャッシュ参照を追加する。<p>
     *
     * @param key キャッシュのキー
     * @param ref キャッシュ参照
     */
    protected void put(K key, KeyCachedReference<K, V> ref){
        if(references == null || getState().compareTo(State.STOPPED) > 0 || ref == null){
            return;
        }
        synchronized(references){
            ref.addCacheRemoveListener(this);
            references.put(key, ref);
            if(overflowControllers.size() != 0){
                for(OverflowController<V> controller : overflowControllers){
                    controller.control(ref);
                }
            }
        }
    }
    
    /**
     * キー付きキャッシュ参照を生成する。<p>
     *
     * @param key キャッシュのキー
     * @param obj キャッシュするオブジェクト
     * @return キー付きキャッシュ参照
     */
    protected abstract KeyCachedReference<K, V> createKeyCachedReference(
        K key,
        V obj
    );
    
    // CacheMapのJavaDoc
    @Override
    public V remove(Object key){
        if(references == null){
            return null;
        }
        V val = null;
        synchronized(references){
            final CachedReference<V> ref = references.remove(key);
            if(ref != null){
                val = ref.get(this, false);
                ref.remove(this);
            }
        }
        return val;
    }
    
    // CacheMapのJavaDoc
    @Override
    public void putAll(Map<? extends K,? extends V> map){
        if(references == null || map == null || map.size() == 0){
            return;
        }
        for(Map.Entry<? extends K,? extends V> entry : map.entrySet()){
            put(entry.getKey(), entry.getValue());
        }
    }
    
    // CacheMapのJavaDoc
    @Override
    public void clear(){
        if(references == null || references.size() == 0){
            return;
        }
        synchronized(references){
            final Object[] keys = references.keySet().toArray();
            for(int i = 0; i < keys.length; i++){
                remove(keys[i]);
            }
        }
    }
    
    // CacheMapのJavaDoc
    @Override
    public Set<K> keySet(){
        return new KeySet<K>();
    }
    
    // CacheMapのJavaDoc
    @Override
    public Collection<V> values(){
        return new ValuesCollection<V>();
    }
    
    // CacheMapのJavaDoc
    @Override
    public Set<Map.Entry<K, V>> entrySet(){
        return new EntrySet<K, V>();
    }
    
    // CacheRemoveListenerのJavaDoc
    @SuppressWarnings("unchecked")
    @Override
    public void removed(CachedReference<V> ref){
        if(references != null && ref instanceof KeyCachedReference){
            final KeyCachedReference<K, V> keyRef = (KeyCachedReference<K, V>)ref;
            references.remove(keyRef.getKey());
        }
    }
    
    /**
     * キャッシュマップのキー集合。<p>
     *
     * @author M.Takata
     * @see AbstractCacheMapService#keySet()
     */
    protected class KeySet<K2>
     implements Set<K2>, java.io.Serializable{
        
        private static final long serialVersionUID = 8251697022788153149L;
        
        private Collection<K> collection;
        
        /**
         * インスタンスを生成する。<p>
         */
        public KeySet(){
            if(references != null){
                collection = references.keySet();
            }
        }
        
        // SetのJavaDoc
        @Override
        public int size(){
            return AbstractCacheMapService.this.size();
        }
        
        // SetのJavaDoc
        @Override
        public boolean isEmpty(){
            return AbstractCacheMapService.this.isEmpty();
        }
        
        // SetのJavaDoc
        @Override
        public boolean contains(Object o){
            return AbstractCacheMapService.this.containsKey(o);
        }
        
        // SetのJavaDoc
        @Override
        public boolean containsAll(Collection<?> c){
            boolean result = true;
            for(Object val : c){
                result &= AbstractCacheMapService.this.containsKey(val);
                if(!result){
                    break;
                }
            }
            return result;
        }
        
        // SetのJavaDoc
        @Override
        public Iterator<K2> iterator(){
            return new KeyIterator<K2>();
        }
        
        // SetのJavaDoc
        @Override
        public Object[] toArray(){
            if(references == null || references.size() == 0){
                return new Object[0];
            }
            final Object[] result = new Object[references.size()];
            synchronized(references){
                int count = 0;
                final Iterator<K> keys = references.keySet().iterator();
                while(keys.hasNext()){
                    result[count++] = keys.next();
                }
            }
            return result;
        }
        
        // SetのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public <T> T[] toArray(T[] a){
            if(references == null || references.size() == 0){
                if(a.length == 0){
                    return a;
                }else{
                    for(int i = 0; i < a.length; i++){
                        a[i] = null;
                    }
                    return a;
                }
            }
            T[] result = null;
            synchronized(references){
                final int length = references.size();
                if(a.length >= length){
                    result = a;
                }else{
                    result = (T[])Array.newInstance(
                        a.getClass().getComponentType(),
                        length
                    );
                }
                int count = 0;
                final Iterator<K> keys = references.keySet().iterator();
                while(keys.hasNext()){
                    result[count++] = (T)keys.next();
                }
            }
            return result;
        }
        
        // SetのJavaDoc
        @Override
        public boolean add(K2 o){
            throw new UnsupportedOperationException();
        }
        
        // SetのJavaDoc
        @Override
        public boolean addAll(Collection<? extends K2> c){
            throw new UnsupportedOperationException();
        }
        
        // SetのJavaDoc
        @Override
        public boolean remove(Object o){
            if(references == null || references.size() == 0){
                return false;
            }
            final Object removed = AbstractCacheMapService.this.remove(o);
            return removed != null;
        }
        
        // SetのJavaDoc
        @Override
        public boolean removeAll(Collection<?> c){
            boolean result = false;
            for(Object val : c){
                result |= remove(val);
            }
            return result;
        }
        
        // SetのJavaDoc
        @Override
        public boolean retainAll(Collection<?> c){
            if(references == null || references.size() == 0){
                return false;
            }
            boolean result = false;
            synchronized(references){
                final Object[] keys = references.keySet().toArray();
                for(int i = 0; i < keys.length; i++){
                    if(!c.contains(keys[i])){
                        result = remove(keys[i]);
                    }
                }
            }
            return result;
        }
        
        // SetのJavaDoc
        @Override
        public void clear(){
            AbstractCacheMapService.this.clear();
        }
        
        // SetのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public boolean equals(Object o){
            if(o == null){
                return false;
            }
            if(o == this){
                return true;
            }
            if(o instanceof KeySet){
                final KeySet<K2> cmp = (KeySet<K2>)o;
                if(collection == null){
                    return cmp.collection == null;
                }else if(collection.equals(cmp.collection)){
                    return true;
                }
            }
            return false;
        }
        
        // SetのJavaDoc
        @Override
        public int hashCode(){
            return collection == null ? 0 : collection.hashCode();
        }
        
        @Override
        public String toString(){
            return collection == null
                 ? super.toString() : collection.toString();
        }
    }
    
    /**
     * キャッシュマップのキー繰り返し。<p>
     *
     * @author M.Takata
     * @see AbstractCacheMapService.KeySet#iterator()
     */
    protected class KeyIterator<K2>
     implements Iterator<K2>, java.io.Serializable{
        
        private static final long serialVersionUID = 8251697022788153149L;
        
        private Iterator<K> iterator;
        private K current;
        
        /**
         * インスタンスを生成する。<p>
         */
        public KeyIterator(){
            if(references != null){
                iterator = new HashSet<K>(references.keySet()).iterator();
            }
        }
        
        // IteratorのJavaDoc
        @Override
        public boolean hasNext(){
            return iterator == null ? false : iterator.hasNext();
        }
        
        // IteratorのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public K2 next(){
            if(iterator == null){
                throw new NoSuchElementException();
            }
            current = null;
            current = iterator.next();
            return (K2)current;
        }
        
        // IteratorのJavaDoc
        @Override
        public void remove(){
            if(current == null){
                throw new IllegalStateException();
            }
            AbstractCacheMapService.this.remove(current);
            iterator.remove();
            if(!iterator.hasNext()){
                current = null;
            }
        }
        
        @Override
        public String toString(){
            return iterator == null
                 ? super.toString() : iterator.toString();
        }
    }
    
    /**
     * キャッシュマップの値集合。<p>
     *
     * @author M.Takata
     * @see AbstractCacheMapService#values()
     */
    protected class ValuesCollection<V2>
     implements Collection<V2>, java.io.Serializable{
        
        private static final long serialVersionUID = -3991603731459237593L;
        
        private Collection<KeyCachedReference<K, V>> collection;
        
        /**
         * インスタンスを生成する。<p>
         */
        public ValuesCollection(){
            if(references != null){
                collection = references.values();
            }
        }
        
        // CollectionのJavaDoc
        @Override
        public int size(){
            return AbstractCacheMapService.this.size();
        }
        
        // CollectionのJavaDoc
        @Override
        public boolean isEmpty(){
            return AbstractCacheMapService.this.isEmpty();
        }
        
        // CollectionのJavaDoc
        @Override
        public boolean contains(Object o){
            return AbstractCacheMapService.this.containsValue(o);
        }
        
        // CollectionのJavaDoc
        @Override
        public boolean containsAll(Collection<?> c){
            boolean result = true;
            for(Object obj : c){
                result &= AbstractCacheMapService.this.containsValue(
                    obj
                );
                if(!result){
                    break;
                }
            }
            return result;
        }
        
        // CollectionのJavaDoc
        @Override
        public Iterator<V2> iterator(){
            return new ValuesIterator<V2>();
        }
        
        // CollectionのJavaDoc
        @Override
        public Object[] toArray(){
            if(references == null || references.size() == 0){
                return new Object[0];
            }
            final Object[] result = new Object[references.size()];
            synchronized(references){
                int count = 0;
                for(CachedReference<V> ref : references.values()){
                    if(ref != null){
                        result[count++] = ref.get(this);
                    }else{
                        result[count++] = null;
                    }
                }
            }
            return result;
        }
        
        // CollectionのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public <T> T[] toArray(T[] a){
            if(references == null || references.size() == 0){
                if(a.length == 0){
                    return a;
                }else{
                    for(int i = 0; i < a.length; i++){
                        a[i] = null;
                    }
                    return a;
                }
            }
            T[] result = null;
            synchronized(references){
                final int length = references.size();
                if(a.length >= length){
                    result = a;
                }else{
                    result = (T[])Array.newInstance(
                        a.getClass().getComponentType(),
                        length
                    );
                }
                int count = 0;
                for(CachedReference<V> ref : references.values()){
                    if(ref != null){
                        result[count++] = (T)ref.get(this);
                    }else{
                        result[count++] = null;
                    }
                }
            }
            return result;
        }
        
        // CollectionのJavaDoc
        @Override
        public boolean add(V2 o){
            throw new UnsupportedOperationException();
        }
        
        // CollectionのJavaDoc
        @Override
        public boolean addAll(Collection<? extends V2> c){
            throw new UnsupportedOperationException();
        }
        
        // CollectionのJavaDoc
        @Override
        public boolean remove(Object o){
            if(references == null || references.size() == 0){
                return false;
            }
            boolean result = false;
            synchronized(references){
                for(KeyCachedReference<K, V>ref : references.values()){
                    V val = null;
                    if(ref != null){
                        val = ref.get(this, false);
                    }
                    Object removed = null;
                    if(val == null){
                        if(o == null){
                            if(ref != null){
                                removed = AbstractCacheMapService.this
                                    .remove(ref.getKey());
                            }
                        }
                    }else if(val.equals(o)){
                        if(ref != null){
                            removed = AbstractCacheMapService.this
                                .remove(ref.getKey());
                        }
                    }
                    if(removed != null){
                        result = true;
                    }
                }
            }
            return result;
        }
        
        // CollectionのJavaDoc
        @Override
        public boolean removeAll(Collection<?> c){
            boolean result = false;
            for(Object val : c){
                result |= remove(val);
            }
            return result;
        }
        
        // CollectionのJavaDoc
        @Override
        public boolean retainAll(Collection<?> c){
            if(references == null || references.size() == 0){
                return false;
            }
            boolean result = false;
            synchronized(references){
                for(KeyCachedReference<K, V>ref : references.values()){
                    V val = null;
                    if(ref != null){
                        val = ref.get(this, false);
                    }
                    if(!c.contains(val)){
                        if(ref != null){
                            if(AbstractCacheMapService.this
                                .remove(ref.getKey()) != null){
                                result = true;
                            }
                        }
                    }
                }
            }
            return result;
        }
        
        // CollectionのJavaDoc
        @Override
        public void clear(){
            AbstractCacheMapService.this.clear();
        }
        
        // CollectionのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public boolean equals(Object o){
            if(o == null){
                return false;
            }
            if(o == this){
                return true;
            }
            if(o instanceof ValuesCollection){
                final ValuesCollection<V2> cmp = (ValuesCollection<V2>)o;
                if(collection == null){
                    return cmp.collection == null;
                }else if(collection.equals(cmp.collection)){
                    return true;
                }
            }
            return false;
        }
        
        // CollectionのJavaDoc
        @Override
        public int hashCode(){
            return collection == null ? 0 : collection.hashCode();
        }
        
        @Override
        public String toString(){
            return collection == null
                 ? super.toString() : collection.toString();
        }
    }
    
    /**
     * キャッシュマップの値繰り返し。<p>
     *
     * @author M.Takata
     * @see AbstractCacheMapService.ValuesCollection#iterator()
     */
    protected class ValuesIterator<V2>
     implements Iterator<V2>, java.io.Serializable{
        
        private static final long serialVersionUID = 1885683078757668887L;
        
        private Iterator<KeyCachedReference<K, V>> iterator;
        private KeyCachedReference<K, V> current;
        
        /**
         * インスタンスを生成する。<p>
         */
        public ValuesIterator(){
            if(references != null){
                iterator = new HashSet<KeyCachedReference<K, V>>(references.values()).iterator();
            }
        }
        
        // IteratorのJavaDoc
        @Override
        public boolean hasNext(){
            return iterator == null ? false : iterator.hasNext();
        }
        
        // IteratorのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public V2 next(){
            if(iterator == null){
                throw new NoSuchElementException();
            }
            current = null;
            current = iterator.next();
            return (V2)current.get(this);
        }
        
        // IteratorのJavaDoc
        @Override
        public void remove(){
            if(current == null){
                throw new IllegalStateException();
            }
            AbstractCacheMapService.this.remove(current.getKey());
            iterator.remove();
            if(!iterator.hasNext()){
                current = null;
            }
        }
        
        @Override
        public String toString(){
            return iterator == null
                 ? super.toString() : iterator.toString();
        }
    }
    
    /**
     * キャッシュマップのマップエントリ集合。<p>
     *
     * @author M.Takata
     * @see AbstractCacheMapService#entrySet()
     */
    protected class EntrySet<K2, V2>
     implements Set<Map.Entry<K2, V2>>, java.io.Serializable{
        
        private static final long serialVersionUID = -6274837740283895811L;
        
        private Collection<K2> collection;
        
        /**
         * インスタンスを生成する。<p>
         */
        @SuppressWarnings("unchecked")
        public EntrySet(){
            if(references != null){
                collection = (Collection<K2>)references.keySet();
            }
        }
        
        // SetのJavaDoc
        @Override
        public int size(){
            return AbstractCacheMapService.this.size();
        }
        
        // SetのJavaDoc
        @Override
        public boolean isEmpty(){
            return AbstractCacheMapService.this.isEmpty();
        }
        
        // SetのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public boolean contains(Object o){
            if(o instanceof Map.Entry){
                final Map.Entry<K2, V2> entry = (Map.Entry<K2, V2>)o;
                return AbstractCacheMapService.this.containsKey(entry.getKey());
            }
            return false;
        }
        
        // SetのJavaDoc
        @Override
        public boolean containsAll(Collection<?> c){
            boolean result = true;
            for(Object val : c){
                result &= contains(val);
                if(!result){
                    break;
                }
            }
            return result;
        }
        
        // SetのJavaDoc
        @Override
        public Iterator<Map.Entry<K2, V2>> iterator(){
            return new EntryIterator<K2, V2>();
        }
        
        // SetのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public Object[] toArray(){
            if(references == null || references.size() == 0){
                return new Object[0];
            }
            final Entry<K2, V2>[] result = new Entry[references.size()];
            synchronized(references){
                int count = 0;
                final Iterator<K> keys = references.keySet().iterator();
                while(keys.hasNext()){
                    result[count++] = new Entry<K2, V2>((K2)keys.next());
                }
            }
            return result;
        }
        
        // SetのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public <T> T[] toArray(T[] a){
            if(references == null || references.size() == 0){
                if(a.length == 0){
                    return a;
                }else{
                    for(int i = 0; i < a.length; i++){
                        a[i] = null;
                    }
                    return a;
                }
            }
            T[] result = null;
            synchronized(references){
                final int length = references.size();
                if(a.length >= length){
                    result = a;
                }else{
                    result = (T[])Array.newInstance(
                        a.getClass().getComponentType(),
                        length
                    );
                }
                int count = 0;
                final Iterator<K> keys = references.keySet().iterator();
                while(keys.hasNext()){
                    result[count++] = (T)new Entry<K,V>(keys.next());
                }
            }
            return result;
        }
        
        // SetのJavaDoc
        @Override
        public boolean add(Map.Entry<K2, V2> o){
            throw new UnsupportedOperationException();
        }
        
        // SetのJavaDoc
        @Override
        public boolean addAll(Collection<? extends Map.Entry<K2, V2>> c){
            throw new UnsupportedOperationException();
        }
        
        // SetのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public boolean remove(Object o){
            if(references == null || references.size() == 0){
                return false;
            }
            if(o instanceof Map.Entry){
                final Map.Entry<K2, V2> entry = (Map.Entry<K2, V2>)o;
                final V removed
                     = AbstractCacheMapService.this.remove(entry.getKey());
                return removed != null;
            }
            return false;
        }
        
        // SetのJavaDoc
        @Override
        public boolean removeAll(Collection<?> c){
            boolean result = false;
            for(Object val : c){
                result |= remove(val);
            }
            return result;
        }
        
        // SetのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public boolean retainAll(Collection<?> c){
            if(references == null || references.size() == 0){
                return false;
            }
            boolean result = false;
            synchronized(references){
                final Object[] keys = references.keySet().toArray();
                final Object[] entries = c.toArray();
                final Set<K2> retainsKeys = new HashSet<K2>();
                for(int i = 0; i < entries.length; i++){
                    if(entries[i] instanceof Map.Entry){
                        final Map.Entry<K2, V2> entry = (Map.Entry<K2, V2>)entries[i];
                        retainsKeys.add(entry.getKey());
                    }
                }
                for(int i = 0; i < keys.length; i++){
                    if(!retainsKeys.contains(keys[i])){
                        final Object removed = AbstractCacheMapService.
                            this.remove(keys[i]);
                        result |= removed != null;
                    }
                }
            }
            return result;
        }
        
        // SetのJavaDoc
        @Override
        public void clear(){
            AbstractCacheMapService.this.clear();
        }
        
        // SetのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public boolean equals(Object o){
            if(o == null){
                return false;
            }
            if(o == this){
                return true;
            }
            if(o instanceof EntrySet){
                final EntrySet<K2, V2> cmp = (EntrySet<K2, V2>)o;
                if(collection == null){
                    return cmp.collection == null;
                }else if(collection.equals(cmp.collection)){
                    return true;
                }
            }
            return false;
        }
        
        // SetのJavaDoc
        @Override
        public int hashCode(){
            return collection == null ? 0 : collection.hashCode();
        }
    }
    
    /**
     * キャッシュマップのマップエントリ繰り返し。<p>
     *
     * @author M.Takata
     * @see AbstractCacheMapService.EntrySet#iterator()
     */
    protected class EntryIterator<K2, V2>
     implements Iterator<Map.Entry<K2, V2>>, java.io.Serializable{
        
        private static final long serialVersionUID = -5858884191202944380L;
        
        private Iterator<K2> iterator;
        private K2 current;
        
        /**
         * インスタンスを生成する。<p>
         */
        @SuppressWarnings("unchecked")
        public EntryIterator(){
            if(references != null){
                iterator = new HashSet<K2>((Set<K2>)references.keySet()).iterator();
            }
        }
        
        // IteratorのJavaDoc
        @Override
        public boolean hasNext(){
            return iterator == null ? false : iterator.hasNext();
        }
        
        // IteratorのJavaDoc
        @Override
        public Map.Entry<K2, V2> next(){
            if(iterator == null){
                throw new NoSuchElementException();
            }
            current = null;
            current = iterator.next();
            return new Entry<K2, V2>(current);
        }
        
        // IteratorのJavaDoc
        @Override
        public void remove(){
            if(current == null){
                throw new IllegalStateException();
            }
            AbstractCacheMapService.this.remove(current);
            iterator.remove();
            if(!iterator.hasNext()){
                current = null;
            }
        }
    }
    
    /**
     * キャッシュマップのマップエントリ<p>
     *
     * @author M.Takata
     * @see AbstractCacheMapService.EntryIterator#next()
     */
    protected class Entry<K2, V2> implements Map.Entry<K2, V2>, java.io.Serializable{
        
        private static final long serialVersionUID = -2047042147063848911L;
        
        private K2 key;
        
        /**
         * インスタンスを生成する。<p>
         */
        public Entry(K2 key){
            this.key = key;
        }
        
        // Map.EntryのJavaDoc
        @Override
        public K2 getKey(){
            return key;
        }
        
        // Map.EntryのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public V2 getValue(){
            return (V2)AbstractCacheMapService.this.get(key);
        }
        
        // Map.EntryのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public V2 setValue(V2 value){
            final V result = AbstractCacheMapService.this.remove(key);
            AbstractCacheMapService.this.put((K)key, (V)value);
            return (V2)result;
        }
        
        // Map.EntryのJavaDoc
        @SuppressWarnings("unchecked")
        @Override
        public boolean equals(Object o){
            if(o == null){
                return false;
            }
            if(o == this){
                return true;
            }
            if(o instanceof Entry){
                final Entry<K2, V2> cmp = (Entry<K2, V2>)o;
                if(key.equals(cmp.key)){
                    return true;
                }
            }
            return false;
        }
        
        // Map.EntryのJavaDoc
        @Override
        public int hashCode(){
            return key.hashCode();
        }
    }
}
