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

/fbgen/gen_templates.py

https://github.com/kavalg/FireBreath
Python | 272 lines | 261 code | 10 blank | 1 comment | 11 complexity | 7720282ae04d9a51c8280497a25b1ce6 MD5 | raw file
  1. #!/usr/bin/env python
  2. import os, re, string, sys, uuid
  3. class AttrDictSimple(dict):
  4. def __getattr__(self, attr): return self[attr]
  5. def __setattr__(self, attr, value): self[attr] = value
  6. def __delattr__(self, attr): del self[attr]
  7. class Template(string.Template):
  8. delimiter = "@"
  9. def __init__(self, filename):
  10. if not os.path.isfile(filename):
  11. raise ValueError('Unable to read file with name %s' % filename)
  12. super(self.__class__, self).__init__(open(filename).read())
  13. def process(self, *args):
  14. params = AttrDictSimple()
  15. for arg in args:
  16. params.update(self.generateReplacementDict(arg))
  17. return self.substitute(params)
  18. def generateReplacementDict(self, obj):
  19. if isinstance(obj, dict):
  20. return obj
  21. assert isinstance(obj, Base), "Must provide a base FBGen class"
  22. retdict = AttrDictSimple([("%s_%s" % (obj.__class__.__name__.upper(), k), obj[k]) for k in obj.keys.keys() if hasattr(obj, k)])
  23. if retdict.get('PLUGIN_disable_gui') != None:
  24. if retdict.get('PLUGIN_disable_gui') == "true":
  25. retdict.update(PLUGIN_disable_gui='set (FB_GUI_DISABLED');
  26. retdict.update(PLUGIN_disable_gui_mac='0');
  27. else:
  28. retdict.update(PLUGIN_disable_gui='#set (FB_GUI_DISABLED');
  29. retdict.update(PLUGIN_disable_gui_mac='1');
  30. return retdict;
  31. class Base(object):
  32. def __getitem__(self, item): return getattr(self, item)
  33. def __init__(self, **kwargs):
  34. for k, v in kwargs.items():
  35. if hasattr(self, k): setattr(self, k, v)
  36. self.keys = AttrDictSimple(
  37. name = ("Name", re.compile(r"^.+$"), "Name must be at least one character, and may not contain carriage returns."),
  38. ident = ("Identifier", re.compile(r"^[a-zA-Z][a-zA-Z\d_]{2,}$"), "Identifier must be 3 or more alphanumeric characters (underscore allowed)."),
  39. desc = ("Description", re.compile(r"^.+$"), "Description must be one or more characters long!"),
  40. prefix = ("Prefix", re.compile(r"^[a-zA-Z][a-zA-Z\d_]{2,4}$"), "Prefix must be 3 to 5 alphanumeric characters (underscores allowed)."),
  41. domain = ("Domain", re.compile(r"^([a-zA-Z0-9]+(\-[a-zA-Z0-9]+)*\.)*[a-zA-Z0-9]+(\-[a-zA-Z0-9]+)*\.[a-zA-Z]{2,4}$"), "Domain must be a valid domain name."),
  42. mimetype = ("MIME type", re.compile(r"^[a-zA-Z0-9]+\/[a-zA-Z0-9\-]+$"), "Please use alphanumeric characters and dashes in the format: application/x-firebreath"),
  43. disable_gui = ("has no UI", re.compile(r"^true$|false$"), "Please enter valid input: true or false"),
  44. )
  45. def getValue(self, key, default):
  46. desc, regex, error = self.keys[key]
  47. if default is None:
  48. default = ""
  49. value = raw_input("%s %s [%s]: " % (self.__class__.__name__, desc, default)) or default
  50. if regex.match(value):
  51. return value
  52. else:
  53. print "Invalid syntax: %s" % error
  54. return self.getValue(key, default)
  55. def promptValues(self):
  56. """
  57. Override in sub-classes. Prompts for necessary configuration information.
  58. """
  59. pass
  60. def readCfg(self, cfg):
  61. """
  62. Override in sub-classes. Reads an existing configuration out of a file for anything not already defined by other means.
  63. """
  64. pass
  65. def updateCfg(self, cfg):
  66. """
  67. Override in sub-classes. Updates a configuration object with current values.
  68. """
  69. pass
  70. class JSAPI_Member(Base):
  71. """
  72. Used to define a JSAPI Member. This may go away in a future version as I haven't quite decided how to deal with these yet.
  73. """
  74. ident = None
  75. type = None
  76. def __init__(self):
  77. print "Initializing JSAPI_Member"
  78. self.types = AttrDictSimple(
  79. string = "std::string",
  80. int = "long", # changed int to long since IE will pass it as a long anyway and we want to avoid issues.
  81. long = "long",
  82. double = "double",
  83. bool = "bool",
  84. variant = "FB::variant",
  85. dynamic = "FB::VariantList",
  86. JSOBJ = "FB::JSAPIPtr",
  87. API = "FB::JSObject",
  88. )
  89. def translateType(self, type):
  90. return self.types[type]
  91. def isValidType(self, type):
  92. return self.types.has_key(type)
  93. def setType(self, type):
  94. self.type = type
  95. def getRealType(self):
  96. return self.translateType(self.type)
  97. class JSAPI_Property(JSAPI_Member):
  98. """
  99. Used to define a JSAPI Property. This may go away in a future version as I haven't quite decided how to deal with these yet.
  100. """
  101. def __init__(self, ident, type):
  102. super(JSAPI_Property, self).__init__()
  103. if not self.isValidType(type):
  104. raise Exception("Invalid type %s. Valid types are: %s" % type, ', '.join(self.types))
  105. self.type = type
  106. self.ident = ident
  107. class JSAPI_Method(JSAPI_Member):
  108. """
  109. Used to define a JSAPI Method. This may go away in a future version as I haven't quite decided how to deal with these yet.
  110. """
  111. argTypes = ["string"]
  112. def __init__(self, ident, type, argTypes):
  113. super(JSAPI_Method, self).__init__()
  114. self.type = type
  115. self.ident = ident
  116. self.argTypes = argTypes
  117. for curArg in argTypes:
  118. if not self.isValidType(curArg):
  119. raise Exception("Invalid type %s. Valid types are: %s" % (curArg, ', '.join(self.types)))
  120. def getRealArgTypes(self):
  121. retVal = []
  122. for cur in self.argTypes:
  123. retVal.append(self.translateType(cur))
  124. return retVal
  125. class Plugin(Base):
  126. name = None
  127. ident = None
  128. prefix = None
  129. desc = None
  130. mimetype = None
  131. disable_gui = None
  132. def makeDefaultPrefix(self, startName, delim = " "):
  133. MIN_LEN_PREFIX=3
  134. MAX_LEN_PREFIX=5
  135. pattern = re.compile(r"([A-Z][A-Z][a-z])|([a-z][A-Z])")
  136. if startName is None:
  137. return None
  138. if MIN_LEN_PREFIX <= len(startName) <= MAX_LEN_PREFIX:
  139. return startName.upper()
  140. normalize = lambda s:s
  141. seperated = normalize(pattern.sub(lambda m: m.group()[:1] + delim + m.group()[1:], startName))
  142. words = seperated.split()
  143. if MIN_LEN_PREFIX <= len(words) <= MAX_LEN_PREFIX:
  144. return "".join( [ lett[0] for lett in words ]).upper()[:MAX_LEN_PREFIX]
  145. postfix = ""
  146. word = len(words) - 1
  147. needed = MIN_LEN_PREFIX - len(words) + 1
  148. while len(postfix) < needed:
  149. stillNeeded = needed - len(postfix)
  150. postfix = words[word][:stillNeeded] + postfix
  151. if len(postfix) < needed:
  152. needed += 1
  153. word -= 1
  154. return "".join( [ lett[0] for lett in words[:word] ] ).upper() + postfix.upper()
  155. def __init__(self, **kwargs):
  156. super(Plugin, self).__init__(**kwargs)
  157. if self.mimetype:
  158. self.mimetype = self.mimetype.lower()
  159. def promptValues(self):
  160. name = self.name
  161. ident = self.ident
  162. self.name = self.getValue("name", self.name)
  163. self.ident = self.getValue("ident", re.sub(r"[^a-zA-Z\d\-_]", "", self.ident or self.name))
  164. self.prefix = self.getValue("prefix", self.prefix if name == self.name else self.makeDefaultPrefix(self.name))
  165. self.mimetype = self.getValue("mimetype", self.mimetype if ident == self.ident else "application/x-%s" % self.ident.lower()).lower()
  166. self.desc = self.getValue("desc", self.desc)
  167. self.disable_gui = self.getValue("disable_gui", self.disable_gui or "false").lower()
  168. def readCfg(self, cfg):
  169. if not cfg.has_section("plugin"):
  170. return
  171. self.name = self.name or cfg.get("plugin", "name")
  172. self.ident = self.ident or cfg.get("plugin", "ident")
  173. self.prefix = self.prefix or cfg.get("plugin", "prefix")
  174. self.mimetype = self.mimetype or cfg.get("plugin", "mimetype").lower()
  175. self.desc = self.desc or cfg.get("plugin", "description")
  176. if self.disable_gui == None:
  177. self.disable_gui = cfg.get("plugin", "disable_gui") or False
  178. def updateCfg(self, cfg):
  179. if not cfg.has_section("plugin"):
  180. cfg.add_section("plugin")
  181. cfg.set("plugin", "name", self.name)
  182. cfg.set("plugin", "ident", self.ident)
  183. cfg.set("plugin", "prefix", self.prefix)
  184. cfg.set("plugin", "mimetype", self.mimetype)
  185. cfg.set("plugin", "description", self.desc)
  186. cfg.set("plugin", "disable_gui", self.disable_gui)
  187. def __str__(self):
  188. return "\nPlugin Details:\n--------------\nName: %(name)s\nIdentifier: %(ident)s\nPrefix: %(prefix)s\nMIME type: %(mimetype)s\nDescription: %(desc)s\nGUI: %(disable_gui)s" % self
  189. class Company(Base):
  190. name = None
  191. ident = None
  192. domain = None
  193. def __init__(self, **kwargs):
  194. super(Company, self).__init__(**kwargs)
  195. def promptValues(self):
  196. self.name = self.getValue("name", self.name)
  197. self.ident = self.getValue("ident", self.ident or re.sub(r"[^a-zA-Z\d\-_]", "", self.name))
  198. self.domain = self.getValue("domain", self.domain or "%s.com" % self.ident.lower())
  199. def readCfg(self, cfg):
  200. if not cfg.has_section("company"):
  201. return
  202. self.name = self.name or cfg.get("company", "name")
  203. self.ident = self.ident or cfg.get("company", "ident")
  204. self.domain = self.domain or cfg.get("company", "domain")
  205. def updateCfg(self, cfg):
  206. if not cfg.has_section("company"):
  207. cfg.add_section("company")
  208. cfg.set("company", "name", self.name)
  209. cfg.set("company", "ident", self.ident)
  210. cfg.set("company", "domain", self.domain)
  211. def __str__(self):
  212. return "\nCompany Details\n---------------\nName: %(name)s\nIdentifier: %(ident)s\nDomain: %(domain)s" % self
  213. class GUID(Base):
  214. """
  215. This class will create a Master GUID based on the plugin identifier and company domain. This allows the generated GUIDs to always be
  216. the same if created with/for the same intent.
  217. """
  218. master = None
  219. domain = None
  220. ident = None
  221. def __init__(self, **kwargs):
  222. super(GUID, self).__init__(**kwargs)
  223. self.master = uuid.uuid5(uuid.NAMESPACE_DNS, self.ident + '.' + self.domain)
  224. def generate(self, string):
  225. return uuid.uuid5(self.master, string)