/*
 * This software is distributed under following license based on modified BSD
 * style license.
 * ----------------------------------------------------------------------
 * 
 * Copyright 2003 The Nimbus 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 Nimbus Project.
 */
package jp.ossc.nimbus.service.context;

import java.util.*;

import jp.ossc.nimbus.core.*;

/**
 * グループコンテキスト。<p>
 * 複数のコンテキストをグルーピングして、１つのコンテキストとして、コンテキスト情報が取得できるようにする。<br>
 * コンテキスト情報の追加はサポートしない。<br>
 * 以下に、サービス定義例を示す。<br>
 * <pre>
 * &lt;?xml version="1.0" encoding="Shift_JIS"?&gt;
 * 
 * &lt;nimbus&gt;
 *     
 *     &lt;manager name="Sample"&gt;
 *         
 *         &lt;service name="Context"
 *                  code="jp.ossc.nimbus.service.context.GroupContextService"&gt;
 *             &lt;attribute name="ContextServiceNames"&gt;
 *                 #Context1
 *                 #Context2
 *             &lt;/attribute&gt;
 *         &lt;/service&gt;
 *         
 *         &lt;service name="Context1"
 *                  code="jp.ossc.nimbus.service.context.DefaultContextService"&gt;
 *             &lt;attribute name="HOME_PATH"&gt;/home&lt;/attribute&gt;
 *         &lt;/service&gt;
 *         
 *         &lt;service name="Context2"
 *                  code="jp.ossc.nimbus.service.context.DefaultContextService"&gt;
 *             &lt;attribute name="DOMAIN"&gt;nimbus2.ossc.jp&lt;/attribute&gt;
 *         &lt;/service&gt;
 *         
 *     &lt;/manager&gt;
 *     
 * &lt;/nimbus&gt;
 * </pre>
 *
 * @author M.Takata
 */
