package jacorb.orb;

/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1997-98  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 java.io.*;
import org.omg.IOP.*;
import jacorb.orb.util.*;
import org.omg.CosNaming.*;

/**
 * Class to convert IOR strings into IOR structures
 *
 * @author Gerald Brose, FU Berlin
 * @version $Id: ParsedIOR.java,v 1.15 2000/03/30 13:55:34 noffke Exp $
 */

public class ParsedIOR 
{
    protected org.omg.IIOP.ProfileBody_1_1 profileBody = null; // 1.1 by devik
    protected String object_key_str = null;
    protected boolean endianness = false;
    protected String ior_str = null;
    protected IOR ior = null;
    protected byte [] object_key;
    
    private jacorb.orb.ORB orb;

    /**
     * After decode this will be filled with IOR's 1.1 components.
     * @author devik
     */
    protected TaggedComponent [] taggedComponents = new TaggedComponent[0];

    public ParsedIOR( String object_reference )
    {
	parse( object_reference );
    }

    public ParsedIOR( String object_reference, ORB orb )
    {
	this.orb = orb;
	parse( object_reference );
    }

    public ParsedIOR( IOR _ior )
    {
	decode( _ior );
    }

    public static org.omg.IIOP.ProfileBody_1_1 getProfileBody(byte [] profile, 
							      int min_minor)
    {
	org.omg.IIOP.ProfileBody_1_1 _profile_body = null;
	CDRInputStream in = new CDRInputStream((org.omg.CORBA.ORB)null, profile);
	try{
	    // look for all profiles, if we found TaggedComponents
	    // we'll extract them
	    in.setLittleEndian(in.read_boolean());
      
	    // mark position because we will pre-read version from stream
	    in.mark(0); 
      
	    // first read the version and observe if we have already
	    // decoded newer version of IIOP IOR
	    int minor = org.omg.IIOP.VersionHelper.read(in).minor;
      
	    if((minor < min_minor) || (minor > 2)) 
		return null;
      
	    // return to start of profile body stream
	    in.reset();
      
	    switch(minor){
	    case 2: // 1.2 is compatible with 1.1
	    case 1:
		_profile_body = org.omg.IIOP.ProfileBody_1_1Helper.read(in);
		break;
	    case 0:
		// convert profile body 1.0 -> 1.1 by adding empty or existing tagged
		// components (should be always empty because if we already read >1.0
		// profile version, we should never encounter these lines)
		org.omg.IIOP.ProfileBody_1_0 pb0;
		pb0 = org.omg.IIOP.ProfileBody_1_0Helper.read(in);
		_profile_body = new org.omg.IIOP.ProfileBody_1_1(pb0.iiop_version,
								 pb0.host,
								 pb0.port,
								 pb0.object_key,
								 new org.omg.IOP.TaggedComponent[0]);
		//taggedComponents);
		if( _profile_body.port < 0 )
		    _profile_body.port += 65536;
		break;
	    }
	}catch ( Exception ex ){
	    jacorb.util.Debug.output(2,ex);
	    throw new org.omg.CORBA.INV_OBJREF();
	};

	return _profile_body;  
    }

    protected void decode_INTERNET_IOP_profile(byte [] profile)
    {
	org.omg.IIOP.ProfileBody_1_1 _body = 
	    getProfileBody(profile, ((profileBody != null)? 
				     profileBody.iiop_version.minor : 0));
      
	if (_body != null)
	    profileBody = _body;

	decode_MULTIPLE_COMPONENTS_profile(profileBody.components);
	jacorb.util.Debug.output(4,"IOP 1.1 decoded");

	object_key_str = new String( profileBody.object_key );
	// debug:
	// System.out.println("PIOR: " + object_key_str );
	object_key =  profileBody.object_key;
    }
	
    /**
     * Adds profiles into variable.
     */

    protected void decode_MULTIPLE_COMPONENTS_profile(TaggedComponent [] profiles)
    {
	TaggedComponent [] result = 
	    new TaggedComponent[taggedComponents.length + profiles.length];

	//  	int i;
	//  	for( i = 0; i < taggedComponents.length; i++)
	//  	    result[i] = taggedComponents[i];
	System.arraycopy(taggedComponents, 0, result, 0, taggedComponents.length);

	//  	for( i = 0; i < profiles.length; i++ )
	//  	    result[ i + taggedComponents.length ] = profiles[i];
	System.arraycopy(profiles, 0, result, taggedComponents.length, 
			 profiles.length);

	taggedComponents = result;
    }
	
