/*
 * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.interceptors;

import org.omg.PortableInterceptor.Interceptor;
import org.omg.PortableInterceptor.ORBInitInfo;
import org.omg.PortableInterceptor.ORBInitInfoPackage.DuplicateName;

import org.omg.CORBA.INTERNAL;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.lang.reflect.Array;

import com.sun.corba.se.impl.logging.InterceptorsSystemException ;

/**
 * Provides a repository of registered Portable Interceptors, organized
 * by type.  This list is designed to be accessed as efficiently as
 * possible during runtime, with the expense of added complexity during
 * initialization and interceptor registration.  The class is designed
 * to easily allow for the addition of new interceptor types.
 */
public class InterceptorList {

    // Interceptor type list.  If additional interceptors are needed,
    // add additional types in numerical order (do not skip numbers),
    // and update NUM_INTERCEPTOR_TYPES and classTypes accordingly.
    // NUM_INTERCEPTOR_TYPES represents the number of interceptor
    // types, so we know how many lists to maintain.
    static final int INTERCEPTOR_TYPE_CLIENT            = 0;
    static final int INTERCEPTOR_TYPE_SERVER            = 1;
    static final int INTERCEPTOR_TYPE_IOR               = 2;

    static final int NUM_INTERCEPTOR_TYPES              = 3;

    // Array of class types for interceptors.  This is used to create the
    // appropriate array type for each interceptor type.  These must
    // match the indices of the constants declared above.
    static final Class[] classTypes = {
        org.omg.PortableInterceptor.ClientRequestInterceptor.class,
        org.omg.PortableInterceptor.ServerRequestInterceptor.class,
        org.omg.PortableInterceptor.IORInterceptor.class
    };

    // True if no further interceptors may be registered with this list.
    private boolean locked = false;
    private InterceptorsSystemException wrapper ;

    // List of interceptors currently registered.  There are
    // NUM_INTERCEPTOR_TYPES lists of registered interceptors.
    // For example, interceptors[INTERCEPTOR_TYPE_CLIENT] contains an array
    // of objects of type ClientRequestInterceptor.
    private Interceptor[][] interceptors =
        new Interceptor[NUM_INTERCEPTOR_TYPES][];

    /**
     * Creates a new Interceptor List.  Constructor is package scope so
     * only the ORB can create it.
     */
    InterceptorList( InterceptorsSystemException wrapper ) {
        this.wrapper = wrapper ;
        // Create empty interceptors arrays for each type:
        initInterceptorArrays();
    }

    /**
     * Registers an interceptor of the given type into the interceptor list.
     * The type is one of:
     * <ul>
     *   <li>INTERCEPTOR_TYPE_CLIENT - ClientRequestInterceptor
     *   <li>INTERCEPTOR_TYPE_SERVER - ServerRequestInterceptor
     *   <li>INTERCEPTOR_TYPE_IOR - IORInterceptor
     * </ul>
     *
     * @exception DuplicateName Thrown if an interceptor of the given
     *     name already exists for the given type.
     */
    void register_interceptor( Interceptor interceptor, int type )
        throws DuplicateName
    {
        // If locked, deny any further addition of interceptors.
        if( locked ) {
            throw wrapper.interceptorListLocked() ;
        }

        // Cache interceptor name:
        String interceptorName = interceptor.name();
        boolean anonymous = interceptorName.equals( "" );
        boolean foundDuplicate = false;
        Interceptor[] interceptorList = interceptors[type];

        // If this is not an anonymous interceptor,
        // search for an interceptor of the same name in this category:
        if( !anonymous ) {
            int size = interceptorList.length;

            // An O(n) search will suffice because register_interceptor is not
            // likely to be called often.
            for( int i = 0; i < size; i++ ) {
                Interceptor in = (Interceptor)interceptorList[i];
                if( in.name().equals( interceptorName ) ) {
                    foundDuplicate = true;
                    break;
                }
            }
        }

        if( !foundDuplicate ) {
            growInterceptorArray( type );
            interceptors[type][interceptors[type].length-1] = interceptor;
        }
        else {
            throw new DuplicateName( interceptorName );
        }
    }

    /**
     * Locks this interceptor list so that no more interceptors may be
     * registered.  This method is called after all interceptors are
     * registered for security reasons.
     */
    void lock() {
        locked = true;
    }

    /**
     * Retrieves an array of interceptors of the given type.  For efficiency,
     * the type parameter is assumed to be valid.
     */
    Interceptor[] getInterceptors( int type ) {
        return interceptors[type];
    }

    /**
     * Returns true if there is at least one interceptor of the given type,
     * or false if not.
     */
    boolean hasInterceptorsOfType( int type ) {
        return interceptors[type].length > 0;
    }

    /**
     * Initializes all interceptors arrays to zero-length arrays of the
     * correct type, based on the classTypes list.
     */
    private void initInterceptorArrays() {
        for( int type = 0; type < NUM_INTERCEPTOR_TYPES; type++ ) {
            Class classType = classTypes[type];

            // Create a zero-length array for each type:
            interceptors[type] =
                (Interceptor[])Array.newInstance( classType, 0 );
        }
    }

    /**
     * Grows the given interceptor array by one:
     */
    private void growInterceptorArray( int type ) {
        Class classType = classTypes[type];
        int currentLength = interceptors[type].length;
        Interceptor[] replacementArray;

        // Create new array to replace the old one.  The new array will be
        // one element larger but have the same type as the old one.
        replacementArray = (Interceptor[])
            Array.newInstance( classType, currentLength + 1 );
        System.arraycopy( interceptors[type], 0,
                          replacementArray, 0, currentLength );
        interceptors[type] = replacementArray;
    }

    /**
     * Destroys all interceptors in this list by invoking their destroy()
     * method.
     */
    void destroyAll() {
        int numTypes = interceptors.length;

        for( int i = 0; i < numTypes; i++ ) {
            int numInterceptors = interceptors[i].length;
            for( int j = 0; j < numInterceptors; j++ ) {
                interceptors[i][j].destroy();
            }
        }
    }

    /**
     * Sort interceptors.
     */
    void sortInterceptors() {
        List sorted = null;
        List unsorted = null;

        int numTypes = interceptors.length;

        for( int i = 0; i < numTypes; i++ ) {
            int numInterceptors = interceptors[i].length;
            if (numInterceptors > 0) {
                // Get fresh sorting bins for each non empty type.
                sorted = new ArrayList(); // not synchronized like we want.
                unsorted = new ArrayList();
            }
            for( int j = 0; j < numInterceptors; j++ ) {
                Interceptor interceptor = interceptors[i][j];
                if (interceptor instanceof Comparable) {
                    sorted.add(interceptor);
                } else {
                    unsorted.add(interceptor);
                }
            }
            if (numInterceptors > 0 && sorted.size() > 0) {
                // Let the RuntimeExceptions thrown by sort
                // (i.e., ClassCastException and UnsupportedOperationException)
                // flow back to the user.
                Collections.sort(sorted);
                Iterator sortedIterator = sorted.iterator();
                Iterator unsortedIterator = unsorted.iterator();
                for( int j = 0; j < numInterceptors; j++ ) {
                    if (sortedIterator.hasNext()) {
                        interceptors[i][j] =
                            (Interceptor) sortedIterator.next();
                    } else if (unsortedIterator.hasNext()) {
                        interceptors[i][j] =
                            (Interceptor) unsortedIterator.next();
                    } else {
                        throw wrapper.sortSizeMismatch() ;
                    }
                }
            }
        }
    }
}
