/*
 * 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.aop;

import java.io.*;

import jp.ossc.nimbus.core.*;

/**
 * {@link InterceptorChain}のデフォルト実装。<p>
 * 現在呼び出されているインターセプタの情報をインスタンス変数に格納するので、スレッドセーフではないインターセプタチェーンである。<br>
 *
 * @author M.Takata
 */
public class DefaultInterceptorChain
 implements InterceptorChain, java.io.Serializable, Cloneable{
    
    private static final long serialVersionUID = 3689361711046717596L;
    
    /**
     * {@link InterceptorChainList}内の、現在の処理中の{@link Interceptor}のインデックス。<p>
     * 初期値は、-1。
     */
    protected int currentIndex = -1;
    
    /**
     * チェーンするインターセプタのリスト。<p>
     */
    protected transient InterceptorChainList interceptorChainList;
    
    /**
     * {@link InterceptorChainList}インタフェースを実装したサービスのサービス名。<p>
     */
    protected ServiceName interceptorChainListServiceName;
    
    /**
     * 本来の呼び出し先を呼び出すInvoker。<p>
     */
    protected transient Invoker invoker;
    
    /**
     * {@link Invoker}インタフェースを実装したサービスのサービス名。<p>
     */
    protected ServiceName invokerServiceName;
    
    /**
     * 空のインターセプタチェーンを生成する。<p>
     */
    public DefaultInterceptorChain(){}
    
    /**
     * 指定された{@link InterceptorChainList}と{@link Invoker}のインターセプタチェーンを生成する。<p>
     *
     * @param list チェーンするインターセプタのリスト
     * @param invoker 本来の呼び出し先を呼び出すInvoker
     */
    public DefaultInterceptorChain(InterceptorChainList list, Invoker invoker){
        setInterceptorChainList(list);
        setInvoker(invoker);
    }
    
    /**
     * 指定された{@link InterceptorChainList}サービスと{@link Invoker}サービスのインターセプタチェーンを生成する。<p>
     *
     * @param listServiceName チェーンするインターセプタのリストInterceptorChainListサービスのサービス名
     * @param invokerServiceName 本来の呼び出し先を呼び出すInvokerサービスのサービス名
     */
    public DefaultInterceptorChain(
        ServiceName listServiceName,
        ServiceName invokerServiceName
    ){
        setInterceptorChainListServiceName(listServiceName);
        setInvokerServiceName(invokerServiceName);
    }
    
    // InterceptorChainのJavaDoc
    @Override
    public Object invokeNext(InvocationContext context) throws Throwable{
        final InterceptorChainList list = getInterceptorChainList();
        if(list == null){
            final Invoker ivk = getInvoker();
            if(ivk != null){
                return ivk.invoke(context);
            }else{
                return null;
            }
        }
        int index = getCurrentInterceptorIndex();
        try{
            setCurrentInterceptorIndex(++index);
            final Interceptor interceptor = list.getInterceptor(context, index);
            if(interceptor != null){
                return interceptor.invoke(context, this);
            }else{
                final Invoker ivk = getInvoker();
                if(ivk != null){
                    return ivk.invoke(context);
                }else{
                    return null;
                }
            }
        }finally{
            setCurrentInterceptorIndex(--index);
        }
    }
    
    // InterceptorChainのJavaDoc
    @Override
    public int getCurrentInterceptorIndex(){
        return currentIndex;
    }
    
    // InterceptorChainのJavaDoc
    @Override
    public void setCurrentInterceptorIndex(int index){
        currentIndex = index;
    }
    
    // InterceptorChainのJavaDoc
    @Override
    public InterceptorChainList getInterceptorChainList(){
        if(interceptorChainListServiceName != null){
            try{
                return ServiceManagerFactory
                        .getServiceObject(interceptorChainListServiceName);
            }catch(ServiceNotFoundException e){
            }
        }
        return interceptorChainList;
    }
    
    /**
     * このインターセプタチェーンが持つインターセプタのリストを設定する。<p>
     *
     * @param list このインターセプタチェーンが持つインターセプタのリスト
     */
    public void setInterceptorChainList(InterceptorChainList list){
        if(interceptorChainList instanceof ServiceBase){
            interceptorChainListServiceName
                 = ((ServiceBase)list).getServiceNameObject();
        }else if(interceptorChainList instanceof Service){
            final Service service = (Service)list;
            if(service.getServiceManagerName() != null){
                interceptorChainListServiceName = new ServiceName(
                    service.getServiceManagerName(),
                    service.getServiceName()
                );
            }
        }
        if(interceptorChainListServiceName == null){
            interceptorChainList = list;
        }
    }
    
    /**
     * このインターセプタチェーンが持つインターセプタのリストInterceptorChainListサービスのサービス名を設定する。<p>
     *
     * @param name このインターセプタチェーンが持つインターセプタのリストInterceptorChainListサービスのサービス名
     */
    public void setInterceptorChainListServiceName(ServiceName name){
        interceptorChainListServiceName = name;
    }
    
    // InterceptorChainのJavaDoc
    @Override
    public Invoker getInvoker(){
        if(invokerServiceName != null){
            try{
                return ServiceManagerFactory
                    .getServiceObject(invokerServiceName);
            }catch(ServiceNotFoundException e){
            }
        }
        return invoker;
    }
    
    /**
     * 最後の呼び出しを行うInvokerを設定する。<p>
     *
     * @param invoker 最後の呼び出しを行うInvoker
     */
    public void setInvoker(Invoker invoker){
        if(invoker instanceof ServiceBase){
            invokerServiceName = ((ServiceBase)invoker).getServiceNameObject();
        }else if(invoker instanceof Service){
            final Service service = (Service)invoker;
            if(service.getServiceManagerName() != null){
                invokerServiceName = new ServiceName(
                    service.getServiceManagerName(),
                    service.getServiceName()
                );
            }
        }
        if(invokerServiceName == null){
            this.invoker = invoker;
        }
    }
    
    /**
     * 最後の呼び出しを行うInvokerサービスのサービス名を設定する。<p>
     *
     * @param name 最後の呼び出しを行うInvokerサービスのサービス名
     */
    public void setInvokerServiceName(ServiceName name){
        this.invokerServiceName = name;
    }
    
    // InterceptorChainのJavaDoc
    @Override
    public InterceptorChain cloneChain(){
        try{
            return (InterceptorChain)clone();
        }catch(CloneNotSupportedException e){
            return null;
        }
    }
    
    private void writeObject(ObjectOutputStream out) throws IOException{
        out.defaultWriteObject();
        if(interceptorChainListServiceName == null){
            out.writeObject(interceptorChainList);
        }
        if(invokerServiceName == null){
            out.writeObject(invoker);
        }
    }
    
    private void readObject(ObjectInputStream in)
     throws IOException, ClassNotFoundException{
        in.defaultReadObject();
        if(interceptorChainListServiceName == null){
            interceptorChainList = (InterceptorChainList)in.readObject();
        }else{
            interceptorChainList = ServiceManagerFactory
                .getServiceObject(interceptorChainListServiceName);
        }
        if(invokerServiceName == null){
            invoker = (Invoker)in.readObject();
        }else{
            invoker = ServiceManagerFactory
                .getServiceObject(invokerServiceName);
        }
    }
}