    /**
     * When multiple internet IOP tags are present, they will probably
     * different versions, we will use highest version between 0 and 1.
     */

    public void decode( IOR _ior) 
    {
	boolean iiopFound=false;
	for (int i=0; i<_ior.profiles.length; i++)
	{
	    jacorb.util.Debug.output(4,"Parsing IOR, found profile id: "+_ior.profiles[i].tag);
	    switch(_ior.profiles[i].tag) //TAG_INTERNET_IOP
	    {
	    case TAG_MULTIPLE_COMPONENTS.value:
		jacorb.util.Debug.output(4,"TAG_MULTIPLE_COMPONENTS found in IOR");
		CDRInputStream in2 = new CDRInputStream((org.omg.CORBA.ORB)null,_ior.profiles[i].profile_data);
		in2.setLittleEndian(in2.read_boolean());
		decode_MULTIPLE_COMPONENTS_profile(MultipleComponentProfileHelper.read(in2));
		break;	
	    case TAG_INTERNET_IOP.value:
		jacorb.util.Debug.output(4,"TAG_INTERNET_IOP found in IOR");
		decode_INTERNET_IOP_profile(_ior.profiles[i].profile_data);
		iiopFound=true;
		break;
	    }
	} 
	if( !iiopFound && _ior.profiles.length > 0 ) // only if this not null
	    throw new org.omg.CORBA.INV_OBJREF("no TAG_INTERNET_IOP found in object_reference");

	ior = _ior;
	ior_str = getIORString();
    }

    /**
     * initialize this ParsedIOR by decoding a CorbaLoc address
     */

