PageRenderTime 110ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 1ms

/fbgen/gen_templates.py

https://github.com/GordonSmith/FireBreath
Python | 273 lines | 263 code | 9 blank | 1 comment | 11 complexity | 68bf84336a8f6d42875b8ce710a8fd18 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. SENTENCE_SYMBOLS_RE=re.compile(r"^[()\w .:,;!?-]+$")
  32. SENTENCE_SYMBOLS_WARNING="must be at least one character long and may only contain letters, digits, spaces, punctuation marks, parantheses, underscores or hyphens."
  33. class Base(object):
  34. def __getitem__(self, item): return getattr(self, item)
  35. def __init__(self, **kwargs):
  36. for k, v in kwargs.items():
  37. if hasattr(self, k): setattr(self, k, v)
  38. self.keys = AttrDictSimple(
  39. name = ("Name", SENTENCE_SYMBOLS_RE, "Name " + SENTENCE_SYMBOLS_WARNING),
  40. ident = ("Identifier", re.compile(r"^[a-zA-Z][a-zA-Z\d_]{2,}$"), "Identifier must be 3 or more alphanumeric characters (underscore allowed)."),
  41. desc = ("Description", SENTENCE_SYMBOLS_RE, "Description " + SENTENCE_SYMBOLS_WARNING),
  42. prefix = ("Prefix", re.compile(r"^[a-zA-Z][a-zA-Z\d_]{2,4}$"), "Prefix must be 3 to 5 alphanumeric characters (underscores allowed)."),
  43. 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."),
  44. 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"),
  45. disable_gui = ("has no UI", re.compile(r"^true$|false$"), "Please enter valid input: true or false"),
  46. )
  47. def getValue(self, key, default):
  48. desc, regex, error = self.keys[key]
  49. if default is None:
  50. default = ""
  51. value = raw_input("%s %s [%s]: " % (self.__class__.__name__, desc, default)) or default
  52. if regex.match(value):
  53. return value
  54. else:
  55. print "Invalid syntax: %s" % error
  56. return self.getValue(key, default)
  57. def promptValues(self):
  58. """
  59. Override in sub-classes. Prompts for necessary configuration information.
  60. """
  61. pass
  62. def readCfg(self, cfg):
  63. """
  64. Override in sub-classes. Reads an existing configuration out of a file for anything not already defined by other means.
  65. """
  66. pass
  67. def updateCfg(self, cfg):
  68. """
  69. Override in sub-classes. Updates a configuration object with current values.
  70. """
  71. pass
  72. class JSAPI_Member(Base):
  73. """
  74. 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.
  75. """
  76. ident = None
  77. type = None
  78. def __init__(self):
  79. print "Initializing JSAPI_Member"
  80. self.types = AttrDictSimple(
  81. string = "std::string",
  82. int = "long", # changed int to long since IE will pass it as a long anyway and we want to avoid issues.
  83. long = "long",
  84. double = "double",
  85. bool = "bool",
  86. variant = "FB::variant",
  87. dynamic = "FB::VariantList",
  88. JSOBJ = "FB::JSAPIPtr",
  89. API = "FB::JSObject",
  90. )
  91. def translateType(self, type):
  92. return self.types[type]
  93. def isValidType(self, type):
  94. return self.types.has_key(type)
  95. def setType(self, type):
  96. self.type = type
  97. def getRealType(self):
  98. return self.translateType(self.type)
  99. class JSAPI_Property(JSAPI_Member):
  100. """
  101. 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.
  102. """
  103. def __init__(self, ident, type):
  104. super(JSAPI_Property, self).__init__()
  105. if not self.isValidType(type):
  106. raise Exception("Invalid type %s. Valid types are: %s" % type, ', '.join(self.types))
  107. self.type = type
  108. self.ident = ident
  109. class JSAPI_Method(JSAPI_Member):
  110. """
  111. 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.
  112. """
  113. argTypes = ["string"]
  114. def __init__(self, ident, type, argTypes):
  115. super(JSAPI_Method, self).__init__()
  116. self.type = type
  117. self.ident = ident
  118. self.argTypes = argTypes
  119. for curArg in argTypes:
  120. if not self.isValidType(curArg):
  121. raise Exception("Invalid type %s. Valid types are: %s" % (curArg, ', '.join(self.types)))
  122. def getRealArgTypes(self):
  123. retVal = []
  124. for cur in self.argTypes:
  125. retVal.append(self.translateType(cur))
  126. return retVal
  127. class Plugin(Base):
  128. name = None
  129. ident = None
  130. prefix = None
  131. desc = None
  132. mimetype = None
  133. disable_gui = None
  134. def makeDefaultPrefix(self, startName, delim = " "):
  135. MIN_LEN_PREFIX=3
  136. MAX_LEN_PREFIX=5
  137. pattern = re.compile(r"([A-Z][A-Z][a-z])|([a-z][A-Z])")
  138. if startName is None:
  139. return None
  140. if MIN_LEN_PREFIX <= len(startName) <= MAX_LEN_PREFIX:
  141. return startName.upper()
  142. normalize = lambda s:s
  143. seperated = normalize(pattern.sub(lambda m: m.group()[:1] + delim + m.group()[1:], startName))
  144. words = seperated.split()
  145. if MIN_LEN_PREFIX <= len(words) <= MAX_LEN_PREFIX:
  146. return "".join( [ lett[0] for lett in words ]).upper()[:MAX_LEN_PREFIX]
  147. postfix = ""
  148. word = len(words) - 1
  149. needed = MIN_LEN_PREFIX - len(words) + 1
  150. while len(postfix) < needed:
  151. stillNeeded = needed - len(postfix)
  152. postfix = words[word][:stillNeeded] + postfix
  153. if len(postfix) < needed:
  154. needed += 1
  155. word -= 1
  156. return "".join( [ lett[0] for lett in words[:word] ] ).upper() + postfix.upper()
  157. def __init__(self, **kwargs):
  158. super(Plugin, self).__init__(**kwargs)
  159. if self.mimetype:
  160. self.mimetype = self.mimetype.lower()
  161. def promptValues(self):
  162. name = self.name
  163. ident = self.ident
  164. self.name = self.getValue("name", self.name)
  165. self.ident = self.getValue("ident", re.sub(r"[^a-zA-Z\d\-_]", "", self.ident or self.name))
  166. self.prefix = self.getValue("prefix", self.prefix if name == self.name else self.makeDefaultPrefix(self.name))
  167. self.mimetype = self.getValue("mimetype", self.mimetype if ident == self.ident else "application/x-%s" % self.ident.lower()).lower()
  168. self.desc = self.getValue("desc", self.desc)
  169. self.disable_gui = self.getValue("disable_gui", self.disable_gui or "false").lower()
  170. def readCfg(self, cfg):
  171. if not cfg.has_section("plugin"):
  172. return
  173. self.name = self.name or cfg.get("plugin", "name")
  174. self.ident = self.ident or cfg.get("plugin", "ident")
  175. self.prefix = self.prefix or cfg.get("plugin", "prefix")
  176. self.mimetype = self.mimetype or cfg.get("plugin", "mimetype").lower()
  177. self.desc = self.desc or cfg.get("plugin", "description")
  178. if self.disable_gui == None:
  179. self.disable_gui = cfg.get("plugin", "disable_gui") or False
  180. def updateCfg(self, cfg):
  181. if not cfg.has_section("plugin"):
  182. cfg.add_section("plugin")
  183. cfg.set("plugin", "name", self.name)
  184. cfg.set("plugin", "ident", self.ident)
  185. cfg.set("plugin", "prefix", self.prefix)
  186. cfg.set("plugin", "mimetype", self.mimetype)
  187. cfg.set("plugin", "description", self.desc)
  188. cfg.set("plugin", "disable_gui", self.disable_gui)
  189. def __str__(self):
  190. 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
  191. class Company(Base):
  192. name = None
  193. ident = None
  194. domain = None
  195. def __init__(self, **kwargs):
  196. super(Company, self).__init__(**kwargs)
  197. def promptValues(self):
  198. self.name = self.getValue("name", self.name)
  199. self.ident = self.getValue("ident", self.ident or re.sub(r"[^a-zA-Z\d\-_]", "", self.name))
  200. self.domain = self.getValue("domain", self.domain or "%s.com" % self.ident.lower())
  201. def readCfg(self, cfg):
  202. if not cfg.has_section("company"):
  203. return
  204. self.name = self.name or cfg.get("company", "name")
  205. self.ident = self.ident or cfg.get("company", "ident")
  206. self.domain = self.domain or cfg.get("company", "domain")
  207. def updateCfg(self, cfg):
  208. if not cfg.has_section("company"):
  209. cfg.add_section("company")
  210. cfg.set("company", "name", self.name)
  211. cfg.set("company", "ident", self.ident)
  212. cfg.set("company", "domain", self.domain)
  213. def __str__(self):
  214. return "\nCompany Details\n---------------\nName: %(name)s\nIdentifier: %(ident)s\nDomain: %(domain)s" % self
  215. class GUID(Base):
  216. """
  217. This class will create a Master GUID based on the plugin identifier and company domain. This allows the generated GUIDs to always be
  218. the same if created with/for the same intent.
  219. """
  220. master = None
  221. domain = None
  222. ident = None
  223. def __init__(self, **kwargs):
  224. super(GUID, self).__init__(**kwargs)
  225. self.master = uuid.uuid5(uuid.NAMESPACE_DNS, self.ident + '.' + self.domain)
  226. def generate(self, string):
  227. return uuid.uuid5(self.master, string)