public class GroupContextService<K, V> extends ServiceBase
 implements Context<K, V>, GroupContextServiceMBean<K, V>, java.io.Serializable{
    
    private static final long serialVersionUID = -5282880238704983055L;
    
    /**
     * グルーピングするコンテキストサービス名配列。<p>
     */
    protected ServiceName[] contextServiceNames;
    
    protected Context<K, V>[] contexts;
    
    // GroupContextServiceMBeanのJavaDoc
    @Override
    public void setContextServiceNames(ServiceName[] names){
        contextServiceNames = names;
    }
    
    // GroupContextServiceMBeanのJavaDoc
    @Override
    public ServiceName[] getContextServiceNames(){
        return contextServiceNames;
    }
    
    /**
     * Contextを設定する。
     */
    public void setContexts(Context<K, V>[] contexts) {
		this.contexts = contexts;
	}

	// ContextのJavaDoc
    @Override
    public V get(Object key){
        Context<K, V> context = null;
        if(contextServiceNames != null){
            for(int i = 0; i < contextServiceNames.length; i++){
                try{
                	Context<K, V> ctx = ServiceManagerFactory
                        .getServiceObject(contextServiceNames[i]);
                    if(ctx.containsKey(key)){
                    	context = ctx;
                        break;
                    }
                }catch(ServiceNotFoundException e){
                }
            }
        } else if(contexts != null) {
        	for(int i=0; i<contexts.length; i++) {
        		Context<K, V> ctx = contexts[i];
        		if(ctx.containsKey(key)){
        			context = ctx;
        			break;
        		}
        	}
        }
        return context == null ? null : context.get(key);
    }
    
    /**
     * サポートしない。<br>
     *
     * @param key キー
     * @param value コンテキスト情報
     * @return 指定されたキーに関連付けられていたコンテキスト情報。存在しない場合は、null
     * @exception UnsupportedOperationException
     */
    @Override
    public V put(K key, V value){
        throw new UnsupportedOperationException();
    }
    
    /**
     * コンテキスト情報のキー集合を取得する。<p>
     * 但し、このキー集合は、変更不可である。<br>
     *
     * @return コンテキスト情報のキー集合
     */
    @Override
    public Set<K> keySet(){
        final Set<K> result = new HashSet<K>();
        if(contextServiceNames != null){
            for(int i = 0; i < contextServiceNames.length; i++){
                try{
                    final Context<K, V> context = ServiceManagerFactory
                        .getServiceObject(contextServiceNames[i]);
                    result.addAll(context.keySet());
                }catch(ServiceNotFoundException e){
                }
            }
        } else if(contexts != null) {
        	for(int i =0; i<contexts.length; i++){
        		final Context<K, V> context = contexts[i];
        		result.addAll(context.keySet());
        	}
        }
        return Collections.unmodifiableSet(result);
    }
    
    /**
     * コンテキスト情報の集合を取得する。<p>
     * 但し、このコンテキスト情報の集合は、変更不可である。<br>
     *
     * @return コンテキスト情報の集合
     */
    @Override
    public Collection<V> values() {
        final Map<K, V> result = new HashMap<K, V>();
        if(contextServiceNames != null){
            for(int i = 0; i < contextServiceNames.length; i++){
                try{
                    final Context<K, V> context = ServiceManagerFactory
                        .getServiceObject(contextServiceNames[i]);
                    result.putAll(context);
                }catch(ServiceNotFoundException e){
                }
            }
        } else if(contexts != null) {
        	for(int i =0; i<contexts.length; i++){
        		final Context<K, V> context = contexts[i];
        		result.putAll(context);
        	}
        }
        return Collections.unmodifiableCollection(result.values());
    }
    
    /**
     * 指定されたキーに関連付けられたコンテキスト情報を削除する。<p>
     * グルーピングする全てのコンテキストから削除し、最後に削除したオブジェクトを返す。<br>
     *
     * @param key キー
     * @return 削除されたコンテキスト情報。削除するコンテキスト情報がない場合は、null
     */
    @Override
    public V remove(Object key) {
        V result = null;
        if(contextServiceNames != null){
            for(int i = 0; i < contextServiceNames.length; i++){
                try{
                    final Context<K, V> context = ServiceManagerFactory
                        .getServiceObject(contextServiceNames[i]);
                    if(context.containsKey(key)){
                        result = context.remove(key);
                    }
                }catch(ServiceNotFoundException e){
                }
            }
        } else if(contexts != null) {
        	for(int i =0; i<contexts.length; i++){
        		final Context<K, V> context = contexts[i];
        		if(context.containsKey(key)){
                    result = context.remove(key);
                }
        	}
        }
        return result;
    }
    
    /**
     * グルーピングする全てのコンテキスト情報を削除する。<p>
     */
    @Override
    public void clear(){
        if(contextServiceNames != null){
            for(int i = 0; i < contextServiceNames.length; i++){
                try{
                    final Context<K, V> context = ServiceManagerFactory
                        .getServiceObject(contextServiceNames[i]);
                    context.clear();
                }catch(ServiceNotFoundException e){
                }
            }
        } else if(contexts != null) {
        	for(int i =0; i<contexts.length; i++){
        		final Context<K, V> context = contexts[i];
        		context.clear();
        	}
        }
    }
    
    // ContextのJavaDoc
    @Override
    public boolean isEmpty(){
        if(contextServiceNames != null){
            for(int i = 0; i < contextServiceNames.length; i++){
                try{
                    final Context<K, V> context = ServiceManagerFactory
                        .getServiceObject(contextServiceNames[i]);
                    if(!context.isEmpty()){
                        return false;
                    }
                }catch(ServiceNotFoundException e){
                }
            }
        } else if(contexts != null) {
        	for(int i =0; i<contexts.length; i++){
        		final Context<K, V> context = contexts[i];
        		if(!context.isEmpty()){
                    return false;
                }
        	}
        }
        return true;
    }
    
    // ContextのJavaDoc
    @Override
    public boolean containsKey(Object key){
        if(contextServiceNames != null){
            for(int i = 0; i < contextServiceNames.length; i++){
                try{
                    final Context<K, V> context = ServiceManagerFactory
                        .getServiceObject(contextServiceNames[i]);
                    if(context.containsKey(key)){
                        return true;
                    }
                }catch(ServiceNotFoundException e){
                }
            }
        } else if(contexts != null) {
        	for(int i =0; i<contexts.length; i++){
        		final Context<K, V> context = contexts[i];
        		if(context.containsKey(key)){
                    return true;
                }
        	}
        }
        return false;
    }
    
    // ContextのJavaDoc
    @Override
    public boolean containsValue(Object value) {
        if(contextServiceNames != null){
            for(int i = 0; i < contextServiceNames.length; i++){
                try{
                    final Context<K, V> context = ServiceManagerFactory
                        .getServiceObject(contextServiceNames[i]);
                    if(context.containsValue(value)){
                        return true;
                    }
                }catch(ServiceNotFoundException e){
                }
            }
        } else if(contexts != null) {
        	for(int i =0; i<contexts.length; i++){
        		final Context<K, V> context = contexts[i];
        		if(context.containsValue(value)){
                    return true;
                }
        	}
        }
        return false;
    }
    
    /**
     * コンテキスト情報のエントリ集合を取得する。<p>
     * 但し、エントリ集合に対する変更は不可である。<br>
     *
     * @return コンテキスト情報のエントリ集合
     */
    @SuppressWarnings("unchecked")
    @Override
    public Set<Map.Entry<K,V>> entrySet(){
        final Map<K, Map.Entry<K,V>> result = new HashMap<K, Map.Entry<K,V>>();
        if(contextServiceNames != null){
            for(int i = 0; i < contextServiceNames.length; i++){
                try{
                    final Context<K, V> context = ServiceManagerFactory
                        .getServiceObject(contextServiceNames[i]);
                    final Set<Map.Entry<K, V>> entrySet = context.entrySet();
                    final Map.Entry<K, V>[] entries = entrySet.<Map.Entry<K, V>>toArray(new Map.Entry[entrySet.size()]);
                    for(Map.Entry<K, V> entry : entries){
                        result.put(
                            entry.getKey(),
                            entry
                        );
                    }
                }catch(ServiceNotFoundException e){
                }
            }
        } else if(contexts != null) {
        	for(int i =0; i<contexts.length; i++){
        		final Context<K, V> context = contexts[i];
                final Set<Map.Entry<K, V>> entrySet = context.entrySet();
                final Map.Entry<K, V>[] entries = entrySet.<Map.Entry<K, V>>toArray(new Map.Entry[entrySet.size()]);
                for(Map.Entry<K, V> entry : entries){
                    result.put(
                        entry.getKey(),
                        entry
                    );
                }
        	}
        }
        return Collections.unmodifiableSet(new HashSet<Map.Entry<K,V>>(result.values()));
    }
    
    // ContextのJavaDoc
    @Override
    public int size(){
        return keySet().size();
    }
    
    /**
     * サポートしない。<p>
     *
     * @param t コンテキスト情報として設定するマップ
     * @exception UnsupportedOperationException
     */
    @Override
    public void putAll(Map<? extends K,? extends V> t){
        throw new UnsupportedOperationException();
    }
    
    // ContextのJavaDoc
    @SuppressWarnings("unchecked")
    @Override
    public boolean equals(Object o){
        if(o == null){
            return false;
        }
        if(o == this){
            return true;
        }
        if(!(o instanceof Map)){
            return false;
        }
        return entrySet().equals(((Map<K, V>)o).entrySet());
    }
    
    // ContextのJavaDoc
    @Override
    public int hashCode(){
        return entrySet().hashCode();
    }
}
