PageRenderTime 39ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/lib/python/indra/ipc/llmessage.py

https://bitbucket.org/lindenlab/viewer-beta/
Python | 372 lines | 363 code | 0 blank | 9 comment | 0 complexity | 0eaaee39e2b130ac5555df73ac518763 MD5 | raw file
Possible License(s): LGPL-2.1
  1. """\
  2. @file llmessage.py
  3. @brief Message template parsing and compatiblity
  4. $LicenseInfo:firstyear=2007&license=mit$
  5. Copyright (c) 2007-2009, Linden Research, Inc.
  6. Permission is hereby granted, free of charge, to any person obtaining a copy
  7. of this software and associated documentation files (the "Software"), to deal
  8. in the Software without restriction, including without limitation the rights
  9. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. copies of the Software, and to permit persons to whom the Software is
  11. furnished to do so, subject to the following conditions:
  12. The above copyright notice and this permission notice shall be included in
  13. all copies or substantial portions of the Software.
  14. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. THE SOFTWARE.
  21. $/LicenseInfo$
  22. """
  23. from compatibility import Incompatible, Older, Newer, Same
  24. from tokenstream import TokenStream
  25. ###
  26. ### Message Template
  27. ###
  28. class Template:
  29. def __init__(self):
  30. self.messages = { }
  31. def addMessage(self, m):
  32. self.messages[m.name] = m
  33. def compatibleWithBase(self, base):
  34. messagenames = (
  35. frozenset(self.messages.keys())
  36. | frozenset(base.messages.keys())
  37. )
  38. compatibility = Same()
  39. for name in messagenames:
  40. selfmessage = self.messages.get(name, None)
  41. basemessage = base.messages.get(name, None)
  42. if not selfmessage:
  43. c = Older("missing message %s, did you mean to deprecate?" % name)
  44. elif not basemessage:
  45. c = Newer("added message %s" % name)
  46. else:
  47. c = selfmessage.compatibleWithBase(basemessage)
  48. c.prefix("in message %s: " % name)
  49. compatibility = compatibility.combine(c)
  50. return compatibility
  51. class Message:
  52. HIGH = "High"
  53. MEDIUM = "Medium"
  54. LOW = "Low"
  55. FIXED = "Fixed"
  56. priorities = [ HIGH, MEDIUM, LOW, FIXED ]
  57. prioritieswithnumber = [ FIXED ]
  58. TRUSTED = "Trusted"
  59. NOTTRUSTED = "NotTrusted"
  60. trusts = [ TRUSTED, NOTTRUSTED ]
  61. UNENCODED = "Unencoded"
  62. ZEROCODED = "Zerocoded"
  63. encodings = [ UNENCODED, ZEROCODED ]
  64. NOTDEPRECATED = "NotDeprecated"
  65. DEPRECATED = "Deprecated"
  66. UDPDEPRECATED = "UDPDeprecated"
  67. UDPBLACKLISTED = "UDPBlackListed"
  68. deprecations = [ NOTDEPRECATED, UDPDEPRECATED, UDPBLACKLISTED, DEPRECATED ]
  69. # in order of increasing deprecation
  70. def __init__(self, name, number, priority, trust, coding):
  71. self.name = name
  72. self.number = number
  73. self.priority = priority
  74. self.trust = trust
  75. self.coding = coding
  76. self.deprecateLevel = 0
  77. self.blocks = [ ]
  78. def deprecated(self):
  79. return self.deprecateLevel != 0
  80. def deprecate(self, deprecation):
  81. self.deprecateLevel = self.deprecations.index(deprecation)
  82. def addBlock(self, block):
  83. self.blocks.append(block)
  84. def compatibleWithBase(self, base):
  85. if self.name != base.name:
  86. # this should never happen in real life because of the
  87. # way Template matches up messages by name
  88. return Incompatible("has different name: %s vs. %s in base"
  89. % (self.name, base.name))
  90. if self.priority != base.priority:
  91. return Incompatible("has different priority: %s vs. %s in base"
  92. % (self.priority, base.priority))
  93. if self.trust != base.trust:
  94. return Incompatible("has different trust: %s vs. %s in base"
  95. % (self.trust, base.trust))
  96. if self.coding != base.coding:
  97. return Incompatible("has different coding: %s vs. %s in base"
  98. % (self.coding, base.coding))
  99. if self.number != base.number:
  100. return Incompatible("has different number: %s vs. %s in base"
  101. % (self.number, base.number))
  102. compatibility = Same()
  103. if self.deprecateLevel != base.deprecateLevel:
  104. if self.deprecateLevel < base.deprecateLevel:
  105. c = Older("is less deprecated: %s vs. %s in base" % (
  106. self.deprecations[self.deprecateLevel],
  107. self.deprecations[base.deprecateLevel]))
  108. else:
  109. c = Newer("is more deprecated: %s vs. %s in base" % (
  110. self.deprecations[self.deprecateLevel],
  111. self.deprecations[base.deprecateLevel]))
  112. compatibility = compatibility.combine(c)
  113. selflen = len(self.blocks)
  114. baselen = len(base.blocks)
  115. samelen = min(selflen, baselen)
  116. for i in xrange(0, samelen):
  117. selfblock = self.blocks[i]
  118. baseblock = base.blocks[i]
  119. c = selfblock.compatibleWithBase(baseblock)
  120. if not c.same():
  121. c = Incompatible("block %d isn't identical" % i)
  122. compatibility = compatibility.combine(c)
  123. if selflen > baselen:
  124. c = Newer("has %d extra blocks" % (selflen - baselen))
  125. elif selflen < baselen:
  126. c = Older("missing %d extra blocks" % (baselen - selflen))
  127. else:
  128. c = Same()
  129. compatibility = compatibility.combine(c)
  130. return compatibility
  131. class Block(object):
  132. SINGLE = "Single"
  133. MULTIPLE = "Multiple"
  134. VARIABLE = "Variable"
  135. repeats = [ SINGLE, MULTIPLE, VARIABLE ]
  136. repeatswithcount = [ MULTIPLE ]
  137. def __init__(self, name, repeat, count=None):
  138. self.name = name
  139. self.repeat = repeat
  140. self.count = count
  141. self.variables = [ ]
  142. def addVariable(self, variable):
  143. self.variables.append(variable)
  144. def compatibleWithBase(self, base):
  145. if self.name != base.name:
  146. return Incompatible("has different name: %s vs. %s in base"
  147. % (self.name, base.name))
  148. if self.repeat != base.repeat:
  149. return Incompatible("has different repeat: %s vs. %s in base"
  150. % (self.repeat, base.repeat))
  151. if self.repeat in Block.repeatswithcount:
  152. if self.count != base.count:
  153. return Incompatible("has different count: %s vs. %s in base"
  154. % (self.count, base.count))
  155. compatibility = Same()
  156. selflen = len(self.variables)
  157. baselen = len(base.variables)
  158. for i in xrange(0, min(selflen, baselen)):
  159. selfvar = self.variables[i]
  160. basevar = base.variables[i]
  161. c = selfvar.compatibleWithBase(basevar)
  162. if not c.same():
  163. c = Incompatible("variable %d isn't identical" % i)
  164. compatibility = compatibility.combine(c)
  165. if selflen > baselen:
  166. c = Newer("has %d extra variables" % (selflen - baselen))
  167. elif selflen < baselen:
  168. c = Older("missing %d extra variables" % (baselen - selflen))
  169. else:
  170. c = Same()
  171. compatibility = compatibility.combine(c)
  172. return compatibility
  173. class Variable:
  174. U8 = "U8"; U16 = "U16"; U32 = "U32"; U64 = "U64"
  175. S8 = "S8"; S16 = "S16"; S32 = "S32"; S64 = "S64"
  176. F32 = "F32"; F64 = "F64"
  177. LLVECTOR3 = "LLVector3"; LLVECTOR3D = "LLVector3d"; LLVECTOR4 = "LLVector4"
  178. LLQUATERNION = "LLQuaternion"
  179. LLUUID = "LLUUID"
  180. BOOL = "BOOL"
  181. IPADDR = "IPADDR"; IPPORT = "IPPORT"
  182. FIXED = "Fixed"
  183. VARIABLE = "Variable"
  184. types = [ U8, U16, U32, U64, S8, S16, S32, S64, F32, F64,
  185. LLVECTOR3, LLVECTOR3D, LLVECTOR4, LLQUATERNION,
  186. LLUUID, BOOL, IPADDR, IPPORT, FIXED, VARIABLE ]
  187. typeswithsize = [ FIXED, VARIABLE ]
  188. def __init__(self, name, type, size):
  189. self.name = name
  190. self.type = type
  191. self.size = size
  192. def compatibleWithBase(self, base):
  193. if self.name != base.name:
  194. return Incompatible("has different name: %s vs. %s in base"
  195. % (self.name, base.name))
  196. if self.type != base.type:
  197. return Incompatible("has different type: %s vs. %s in base"
  198. % (self.type, base.type))
  199. if self.type in Variable.typeswithsize:
  200. if self.size != base.size:
  201. return Incompatible("has different size: %s vs. %s in base"
  202. % (self.size, base.size))
  203. return Same()
  204. ###
  205. ### Parsing Message Templates
  206. ###
  207. class TemplateParser:
  208. def __init__(self, tokens):
  209. self._tokens = tokens
  210. self._version = 0
  211. self._numbers = { }
  212. for p in Message.priorities:
  213. self._numbers[p] = 0
  214. def parseTemplate(self):
  215. tokens = self._tokens
  216. t = Template()
  217. while True:
  218. if tokens.want("version"):
  219. v = float(tokens.require(tokens.wantFloat()))
  220. self._version = v
  221. t.version = v
  222. continue
  223. m = self.parseMessage()
  224. if m:
  225. t.addMessage(m)
  226. continue
  227. if self._version >= 2.0:
  228. tokens.require(tokens.wantEOF())
  229. break
  230. else:
  231. if tokens.wantEOF():
  232. break
  233. tokens.consume()
  234. # just assume (gulp) that this is a comment
  235. # line 468: "sim -> dataserver"
  236. return t
  237. def parseMessage(self):
  238. tokens = self._tokens
  239. if not tokens.want("{"):
  240. return None
  241. name = tokens.require(tokens.wantSymbol())
  242. priority = tokens.require(tokens.wantOneOf(Message.priorities))
  243. if self._version >= 2.0 or priority in Message.prioritieswithnumber:
  244. number = int("+" + tokens.require(tokens.wantInteger()), 0)
  245. else:
  246. self._numbers[priority] += 1
  247. number = self._numbers[priority]
  248. trust = tokens.require(tokens.wantOneOf(Message.trusts))
  249. coding = tokens.require(tokens.wantOneOf(Message.encodings))
  250. m = Message(name, number, priority, trust, coding)
  251. if self._version >= 2.0:
  252. d = tokens.wantOneOf(Message.deprecations)
  253. if d:
  254. m.deprecate(d)
  255. while True:
  256. b = self.parseBlock()
  257. if not b:
  258. break
  259. m.addBlock(b)
  260. tokens.require(tokens.want("}"))
  261. return m
  262. def parseBlock(self):
  263. tokens = self._tokens
  264. if not tokens.want("{"):
  265. return None
  266. name = tokens.require(tokens.wantSymbol())
  267. repeat = tokens.require(tokens.wantOneOf(Block.repeats))
  268. if repeat in Block.repeatswithcount:
  269. count = int(tokens.require(tokens.wantInteger()))
  270. else:
  271. count = None
  272. b = Block(name, repeat, count)
  273. while True:
  274. v = self.parseVariable()
  275. if not v:
  276. break
  277. b.addVariable(v)
  278. tokens.require(tokens.want("}"))
  279. return b
  280. def parseVariable(self):
  281. tokens = self._tokens
  282. if not tokens.want("{"):
  283. return None
  284. name = tokens.require(tokens.wantSymbol())
  285. type = tokens.require(tokens.wantOneOf(Variable.types))
  286. if type in Variable.typeswithsize:
  287. size = tokens.require(tokens.wantInteger())
  288. else:
  289. tokens.wantInteger() # in LandStatRequest: "{ ParcelLocalID S32 1 }"
  290. size = None
  291. tokens.require(tokens.want("}"))
  292. return Variable(name, type, size)
  293. def parseTemplateString(s):
  294. return TemplateParser(TokenStream().fromString(s)).parseTemplate()
  295. def parseTemplateFile(f):
  296. return TemplateParser(TokenStream().fromFile(f)).parseTemplate()