"""
adesklets strings handling module
"""

from types import NoneType, BooleanType, StringType
from tempfile import mktemp
from sys import platform
from os import unlink
from string import maketrans, translate
import mmap

class String:
    """
    Make sure objects get string representation usable by 
    adesklets interpreter. This class simply flattened recursively
    all sequences passed to it, while preserving all other objects
    but 'None', which is stripped-out and booleans which is transformed
    to '0' or '1'.

    Of course, this does not dispense you from having terminal types
    with __str__ methods.

    For instance, str(String([1,None,('essai',True),4])) is '1 essai 1 4'

    Limitation: any large (>=1204 elements) sequences containing booleans
    or undefined objects will contain 'True' 'False' or 'None' substrings
    in the final result, and will therefore not be usable by adesklets.
    Now, this is not really a concern since such large entries are not used
    for any adesklets commands with anything but arrays of integers.
    Read below for further explanations on this.

    It should also be noted that the performance tune up possible on linux
    using mmap for large sequences is presently not working on all BSD's,
    so it is turned off by default; if anyone knows freebsd, openbsd or netbsd
    well enough to give me advice on this, I will be glad to change this code.
    """
    def __init__(self, object):
        self.__object=object
        
    def __str__(self):
        return_value=''
        if self._is_sequence(self.__object):
            if len(self.__object)<=1024 or not platform=='linux2':
                for element in self.__object:
                    return_value+=' ' + str(String(element))
            else:
                # We got a big structure. Let's not transliterate
                # things ourselves below this point in the tree,
                # but let's delegate the operation to native print in
                # the hope it will be quicker (usually, it is _much_
                # quicker indeed).
                #
                # I just dislike having to create a temporary file
                # for this, but since I have no native python
                # interface to mremap() or understandably to the
                # POSIXtly correct MAP_AUTOGROW flag for mmap
                # (still unsupported on many systems), it is the best
                # portable solution I have found yet. A better
                # implementation is welcomed!
                #
                # This works based on the fact that adesklets just
                # does not care what kind of spacing you use between
                # arguments. Therefore, it is enough to:
                #	- dump object content into a file, which is
                #         quite more efficient than doing our little
                #         tree search, cut, paste and replace
                #         magic for most of the built-in types.
                #       - Mmap the whole file, then plainly replace all
                #	  separator characters in-place.
                #
                # Result is bad looking, used a temporary file to
                # be constructed, do not transliterate Boolean nor NoneType,
                # but is built in reasonable time even for heavy structures :
                # a five megabytes string is generated in about three seconds
                # on a Pentium III 1GHz running linux, which greatly facilitate
                # smooth interaction with external packages such as PIL.
                #
                self.__filename=mktemp()
                self.__file=file(self.__filename,'w+')
                print >> self.__file, self.__object,
		print 'size:', self.__file.tell()


                self.__mmap=mmap.mmap(self.__file.fileno(),
				      100,
                                      mmap.MAP_PRIVATE)

                return translate(self.__mmap[0:self.__file.tell()-1],
                                 maketrans("'[](),",'      '))
        else:
            if type(self.__object)!=NoneType:
                if type(self.__object)!=BooleanType:
                    return_value=str(self.__object)
                else:
                    if self.__object:
                        return_value='1'
                    else:
                        return_value='0'
        return return_value.strip()
    
    def _is_sequence(self,object):
        """
        Since slice operation is common to all sequence objects,
        we do use it to determine if a given object is a sequence.
        By convention, strings are not considered sequences either
        (since we do not want to 's p l i t' them apart).

        This is taken from a mailing list post suggestion
        by Max M. <maxm@mxm.dk> :
        
        http://mail.python.org/pipermail/python-list/
        """
        try:
            test = object[0:0]
        except:
            return False
        else:
            return type(object)!=StringType

    def __del__(self):
        try:
            self.__file.close()
            self.__mmap.close()
            unlink(self.__filename)
        except:
            pass
