llsd.py

Go to the documentation of this file.
00001 """\
00002 @file llsd.py
00003 @brief Types as well as parsing and formatting functions for handling LLSD.
00004 
00005 $LicenseInfo:firstyear=2006&license=mit$
00006 
00007 Copyright (c) 2006-2008, Linden Research, Inc.
00008 
00009 Permission is hereby granted, free of charge, to any person obtaining a copy
00010 of this software and associated documentation files (the "Software"), to deal
00011 in the Software without restriction, including without limitation the rights
00012 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00013 copies of the Software, and to permit persons to whom the Software is
00014 furnished to do so, subject to the following conditions:
00015 
00016 The above copyright notice and this permission notice shall be included in
00017 all copies or substantial portions of the Software.
00018 
00019 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00020 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00021 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00022 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00023 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00024 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00025 THE SOFTWARE.
00026 $/LicenseInfo$
00027 """
00028 
00029 import datetime
00030 import base64
00031 import struct
00032 import time
00033 import types
00034 import re
00035 
00036 from indra.util.fastest_elementtree import fromstring
00037 from indra.base import lluuid
00038 
00039 int_regex = re.compile("[-+]?\d+")
00040 real_regex = re.compile("[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?")
00041 alpha_regex = re.compile("[a-zA-Z]+")
00042 date_regex = re.compile("(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})T(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})(?P<second_float>\.\d{2})?Z")
00043 #date: d"YYYY-MM-DDTHH:MM:SS.FFZ"
00044 
00045 class LLSDParseError(Exception):
00046     pass
00047 
00048 class LLSDSerializationError(Exception):
00049     pass
00050 
00051 
00052 class binary(str):
00053     pass
00054 
00055 class uri(str):
00056     pass
00057 
00058 
00059 BOOL_TRUE = ('1', '1.0', 'true')
00060 BOOL_FALSE = ('0', '0.0', 'false', '')
00061 
00062 
00063 def format_datestr(v):
00064     """ Formats a datetime object into the string format shared by xml and notation serializations."""
00065     second_str = ""
00066     if v.microsecond > 0:
00067         seconds = v.second + float(v.microsecond) / 1000000
00068         second_str = "%05.2f" % seconds
00069     else:
00070         second_str = "%d" % v.second
00071     return '%s%sZ' % (v.strftime('%Y-%m-%dT%H:%M:'), second_str)
00072 
00073 
00074 def parse_datestr(datestr):
00075     """Parses a datetime object from the string format shared by xml and notation serializations."""
00076     if datestr == "":
00077         return datetime.datetime(1970, 1, 1)
00078     
00079     match = re.match(date_regex, datestr)
00080     if not match:
00081         raise LLSDParseError("invalid date string '%s'." % datestr)
00082     
00083     year = int(match.group('year'))
00084     month = int(match.group('month'))
00085     day = int(match.group('day'))
00086     hour = int(match.group('hour'))
00087     minute = int(match.group('minute'))
00088     second = int(match.group('second'))
00089     seconds_float = match.group('second_float')
00090     microsecond = 0
00091     if seconds_float:
00092         microsecond = int(seconds_float[1:]) * 10000
00093     return datetime.datetime(year, month, day, hour, minute, second, microsecond)
00094 
00095 
00096 def bool_to_python(node):
00097     val = node.text or ''
00098     if val in BOOL_TRUE:
00099         return True
00100     else:
00101         return False
00102 
00103 def int_to_python(node):
00104     val = node.text or ''
00105     if not val.strip():
00106         return 0
00107     return int(val)
00108 
00109 def real_to_python(node):
00110     val = node.text or ''
00111     if not val.strip():
00112         return 0.0
00113     return float(val)
00114 
00115 def uuid_to_python(node):
00116     return lluuid.UUID(node.text)
00117 
00118 def str_to_python(node):
00119     return unicode(node.text or '').encode('utf8', 'replace')
00120 
00121 def bin_to_python(node):
00122     return binary(base64.decodestring(node.text or ''))
00123 
00124 def date_to_python(node):
00125     val = node.text or ''
00126     if not val:
00127         val = "1970-01-01T00:00:00Z"
00128     return parse_datestr(val)
00129 
00130 def uri_to_python(node):
00131     val = node.text or ''
00132     if not val:
00133         return None
00134     return uri(val)
00135 
00136 def map_to_python(node):
00137     result = {}
00138     for index in range(len(node))[::2]:
00139         result[node[index].text] = to_python(node[index+1])
00140     return result
00141 
00142 def array_to_python(node):
00143     return [to_python(child) for child in node]
00144 
00145 
00146 NODE_HANDLERS = dict(
00147     undef=lambda x: None,
00148     boolean=bool_to_python,
00149     integer=int_to_python,
00150     real=real_to_python,
00151     uuid=uuid_to_python,
00152     string=str_to_python,
00153     binary=bin_to_python,
00154     date=date_to_python,
00155     uri=uri_to_python,
00156     map=map_to_python,
00157     array=array_to_python,
00158     )
00159 
00160 def to_python(node):
00161     return NODE_HANDLERS[node.tag](node)
00162 
00163 class Nothing(object):
00164     pass
00165 
00166 
00167 class LLSDXMLFormatter(object):
00168     def __init__(self):
00169         self.type_map = {
00170             type(None) : self.UNDEF,
00171             bool : self.BOOLEAN,
00172             int : self.INTEGER,
00173             long : self.INTEGER,
00174             float : self.REAL,
00175             lluuid.UUID : self.UUID,
00176             binary : self.BINARY,
00177             str : self.STRING,
00178             unicode : self.STRING,
00179             uri : self.URI,
00180             datetime.datetime : self.DATE,
00181             list : self.ARRAY,
00182             tuple : self.ARRAY,
00183             types.GeneratorType : self.ARRAY,
00184             dict : self.MAP,
00185             LLSD : self.LLSD
00186         }
00187 
00188     def elt(self, name, contents=None):
00189         if(contents is None or contents is ''):
00190             return "<%s />" % (name,)
00191         else:
00192             return "<%s>%s</%s>" % (name, contents, name)
00193 
00194     def xml_esc(self, v):
00195         return v.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
00196 
00197     def LLSD(self, v):
00198         return self.generate(v.thing)
00199     def UNDEF(self, v):
00200         return self.elt('undef')
00201     def BOOLEAN(self, v):
00202         if v:
00203             return self.elt('boolean', 'true')
00204         else:
00205             return self.elt('boolean', 'false')
00206     def INTEGER(self, v):
00207         return self.elt('integer', v)
00208     def REAL(self, v):
00209         return self.elt('real', v)
00210     def UUID(self, v):
00211         if(v.isNull()):
00212             return self.elt('uuid')
00213         else:
00214             return self.elt('uuid', v)
00215     def BINARY(self, v):
00216         return self.elt('binary', base64.encodestring(v))
00217     def STRING(self, v):
00218         return self.elt('string', self.xml_esc(v))
00219     def URI(self, v):
00220         return self.elt('uri', self.xml_esc(str(v)))
00221     def DATE(self, v):
00222         return self.elt('date', format_datestr(v))
00223     def ARRAY(self, v):
00224         return self.elt('array', ''.join([self.generate(item) for item in v]))
00225     def MAP(self, v):
00226         return self.elt(
00227             'map',
00228             ''.join(["%s%s" % (self.elt('key', key), self.generate(value))
00229              for key, value in v.items()]))
00230 
00231     typeof = type
00232     def generate(self, something):
00233         t = self.typeof(something)
00234         if self.type_map.has_key(t):
00235             return self.type_map[t](something)
00236         else:
00237             raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % (
00238                 t, something))
00239 
00240     def format(self, something):
00241         return '<?xml version="1.0" ?>' + self.elt("llsd", self.generate(something))
00242 
00243 def format_xml(something):
00244     return LLSDXMLFormatter().format(something)
00245 
00246 class LLSDNotationFormatter(object):
00247     def __init__(self):
00248         self.type_map = {
00249             type(None) : self.UNDEF,
00250             bool : self.BOOLEAN,
00251             int : self.INTEGER,
00252             long : self.INTEGER,
00253             float : self.REAL,
00254             lluuid.UUID : self.UUID,
00255             binary : self.BINARY,
00256             str : self.STRING,
00257             unicode : self.STRING,
00258             uri : self.URI,
00259             datetime.datetime : self.DATE,
00260             list : self.ARRAY,
00261             tuple : self.ARRAY,
00262             types.GeneratorType : self.ARRAY,
00263             dict : self.MAP,
00264             LLSD : self.LLSD
00265         }
00266 
00267     def LLSD(self, v):
00268         return self.generate(v.thing)
00269     def UNDEF(self, v):
00270         return '!'
00271     def BOOLEAN(self, v):
00272         if v:
00273             return 'true'
00274         else:
00275             return 'false'
00276     def INTEGER(self, v):
00277         return "i%s" % v
00278     def REAL(self, v):
00279         return "r%s" % v
00280     def UUID(self, v):
00281         return "u%s" % v
00282     def BINARY(self, v):
00283         raise LLSDSerializationError("binary notation not yet supported")
00284     def STRING(self, v):
00285         return "'%s'" % v.replace("\\", "\\\\").replace("'", "\\'")
00286     def URI(self, v):
00287         return 'l"%s"' % str(v).replace("\\", "\\\\").replace('"', '\\"')
00288     def DATE(self, v):
00289         return 'd"%s"' % format_datestr(v)
00290     def ARRAY(self, v):
00291         return "[%s]" % ','.join([self.generate(item) for item in v])
00292     def MAP(self, v):
00293         return "{%s}" % ','.join(["'%s':%s" % (key.replace("\\", "\\\\").replace("'", "\\'"), self.generate(value))
00294              for key, value in v.items()])
00295 
00296     def generate(self, something):
00297         t = type(something)
00298         if self.type_map.has_key(t):
00299             return self.type_map[t](something)
00300         else:
00301             raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % (
00302                 t, something))
00303 
00304     def format(self, something):
00305         return self.generate(something)
00306 
00307 def format_notation(something):
00308     return LLSDNotationFormatter().format(something)
00309 
00310 def _hex_as_nybble(hex):
00311     if (hex >= '0') and (hex <= '9'):
00312         return ord(hex) - ord('0')
00313     elif (hex >= 'a') and (hex <='f'):
00314         return 10 + ord(hex) - ord('a')
00315     elif (hex >= 'A') and (hex <='F'):
00316         return 10 + ord(hex) - ord('A');
00317 
00318 class LLSDBinaryParser(object):
00319     def __init__(self):
00320         pass
00321 
00322     def parse(self, buffer, ignore_binary = False):
00323         """
00324         This is the basic public interface for parsing.
00325 
00326         @param buffer the binary data to parse in an indexable sequence.
00327         @param ignore_binary parser throws away data in llsd binary nodes.
00328         @return returns a python object.
00329         """
00330         self._buffer = buffer
00331         self._index = 0
00332         self._keep_binary = not ignore_binary
00333         return self._parse()
00334 
00335     def _parse(self):
00336         cc = self._buffer[self._index]
00337         self._index += 1
00338         if cc == '{':
00339             return self._parse_map()
00340         elif cc == '[':
00341             return self._parse_array()
00342         elif cc == '!':
00343             return None
00344         elif cc == '0':
00345             return False
00346         elif cc == '1':
00347             return True
00348         elif cc == 'i':
00349             # 'i' = integer
00350             idx = self._index
00351             self._index += 4
00352             return struct.unpack("!i", self._buffer[idx:idx+4])[0]
00353         elif cc == ('r'):
00354             # 'r' = real number
00355             idx = self._index
00356             self._index += 8
00357             return struct.unpack("!d", self._buffer[idx:idx+8])[0]
00358         elif cc == 'u':
00359             # 'u' = uuid
00360             idx = self._index
00361             self._index += 16
00362             return lluuid.uuid_bits_to_uuid(self._buffer[idx:idx+16])
00363         elif cc == 's':
00364             # 's' = string
00365             return self._parse_string()
00366         elif cc in ("'", '"'):
00367             # delimited/escaped string
00368             return self._parse_string_delim(cc)
00369         elif cc == 'l':
00370             # 'l' = uri
00371             return uri(self._parse_string())
00372         elif cc == ('d'):
00373             # 'd' = date in seconds since epoch
00374             idx = self._index
00375             self._index += 8
00376             seconds = struct.unpack("!d", self._buffer[idx:idx+8])[0]
00377             return datetime.datetime.fromtimestamp(seconds)
00378         elif cc == 'b':
00379             binary = self._parse_string()
00380             if self._keep_binary:
00381                 return binary
00382             # *NOTE: maybe have a binary placeholder which has the
00383             # length.
00384             return None
00385         else:
00386             raise LLSDParseError("invalid binary token at byte %d: %d" % (
00387                 self._index - 1, ord(cc)))
00388 
00389     def _parse_map(self):
00390         rv = {}
00391         size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0]
00392         self._index += 4
00393         count = 0
00394         cc = self._buffer[self._index]
00395         self._index += 1
00396         key = ''
00397         while (cc != '}') and (count < size):
00398             if cc == 'k':
00399                 key = self._parse_string()
00400             elif cc in ("'", '"'):
00401                 key = self._parse_string_delim(cc)
00402             else:
00403                 raise LLSDParseError("invalid map key at byte %d." % (
00404                     self._index - 1,))
00405             value = self._parse()
00406             #print "kv:",key,value
00407             rv[key] = value
00408             count += 1
00409             cc = self._buffer[self._index]
00410             self._index += 1
00411         if cc != '}':
00412             raise LLSDParseError("invalid map close token at byte %d." % (
00413                 self._index,))
00414         return rv
00415 
00416     def _parse_array(self):
00417         rv = []
00418         size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0]
00419         self._index += 4
00420         count = 0
00421         cc = self._buffer[self._index]
00422         while (cc != ']') and (count < size):
00423             rv.append(self._parse())
00424             count += 1
00425             cc = self._buffer[self._index]
00426         if cc != ']':
00427             raise LLSDParseError("invalid array close token at byte %d." % (
00428                 self._index,))
00429         self._index += 1
00430         return rv
00431 
00432     def _parse_string(self):
00433         size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0]
00434         self._index += 4
00435         rv = self._buffer[self._index:self._index+size]
00436         self._index += size
00437         return rv
00438 
00439     def _parse_string_delim(self, delim):
00440         list = []
00441         found_escape = False
00442         found_hex = False
00443         found_digit = False
00444         byte = 0
00445         while True:
00446             cc = self._buffer[self._index]
00447             self._index += 1
00448             if found_escape:
00449                 if found_hex:
00450                     if found_digit:
00451                         found_escape = False
00452                         found_hex = False
00453                         found_digit = False
00454                         byte <<= 4
00455                         byte |= _hex_as_nybble(cc)
00456                         list.append(chr(byte))
00457                         byte = 0
00458                     else:
00459                         found_digit = True
00460                         byte = _hex_as_nybble(cc)
00461                 elif cc == 'x':
00462                     found_hex = True
00463                 else:
00464                     if cc == 'a':
00465                         list.append('\a')
00466                     elif cc == 'b':
00467                         list.append('\b')
00468                     elif cc == 'f':
00469                         list.append('\f')
00470                     elif cc == 'n':
00471                         list.append('\n')
00472                     elif cc == 'r':
00473                         list.append('\r')
00474                     elif cc == 't':
00475                         list.append('\t')
00476                     elif cc == 'v':
00477                         list.append('\v')
00478                     else:
00479                         list.append(cc)
00480                     found_escape = False
00481             elif cc == '\\':
00482                 found_escape = True
00483             elif cc == delim:
00484                 break
00485             else:
00486                 list.append(cc)
00487         return ''.join(list)
00488 
00489 class LLSDNotationParser(object):
00490     """ Parse LLSD notation:
00491     map: { string:object, string:object }
00492     array: [ object, object, object ]
00493     undef: !
00494     boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE
00495     integer: i####
00496     real: r####
00497     uuid: u####
00498     string: "g\'day" | 'have a "nice" day' | s(size)"raw data"
00499     uri: l"escaped"
00500     date: d"YYYY-MM-DDTHH:MM:SS.FFZ"
00501     binary: b##"ff3120ab1" | b(size)"raw data"
00502     """
00503     def __init__(self):
00504         pass
00505 
00506     def parse(self, buffer, ignore_binary = False):
00507         """
00508         This is the basic public interface for parsing.
00509 
00510         @param buffer the notation string to parse.
00511         @param ignore_binary parser throws away data in llsd binary nodes.
00512         @return returns a python object.
00513         """
00514         if buffer == "":
00515             return False
00516 
00517         self._buffer = buffer
00518         self._index = 0
00519         return self._parse()
00520 
00521     def _parse(self):
00522         cc = self._buffer[self._index]
00523         self._index += 1
00524         if cc == '{':
00525             return self._parse_map()
00526         elif cc == '[':
00527             return self._parse_array()
00528         elif cc == '!':
00529             return None
00530         elif cc == '0':
00531             return False
00532         elif cc == '1':
00533             return True
00534         elif cc in ('F', 'f'):
00535             self._skip_alpha()
00536             return False
00537         elif cc in ('T', 't'):
00538             self._skip_alpha()
00539             return True
00540         elif cc == 'i':
00541             # 'i' = integer
00542             return self._parse_integer()
00543         elif cc == ('r'):
00544             # 'r' = real number
00545             return self._parse_real()
00546         elif cc == 'u':
00547             # 'u' = uuid
00548             return self._parse_uuid()
00549         elif cc in ("'", '"', 's'):
00550             return self._parse_string(cc)
00551         elif cc == 'l':
00552             # 'l' = uri
00553             delim = self._buffer[self._index]
00554             self._index += 1
00555             val = uri(self._parse_string(delim))
00556             if len(val) == 0:
00557                 return None
00558             return val
00559         elif cc == ('d'):
00560             # 'd' = date in seconds since epoch
00561             return self._parse_date()
00562         elif cc == 'b':
00563             raise LLSDParseError("binary notation not yet supported")
00564         else:
00565             raise LLSDParseError("invalid token at index %d: %d" % (
00566                 self._index - 1, ord(cc)))
00567 
00568     def _parse_map(self):
00569         """ map: { string:object, string:object } """
00570         rv = {}
00571         cc = self._buffer[self._index]
00572         self._index += 1
00573         key = ''
00574         found_key = False
00575         while (cc != '}'):
00576             if not found_key:
00577                 if cc in ("'", '"', 's'):
00578                     key = self._parse_string(cc)
00579                     found_key = True
00580                     #print "key:",key
00581                 elif cc.isspace() or cc == ',':
00582                     cc = self._buffer[self._index]
00583                     self._index += 1
00584                 else:
00585                     raise LLSDParseError("invalid map key at byte %d." % (
00586                                         self._index - 1,))
00587             else:
00588                 if cc.isspace() or cc == ':':
00589                     #print "skipping whitespace '%s'" % cc
00590                     cc = self._buffer[self._index]
00591                     self._index += 1
00592                     continue
00593                 self._index += 1
00594                 value = self._parse()
00595                 #print "kv:",key,value
00596                 rv[key] = value
00597                 found_key = False
00598                 cc = self._buffer[self._index]
00599                 self._index += 1
00600                 #if cc == '}':
00601                 #    break
00602                 #cc = self._buffer[self._index]
00603                 #self._index += 1
00604 
00605         return rv
00606 
00607     def _parse_array(self):
00608         """ array: [ object, object, object ] """
00609         rv = []
00610         cc = self._buffer[self._index]
00611         while (cc != ']'):
00612             if cc.isspace() or cc == ',':
00613                 self._index += 1
00614                 cc = self._buffer[self._index]
00615                 continue
00616             rv.append(self._parse())
00617             cc = self._buffer[self._index]
00618 
00619         if cc != ']':
00620             raise LLSDParseError("invalid array close token at index %d." % (
00621                 self._index,))
00622         self._index += 1
00623         return rv
00624 
00625     def _parse_uuid(self):
00626         match = re.match(lluuid.UUID.uuid_regex, self._buffer[self._index:])
00627         if not match:
00628             raise LLSDParseError("invalid uuid token at index %d." % self._index)
00629 
00630         (start, end) = match.span()
00631         start += self._index
00632         end += self._index
00633         self._index = end
00634         return lluuid.UUID(self._buffer[start:end])
00635 
00636     def _skip_alpha(self):
00637         match = re.match(alpha_regex, self._buffer[self._index:])
00638         if match:
00639             self._index += match.end()
00640             
00641     def _parse_date(self):
00642         delim = self._buffer[self._index]
00643         self._index += 1
00644         datestr = self._parse_string(delim)
00645         return parse_datestr(datestr)
00646 
00647     def _parse_real(self):
00648         match = re.match(real_regex, self._buffer[self._index:])
00649         if not match:
00650             raise LLSDParseError("invalid real token at index %d." % self._index)
00651 
00652         (start, end) = match.span()
00653         start += self._index
00654         end += self._index
00655         self._index = end
00656         return float( self._buffer[start:end] )
00657 
00658     def _parse_integer(self):
00659         match = re.match(int_regex, self._buffer[self._index:])
00660         if not match:
00661             raise LLSDParseError("invalid integer token at index %d." % self._index)
00662 
00663         (start, end) = match.span()
00664         start += self._index
00665         end += self._index
00666         self._index = end
00667         return int( self._buffer[start:end] )
00668 
00669     def _parse_string(self, delim):
00670         """ string: "g\'day" | 'have a "nice" day' | s(size)"raw data" """
00671         rv = ""
00672 
00673         if delim in ("'", '"'):
00674             rv = self._parse_string_delim(delim)
00675         elif delim == 's':
00676             rv = self._parse_string_raw()
00677         else:
00678             raise LLSDParseError("invalid string token at index %d." % self._index)
00679 
00680         return rv
00681 
00682 
00683     def _parse_string_delim(self, delim):
00684         """ string: "g'day 'un" | 'have a "nice" day' """
00685         list = []
00686         found_escape = False
00687         found_hex = False
00688         found_digit = False
00689         byte = 0
00690         while True:
00691             cc = self._buffer[self._index]
00692             self._index += 1
00693             if found_escape:
00694                 if found_hex:
00695                     if found_digit:
00696                         found_escape = False
00697                         found_hex = False
00698                         found_digit = False
00699                         byte <<= 4
00700                         byte |= _hex_as_nybble(cc)
00701                         list.append(chr(byte))
00702                         byte = 0
00703                     else:
00704                         found_digit = True
00705                         byte = _hex_as_nybble(cc)
00706                 elif cc == 'x':
00707                     found_hex = True
00708                 else:
00709                     if cc == 'a':
00710                         list.append('\a')
00711                     elif cc == 'b':
00712                         list.append('\b')
00713                     elif cc == 'f':
00714                         list.append('\f')
00715                     elif cc == 'n':
00716                         list.append('\n')
00717                     elif cc == 'r':
00718                         list.append('\r')
00719                     elif cc == 't':
00720                         list.append('\t')
00721                     elif cc == 'v':
00722                         list.append('\v')
00723                     else:
00724                         list.append(cc)
00725                     found_escape = False
00726             elif cc == '\\':
00727                 found_escape = True
00728             elif cc == delim:
00729                 break
00730             else:
00731                 list.append(cc)
00732         return ''.join(list)
00733 
00734     def _parse_string_raw(self):
00735         """ string: s(size)"raw data" """
00736         # Read the (size) portion.
00737         cc = self._buffer[self._index]
00738         self._index += 1
00739         if cc != '(':
00740             raise LLSDParseError("invalid string token at index %d." % self._index)
00741 
00742         rparen = self._buffer.find(')', self._index)
00743         if rparen == -1:
00744             raise LLSDParseError("invalid string token at index %d." % self._index)
00745 
00746         size = int(self._buffer[self._index:rparen])
00747 
00748         self._index = rparen + 1
00749         delim = self._buffer[self._index]
00750         self._index += 1
00751         if delim not in ("'", '"'):
00752             raise LLSDParseError("invalid string token at index %d." % self._index)
00753 
00754         rv = self._buffer[self._index:(self._index + size)]
00755         self._index += size
00756         cc = self._buffer[self._index]
00757         self._index += 1
00758         if cc != delim:
00759             raise LLSDParseError("invalid string token at index %d." % self._index)
00760 
00761         return rv
00762         
00763 def format_binary(something):
00764     return '<?llsd/binary?>\n' + _format_binary_recurse(something)
00765 
00766 def _format_binary_recurse(something):
00767     if something is None:
00768         return '!'
00769     elif isinstance(something, LLSD):
00770         return _format_binary_recurse(something.thing)
00771     elif isinstance(something, bool):
00772         if something:
00773             return '1'
00774         else:
00775             return '0'
00776     elif isinstance(something, (int, long)):
00777         return 'i' + struct.pack('!i', something)
00778     elif isinstance(something, float):
00779         return 'r' + struct.pack('!d', something)
00780     elif isinstance(something, lluuid.UUID):
00781         return 'u' + something._bits
00782     elif isinstance(something, binary):
00783         return 'b' + struct.pack('!i', len(something)) + something
00784     elif isinstance(something, (str, unicode)):
00785         return 's' + struct.pack('!i', len(something)) + something
00786     elif isinstance(something, uri):
00787         return 'l' + struct.pack('!i', len(something)) + something
00788     elif isinstance(something, datetime.datetime):
00789         seconds_since_epoch = time.mktime(something.timetuple())
00790         return 'd' + struct.pack('!d', seconds_since_epoch)
00791     elif isinstance(something, (list, tuple)):
00792         array_builder = []
00793         array_builder.append('[' + struct.pack('!i', len(something)))
00794         for item in something:
00795             array_builder.append(_format_binary_recurse(item))
00796         array_builder.append(']')
00797         return ''.join(array_builder)
00798     elif isinstance(something, dict):
00799         map_builder = []
00800         map_builder.append('{' + struct.pack('!i', len(something)))
00801         for key, value in something.items():
00802             map_builder.append('k' + struct.pack('!i', len(key)) + key)
00803             map_builder.append(_format_binary_recurse(value))
00804         map_builder.append('}')
00805         return ''.join(map_builder)
00806     else:
00807         raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % (
00808             type(something), something))
00809 
00810 
00811 def parse(something):
00812     try:
00813         if something.startswith('<?llsd/binary?>'):
00814             just_binary = something.split('\n', 1)[1]
00815             return LLSDBinaryParser().parse(just_binary)
00816         # This should be better.
00817         elif something.startswith('<'):
00818             return to_python(fromstring(something)[0])
00819         else:
00820             return LLSDNotationParser().parse(something)
00821     except KeyError, e:
00822         raise Exception('LLSD could not be parsed: %s' % (e,))
00823 
00824 class LLSD(object):
00825     def __init__(self, thing=None):
00826         self.thing = thing
00827 
00828     def __str__(self):
00829         return self.toXML(self.thing)
00830 
00831     parse = staticmethod(parse)
00832     toXML = staticmethod(format_xml)
00833     toBinary = staticmethod(format_binary)
00834     toNotation = staticmethod(format_notation)
00835 
00836 
00837 undef = LLSD(None)
00838 
00839 # register converters for llsd in mulib, if it is available
00840 try:
00841     from mulib import stacked, mu
00842     stacked.NoProducer()  # just to exercise stacked
00843     mu.safe_load(None)    # just to exercise mu
00844 except:
00845     # mulib not available, don't print an error message since this is normal
00846     pass
00847 else:
00848     mu.add_parser(parse, 'application/llsd+xml')
00849     mu.add_parser(parse, 'application/llsd+binary')
00850 
00851     def llsd_convert_xml(llsd_stuff, request):
00852         request.write(format_xml(llsd_stuff))
00853 
00854     def llsd_convert_binary(llsd_stuff, request):
00855         request.write(format_binary(llsd_stuff))
00856 
00857     for typ in [LLSD, dict, list, tuple, str, int, float, bool, unicode, type(None)]:
00858         stacked.add_producer(typ, llsd_convert_xml, 'application/llsd+xml')
00859         stacked.add_producer(typ, llsd_convert_xml, 'application/xml')
00860         stacked.add_producer(typ, llsd_convert_xml, 'text/xml')
00861 
00862         stacked.add_producer(typ, llsd_convert_binary, 'application/llsd+binary')
00863 
00864     stacked.add_producer(LLSD, llsd_convert_xml, '*/*')

Generated on Fri May 16 08:31:53 2008 for SecondLife by  doxygen 1.5.5