package jacorb.ir;

/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1997-2001  Gerald Brose.
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

import jacorb.orb.TypeCode;
//import jacorb.orb.TypeCodeUtil;

import jacorb.util.Debug;

import java.lang.reflect.*;
import java.util.*;
import org.omg.CORBA.ExceptionDefPOATie;

public class OperationDef 
    extends Contained
    implements org.omg.CORBA.OperationDefOperations
{
    private org.omg.CORBA.TypeCode result = null;
    private org.omg.CORBA.IDLType result_def = null;
    private org.omg.CORBA.ExceptionDef[] exceptions = null;
    private org.omg.CORBA.ParameterDescription[] params = null;

    private String[] contexts = new String[0];
    private org.omg.CORBA.OperationMode mode;

    private Method method;

    /** the extra information on the operation that is provided in the IRHelper */
    private String opInfo;
    private String returnTypeName;
    private String[] paramTypeNames = new String[0];

    private boolean defined = false;

    public OperationDef( Method m, Class def_in, Class irHelper, org.omg.CORBA.InterfaceDef i_def )
    {
        def_kind = org.omg.CORBA.DefinitionKind.dk_Operation;
        name( m.getName());

        Debug.assert( def_in != null, "Class argument null" );
        Debug.assert( i_def != null, "Idef argument null" );

        id( RepositoryID.toRepositoryID( RepositoryID.className(i_def.id())
                                         + "/" + name() ));
        version(id().substring( id().lastIndexOf(':')));
        defined_in = i_def;
        containing_repository = i_def.containing_repository();
        String className = def_in.getName();
        absolute_name = i_def.absolute_name() + "::" + name;
        method = m;

        jacorb.util.Debug.output(2, "New OperationDef, name: " + name + 
                                 " "  + absolute_name );
        Hashtable irInfo = null;
        opInfo = null;
        try
        {
            irInfo = (Hashtable)irHelper.getDeclaredField("irInfo").get(null);
            opInfo = (String)irInfo.get( name()  );
        }
        catch( Exception e )
        {
            e.printStackTrace();
        }

        /* parse extra operation information that's in the opInfo string */

        if( opInfo != null )
        {
            if( opInfo.endsWith("-oneway"))
            {
                mode = org.omg.CORBA.OperationMode.OP_ONEWAY;
            }

            if( opInfo.indexOf("(") > 0 )
                returnTypeName = opInfo.substring(0,opInfo.indexOf("("));


            StringTokenizer strtok = 
                new StringTokenizer(opInfo.substring(opInfo.indexOf("(") + 1, 
                                                     opInfo.lastIndexOf(")")), ",");

            paramTypeNames = new String[strtok.countTokens()];
            for( int i = 0; i < paramTypeNames.length; i++ )
            {
                String token = strtok.nextToken();
                paramTypeNames[i] = ( !token.equals(",") ? token : null );
            }

        }

        if( mode == null )
        {
            mode = org.omg.CORBA.OperationMode.OP_NORMAL;
        }


        contexts = new String[0];
    }

    void define()
    {
        try
        {
            result = 
                TypeCodeUtil.getTypeCode( method.getReturnType(), 
                                          RepositoryImpl.loader,
                                          null, 
                                          returnTypeName );

            result_def = jacorb.ir.IDLType.create( result, containing_repository );
        }
        catch( Exception e )
        { 
            e.printStackTrace();
        }

        params = getParameterDescriptions();

        Class [] ex_classes = method.getExceptionTypes();
        Class uexc = null;
        try 
        {
            uexc = RepositoryImpl.loader.loadClass("org.omg.CORBA.UserException");
        }
        catch ( ClassNotFoundException e1)
        {
            throw new org.omg.CORBA.INTF_REPOS(ErrorMsg.IR_Definition_Not_Found,
                                               org.omg.CORBA.CompletionStatus.COMPLETED_NO);
        }

        Vector v = new Vector();
        for( int ix = 0; ix < ex_classes.length; ix++ )
        {
            if( uexc.isAssignableFrom(ex_classes[ix]))
            {
                try
                {
                    ExceptionDef ex =  new ExceptionDef( ex_classes[ ix ], 
                                                         defined_in, 
                                                         containing_repository );
                    org.omg.CORBA.ExceptionDef exRef = 
                        org.omg.CORBA.ExceptionDefHelper.narrow(
                              RepositoryImpl.poa.servant_to_reference( 
                                   new ExceptionDefPOATie (  ex )
								   )
							  );
					
					v.addElement( exRef );
					ex.setReference( exRef );
                }
                catch( Exception e )
                {
                    e.printStackTrace();
                }
            }
        }

        exceptions = new org.omg.CORBA.ExceptionDef[ v.size() ];
        v.copyInto( exceptions );

        defined = true;
    }


    org.omg.CORBA.ParameterDescription[] getParameterDescriptions()
    {
        TypeCode tc = null;
        Class m_params[] = method.getParameterTypes();

        org.omg.CORBA.ParameterDescription[] params = 
            new org.omg.CORBA.ParameterDescription[m_params.length];

        if( paramTypeNames.length > 0 )
        {
            Debug.assert( paramTypeNames.length == m_params.length,
                          "Different parameter type numbers! " + 
                          paramTypeNames.length + " vs. " + m_params.length + 
                          " inforString: " + opInfo);
        }

        for( int i = 0; i < params.length; i++)
        {
            String name = "arg_" + i;
            String paramInfo = null;
            org.omg.CORBA.ParameterMode mode = null;
            try 
            {
                String parameterTypeName = m_params[i].getName();

                if( paramTypeNames.length != 0 )
                    paramInfo = paramTypeNames[i];

                if( ! parameterTypeName.endsWith("Holder")  )
                {
                    mode = org.omg.CORBA.ParameterMode.PARAM_IN;
                    if( paramInfo != null && paramInfo.indexOf(' ') > 0 )
                    {
                        parameterTypeName = paramInfo.substring( paramInfo.indexOf(' ')+1);
                    }
                }
                else
                {
                    Debug.assert( paramInfo != null && (paramInfo.indexOf(' ') > 0),
                                  "No param info for " + parameterTypeName);
                   
                    if( paramInfo.substring(0, (paramInfo.indexOf(' ')-1)).equals("inout"))
                        mode = org.omg.CORBA.ParameterMode.PARAM_INOUT;
                    else
                        mode = org.omg.CORBA.ParameterMode.PARAM_OUT;

                    parameterTypeName =  
                       parameterTypeName.substring(0, parameterTypeName.indexOf("Holder"));
                }

                jacorb.util.Debug.output(2,"Operation " + name() + ", param #"+ i + 
                                         ", paramTypeName " + parameterTypeName + 
                                         paramInfo );

                tc = TypeCodeUtil.getTypeCode( m_params[i], 
                                               RepositoryImpl.loader,
                                               null, 
                                               parameterTypeName );
            } 
            catch ( Exception e )
            {
                e.printStackTrace();
                throw new org.omg.CORBA.INTF_REPOS( ErrorMsg.IR_Definition_Not_Found, 
						    org.omg.CORBA.CompletionStatus.COMPLETED_NO);
            }
            org.omg.CORBA.IDLType type_def = IDLType.create( tc, containing_repository );
            params[i] = 
                new org.omg.CORBA.ParameterDescription( name, tc, type_def, mode);
        }
        return params;
    }

    public org.omg.CORBA.IDLType result_def()
    {
        Debug.assert( defined , "OperationDef undefined");
        Debug.assert( result_def != null, 
                      "Result def for op " + name() +" null");
        return result_def;
    }

    public void result_def(org.omg.CORBA.IDLType a)
    {
        result_def = a;
    }

    public org.omg.CORBA.OperationMode mode()
    {
        return mode;
    }

    public void mode( org.omg.CORBA.OperationMode a)
    {
        mode = a;
    }

    public org.omg.CORBA.TypeCode result()
    {
        Debug.assert( defined , "OperationDeg undefined");

        return result;
    }

    public org.omg.CORBA.ParameterDescription[] params()
    {
        Debug.assert( defined , "OperationDeg undefined");
        if( !defined )
            define();
        return params;
    }

    public void params(org.omg.CORBA.ParameterDescription[] a)
    {
        params = a;
    }

    public java.lang.String[] contexts()
    {
        Debug.assert( defined , "OperationDeg undefined");


        if( !defined )
            define();
        return contexts;
    }

    public void contexts(java.lang.String[] a)
    {
        contexts = a;
    }

    public org.omg.CORBA.ExceptionDef[] exceptions()
    {
        Debug.assert( defined , "OperationDeg undefined");
        if( !defined )
            define();
        return exceptions;
    }

    public void exceptions(org.omg.CORBA.ExceptionDef[] a)
    {
        exceptions = a;
    }

    public org.omg.CORBA.OperationDescription describe_operation()
    {
        if( !defined )
            define();

        org.omg.CORBA.ExceptionDescription ex_des[] = 
            new org.omg.CORBA.ExceptionDescription[exceptions.length];

        for( int i = 0; i < exceptions.length; i++ )
        {
			org.omg.CORBA.ContainedPackage.Description cd = exceptions[i].describe();

            if( cd.kind != org.omg.CORBA.DefinitionKind.dk_Exception )
            {
                throw new org.omg.CORBA.INTF_REPOS( ErrorMsg.IR_Unexpected_Definition_Kind,
                                                    org.omg.CORBA.CompletionStatus.COMPLETED_NO);
            }
            ex_des[i] = org.omg.CORBA.ExceptionDescriptionHelper.extract( cd.value );
        }
        return new org.omg.CORBA.OperationDescription(name, 
                                                      id, 
                                                      org.omg.CORBA.ContainedHelper.narrow(defined_in).id(), 
                                                      version, 
                                                      result,  mode,  contexts, params, ex_des);
    }

    // from IRObject

    public void destroy()
    {
        throw new org.omg.CORBA.INTF_REPOS( 
                                           ErrorMsg.IR_Not_Implemented,
                                           org.omg.CORBA.CompletionStatus.COMPLETED_NO);
    }


    // from Contained

    public org.omg.CORBA.ContainedPackage.Description describe()
    {
        Debug.assert( defined , "OperationDef undefined");

        org.omg.CORBA.Any a = orb.create_any();
        org.omg.CORBA.OperationDescriptionHelper.insert( a, describe_operation() );
        return new org.omg.CORBA.ContainedPackage.Description( org.omg.CORBA.DefinitionKind.dk_Operation, a);
    }


}