    public void decode( CorbaLoc corbaLoc )
    {
	IOR ior = null;
	CorbaLoc.ObjectAddress address = corbaLoc.objectAddressList[0];

	if( address.protocol_identifier.equals("rir"))
	{
	    try
	    {
		org.omg.CORBA.Object obj = orb.resolve_initial_references(corbaLoc.getKeyString());
		ior = ((Delegate)((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate()).getIOR();
	    }
	    catch( Exception e )
	    {
		jacorb.util.Debug.output(2, e );
		throw new IllegalArgumentException("Invalid corbaloc: URL");
	    }
	}
	else if( address.protocol_identifier.equals("iiop"))
	{
	    org.omg.IIOP.ProfileBody_1_0 profile_body = 
		new org.omg.IIOP.ProfileBody_1_0(address.getVersion(),
						 address.host,
						 (short)address.port,
						 corbaLoc.getKey());

	    ior = createIOR( "IDL:org.omg/CORBA/Object:1.0", profile_body);
	}
	decode( ior );
    }


    public static org.omg.IOP.IOR createIOR(String typeId, 
					    org.omg.IIOP.ProfileBody_1_0 profileBody)
    {
	org.omg.IOP.IOR ior = new org.omg.IOP.IOR();
	ior.type_id = typeId;
	ior.profiles = new org.omg.IOP.TaggedProfile[1];
	ior.profiles[0] = new org.omg.IOP.TaggedProfile();
	ior.profiles[0].tag = 0; // IIOP
	CDROutputStream out = new CDROutputStream();
	out.write_boolean(false);
	org.omg.IIOP.ProfileBody_1_0Helper.write( out, profileBody );
	ior.profiles[0].profile_data = out.getBufferCopy();
	return ior;
    }

    public byte[] get_object_key()
    {
	return object_key;
    }

    public org.omg.IOP.IOR getIOR()
    {
	return ior;
    }

    public String getIORString()
    {
	if( ior_str == null )
	{
	    try 
	    {	
		CDROutputStream out = new CDROutputStream();

       		// endianness = false, big-endian
		out.write_boolean(false);
		org.omg.IOP.IORHelper.write(out,ior);

		byte bytes[] = out.getBufferCopy();
		StringBuffer sb = new StringBuffer("IOR:");
		for (int j=0; j<bytes.length; j++)
		{
		    int b = bytes[j];
		    if(b<0) b+= 256;
		    int n1 = (0xff & b) / 16;
		    int n2 = (0xff & b) % 16;
		    int c1 = (n1 < 10) ? ('0' + n1) : ('a' + (n1 - 10));
		    int c2 = (n2 < 10) ? ('0' + n2) : ('a' + (n2 - 10));
		    //java.lang.System.out.println("" + b +","+ n1 +","+ n2 +","+ (char)c1 + (char)c2);
		    sb.append((char)c1);
		    sb.append((char)c2);
		}
		ior_str = sb.toString();
	    } 
	    catch (Exception e) 
	    { 
		jacorb.util.Debug.output(2,e); 
		throw new org.omg.CORBA.UNKNOWN("Error in building IIOP-IOR");
	    }
	}
	return ior_str;
    }
    public String getObjKey()
    {
	return object_key_str;
    }

    public org.omg.IIOP.ProfileBody_1_1 getProfileBody() // chg by devik
    {
	return profileBody;
    }

    public String getAddress()
    {
	int port = profileBody.port;
	if( port < 0 )
	    port += 65536;
	return profileBody.host + ":" + port;
    }

    public String getTypeId() 
    {
	return ior.type_id;
    }
    public boolean isNull()
    {
	return ( ior.type_id.equals("") && ( ior.profiles.length == 0 ));
    }

    protected void parse( String object_reference )
    {
	if (object_reference.startsWith("IOR:"))
	{
	    ior_str = object_reference;
	    ByteArrayOutputStream bos = new ByteArrayOutputStream();
	    int cnt = (object_reference.length()-4) / 2;
	    for(int j=0; j<cnt; j++)
	    {
		char c1 = object_reference.charAt(j*2+4);
		char c2 = object_reference.charAt(j*2+5);
		int i1 = (c1 >= 'a') ? (10 + c1 - 'a') :
		    ((c1 >= 'A') ? (10 + c1 - 'A') :
		     (c1 - '0'));
		int i2 = (c2 >= 'a') ? (10 + c2 - 'a') :
		    ((c2 >= 'A') ? (10 + c2 - 'A') :
		     (c2 - '0'));
		bos.write((i1*16+i2));
	    }
	    CDRInputStream in_ = new CDRInputStream(org.omg.CORBA.ORB.init(), bos.toByteArray());
	    //Connection.dumpBA( bos.toByteArray());
	    
	    endianness = in_.read_boolean();
	    if(endianness)
		in_.setLittleEndian(true);
	    
	    org.omg.IOP.IOR _ior =  org.omg.IOP.IORHelper.read(in_);
	    decode( _ior );
	}
	else if (object_reference.startsWith("corbaloc:"))
	{
	    decode( new CorbaLoc( object_reference ));
	}
	else if(object_reference.startsWith("corbaname:") )
	{
	    String corbaloc;
	    String name = "";

	    if( object_reference.indexOf('#') == -1 )
		corbaloc = "corbaloc:" + object_reference.substring(object_reference.indexOf(':')+1 );
	    else
	    {
		corbaloc = "corbaloc:" + object_reference.substring(object_reference.indexOf(':')+1, 
								    object_reference.indexOf('#'));
		name = object_reference.substring(object_reference.indexOf('#')+1);
	    }

	    /* empty key string in corbaname becomes NameService */
	    if( corbaloc.indexOf('/') == -1 )
		corbaloc += "/NameService";

	    jacorb.util.Debug.output(4,corbaloc);

	    try
	    {
		NamingContextExt n = NamingContextExtHelper.narrow( orb.string_to_object(corbaloc));
		org.omg.CORBA.Object target = n.resolve_str( name );
		IOR ior = ((Delegate)((org.omg.CORBA.portable.ObjectImpl)target)._get_delegate()).getIOR();
		decode(ior);
	    }
	    catch( Exception e )
	    {
		jacorb.util.Debug.output(4, e );
		throw new RuntimeException("Invalid object reference: " + object_reference);
	    }
	}
	else
	    throw new RuntimeException("Invalid IOR format: " + object_reference );
    }
}


