#!/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: /cvsroot/fnorb/fnorb/orb/Util.py,v $ # Version: @(#)$RCSfile: Util.py,v $ $Revision: 1.24 $ # ############################################################################# """ Module for miscellaneous implementation dependent definitions! """ # Standard/built-in modules. import keyword, new, string, UserList # 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 = 2 # The version of the 'cdr' extension module that we must have. CDR_VERSION = "1.1" # 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 # OSF codeset registry mapping to Python codec names # numbers according to # ftp://ftp.opengroup.org/pub/code_set_registry/cs_registry1.2h osf2codec = { 0x00010001: 'iso-8859-1', 0x00010002: 'iso-8859-2', 0x00010003: 'iso-8859-3', 0x00010004: 'iso-8859-4', 0x00010005: 'iso-8859-5', 0x00010006: 'iso-8859-6', 0x00010007: 'iso-8859-7', 0x00010008: 'iso-8859-8', 0x00010009: 'iso-8859-9', 0x0001000a: 'iso-8859-10', 0x0001000f: 'iso-8859-15', 0x00010020: 'ascii', # The following encodings should not be used on the # wire because they have unclear endianness 0x00010100: 'utf-16-be', #UCS-2, Level 1; JDK 1.3 interprets this as big-endian # 0x00010101: UCS-2, Level 2 # 0x00010102: UCS-2, Level 3 # 0x00010104: UCS-4, Level 1 # 0x00010105: UCS-4, Level 2 # 0x00010106: UCS-4, Level 3 # 0x00010108: UTF-1, historic # 0x00010109: UTF-16, unclear endianness? # GIOP 1.2 specifies that it is BigEndian 0x00010109: 'utf-16-be', # The following encodings might be available with JapaneseCodecs # 0x00030001, JIS X0201:1976 # 0x00030004, JIS X0208:1978 # 0x00030005, JIS X0208:1983 # 0x00030006, JIS X0208:1990 # 0x0003000a, JIS X0212:1990 # 0x00030010, JIS eucJP:1993 # KoreanCodecs # 0x00040001, KS C5601:1987 # 0x00040002, KS C5657:1991 # 0x0004000a, KS eucKR:1991 # ChineseCodecs # 0x00050001, CNS 11643:1986 # 0x00050002, CNS 11643:1992 # 0x0005000a, CNS eucTW:1991 # 0x00050010, CNS eucTW:1993 # Omitted: 0x000b0001 0x000d0001 # OSF codesets: 0x0500???? 0x05010001: 'utf-8', # omitted: OSF JVC 0x0502???? # DEC: 0x1000???? # HP: 0x1001???? # IBM: 0x1002???? (maps to cp???) 0x10020025: 'cp037', 0x100201a8: 'cp424', 0x100201b5: 'cp437', 0x100201f4: 'cp500', # 737, 775 supported in Python, but not registered with OSF 0x10020352: 'cp850', 0x10020354: 'cp852', 0x10020357: 'cp855', 0x10020358: 'cp856', 0x10020359: 'cp857', # 860 supported in Python, but not registered with OSF 0x1002035d: 'cp861', 0x1002035e: 'cp862', 0x1002035f: 'cp863', 0x10020360: 'cp864', # 865 supported in Python, but not registered with OSF 0x10020362: 'cp866', 0x10020365: 'cp869', 0x1002036a: 'cp874', 0x1002036b: 'cp875', 0x100203ee: 'cp1026', # 1140 supported in Python, but not registered with OSF 0x100204e2: 'cp1250', 0x100204e3: 'cp1251', 0x100204e4: 'cp1252', 0x100204e5: 'cp1253', 0x100204e6: 'cp1254', 0x100204e7: 'cp1255', 0x100204e8: 'cp1256', 0x100204e9: 'cp1257', # 1258 supported in Python, but not registered with OSF # Hitachi: 0x1003???? # Fujitsu: 0x1004???? } codec2osf = {} for k,v in osf2codec.items(): codec2osf[v] = k # There is no narrow_ccs constant since that is changed dynamically. narrow_conversion_charsets = [ # We could insert any of the supported character sets here. # In practice, only presence of Latin-1 should ever matter 0x00010001 ] wide_pcs = 0x00010109 wide_conversion_charsets = [ # Again, any character sets could be offered for which we have # codecs. Since UTF-16BE will be the "native" wide character set, # we offer UTF-8 in addition. Since JDK 1.3 uses UCS-2 (in big-endian) # we also offer this as a conversion character set 0x05010001, 0x00010100 ] # In GIOP 1.1, the receiver must know whether a codeset is byte-oriented, # or based on larger units. # Delay creation of dictionary until it is needed def giop_11_width(osf_code): dict = { 0x00010001: 1, 0x00010002: 1, 0x00010003: 1, 0x00010004: 1, 0x00010005: 1, 0x00010006: 1, 0x00010007: 1, 0x00010008: 1, 0x00010009: 1, 0x0001000a: 1, 0x0001000f: 1, 0x00010020: 1, 0x00010100: 2, 0x00010101: 2, 0x00010102: 2, 0x00010104: 4, 0x00010105: 4, 0x00010106: 4, 0x00010109: 2, 0x05010001: 1, 0x10020025: 1, 0x100201a8: 1, 0x100201b5: 1, 0x100201f4: 1, 0x10020352: 1, 0x10020354: 1, 0x10020357: 1, 0x10020358: 1, 0x10020359: 1, 0x1002035d: 1, 0x1002035e: 1, 0x1002035f: 1, 0x10020360: 1, 0x10020362: 1, 0x10020365: 1, 0x1002036a: 1, 0x1002036b: 1, 0x100203ee: 1, 0x100204e2: 1, 0x100204e3: 1, 0x100204e4: 1, 0x100204e5: 1, 0x100204e6: 1, 0x100204e7: 1, 0x100204e8: 1, 0x100204e9: 1, } return dict[osf_code] def negotiate_codeset(comp): """Given the process codeset and a CodeSetComponentInfo, return the negotiated CodeSets service context.""" import codecs, CORBA, OctetStream, CONV_FRAME, IOP # Take the first codeset we recognize # This implicitly implements CMIR-then-SMIR for tcs_c in [comp.ForCharData.native_code_set] + \ comp.ForCharData.conversion_code_sets: try: codecs.lookup(osf2codec[tcs_c]) break except LookupError: continue else: raise CORBA.CODESET_INCOMPATIBLE for tcs_w in [comp.ForWcharData.native_code_set] + \ comp.ForWcharData.conversion_code_sets: try: codecs.lookup(osf2codec[tcs_w]) break except LookupError: continue else: raise CORBA.CODESET_INCOMPATIBLE # Now, create the code set service context csc = CONV_FRAME.CodeSetContext(tcs_c, tcs_w) csc_tc = CORBA.typecode( "IDL:omg.org/CONV_FRAME/CodeSetContext:1.0") encaps = OctetStream.Encapsulation() csc_tc._fnorb_marshal_value(encaps.cursor(), csc); data = encaps.data() return IOP.ServiceContext(IOP.CodeSets, data) 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. """ # note that this class can only be instantiated maxint times nextID = 0 def __init__(self, name, value): """ Constructor. """ self._name = name self._value = value self._ID = EnumMember.nextID EnumMember.nextID = EnumMember.nextID + 1 return def __getinitargs__(self): """ Return the constructor arguments for unpickling. """ return (self._name, self._value) def __cmp__(self, other): """ Compare two enums. """ if (not isinstance(other, EnumMember)): return 0 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 support GIOP ############################################################################# def _fnorb_fragment_11(dict): """Given a dictionary id:(header,cursor), check whether there is exactly one message that expects more fragments. Return (header,cursor) or None.""" # GIOP 1.1: no request ID in fragment header. We must # have exactly one reply with a set more-fragments bit result = None for k,(header,cursor) in self.replies.items(): if cursor.stream().more_fragments(): if result: # already have one return None else: result = (header, cursor) return result ############################################################################# # 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, has_prefix = 1): """ 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 = [] if has_prefix: # Backwards compatibility: strip off name: stringified_name = stringified_name[5:] # Backslashes can be used to escape '.', '/', and '\\'. To simplify # processing, we unescape those first with # to the characters \x01, \x02, and \x03 dotrans = 0 while 1: pos = stringified_name.find('\\') if pos == -1: break s1 = stringified_name[:pos] s2 = stringified_name[pos+1] s3 = stringified_name[pos+2:] if s2 == '.': s2 = '\x01' elif s2 == '/': s2 = '\x02' elif s2 == '\\': s2 = '\x03' stringified_name = s1 + s2 + s3 dotrans = 1 if dotrans: map = string.maketrans('\x01\x02\x03','./\\') # Slashes are used to separate name components. components = string.split(stringified_name, '/') for component in components: if component.find('.') == -1: id, kind = component, '' else: id, kind = component.split('.') if dotrans: id = id.translate(map) kind = kind.translate(map) name.append(CosNaming.NameComponent(id, kind)) return name #############################################################################