#!/usr/bin/env python
#############################################################################
# Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1997, 1998, 1999
# All Rights Reserved.
#
# The software contained on this media is the property of the DSTC Pty
# Ltd.  Use of this software is strictly in accordance with the
# license agreement in the accompanying LICENSE.HTML file.  If your
# distribution of this software does not contain a LICENSE.HTML file
# then you have no rights to use this software in any manner and
# should contact DSTC at the address below to determine an appropriate
# licensing arrangement.
# 
#      DSTC Pty Ltd
#      Level 7, GP South
#      Staff House Road
#      University of Queensland
#      St Lucia, 4072
#      Australia
#      Tel: +61 7 3365 4310
#      Fax: +61 7 3365 4311
#      Email: enquiries@dstc.edu.au
# 
# This software is being provided "AS IS" without warranty of any
# kind.  In no event shall DSTC Pty Ltd be liable for damage of any
# kind arising out of or in connection with the use or performance of
# this software.
#
# Project:      Fnorb
# File:         $Source: /projects/fnorb/Fnorb-1.1/orb/RCS/Util.py,v $
# Version:      @(#)$RCSfile: Util.py,v $ $Revision: 1.15 $
#
#############################################################################
""" Module for miscellaneous implementation dependent definitions! """


# Standard/built-in modules.
import keyword, new, string, UserList

# Fnorb extension modules.
import cdr


# The first four bytes of EVERY GIOP message header must contain this!
MAGIC = ['G', 'I', 'O', 'P']

# The version of the GIOP specification that we are implementing.
GIOP_VERSION_MAJOR = 1
GIOP_VERSION_MINOR = 0

# The version of the 'cdr' extension module that we must have.
CDR_VERSION = "1.1"
if CDR_VERSION != cdr.version():
    raise "Incompatible version of the 'cdr' extension module"

# Define the byte ordering appropriate for the host architecture.
#
# e.g DEC Alpha - LITTLE_ENDIAN
#     Linux     - LITTLE_ENDIAN
#     Solaris   - BIG_ENDIAN
#
# We get the byte order from the cdr extension module.  This is to try and
# localise any parts of the code that require configuration for specific
# architectures.
HOST_BYTE_ORDER = cdr.host_byte_order()

# Byte ordering options for GIOP messages.
BIG_ENDIAN    = 0
LITTLE_ENDIAN = 1

# Threading models.
REACTIVE = 0
THREADED = 1

# Err, the default size of the thread pool ;^)
DEFAULT_THREAD_POOL_SIZE = 10


def python_name(name):
    """ Add an underscore prefix to any name that is a Python keyword. """

    if keyword.iskeyword(name):
	return '_' + name

    return name


class Union:
    """ Base class for unions. """

    def __init__(self, discriminator, value):
	""" Constructor. """

	self.d = discriminator
	self.v = value

	return

    def __getinitargs__(self):
	""" Return the constructor arguments for unpickling. """

	return (self.d, self.v)

    def __str__(self):
	""" Return a string representation of the union. """

	return "%s(%s, %s)" % (self.__class__.__name__,str(self.d),str(self.v))


class Enum(UserList.UserList):
    """ Base class for enumerations. """

    def __init__(self, id, data):
	""" Constructor. """

	self._FNORB_ID = id
	self.data = data

	return

    def __getinitargs__(self):
	""" Return the constructor arguments for unpickling. """

	return (self._FNORB_ID, self.data)


class EnumMember:
    """ Enum member class. """

    def __init__(self, name, value):
	""" Constructor. """

	self._name = name
	self._value = value

	return

    def __getinitargs__(self):
	""" Return the constructor arguments for unpickling. """

	return (self._name, self._value)

    def __cmp__(self, other):
	""" Compare two enums. """

	return cmp(self._value, other._value)

    def __int__(self):
	""" Return the enum as an 'integer' for array indexing etc. """

	return self._value

    def __hash__(self):
	return self._value

    def __str__(self):
	return self._name

    __repr__ = __str__

    def name(self):
	return self._name

    def value(self):
	return self._value


class RepositoryId:
    """ CORBA Interface Repository Ids.

    An Interface Repository id has three colon-separated parts:-
	
    1) The format of the repository id (currently we only support 'IDL')
    2) The scoped name of the type definition
    3) The version of the type definition
	
    eg:- 'IDL:Module/Interface/Hello:1.0'
	
    has parts:-

    1) IDL
    2) Module/Interface/Hello
    3) 1.0

    """
    def __init__(self, str_id):
	""" Constructor.

	'str_id' is the string representation of a repository id.

	"""
	# Initialise the instance from the string.
	self._from_string(str_id)

	return

    def __getinitargs__(self):
	""" Return the constructor arguments for unpickling. """

	return (str(self),)

    def __str__(self):
	""" Return the string version of the repository id. """

	scoped_name = self._scoped_name.join('/')
	return self._format + ':' + scoped_name + ':' + self._version

    def format(self):
	""" Return the format.

	Currently this is ALWAYS 'IDL'.

	"""
	return self._format

    def scoped_name(self):
	""" Return the scoped name. """

	return self._scoped_name
    
    def version(self):
	""" Return the version. """

	return self._version

    def set_scoped_name(self, scoped_name):
	""" Set the scoped name. """

	self._scoped_name = scoped_name
	return

    def set_version(self, version):
	""" Set the version. """

	self._version = version
	return

    #########################################################################
    # Internal methods.
    #########################################################################

    def _from_string(self, str_id):
	""" Initialise the repository id from a string.

	'str_id' is the string representation of a repository id.

	An Interface Repository id has three colon-separated parts:-
	
	1) The format of the repository id (currently we only support 'IDL')
	2) The scoped name of the type definition
	3) The version of the type definition
	
	eg:- 'IDL:Module/Interface/Hello:1.0'

	has parts:-

	1) IDL
	2) Module/Interface/Hello
	3) 1.0
	
	"""
	if len(str_id) == 0:
	    raise 'Invalid (empty) Repository Id.'

	else:
	    [self._format, temp_name, self._version] = string.split(str_id,':')
	    if self._format != 'IDL':
		raise 'Invalid Repository Id.', str_id

	    # Each scope in the type name is delimited by the '/' character.
	    temp_components = string.split(temp_name, '/')

	    # 'Real' scoped names are separated by '::'!
	    self._scoped_name = ScopedName(string.join(temp_components, '::'))

	return


