PyXR

c:\projects\bitpim\src \ prototypes.py



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2003-2005 Roger Binns <rogerb@rogerbinns.com>
0004 ###
0005 ### This program is free software; you can redistribute it and/or modify
0006 ### it under the terms of the BitPim license as detailed in the LICENSE file.
0007 ###
0008 ### $Id: prototypes.py 4660 2008-08-03 17:23:40Z hjelmn $
0009 
0010 """The various types used in protocol descriptions
0011 
0012 To implement a type used for protocol descriptions,
0013 examine the code for UINTlsb in this file.  Of note:
0014 
0015   - Inherit from BaseProtogenClass
0016   - Call superclass constructors using super()
0017   - Do not process any of the args or kwargs in the constructor unless
0018     you need them to alter how you are constructed
0019   - At the end of the constructor, call _update if you are the most
0020     derived class
0021   - In _update, call super()._update, and then delete keyword arguments
0022     as you process them (consider using L{BaseProtogenClass._consumekw} function)
0023   - If you are the most derived class, complain about
0024     unused keyword arguments (consider using
0025     L{BaseProtogenClass._complainaboutunusedargs} function)
0026   - set _bufferstartoffset and _bufferendoffset whenever
0027     you are read or written from a buffer
0028   - (optionally) define a getvalue() method that returns
0029     a better type.  For example if your class is integer
0030     like then this would return a real int.  If string like,
0031     then this will return a real string.
0032   - If you are a container, override iscontainer.  You will
0033     also need to provide a containerelements() method which
0034     lets you iterate over the entries.
0035 
0036 containerelements method:
0037 
0038   - You should return tuples of (fieldname, fieldvalue, descriptionstring or None)
0039   - fieldvalue should be the actual object, not a pretty version (eg a USTRING not str)
0040   
0041     
0042 """
0043 import sys
0044 import calendar
0045 import cStringIO
0046 import re
0047 import time
0048 
0049 import common
0050 
0051 class ProtogenException(Exception):
0052     """Base class for exceptions encountered with data marshalling"""
0053     def __init__(self, *args, **kwargs):
0054         Exception.__init__(self, *args, **kwargs)
0055 
0056 class SizeNotKnownException(ProtogenException):
0057     "Unable to marshal since size isn't known"
0058     def __init__(self):
0059         ProtogenException.__init__(self, "The size of this item is not known and hence cannot be en/decoded")
0060 
0061 class ValueNotSetException(ProtogenException):
0062     "Value not been set"
0063     def __init__(self):
0064         ProtogenException.__init__(self, "The value for this object has not been set.")
0065 
0066 class ValueException(ProtogenException):
0067     "Some sort of problem with the value"
0068     def __init__(self, str):
0069         ProtogenException.__init__(self,str)
0070 
0071 class NotTerminatedException(ProtogenException):
0072     "The value should have been terminated and wasn't"
0073     def __init__(self):
0074         ProtogenException.__init__(self,"The value should have been terminated and wasn't")
0075 
0076 class ValueLengthException(ProtogenException):
0077     "The value is the wrong size (too big or too small)"
0078     def __init__(self, sz, space):
0079         ProtogenException.__init__(self, "The value (length %d) is the wrong size for space %d" % (sz,space))
0080     
0081 class MissingQuotesException(ProtogenException):
0082     "The value does not have quotes around it"
0083     def __init__(self):
0084         ProtogenException.__init__(self, "The value does not have the required quote characters around it")
0085     
0086 
0087 class BaseProtogenClass(object):
0088     """All types are derived from this"""
0089     def packetsize(self):
0090         "Returns size in bytes that we occupy"
0091         # This is implemented by writing to a buffer and seeing how big the result is.
0092         # The code generator used to make one per class but it was so rarely used (twice)
0093         # that this implementation is available instead
0094         b=buffer()
0095         self.writetobuffer(b)
0096         return len(b.getvalue())
0097         
0098     def writetobuffer(self, buf):
0099         "Scribble ourselves to the buf"
0100         raise NotImplementedError("writetobuffer()")
0101     def readfrombuffer(self, buf):
0102         "Get our value from the buffer"
0103         raise NotImplementedError("readfrombuffer()")
0104     def getvalue(self):
0105         "Returns our underlying value if sensible (eg an integer, string or list) else returns self"
0106         return self
0107     def packetspan(self):
0108         """Returns tuple of begining,end offsets from last packet we were read or written from.
0109 
0110         Note that in normal Python style, end is one beyond the last byte we
0111         actually own"""
0112         return self._bufferstartoffset, self._bufferendoffset
0113     def _consumekw(self, dict, consumelist):
0114         """A helper function for easily setting internal values from the dict
0115 
0116         For each name in consumelist, we look for it in the dict and
0117         set self._name to the value from dict.  The key is then deleted
0118         from the dict."""
0119         for name in consumelist:
0120             if dict.has_key(name):
0121                 setattr(self, "_"+name, dict[name])
0122                 del dict[name]
0123     def _complainaboutunusedargs(self, klass, dict):
0124         """A helper function that will raise an exception if there are unused keyword arguments.
0125 
0126         @Note: that we only complain if in the most derived class, so it is safe
0127         to always call this helper as the last line of your constructor.
0128 
0129         @param klass:  This should be the class you are calling this function from
0130         @param dict:   The keyword arguments still in play
0131         """
0132         if len(dict) and self.__class__.__mro__[0]==klass:
0133             raise TypeError('Unexpected keyword args supplied: '+`dict`)
0134 
0135     def _ismostderived(self, klass):
0136         
0137         return self.__class__.__mro__[0]==klass
0138 
0139     def _update(self, args, kwargs):
0140         return
0141 
0142     def iscontainer(self):
0143         """Do we contain fields?"""
0144         return False
0145 
0146     def update(self, *args, **kwargs):
0147         self._update(args, kwargs)
0148 
0149     # help with logging
0150     def autologwrite(self, buf, logtitle="<written data>"):
0151         f=sys._getframe() # my frame
0152         f=f.f_back # my caller
0153         if f:
0154             f=f.f_back # their caller
0155             if f:
0156                 caller=f.f_locals.get("self", None)
0157                 if caller:
0158                     try:
0159                         caller.logdata(logtitle, buf.getvalue(), self)
0160                     except:
0161                         pass
0162 
0163     def autologread(self, buf, logtitle="<read data>"):
0164         f=sys._getframe() # my frame
0165         f=f.f_back # my caller
0166         if f:
0167             f=f.f_back # their caller
0168             if f:
0169                 caller=f.f_locals.get("self", None)
0170                 if caller:
0171                     try:
0172                         caller.logdata(logtitle, buf.getdata(), self)
0173                     except:
0174                         pass
0175 
0176 class UINTlsb(BaseProtogenClass):
0177     "An integer in Least Significant Byte first order"
0178     def __init__(self, *args, **kwargs):
0179         """
0180         An integer value can be specified in the constructor, or as the value keyword arg.
0181 
0182         @keyword constant:  (Optional) A constant value.  All reads must have this value
0183         @keyword constantexception: (Optional) Type of exception raised when data doesn't match constant.
0184         @keyword sizeinbytes: (Mandatory for writing, else Optional) How big we are in bytes
0185         @keyword default:  (Optional) Our default value
0186         @keyword value: (Optional) The value
0187         """
0188         super(UINTlsb, self).__init__(*args, **kwargs)
0189         self._constant=None
0190         self._constantexception=ValueError
0191         self._sizeinbytes=None
0192         self._value=None
0193         self._default=None
0194 
0195         if self._ismostderived(UINTlsb):
0196             self._update(args,kwargs)
0197 
0198 
0199     def _update(self, args, kwargs):
0200         super(UINTlsb,self)._update(args, kwargs)
0201         
0202         self._consumekw(kwargs, ("constant", "constantexception", "sizeinbytes", "default", "value"))
0203         self._complainaboutunusedargs(UINTlsb,kwargs)
0204 
0205         # Set our value if one was specified
0206         if len(args)==0:
0207             pass
0208         elif len(args)==1:
0209             self._value=int(args[0])
0210         else:
0211             raise TypeError("Unexpected arguments "+`args`)
0212 
0213         if self._value is None and self._default is not None:
0214             self._value=self._default
0215 
0216         if self._value is None and self._constant is not None:
0217             self._value=self._constant
0218 
0219         if self._constant is not None and self._constant!=self._value:
0220             raise self._constantexception("This field is a constant of %d.  You tried setting it to %d" % (self._constant, self._value))
0221 
0222 
0223     def readfrombuffer(self, buf):
0224         if self._sizeinbytes is None:
0225             raise SizeNotKnownException()
0226         self._bufferstartoffset=buf.getcurrentoffset()
0227         # lsb read
0228         res=0
0229         shift=0
0230         for dummy in range(self._sizeinbytes):
0231             res|=buf.getnextbyte()<<shift
0232             shift+=8
0233         self._value=res
0234         self._bufferendoffset=buf.getcurrentoffset()
0235         if self._constant is not None and self._value!=self._constant:
0236             raise self._constantexception("The value read should be a constant of %d, but was %d instead" % (self._constant, self._value))
0237          
0238     def writetobuffer(self, buf):
0239         if self._sizeinbytes is None:
0240             raise SizeNotKnownException()
0241         if self._value is None:
0242             raise ValueNotSetException()
0243         
0244         self._bufferstartoffset=buf.getcurrentoffset()
0245         # lsb write
0246         res=self._value
0247         for dummy in range(self._sizeinbytes):
0248             buf.appendbyte(res&0xff)
0249             res>>=8
0250         self._bufferendoffset=buf.getcurrentoffset()
0251 
0252     def packetsize(self):
0253         if self._sizeinbytes is None:
0254             raise SizeNotKnownException()
0255         return self._sizeinbytes
0256         
0257     def getvalue(self):
0258         """Returns the integer we are"""
0259         if self._value is None:
0260             raise ValueNotSetException()
0261         return self._value
0262 
0263 class BOOLlsb(UINTlsb):
0264     "An Boolean in Least Significant Byte first order"
0265     def __init__(self, *args, **kwargs):
0266         """
0267         A boolean value can be specified in the constructor.
0268 
0269         Keyword arguments are the same a UINTlsb
0270         """
0271         super(BOOLlsb, self).__init__(*args, **kwargs)
0272 
0273         if self._ismostderived(BOOLlsb):
0274             self._update(args,kwargs)
0275 
0276     def _update(self, args, kwargs):
0277         super(BOOLlsb,self)._update(args,kwargs)
0278         self._complainaboutunusedargs(BOOLlsb,kwargs)
0279         self._boolme()
0280 
0281     def _boolme(self):
0282         if self._value is not None:
0283             self._value=bool(self._value)
0284 
0285     def readfrombuffer(self, buf):
0286         UINTlsb.readfrombuffer(self,buf)
0287         self._boolme()
0288     
0289 class STRING(BaseProtogenClass):
0290     "A text string DEPRECATED USE USTRING "
0291     def __init__(self, *args, **kwargs):
0292         """
0293         A string value can be specified to this constructor, or in the value keyword arg.
0294 
0295         @keyword constant: (Optional) A constant value.  All reads must have this value
0296         @keyword terminator: (Default=0) The string terminator (or None).  If set there will
0297              always be a terminator when writing.  The terminator is not returned when getting
0298              the value.
0299         @keyword pad: (Default=0) The padding byte if fixed length when writing, or stripped off
0300                        when reading
0301         @keyword sizeinbytes: (Optional) Set if fixed length.
0302              If not set, then the terminator will be used to find the end of strings on reading.
0303              If not set and the terminator is None, then reads will be entire rest of buffer.
0304         @keyword maxsizeinbytes: (Optional) Max string length.  Used together
0305              with terminator to limit the max length of the value.
0306              TODO: Need to add this to USTRING also.
0307         @keyword default: (Optional) Our default value
0308         @keyword raiseonunterminatedread: (Default True) raise L{NotTerminatedException} if there is
0309              no terminator on the value being read in.  terminator must also be set.
0310         @keyword raiseontruncate: (Default True) raise L{ValueLengthException} if the supplied
0311              value is too large to fit within sizeinbytes.
0312         @keyword value: (Optional) Value
0313         @keyword pascal: (Default False) The string is preceded with one byte giving the length
0314                          of the string (including terminator if there is one)
0315         """
0316         super(STRING, self).__init__(*args, **kwargs)
0317         
0318         self._constant=None
0319         self._terminator=0
0320         self._pad=0
0321         self._sizeinbytes=None
0322         self._default=None
0323         self._raiseonunterminatedread=True
0324         self._raiseontruncate=True
0325         self._value=None
0326         self._pascal=False
0327         self._maxsizeinbytes=None
0328 
0329         if self._ismostderived(STRING):
0330             self._update(args,kwargs)
0331 
0332     def _update(self, args, kwargs):
0333         super(STRING,self)._update(args, kwargs)
0334 
0335         self._consumekw(kwargs, ("constant", "terminator", "pad", "pascal",
0336         "sizeinbytes", "default", "raiseonunterminatedread", "value",
0337                                  "raiseontruncate", "maxsizeinbytes"))
0338         self._complainaboutunusedargs(STRING,kwargs)
0339 
0340         # Set our value if one was specified
0341         if len(args)==0:
0342             pass
0343         elif len(args)==1:
0344             self._value=common.forceascii(args[0])
0345             if self._constant is not None and self._constant!=self._value:
0346                 raise ValueException("This field is a constant of '%s'.  You tried setting it to '%s'" % (self._constant, self._value))
0347         else:
0348             raise TypeError("Unexpected arguments "+`args`)
0349         if self._value is None and self._default is not None:
0350             self._value=self._default
0351 
0352         if self._value is not None:
0353             self._value=str(self._value) # no unicode here!
0354             l=len(self._value)
0355             if self._sizeinbytes is not None:
0356                 if self._terminator is not None:
0357                     l+=1
0358                 if l>self._sizeinbytes:
0359                     if self._raiseontruncate:
0360                         raise ValueLengthException(l, self._sizeinbytes)
0361                     
0362                     self._value=self._value[:self._sizeinbytes]
0363                     if len(self._value) and self._terminator is not None:
0364                         self._value=self._value[:-1]
0365             elif self._maxsizeinbytes is not None:
0366                 if l>self._maxsizeinbytes:
0367                     if self._raiseontruncate:
0368                         raise ValueLengthException(l, self._maxsizeinbytes)
0369                     self._value=self._value[:self._maxsizeinbytes]
0370 
0371     def readfrombuffer(self, buf):
0372         self._bufferstartoffset=buf.getcurrentoffset()
0373         
0374         flush=0
0375         if self._pascal: 
0376             if self._sizeinbytes is None:
0377                 self._sizeinbytes=buf.getnextbyte()
0378             # allow for fixed size pascal strings
0379             else:
0380                 temp=self._sizeinbytes-1
0381                 self._sizeinbytes=buf.getnextbyte()
0382                 flush=temp-self._sizeinbytes
0383                 if(temp < 0):
0384                     raise ValueLengthException()
0385 
0386         if self._sizeinbytes is not None:
0387             # fixed size
0388             self._value=buf.getnextbytes(self._sizeinbytes)
0389             if self._terminator is not None:
0390                 # using a terminator
0391                 pos=self._value.find(chr(self._terminator))
0392                 if pos>=0:
0393                     self._value=self._value[:pos]
0394                 elif self._raiseonunterminatedread:
0395                     raise NotTerminatedException()
0396             elif self._pad is not None:
0397                 # else remove any padding
0398                 while len(self._value) and self._value[-1]==chr(self._pad):
0399                     self._value=self._value[:-1]
0400         else:
0401             if self._terminator is None:
0402                 # read in entire rest of packet
0403                 self._value=buf.getremainingbytes()
0404             else:
0405                 # read up to terminator
0406                 self._value=""
0407                 while buf.hasmore():
0408                     self._value+=chr(buf.getnextbyte())
0409                     if self._value[-1]==chr(self._terminator):
0410                         break
0411                 if self._value[-1]!=chr(self._terminator):
0412                     if self._raiseonunterminatedread:
0413                         raise NotTerminatedException()
0414                 else:
0415                     self._value=self._value[:-1]
0416 
0417         if self._maxsizeinbytes is not None:
0418             self._value=self._value[:self._maxsizeinbytes]
0419 
0420         if self._constant is not None and self._value!=self._constant:
0421             raise ValueException("The value read was not the constant")
0422 
0423         # for fixed size pascal strings flush the remainder of the buffer
0424         if(flush):
0425             buf.getnextbytes(flush)
0426 
0427         self._bufferendoffset=buf.getcurrentoffset()
0428 
0429     def writetobuffer(self, buf):
0430         if self._value is None:
0431             raise ValueNotSetException()
0432 
0433         self._bufferstartoffset=buf.getcurrentoffset()
0434         # calculate length
0435         l=len(self._value)
0436         if self._terminator is not None:
0437             l+=1
0438         if self._pascal:
0439             buf.appendbyte(l)
0440             l+=1
0441         buf.appendbytes(self._value)
0442         if self._terminator is not None:
0443             buf.appendbyte(self._terminator)
0444         if self._sizeinbytes is not None:
0445             if l<self._sizeinbytes:
0446                 buf.appendbytes(chr(self._pad)*(self._sizeinbytes-l))
0447 
0448         self._bufferendoffset=buf.getcurrentoffset()
0449 
0450     def packetsize(self):
0451         if self._sizeinbytes is not None:
0452             return self._sizeinbytes
0453 
0454         if self._value is None:
0455             raise ValueNotSetException()
0456 
0457         l=len(self._value)
0458         if self._terminator is not None:
0459             l+=1
0460         if self._pascal:
0461             l+=1
0462         return l
0463 
0464     def getvalue(self):
0465         """Returns the string we are"""
0466         if self._value is None:
0467             raise ValueNotSetException()
0468         return self._value
0469 
0470 
0471 class USTRING(BaseProtogenClass):
0472     "A text string that supports configurable encodings"
0473     def __init__(self, *args, **kwargs):
0474         """
0475         A string value can be specified to this constructor, or in the value keyword arg.
0476 
0477         @keyword constant: (Optional) A constant value.  All reads must have this value
0478         @keyword terminator: (Default=0) The string terminator (or None).  If set there will
0479              always be a terminator when writing.  The terminator is not returned when getting
0480              the value.
0481         @keyword terminator_length: (Default=1) (min:1, max:4)The length of the string terminator. 
0482              This keyword is not used if the terminator is None. Multi-byte terminators are treated 
0483              as LSB when read from the phone.
0484         @keyword pad: (Default=0) The padding byte if fixed length when writing, or stripped off
0485                        when reading
0486         @keyword sizeinbytes: (Optional) Set if fixed length.
0487              If not set, then the terminator will be used to find the end of strings on reading.
0488              If not set and the terminator is None, then reads will be entire rest of buffer.
0489         @keyword maxsizeinbytes: (Optional) Max string length.  Used together
0490              with terminator to limit the max length of the value.
0491         @keyword default: (Optional) Our default value
0492         @keyword raiseonunterminatedread: (Default True) raise L{NotTerminatedException} if there is
0493              no terminator on the value being read in. Terminator must also be set.
0494         @keyword raiseontruncate: (Default True) raise L{ValueLengthException} if the supplied
0495              value is too large to fit within sizeinbytes.
0496         @keyword value: (Optional) Value
0497         @keyword pascal: (Default False) The string is preceded with one byte giving the length
0498                          of the string (including terminator if there is one)
0499         @keyword encoding: (Default 'ascii') The charset to use when reading/writing to a buffer
0500         @keyword read_encoding: (Default keyword:encoding) The charset to use when reading from a buffer
0501         @keyword write_encoding: (Default keyword:encoding) The charset to use when writing to a buffer
0502         """
0503         super(USTRING, self).__init__(*args, **kwargs)
0504         
0505         self._constant=None
0506         self._terminator=0
0507         self._pad=0
0508         self._sizeinbytes=None
0509         self._default=None
0510         self._raiseonunterminatedread=True
0511         self._raiseontruncate=True
0512         self._value=None
0513         self._pascal=False
0514         self._maxsizeinbytes=None
0515         self._encoding='ascii'
0516         self._read_encoding=None
0517         self._write_encoding=None
0518         self._terminator_length=1
0519 
0520         if self._ismostderived(USTRING):
0521             self._update(args,kwargs)
0522 
0523     def _update(self, args, kwargs):
0524         super(USTRING,self)._update(args, kwargs)
0525 
0526         self._consumekw(kwargs, ("constant", "terminator", "pad", "pascal",
0527         "sizeinbytes", "default", "raiseonunterminatedread", "value", "raiseontruncate",
0528         "encoding", "read_encoding", "write_encoding", "maxsizeinbytes"))
0529         self._complainaboutunusedargs(USTRING,kwargs)
0530         if self._read_encoding==None:
0531             self._read_encoding=self._encoding
0532         if self._write_encoding==None:
0533             self._write_encoding=self._encoding
0534         if self._terminator_length < 1 or self._terminator_length > 4:
0535             raise ValueException("Terminator length outside allowed range of 1-4.  You tried setting it to %d" % self._terminator_length)
0536         
0537         # Set our value if one was specified
0538         if len(args)==0:
0539             pass
0540         elif len(args)==1:
0541             self._value=args[0]
0542         else:
0543             raise TypeError("Unexpected arguments "+`args`)
0544         if self._value is None and self._default is not None:
0545             self._value=self._default
0546 
0547         if self._value is not None:
0548             # convert _value to unicode, non-string objects convert to string representation
0549             # allows strings to be initialised with integers etc.
0550             if not isinstance(self._value, (str, unicode)):
0551                 # convert numbers into strings
0552                 temp=str(self._value)
0553                 self._value=unicode(temp, 'ascii', 'replace')
0554             # we should only receive unicode strings from bitpim, but...
0555             elif not isinstance(self._value, unicode):
0556                 # there is a bug, the docs say this should work on unicode strings but it does not
0557                 self._value=unicode(self._value, 'ascii', 'replace')
0558 
0559         if self._constant is not None and self._constant!=self._value:
0560             raise ValueException("This field is a constant of '%s'.  You tried setting it to '%s'" % (self._constant, self._value))
0561         # test that we can convert the string, also needed for "sizeinbytes" 
0562         try:
0563             test=self.convert_for_write()
0564         except UnicodeEncodeError:
0565             raise common.PhoneStringEncodeException(self._value, uni_string_codec) 
0566 
0567         if self._value is not None:
0568             max_size=None
0569             if self._sizeinbytes is not None:
0570                 max_size=self._sizeinbytes
0571             elif self._maxsizeinbytes is not None:
0572                 max_size=self._maxsizeinbytes
0573             if max_size is not None:
0574                 l=len(test)
0575                 if self._terminator is not None:
0576                     l+=self._terminator_length
0577                 if l>max_size:
0578                     if self._raiseontruncate:
0579                         raise ValueLengthException(l, self._sizeinbytes)
0580                     # truncate, but the number of bytes might be larger than the number of
0581                     # unicode characters depending on conversion
0582                     self._value=self._value[:max_size]
0583                     term_len=0
0584                     if self._terminator!=None:
0585                         term_len=self._terminator_length
0586                     # adjust for terminator and multibyte characters
0587                     while (len(self.convert_for_write())+term_len)>max_size:
0588                         self._value=self._value[:-1]
0589 
0590     def readfrombuffer(self, buf):
0591         self._bufferstartoffset=buf.getcurrentoffset()
0592         
0593         flush=0
0594         _value=''
0595         if self._pascal: 
0596             if self._sizeinbytes is None:
0597                 self._sizeinbytes=buf.getnextbyte()
0598             # allow for fixed size pascal strings
0599             else:
0600                 temp=self._sizeinbytes-1
0601                 self._sizeinbytes=buf.getnextbyte()
0602                 flush=temp-self._sizeinbytes
0603                 if(temp < 0):
0604                     raise ValueLengthException()
0605 
0606         if self._sizeinbytes is not None:
0607             # fixed size
0608             _value=buf.getnextbytes(self._sizeinbytes)
0609             if self._terminator is not None:
0610                 # using a terminator
0611                 pos=-1
0612                 for i in range(0, self._sizeinbytes, self._terminator_length):
0613                     term=0
0614                     for j in range(self._terminator_length):
0615                         term+=ord(_value[i+j])<<(j*8)
0616                     if term==self._terminator:
0617                         pos=i
0618                         break
0619                 if pos>=0:
0620                     _value=_value[:pos]
0621                 elif self._raiseonunterminatedread:
0622                     raise NotTerminatedException()
0623             elif self._pad is not None:
0624                 # else remove any padding
0625                 while len(_value) and _value[-1]==chr(self._pad):
0626                     _value=_value[:-1]
0627         else:
0628             if self._terminator is None:
0629                 # read in entire rest of packet
0630                 _value=buf.getremainingbytes()
0631             else:
0632                 # read up to terminator
0633                 _value=""
0634                 count=0
0635                 term=0
0636                 while buf.hasmore():
0637                     _value+=chr(buf.getnextbyte())
0638                     count+=1
0639                     if (count % self._terminator_length)==0:
0640                         term=0
0641                         # see if we have a terminator
0642                         for j in range(self._terminator_length):
0643                             term=(term<<8)+ord(_value[count-1-j])
0644                         if term==self._terminator:
0645                             break
0646                 if term!=self._terminator and self._raiseonunterminatedread:
0647                     raise NotTerminatedException()
0648                 else:
0649                     _value=_value[:-1]
0650 
0651         if self._maxsizeinbytes is not None:
0652             self._value=self._value[:self._maxsizeinbytes]
0653 
0654         if self._constant is not None and _value!=self._constant:
0655             raise ValueException("The value read was not the constant")
0656 
0657         # for fixed size pascal strings flush the remainder of the buffer
0658         if(flush):
0659             buf.getnextbytes(flush)
0660 
0661         self._bufferendoffset=buf.getcurrentoffset()
0662 
0663         # we will convert to unicode when the value is retrieved.
0664         # this prevents garbage that will never be used from causing an
0665         # exception. e.g. A deleted calendar record on some LG phones may contain
0666         # all f's, this causes an exception if we try to convert to unicode.
0667         self._value=_value
0668 
0669     def writetobuffer(self, buf):
0670         if self._value is None:
0671             raise ValueNotSetException()
0672         self._bufferstartoffset=buf.getcurrentoffset()
0673         # calculate length
0674         temp_str=self.convert_for_write()
0675         l=len(temp_str)
0676         if self._terminator is not None:
0677             l+=1
0678         if self._pascal:
0679             buf.appendbyte(l)
0680             l+=1
0681         buf.appendbytes(temp_str)
0682         term=self._terminator
0683         if self._terminator is not None:
0684             for j in range(self._terminator_length):
0685                 buf.appendbyte((term & 0xFF))
0686                 term=term>>8
0687         if self._sizeinbytes is not None:
0688             if l<self._sizeinbytes:
0689                 buf.appendbytes(chr(self._pad)*(self._sizeinbytes-l))
0690         self._bufferendoffset=buf.getcurrentoffset()
0691 
0692     def convert_for_write(self):
0693         # if we are not in unicode this means that we contain data read from the phone
0694         # in this case just return this back, there is no need to convert twice.
0695         if not isinstance(self._value, unicode):
0696             return self._value
0697         try:
0698             temp_str=common.encode_with_degrade(self._value, self._write_encoding)
0699         except UnicodeError:
0700             raise common.PhoneStringEncodeException(self._value, self._write_encoding) 
0701         return temp_str
0702 
0703     def packetsize(self):
0704         if self._sizeinbytes is not None:
0705             return self._sizeinbytes
0706 
0707         if self._value is None:
0708             raise ValueNotSetException()
0709 
0710         l=len(self.convert_for_write())
0711         if self._terminator is not None:
0712             l+=1
0713 
0714         return l
0715 
0716     def getvalue(self):
0717         """Returns the string we are"""
0718         if self._value is None:
0719             raise ValueNotSetException()
0720         # convert to unicode if we are not already
0721         if not isinstance(self._value, unicode):
0722             try:
0723                 self._value=self._value.decode(self._read_encoding)
0724             except UnicodeDecodeError:
0725                 # this means the codec is set wrong for this phone !!
0726                 raise common.PhoneStringDecodeException(self._value, self._read_encoding) 
0727         return self._value
0728 
0729 class SEVENBITSTRING(BaseProtogenClass):
0730     """A text string where ASCII characters are stored as packed 7 bit characters.  This is
0731     typically used in SMS messages."""
0732     def __init__(self, *args, **kwargs):
0733         """
0734         @keyword terminator: (Default=\x00) The termination character
0735         @keyword sizeinbytes: Amount of space the string sits in
0736         """
0737         super(SEVENBITSTRING, self).__init__(*args, **kwargs)
0738         self._value=None
0739         self._terminator='\x00'
0740         self._sizeinbytes=None
0741         if self._ismostderived(SEVENBITSTRING):
0742             self._update(args,kwargs)
0743 
0744     def _update(self, args, kwargs):
0745         super(SEVENBITSTRING,self)._update(args, kwargs)
0746 
0747         self._consumekw(kwargs, ("terminator", "value", "sizeinbytes"))
0748         self._complainaboutunusedargs(SEVENBITSTRING, kwargs)
0749         if len(args):
0750             raise TypeError("Unexpected arguments "+`args`)
0751         if self._sizeinbytes is None:
0752             raise ValueException("You must specify a size in bytes")
0753 
0754     def readfrombuffer(self, buf):
0755         self._bufferstartoffset=buf.getcurrentoffset()
0756         bytes=buf.getnextbytes(self._sizeinbytes)
0757         self._value=common.decodecharacterbits(bytes, bitsperchar=7, charconv=chr, terminator=self._terminator)
0758         self._bufferendoffset=buf.getcurrentoffset()
0759 
0760     def getvalue(self):
0761         """Returns the string we are"""
0762         if self._value is None:
0763             raise ValueNotSetException()
0764         return self._value        
0765         
0766 class SMSDATE(BaseProtogenClass):
0767     """A date as used in SMS messages.  It is six bytes long with the
0768     bytes being year month day hour minute second.  From stuff on the
0769     web, it appears GSM phones swap each nybble."""
0770     def __init__(self, *args, **kwargs):
0771         """@keyword sizeinbytes: (optional) Must be six"""
0772         super(SMSDATE, self).__init__(*args, **kwargs)
0773         self._values=None
0774         self._sizeinbytes=6
0775         if self._ismostderived(SMSDATE):
0776             self._update(args, kwargs)
0777 
0778     def _update(self, args, kwargs):
0779         super(SMSDATE, self)._update(args, kwargs)
0780         self._consumekw(kwargs, ("sizeinbytes",))
0781         self._complainaboutunusedargs(SMSDATE, kwargs)
0782         if len(args):
0783             raise TypeError("Unexpected arguments "+`args`)
0784         if self._sizeinbytes != 6:
0785             raise ValueNotSetException("You can only specify 6 as the size in bytes")
0786 
0787     def readfrombuffer(self, buf):
0788         self._bufferstartoffset=buf.getcurrentoffset()
0789         year=buf.getnextbyte()
0790         if year<0:
0791             year+=1900
0792         else:
0793             year+=2000
0794         month=buf.getnextbyte()
0795         day=buf.getnextbyte()
0796         hour=buf.getnextbyte()
0797         minute=buf.getnextbyte()
0798         second=buf.getnextbyte()
0799         self._value=year,month,day, hour,minute,second
0800         self._bufferendoffset=buf.getcurrentoffset()
0801 
0802     def getvalue(self):
0803          """Returns the  ISO date time string we are"""
0804          if self._value is None:
0805              raise ValueNotSetException()
0806          return "%d%02d%02dT%02d%02d%02d" % self._value
0807 
0808 
0809 class CSVSTRING(BaseProtogenClass):
0810     """A text string enclosed in quotes, with a way to escape quotes that a supposed
0811     to be part of the string.  Typical of Samsung phones."""
0812     def __init__(self, *args, **kwargs):
0813         """
0814         A string value can be specified to this constructor, or in the value keyword arg.
0815 
0816         @keyword constant: (Optional) A constant value.  All reads must have this value
0817         @keyword terminator: (Default=,) The string terminator (or None).  If set there will
0818              always be a terminator when writing.  The terminator is not returned when getting
0819              the value.
0820         @keyword quotechar: (Default=Double Quote) Quote character that surrounds string
0821         @keyword readescape: (Default=True) Interpret PPP escape char (0x7d)
0822         @keywors writeescape: (Default=False) Escape quotechar.  If false, drop quotechar in string.
0823         @keyword maxsizeinbytes: (Optional) On writing, truncate strings longer than this (length is before
0824                        any escaping and quoting
0825         @keyword default: (Optional) Our default value
0826         @keyword raiseonunterminatedread: (Default True) raise L{NotTerminatedException} if there is
0827              no terminator on the value being read in.  terminator must also be set.
0828         @keyword raiseontruncate: (Default True) raise L{ValueLengthException} if the supplied
0829              value is too large to fit within sizeinbytes.
0830         @keyword raiseonmissingquotes: (Default True) raise L{MissingQuotesException} if the string does
0831              not have quote characters around it
0832         @keyword value: (Optional) Value
0833         @keyword invalidchars: (Default=quotechar) A string containing invalid
0834              characters which would be removed before writing to buffer.
0835         @keyword encoding: (Default=None) If specified Unicode charset.
0836         @keyword raiseonunicodeerror: (Default=True) raise exception if fail
0837         to encode/decode Unicode.
0838         """
0839         super(CSVSTRING, self).__init__(*args, **kwargs)
0840         
0841         self._constant=None
0842         self._terminator=ord(',')
0843         self._quotechar=ord('"')
0844         self._readescape=True
0845         self._writeescape=False
0846         self._maxsizeinbytes=None
0847         self._default=None
0848         self._raiseonunterminatedread=True
0849         self._raiseontruncate=True
0850         self._raiseonmissingquotes=True
0851         self._invalidchars=chr(self._quotechar)
0852         self._value=None
0853         self._encoding=None
0854         self._raiseonunicodeerror=True
0855 
0856         if self._ismostderived(CSVSTRING):
0857             self._update(args,kwargs)
0858 
0859     def _update(self, args, kwargs):
0860         super(CSVSTRING,self)._update(args, kwargs)
0861 
0862         self._consumekw(kwargs, ("constant", "terminator", "quotechar", "readescape",
0863                                  "writeescape", "maxsizeinbytes", "default",
0864                                  "raiseonunterminatedread", "value",
0865                                  "raiseontruncate", "raiseonmissingquotes",
0866                                  "invalidchars"))
0867         self._complainaboutunusedargs(CSVSTRING,kwargs)
0868 
0869         # Set our value if one was specified
0870         if len(args)==0:
0871             pass
0872         elif len(args)==1:
0873             self._value=common.forceascii(args[0])
0874             if self._constant is not None and self._constant!=self._value:
0875                 raise ValueException("This field is a constant of '%s'.  You tried setting it to '%s'" % (self._constant, self._value))
0876         else:
0877             raise TypeError("Unexpected arguments "+`args`)
0878         if self._value is None and self._default is not None:
0879             self._value=self._default
0880 
0881         if self._value is not None:
0882             self._value=str(self._value) # no unicode here!
0883             if self._invalidchars:
0884                 self._value=re.sub(r'[%s]'%self._invalidchars, r'', self._value)
0885             if self._maxsizeinbytes is not None:
0886                 l=len(self._value)
0887                 if l>self._maxsizeinbytes:
0888                     if self._raiseontruncate:
0889                         raise ValueLengthException(l, self._maxsizeinbytes)
0890                     
0891                     self._value=self._value[:self._maxsizeinbytes]
0892 
0893     def readfrombuffer(self, buf):
0894         self._bufferstartoffset=buf.getcurrentoffset()
0895 
0896         # First character better be a terminator
0897         # Raise on, first char not quote, trailing quote never found, character after last quote
0898         # not , or EOL.  Will need an option to not raise if initial quote not found
0899 
0900         if self._terminator is None:
0901             # read in entire rest of packet
0902             # Ignore maxsizeinbytes
0903             # Check for leading and trailing quotes later
0904             self._value=buf.getremainingbytes()
0905         else:
0906             # Possibly quoted string.  If first character is not a quote, read until
0907             # terminator or end of line.  Exception will be thrown after reading if
0908             # the string was not quoted and it was supposed to be.
0909             self._value=chr(buf.getnextbyte())
0910             if self._value == ',':
0911                 self._value = ''
0912             else:
0913                 inquotes=False
0914                 if self._quotechar is not None:
0915                     if self._value[0]==chr(self._quotechar):
0916                         inquotes=True
0917                 while buf.hasmore():
0918                     self._value+=chr(buf.getnextbyte())
0919                     if inquotes:
0920                         if self._value[-1]==chr(self._quotechar):
0921                             inquotes=False
0922                     else:
0923                         if self._value[-1]==chr(self._terminator):
0924                             break
0925                 if self._value[-1]==self._terminator:
0926                     if self._raiseonunterminatedread:
0927                         raise NotTerminatedException()
0928                 else:
0929                     self._value=self._value[:-1]
0930 
0931         if self._quotechar is not None and self._value:
0932             if self._value[0]==chr(self._quotechar) and self._value[-1]==chr(self._quotechar):
0933                 self._value=self._value[1:-1]
0934             else:
0935                 raise MissingQuotesException()
0936 
0937         if self._readescape:
0938             self._value=common.pppunescape(self._value)
0939             
0940         if self._constant is not None and self._value!=self._constant:
0941             raise ValueException("The value read was not the constant")
0942 
0943         self._bufferendoffset=buf.getcurrentoffset()
0944 
0945     def writetobuffer(self, buf):
0946         # Need to raise exception if string exceeds maxsizeinbytes
0947         if self._value is None:
0948             raise ValueNotSetException()
0949 
0950         if self._encoding and isinstance(self._value, unicode):
0951             try:
0952                 _value=common.encode_with_degrade(self._value,
0953                                                   self._encoding)
0954             except UnicodeError:
0955                 if self._raiseonunicodeerror:
0956                     raise common.PhoneStringEncodeException(self._value,
0957                                                             self._encoding)
0958                 else:
0959                     # failed to encode, clear it out
0960                     _value=''
0961         else:
0962             _value=self._value
0963 
0964         self._bufferstartoffset=buf.getcurrentoffset()
0965 
0966         if self._quotechar is not None:
0967             buf.appendbyte(self._quotechar)
0968         buf.appendbytes(_value)
0969         if self._quotechar is not None:
0970             buf.appendbyte(self._quotechar)
0971         if self._terminator is not None:
0972             buf.appendbyte(self._terminator)
0973 
0974         self._bufferendoffset=buf.getcurrentoffset()
0975 
0976     def packetsize(self):
0977         if self._sizeinbytes is not None:
0978             return self._sizeinbytes
0979 
0980         if self._value is None:
0981             raise ValueNotSetException()
0982 
0983         l=len(self._value)
0984         if self._terminator is not None:
0985             l+=1
0986 
0987         return l
0988 
0989     def getvalue(self):
0990         """Returns the string we are"""
0991         if self._value is None:
0992             raise ValueNotSetException()
0993         # convert to unicode if we are not already
0994         if self._encoding and not isinstance(self._value, unicode):
0995             try:
0996                 if self._raiseonunicodeerror:
0997                     self._value=self._value.decode(self._encoding)
0998                 else:
0999                     self._value=self._value.decode(self._encoding, 'ignore')
1000             except UnicodeDecodeError:
1001                 # this means the codec is set wrong for this phone !!
1002                 raise common.PhoneStringDecodeException(self._value, self._encoding) 
1003         return self._value
1004 
1005 class CSVINT(CSVSTRING):
1006     """Integers in CSV lines"""
1007     def __init__(self, *args, **kwargs):
1008         super(CSVINT,self).__init__(*args, **kwargs)
1009 
1010         self._quotechar=None
1011 
1012         if self._ismostderived(CSVINT):
1013             self._update(args,kwargs)
1014 
1015     def _update(self, args, kwargs):
1016         for k in 'constant', 'default', 'value':
1017             if kwargs.has_key(k):
1018                 kwargs[k]=str(kwargs[k])
1019         if len(args)==0:
1020             pass
1021         elif len(args)==1:
1022             args=(str(args[0]),)
1023         else:
1024             raise TypeError("expected integer as arg")
1025             
1026         super(CSVINT,self)._update(args,kwargs)
1027         self._complainaboutunusedargs(CSVINT,kwargs)
1028         
1029     def getvalue(self):
1030         """Convert the string into an integer
1031 
1032         @rtype: integer
1033         """
1034 
1035         # Will probably want an flag to allow null strings, converting
1036         # them to a default value
1037         
1038         val=super(CSVINT,self).getvalue()
1039         try:
1040             ival=int(val)
1041         except:
1042             try:
1043                 ival=int(self._default)
1044             except:
1045                 raise ValueException("The field '%s' is not an integer" % (val))
1046         return ival
1047 
1048 class CSVDATE(CSVSTRING):
1049     """Dates in CSV lines"""
1050     def __init__(self, *args, **kwargs):
1051         super(CSVDATE,self).__init__(*args, **kwargs)
1052 
1053         self._valuedate=(0,0,0) # Year month day
1054 
1055         self._quotechar=None
1056         
1057         if self._ismostderived(CSVDATE):
1058             self._update(args,kwargs)
1059 
1060     def _update(self, args, kwargs):
1061         for k in 'constant', 'default', 'value':
1062             if kwargs.has_key(k):
1063                 kwargs[k]=self._converttostring(kwargs[k])
1064         if len(args)==0:
1065             pass
1066         elif len(args)==1:
1067             args=(self._converttostring(args[0]),)
1068         else:
1069             raise TypeError("expected (year,month,day) as arg")
1070 
1071         super(CSVDATE,self)._update(args, kwargs) # we want the args
1072         self._complainaboutunusedargs(CSVDATE,kwargs)
1073 
1074     def getvalue(self):
1075         """Unpack the string into the date
1076 
1077         @rtype: tuple
1078         @return: (year, month, day)
1079         """
1080 
1081         s=super(CSVDATE,self).getvalue()
1082         val=s.split("/") # List of of Month, day, year
1083         if len(val)<2:
1084             year = 0
1085             month = 0
1086             day = 0
1087         else:
1088             year=int(val[2])
1089             month=int(val[0])
1090             day=int(val[1])
1091         return (year, month, day)
1092         
1093     def _converttostring(self, date):
1094         if len(date)>=3:
1095             year,month,day=date[:3]
1096             if month>0 or day>0 or year>0:
1097                 s='%2.2d/%2.2d/%4.4d'%(month, day, year)
1098             else:
1099                 s=""
1100         else:
1101             s=""
1102         return s
1103         
1104 
1105 class CSVTIME(CSVSTRING):
1106     """Timestamp in CSV lines"""
1107     def __init__(self, *args, **kwargs):
1108         super(CSVTIME,self).__init__(*args, **kwargs)
1109 
1110         self._valuetime=(0,0,0,0,0,0) # Year month day, hour, minute, second
1111 
1112         self._quotechar=None
1113         
1114         if self._ismostderived(CSVTIME):
1115             self._update(args,kwargs)
1116 
1117     def _update(self, args, kwargs):
1118         for k in 'constant', 'default', 'value':
1119             if kwargs.has_key(k):
1120                 kwargs[k]=self._converttostring(kwargs[k])
1121         if len(args)==0:
1122             pass
1123         elif len(args)==1:
1124             args=(self._converttostring(args[0]),)
1125         else:
1126             raise TypeError("expected (year,month,day) as arg")
1127 
1128         super(CSVTIME,self)._update(args, kwargs) # we want the args
1129         self._complainaboutunusedargs(CSVTIME,kwargs)
1130 
1131     def getvalue(self):
1132         """Unpack the string into the date
1133 
1134         @rtype: tuple
1135         @return: (year, month, day)
1136         """
1137 
1138         s=super(CSVTIME,self).getvalue()
1139         year=int(s[0:4])
1140         month=int(s[4:6])
1141         day=int(s[6:8])
1142         hour=int(s[9:11])
1143         minute=int(s[11:13])
1144         second=int(s[13:15])
1145         return (year, month, day, hour, minute, second)
1146         
1147     def _converttostring(self, time):
1148         if len(time)>=6:
1149             year,month,day,hour,minute,second=time[:6]
1150             s='%4.4d%2.2d%2.2dT%2.2d%2.2d%2.2d'%(year, month, day, hour, minute, second)
1151         else:
1152             s=""
1153         return s
1154         
1155 
1156 class COUNTEDBUFFEREDSTRING(BaseProtogenClass):
1157     """A string as used on Audiovox.  There is a one byte header saying how long the string
1158     is, followed by the string in a fixed sized buffer"""
1159     def __init__(self, *args, **kwargs):
1160         """
1161         A string value can be specified to this constructor, or in the value keyword arg.
1162 
1163         @keyword constant: (Optional) A constant value.  All reads must have this value
1164         @keyword pad: (Default=32 - space) When writing, what to pad the rest of the buffer with
1165         @keyword default: (Optional) Our default value
1166         @keyword raiseontruncate: (Default True) raise L{ValueLengthException} if the supplied
1167              value is too large to fit within the buffer.
1168         @keyword value: (Optional) Value
1169         @keyword sizeinbytes: (Mandatory) Size of the buffer, including the count byte
1170         """
1171         super(COUNTEDBUFFEREDSTRING,self).__init__(*args, **kwargs)
1172 
1173         self._constant=None
1174         self._pad=32
1175         self._sizeinbytes=None
1176         self._default=None
1177         self._raiseontruncate=True
1178         self._value=None
1179 
1180         if self._ismostderived(COUNTEDBUFFEREDSTRING):
1181             self._update(args, kwargs)
1182 
1183     def _update(self, args, kwargs):
1184         super(COUNTEDBUFFEREDSTRING,self)._update(args, kwargs)
1185 
1186         self._consumekw(kwargs, ("constant", "pad", "sizeinbytes", "default", "raiseontruncate", "value"))
1187         self._complainaboutunusedargs(COUNTEDBUFFEREDSTRING,kwargs)
1188         # Set our value if one was specified
1189         if len(args)==0:
1190             pass
1191         elif len(args)==1:
1192             self._value=str(args[0])
1193             if self._constant is not None and self._constant!=self._value:
1194                 raise ValueException("This field is a constant of '%s'.  You tried setting it to '%s'" % (self._constant, self._value))
1195         else:
1196             raise TypeError("Unexpected arguments "+`args`)
1197         if self._value is None and self._default is not None:
1198             self._value=self._default
1199 
1200         if self._sizeinbytes is None:
1201             raise ValueException("sizeinbytes must be specified for COUNTEDBUFFEREDSTRING")
1202 
1203         if self._value is not None:
1204             l=len(self._value)
1205             if l>self._sizeinbytes-1:
1206                 if self._raiseontruncate:
1207                     raise ValueLengthException(l, self._sizeinbytes-1)
1208                     
1209                 self._value=self._value[:self._sizeinbytes-1]
1210 
1211     def readfrombuffer(self, buf):
1212         assert self._sizeinbytes is not None
1213         self._bufferstartoffset=buf.getcurrentoffset()
1214 
1215         strlen=buf.getnextbyte()
1216         if strlen>self._sizeinbytes-1:
1217             raise ValueException("counter specifies size of %d which is greater than remaining stringbuffer size of %d!" % (strlen, self._sizeinbytes-1))
1218         self._value=buf.getnextbytes(self._sizeinbytes-1) # -1 due to counter byte
1219         self._value=self._value[:strlen]
1220         if self._constant is not None and self._value!=self._constant:
1221             raise ValueException("The value read was not the constant")
1222 
1223         self._bufferendoffset=buf.getcurrentoffset()
1224 
1225     def writetobuffer(self, buf):
1226         assert self._sizeinbytes is not None
1227         if self._value is None:
1228             raise ValueNotSetException()
1229 
1230         self._bufferstartoffset=buf.getcurrentoffset()
1231         buf.appendbyte(len(self._value))
1232         buf.appendbytes(self._value)
1233         if len(self._value)+1<self._sizeinbytes:
1234             buf.appendbytes(chr(self._pad)*(self._sizeinbytes-1-len(self._value)))
1235 
1236         self._bufferendoffset=buf.getcurrentoffset()
1237 
1238     def packetsize(self):
1239         assert self._sizeinbytes is not None
1240         return self._sizeinbytes
1241 
1242     def getvalue(self):
1243         """Returns the string we are"""
1244         if self._value is None:
1245             raise ValueNotSetException()
1246         return self._value
1247             
1248 class DATA(BaseProtogenClass):
1249     "A block of bytes"
1250     def __init__(self, *args, **kwargs):
1251         """
1252         A data value can be specified to this constructor or in the value keyword arg
1253 
1254         @keyword constant: (Optional) A constant value.  All reads must have this value
1255         @keyword pad: (Default=0) The padding byte if fixed length when writing and the
1256              value isn't long enough
1257         @keyword sizeinbytes: (Optional) Set if fixed length.
1258              If not set, then the rest of the packet will be consumed on reads.
1259         @keyword default: (Optional) Our default value
1260         @keyword raiseonwrongsize: (Default True) raise L{ValueLengthException} if the supplied
1261              value is too large to fit within sizeinbytes.
1262         """
1263         super(DATA, self).__init__(*args, **kwargs)
1264         
1265         self._constant=None
1266         self._pad=0
1267         self._sizeinbytes=None
1268         self._default=None
1269         self._raiseonwrongsize=True
1270         self._value=None
1271 
1272         if self._ismostderived(DATA):
1273             self._update(args,kwargs)
1274 
1275     def _update(self, args, kwargs):
1276         super(DATA,self)._update(args, kwargs)
1277 
1278         self._consumekw(kwargs, ("constant", "pad", "sizeinbytes", "default", "raiseonwrongsize", "value"))
1279         self._complainaboutunusedargs(DATA,kwargs)
1280 
1281         # Set our value if one was specified
1282         if len(args)==0:
1283             pass
1284         elif len(args)==1:
1285             self._value=args[0]
1286             if self._constant is not None and self._constant!=self._value:
1287                 raise ValueException("This field is a constant and you set it to a different value")
1288         else:
1289             raise TypeError("Unexpected arguments "+`args`)
1290         if self._value is None and self._default is not None:
1291             self._value=self._default
1292 
1293         if self._value is not None:
1294             if self._sizeinbytes is not None:
1295                 l=len(self._value)
1296                 if l<self._sizeinbytes:
1297                     if self._pad is not None:
1298                         self._value+=chr(self._pad)*(self._sizeinbytes-l)
1299 
1300                 l=len(self._value)
1301 
1302                 if l!=self._sizeinbytes:
1303                     if self._raiseonwrongsize:
1304                         raise ValueLengthException(l, self._sizeinbytes)
1305                     else:
1306                         self._value=self._value[:self._sizeinbytes]
1307 
1308 
1309     def readfrombuffer(self, buf):
1310         self._bufferstartoffset=buf.getcurrentoffset()
1311 
1312         if self._sizeinbytes is not None:
1313             # fixed size
1314             self._value=buf.getnextbytes(self._sizeinbytes)
1315         else:
1316             # read in entire rest of packet
1317             self._value=buf.getremainingbytes()
1318 
1319         if self._constant is not None and self._value!=self._constant:
1320             raise ValueException("The value read was not the constant")
1321         self._bufferendoffset=buf.getcurrentoffset()
1322 
1323     def writetobuffer(self, buf):
1324         if self._value is None:
1325             raise ValueNotSetException()
1326                 
1327         self._bufferstartoffset=buf.getcurrentoffset()
1328         buf.appendbytes(self._value)
1329         self._bufferendoffset=buf.getcurrentoffset()
1330 
1331     def packetsize(self):
1332         if self._sizeinbytes is not None:
1333             return self._sizeinbytes
1334 
1335         if self._value is None:
1336             raise ValueNotSetException()
1337 
1338         l=len(self._value)
1339 
1340         return l
1341 
1342     def getvalue(self):
1343         """Returns the bytes we are"""
1344         if self._value is None:
1345             raise ValueNotSetException()
1346         return self._value
1347 
1348 
1349 class UNKNOWN(DATA):
1350     "A block of bytes whose purpose we don't know"
1351 
1352     def __init__(self, *args, **kwargs):
1353         """
1354         Same arguments as L{DATA.__init__}.  We default to a block
1355         of pad chars (usually \x00)
1356         """
1357         dict={'pad':0 , 'default': ""}
1358         dict.update(kwargs)
1359         super(UNKNOWN,self).__init__(*args, **dict)
1360 
1361         if self._ismostderived(UNKNOWN):
1362             self._update(args,dict)
1363 
1364     def _update(self, args, kwargs):
1365         super(UNKNOWN,self)._update(args, kwargs)
1366         self._complainaboutunusedargs(UNKNOWN,kwargs)
1367 
1368         # Was a value specified?
1369         if len(args)==1:
1370             self._value=args[0]
1371         elif len(args)>1:
1372             raise TypeError("Unexpected arguments "+`args`)
1373 
1374 class LIST(BaseProtogenClass):
1375     """A list of items
1376 
1377     You can generally treat this class as though it is a list.  Note that some
1378     list like methods haven't been implemented (there are so darn many!)  If you
1379     are missing one you want to use, please add it to this class.
1380     """
1381 
1382     def __init__(self, *args, **kwargs):
1383         """
1384         You can pass objects to start the list with, or to the value keyword arg
1385 
1386         @keyword createdefault:  (Default False) Creates default members of the list if enough
1387             were not supplied before writing.
1388         @keyword length:  (Optional) How many items there are in the list
1389         @keyword raiseonbadlength: (Default True) raises L{ValueLengthException} if there are
1390             the wrong number of items in the list.  Note that this checking is only done
1391             when writing or reading from a buffer.  length must be set for this to have any
1392             effect.  If you have createdefault set then having less than length elements will
1393             not cause the exception.
1394         @keyword elementclass: (Mandatory) The class of each element
1395         @keyword elementinitkwargs: (Optional) KWargs for the constructor of each element
1396         @keyword value: (Optional) Value
1397         """
1398         self._thelist=[]
1399         super(LIST, self).__init__(*args, **kwargs)
1400         self._createdefault=False
1401         self._length=None
1402         self._raiseonbadlength=True
1403         self._raiseonincompleteread=True
1404         self._elementclass=None
1405         self._elementinitkwargs={}
1406 
1407         if self._ismostderived(LIST):
1408             self._update(args,kwargs)
1409 
1410     def _update(self, args, kwargs):
1411         super(LIST,self)._update(args, kwargs)
1412         self._consumekw(kwargs, ("createdefault","length","raiseonbadlength","elementclass","elementinitkwargs","raiseonincompleteread"))
1413         if kwargs.has_key("value"):
1414             self._thelist=list(kwargs['value'])
1415             del kwargs['value']
1416             
1417         self._complainaboutunusedargs(LIST,kwargs)
1418 
1419         if self._elementclass is None:
1420             raise TypeError("elementclass argument was not supplied")
1421         
1422         if len(args):
1423             self.extend(args)
1424 
1425     def readfrombuffer(self,buf):
1426         self._bufferstartoffset=buf.getcurrentoffset()
1427         # delete all existing items
1428         self._thelist=[]
1429             
1430         if self._length is None:
1431             # read rest of buffer
1432             while buf.hasmore():
1433                 x=self._makeitem()
1434                 x.readfrombuffer(buf)
1435                 self._thelist.append(x)
1436         else:
1437             for dummy in range(self._length):
1438                 # read specified number of items
1439                 x=self._makeitem()
1440                 try:
1441                     x.readfrombuffer(buf)
1442                 except IndexError:
1443                     if self._raiseonincompleteread:
1444                         raise IndexError("tried to read too many list items")
1445                 self._thelist.append(x)
1446 
1447         self._bufferendoffset=buf.getcurrentoffset()
1448 
1449     def writetobuffer(self, buf):
1450         self._bufferstartoffset=buf.getcurrentoffset()
1451 
1452         self._ensurelength()
1453         for i in self:
1454             i.writetobuffer(buf)
1455 
1456         self._bufferendoffset=buf.getcurrentoffset()
1457 
1458     def packetsize(self):
1459         self._ensurelength()
1460         sz=0
1461         for item in self:
1462             sz+=item.packetsize()
1463         return sz
1464 
1465     def iscontainer(self):
1466         return True
1467 
1468     def containerelements(self):
1469         self._ensurelength()
1470         for i,v in enumerate(self._thelist):
1471             yield "["+`i`+"]",v,None
1472         
1473 
1474     # Provide various list methods.  I initially tried just subclassing list,
1475     # but it was impossible to tell which methods needed to be reimplemented.
1476     # I was concerned about double conversions.
1477     # For example if my append turned the item into elementclass
1478     # and then the builtin list append called setitem to do the append, which I
1479     # also override so it then converts the elementclass into another
1480     # elementclass.
1481     def append(self, item):
1482         self._thelist.append(self._makeitem(item))
1483 
1484     def extend(self, items):
1485         self._thelist.extend(map(self._makeitem, items))
1486 
1487     def insert(self, index, item):
1488         self._thelist.insert(index, self._makeitem(item))
1489 
1490     def __getitem__(self, index):
1491         return self._thelist[index]
1492 
1493     def __iter__(self):
1494         try:
1495             return self._thelist.__iter__()
1496         except:
1497             return self.__fallbackiter()
1498 
1499     def __fallbackiter(self):
1500         # used for Python 2.2 which doesn't have list.__iter__
1501         for item in self._thelist:
1502             yield item
1503 
1504     def __len__(self):
1505         return self._thelist.__len__()
1506 
1507     def __setitem__(self, index, value):
1508         self._thelist.__setitem__(index, self._makeitem(value))
1509 
1510     def __delitem__(self, index):
1511         self._thelist.__delitem__(index)
1512         
1513 
1514     def _makeitem(self, *args, **kwargs):
1515         "Creates a child element"
1516         # if already of the type, then return it
1517         if len(args)==1 and isinstance(args[0], self._elementclass):
1518             return args[0]
1519         d={}
1520         d.update(self._elementinitkwargs)
1521         d.update(kwargs)
1522         return self._elementclass(*args, **d)
1523 
1524     def _ensurelength(self):
1525         "Ensures we are the correct length"
1526         if self._createdefault and self._length is not None and len(self._thelist)<self._length:
1527             while len(self._thelist)<self._length:
1528                 x=self._makeitem()
1529                 self._thelist.append(x)
1530             return
1531         if self._length is not None and self._raiseonbadlength and len(self._thelist)!=self._length:
1532             raise ValueLengthException(len(self), self._length)
1533 
1534 class buffer(object):
1535     "This is used for reading and writing byte data"
1536     def __init__(self, data=None):
1537         "Call with data to read from it, or with None to write to it"
1538         if data is not None:
1539             self._data=data
1540         else:
1541             self._buffer=cStringIO.StringIO()
1542 
1543         self._offset=0
1544 
1545     def getcurrentoffset(self):
1546         "Returns distance into data we are"
1547         return self._offset
1548     def setcurrentoffset(self, ofs):
1549         "Set the current offset"
1550         if ofs>len(self._data):
1551             raise IndexError('Trying to set offset beyond end of %d byte buffer'%len(self._data))
1552         self._offset=ofs
1553     offset=property(fget=getcurrentoffset, fset=setcurrentoffset)
1554 
1555     def peeknextbyte(self, howmuch=0):
1556         "Returns value of next byte, but doesn't advance position"
1557         if self._offset+howmuch>=len(self._data):
1558             return None
1559         return ord(self._data[self._offset+howmuch]) 
1560 
1561     def getnextbyte(self):
1562         "Returns next byte"
1563         if self._offset>=len(self._data):
1564             raise IndexError("trying to read one byte beyond end of "+`len(self._data)`+" byte buffer")
1565         res=ord(self._data[self._offset])
1566         self._offset+=1
1567         return res
1568 
1569     def getnextbytes(self, howmany):
1570         "Returns howmany bytes"
1571         assert howmany>=0
1572         if self._offset+howmany>len(self._data):
1573             raise IndexError("Trying to read "+`howmany`+" bytes starting at "+`self._offset`+" which will go beyond end of "+`len(self._data)`+" byte buffer")
1574         res=self._data[self._offset:self._offset+howmany]
1575         self._offset+=howmany
1576         return res
1577 
1578     def peeknextbytes(self, howmany):
1579         if self._offset+howmany>len(self._data):
1580             return None
1581         return self._data[self._offset:self._offset+howmany]
1582 
1583     def getremainingbytes(self):
1584         "Returns rest of buffer"
1585         sz=len(self._data)-self._offset
1586         return self.getnextbytes(sz)
1587 
1588     def hasmore(self):
1589         "Is there any data left?"
1590         return self._offset<len(self._data)
1591 
1592     def howmuchmore(self):
1593         "Returns how many bytes left"
1594         return len(self._data)-self._offset
1595 
1596     def appendbyte(self, val):
1597         """Appends byte to data.
1598         @param val: a number 0 <= val <=255
1599         """
1600         assert val>=0 and val<=255
1601         self._buffer.write(chr(val))
1602         self._offset+=1
1603         assert self._offset==len(self._buffer.getvalue())
1604 
1605     def appendbytes(self, bytes):
1606         "Adds bytes to end"
1607         self._buffer.write(bytes)
1608         self._offset+=len(bytes)
1609         assert self._offset==len(self._buffer.getvalue())
1610 
1611     def getvalue(self):
1612         "Returns the buffer being built"
1613         return self._buffer.getvalue()
1614 
1615     def getdata(self):
1616         "Returns the data passed in"
1617         return self._data
1618         
1619     
1620 

Generated by PyXR 0.9.4