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

/fbgen/gen_templates.py

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