class CompoundName:
    """ Compound names.

    A compound name is made up of one or more components separated by a 
    given character sequence (string).

    This class is an abstract class used to derive classes representing IDL
    scoped names and Python package names.

    """
    # The component separator (set in each concrete derived class).
    _SEPARATOR = None

    def __init__(self, stringified_name=''):
	""" Constructor.

	'stringified_name' is the string representation of the compound name.

	"""
	# Initialise the compound name from the string.
	self._from_string(stringified_name)

	return

    def __add__(self, other):
	""" Add two compound names. """

	new_name = new.instance(self.__class__, {})
	new_name._components = self._components + other._components

	return new_name

    def __cmp__(self, other):
	""" Compare two compound names. """

	return cmp(self._components, other._components)

    def __delitem__(self, i):
	""" Delete the i'th component of the compound name. """

	del self._components[i]
	return

    def __delslice__(self, i, j):
	""" Delete the slice [i:j]. """

	del self._components[i:j]
	return

    def __getitem__(self, i):
	""" Get the i'th component of the compound name. """

	return self._components[i]

    def __getinitargs__(self):
	""" Return the constructor arguments for unpickling. """

	return (str(self),)

    def __getslice__(self, i, j):
	""" Return a new compound name consisting of the slice [i:j]. """

	new_name = new.instance(self.__class__, {})
	new_name._components = self._components[i:j]

	return new_name

    def __len__(self):
	""" Return the number of components in the compound name! """

	return len(self._components)

    def __mul__(self, n):
	""" Multiply the compound name! """

	new_name = new.instance(self.__class__, {})
	new_name._components = self._components * n

	return new_name

    def __setitem__(self, i, value):
	""" Set the i'th component of the compound name. """

	self._components[i] = value
	return

    def __setslice__(self, i, j, other):
	""" Set the slice [i:j]. """

	self._components[i:j] = other._components[i:j]
	return

    def __str__(self):
	""" Return the string representation of the compound name. """

	return string.join(self._components, self._SEPARATOR)

    __repr__ = __str__

    def append(self, component):
	""" Append onto a compound name. """

	self._components.append(component)
	return

    def insert(self, i, component):
	""" Insert into a compound name. """

	self._components.insert(i, component)
	return

    def join(self, sep=' '):
	""" Just like 'string.join' ;^) """

	return string.join(self._components, sep)

    def pythonise(self):
	""" Fix up any clashes with Python keywords. """

	self._components = map(python_name, self._components)
	return
    
    #########################################################################
    # Internal methods.
    #########################################################################

    def _from_string(self, stringified_name):
	""" Initialise the compound name from a string. """

	# This is because string.split('', self._SEPARATOR) = [''] and not []!
	if len(stringified_name) == 0:
	    self._components = []

	else:
	    self._components = string.split(stringified_name, self._SEPARATOR)

	return


class ScopedName(CompoundName):
    """ CORBA Interface Repository scoped names.

    A scoped name uniquely identifies modules, interfaces, constants,
    typedefs, exceptions, attributes and operations within an Interface
    Repository.

    A scoped name is a compound name made up of one or more identifiers
    separated by "::".
	
    eg:- 'AModule::AnInterface::SomeContainedObject'

    """
    # The component separator.
    _SEPARATOR = '::'


class PackageName(CompoundName):
    """ Python package names.

    A package name is a compound name made up of one or more identifiers
    separated by ".".
	
    eg:- 'Foo.Bar.Baz'

    """
    # The component separator.
    _SEPARATOR = '.'


#############################################################################
# Functions to stringify and unstringify Naming service names.
#############################################################################

def _fnorb_name_to_string(name):
    """ Convert a 'real' name into a stringified name! """

    components = []
    for component in name:
	components.append(component.id)
	
    # Backslashes are currently used to separate name components.
    return 'name:' + string.join(components, '/')

def _fnorb_string_to_name(stringified_name):
    """ Convert a stringified name into a 'real' name! """

    # fixme: We do the import here to get around the old circular import
    # problems!
    from Fnorb.cos.naming import CosNaming

    # Naming service names are simple lists of 'NameComponent's.
    name = []

    # Backslashes are currently used to separate name components.
    components = string.split(stringified_name[5:], '/')
    for component in components:
	name.append(CosNaming.NameComponent(component, ''))

    return name

#############################################################################
