#! /usr/bin/env python """Classes for creating CIPRES UI XML from python see $PIPRES_ROOT/examples=*xml-source.py for examples""" _debugging = False def logMessage(msg): if _debugging: print msg from xml.sax.saxutils import quoteattr, escape class UI_Operator: EQ, NE, GT, GE, LT, LE, AND, OR, XOR, BOOL, NOT = range(11) LogicalBinaryOperators = [AND, OR, XOR,] toVOneNames = { EQ: 'equals', GT: 'greater_than', GE: 'greater_or_equal', LT: 'less_than', LE: 'less_or_equal', AND: 'and', OR: 'or', } toImperativeSnippets = { EQ: 'must equal', GT: 'must be greater than', GE: 'must be greater than or equal to', LT: 'must be less than', LE: 'must be less than or equal to', AND: 'and', OR: 'or', } def vOneToStr(x): try: return UI_Operator.toVOneNames[x] except: raise AssertionError, 'unknown UI_Operator %s' % str(x) vOneToStr = staticmethod(vOneToStr) def toImperative(x): try: return UI_Operator.toImperativeSnippets[x] except: raise AssertionError, 'unknown UI_Operator %s' % str(x) toImperative = staticmethod(toImperative) def writeXMLElement(outStream, elName, atts=None, body='', indentation='', close=False, addNewLine=True): if elName != quoteattr(elName)[1:-1]: raise AssertionError, '%s is in illegal element name' % elName if atts: attStr = ' ' + ' '.join(['%s=%s' % (k, quoteattr(v)) for k,v in atts]) else: attStr = '' outStream.write('%s<%s%s>%s' % (indentation, elName, attStr, escape(body))) if close: writeCloseXMLElement(outStream, elName, addNewLine=addNewLine) elif addNewLine: outStream.write('\n') def writeCloseXMLElement(outStream, elName, indentation='', addNewLine=True): n = addNewLine and '\n' or '' if elName != quoteattr(elName)[1:-1]: raise AssertionError, '%s is in illegal element name' % elName outStream.write('%s%s' % (indentation, elName, n)) class UI_Group: def __init__(self, label): self.__dict__['_in_order'] = [] self.label = label def __setattr__(self, item, value): self.__dict__[item] = value if item != 'label': if isinstance(value, UI_Param) and value.__dict__.get('name') is None: value.name = item self.__dict__.get('_in_order', []).append(item) def writeToXMLvOne(self, o, indentation=''): o.write('\n') writeXMLElement(o, 'service-command-panel', indentation=indentation, atts=[ ['xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'], ['xsi:noNamespaceSchemaLocation', 'CipresCommandLanguage.xsd'], ['label', self.label]]) for k in self._in_order: self.__dict__[k].writeToXMLvOne(o, indentation=indentation + '\t') writeCloseXMLElement(o,'service-command-panel', indentation=indentation) def toXMLvOne(self): from cStringIO import StringIO s = StringIO() self.writeToXMLvOne(s) return s.getvalue() class UI_TestContext: AVAILABILITY, CONSTRAINT = range(2) class UI_Test: currTestNum = 0 def Not(o): '''Creates a (not cond == True) UI_Test. not is a keyword and cannot be overloaded.''' logMessage('Not') return UI_Test(o, UI_Operator.NOT) Not = staticmethod(Not) def __init__(self, loperand, operator=UI_Operator.BOOL, roperand = None, message=None): self.loperand = loperand self.operator = operator self.roperand = roperand self.message = message def __and__(self, other): logMessage('__and__') return UI_Test(self, UI_Operator.AND, other) def __or__(self, other): logMessage('__or__') return UI_Test(self, UI_Operator.OR, other) def getName(self): if 'name' not in self.__dict__: self.name = 'unnamed-%d' % UI_Test.currTestNum UI_Test.currTestNum += 1 return self.name def writeToXMLvOneTestDef(self, o, context=UI_TestContext.CONSTRAINT, indentation=''): if self.operator in UI_Operator.LogicalBinaryOperators: self.loperand.writeToXMLvOneTestDef(o, context, indentation) self.roperand.writeToXMLvOneTestDef(o, context, indentation) return nextIndent = indentation + '\t' writeXMLElement(o, 'test', atts=[['test-name', self.getName()]], indentation=indentation) writeXMLElement(o, 'left-operand', body=self.getLOpString(), indentation=nextIndent, close=True) writeXMLElement(o, 'operator', body=UI_Operator.vOneToStr(self.operator), indentation=nextIndent, close=True) writeXMLElement(o, 'right-operand', body=self.getROpString(), indentation=nextIndent, close=True) writeCloseXMLElement(o, 'test', indentation=indentation) def getVOneUseIElementText(self): if self.operator in UI_Operator.LogicalBinaryOperators: return '(%s %s %s)' % (self.loperand.getVOneUseIElementText(), UI_Operator.vOneToStr(self.operator), self.roperand.getVOneUseIElementText()) return self.getName() def writeToXMLvOneTestUse(self, o, context=UI_TestContext.CONSTRAINT, indentation=''): snippet = context == UI_TestContext.CONSTRAINT and 'requirement' or 'availability' writeXMLElement(o, '%s-test-expression' % snippet, body=self.getVOneUseIElementText(), indentation=indentation, close=True) writeXMLElement(o, '%s-test-expression-message' % snippet, body=self.getMessage(), indentation=indentation, close=True) def getMessage(self): return self.message or self.composeMessage() def composeMessage(self): return '%s %s %s' % (self.getLOpMessage(), UI_Operator.toImperative(self.operator), self.getROpMessage()) def getLOpString(self): return isinstance(self.loperand, UI_Param) and ('$%s' % self.loperand.name) or str(self.loperand) def getROpString(self): return isinstance(self.roperand, UI_Param) and ('$%s' % self.roperand.name) or str(self.roperand) def getLOpMessage(self): return isinstance(self.loperand, UI_Param) and ('the "%s" setting' % self.loperand.label) or str(self.loperand) def getROpMessage(self): return isinstance(self.roperand, UI_Param) and ('the "%s" setting' % self.roperand.label) or str(self.roperand) class UI_Choice: def __init__(self, val, label, **kwargs): self.value = val self.label = label if kwargs.get('help') is not None: self.help = kwargs.get('help') def writeToXMLvOne(self, o, isDefault=False, indentation=''): atts = isDefault and [['is-default','true']] or None nextIndent = indentation + '\t' writeXMLElement(o, 'list-item', atts=atts, indentation=indentation) writeXMLElement(o, 'value', body=self.value, indentation=nextIndent, close=True) writeXMLElement(o, 'display', body=self.label, indentation=nextIndent, close=True) writeCloseXMLElement(o, 'list-item', indentation=indentation) def addToList(listToAddTo, x): if isinstance(x, list): listToAddTo.extend(x) else: listToAddTo.append(x) return listToAddTo def _assertIsUITest(x, c): try: for i in x: if not isinstance(i, UI_Test): raise ValueError, '%s field should be UI_Test instance (make sure you are using & and | operators in place of "and" and "or")' % c except TypeError: if not isinstance(x, UI_Test): raise ValueError, '%s field should be UI_Test instance (make sure you are using & and | operators in place of "and" and "or")' % c class UI_StringTemplate: VALUE, NAME_EQ_VALUE, KEY_VAL_TEMPLATE = range(3) def nameValueTemplate(s): return (UI_StringTemplate.KEY_VAL_TEMPLATE, s) nameValueTemplate = staticmethod(nameValueTemplate) class UI_Param(object): IS_VALID, IS_VALID_IF_SET = range(2) def __init__(self, label, **kwargs): self.label = label self.subParams = [] self.help = kwargs.get('help', '') self.default = kwargs.get('default') self.availablity = kwargs.get('availablity', []) self.constraint = kwargs.get('constraint', []) self.formatter = kwargs.get('formatter', UI_StringTemplate.NAME_EQ_VALUE) self.demand = kwargs.get('demand', UI_Param.IS_VALID_IF_SET) self.stringTerminator = '' def getAvailability(self): return self._availability def setAvailability(self, x): if x is None: x = [] _assertIsUITest(x, 'availibility') self._availability = x def getConstraint(self): return self._constraint def setConstraint(self, x): if x is None: x = [] _assertIsUITest(x, 'requirement') self._constraint = x availablity = property(getAvailability, setAvailability) constraint = property(getConstraint, setConstraint) def getName(self): return self.__dict__.get('name') or '' def getTestsAndContext(self): t = [] if self.constraint: t.append([self.constraint, UI_TestContext.CONSTRAINT]) if self.availablity: t.append([self.availablity, UI_TestContext.AVAILABILITY]) return t def addSubParam(self, s): self.subParams.append(s) def __nonzero__(self): logMessage('__nonzero__') return UI_Test(self, UI_Operator.BOOL) def __eq__(self, other): logMessage('__eq__') return UI_Test(self, UI_Operator.EQ, other) def __ne__(self, other): logMessage('__ne__') return UI_Test(self, UI_Operator.NE, other) def __gt__(self, other): logMessage('__gt__') return UI_Test(self, UI_Operator.GT, other) def __ge__(self, other): logMessage('__ge__') return UI_Test(self, UI_Operator.GE, other) def __lt__(self, other): logMessage('__lt__') return UI_Test(self, UI_Operator.LT, other) def __le__(self, other): logMessage('__le__') return UI_Test(self, UI_Operator.LE, other) def writeToXMLvOneRepositoryID(self, o, indentation=''): pass def writeToXMLvOneValue(self, o, indentation=''): pass def writeToXMLvOneDefault(self, o, indentation=''): if self.default is not None: writeXMLElement(o, 'default-value', body=str(self.default), indentation=indentation, close=True) def getVOneTemplate(self): if self.formatter == UI_StringTemplate.NAME_EQ_VALUE: x = '%s=$%s' % (self.name, self.name) elif self.formatter == UI_StringTemplate.VALUE: x = '$%s' % self.name elif isinstance(self.formatter, tuple) and self.formatter[0] == UI_StringTemplate.KEY_VAL_TEMPLATE: try: val = '$%s' % self.name x = self.formatter[1] % (self.name, val) except TypeError: raise ValueError, 'Illegal name-value formatter (does not format two strings) for the parameter %s' % self.getName() else: raise AssertionError, 'unknown value for formatter' return x + self.stringTerminator def writeToXMLvOne(self, o, indentation=''): nextIndent = indentation + '\t' writeXMLElement(o, 'cmd-params', atts=[['name', self.name]], indentation=indentation) writeXMLElement(o, 'datatype', body=self.getVOneDatatype(), indentation=nextIndent, close=True) self.writeToXMLvOneRepositoryID(o, nextIndent) mandatory = self.demand == UI_Param.IS_VALID_IF_SET and 'false' or 'true' writeXMLElement(o, 'is-mandatory', body=mandatory, indentation=nextIndent, close=True) writeXMLElement(o, 'label', body=self.label, indentation=nextIndent, close=True) self.writeToXMLvOneDefault(o, nextIndent) writeXMLElement(o, 'tool-tip-text', body=self.help, indentation=nextIndent, close=True) self.writeToXMLvOneValue(o, nextIndent) writeXMLElement(o, 'code-template', body=self.getVOneTemplate(), indentation=nextIndent, close=True) for s in self.subParams: s.writeToXMLvOne(o, nextIndent) for t,c in self.getTestsAndContext(): t.writeToXMLvOneTestDef(o, c, nextIndent) for t,c in self.getTestsAndContext(): t.writeToXMLvOneTestUse(o, c, nextIndent) writeCloseXMLElement(o,'cmd-params', indentation=indentation) class UI_List(UI_Param): def __init__(self, label, choices, **kwargs): self.choices = choices super(UI_List, self).__init__(label, **kwargs) def getVOneDatatype(self): return 'Excl' def writeToXMLvOneDefault(self, o, indentation=''): pass def writeToXMLvOneValue(self, o, indentation=''): default = self.__dict__.get('default') for n, c in enumerate(self.choices): c.writeToXMLvOne(o, n == default, indentation) class UI_Int(UI_Param): def getVOneDatatype(self): return 'Integer' class UI_Float(UI_Param): def getVOneDatatype(self): return 'Float' class UI_Service(UI_List): def __init__(self, label, repositoryID, **kwargs): self.repositoryID = repositoryID super(UI_Service, self).__init__(label, kwargs.get('choices',[]), **kwargs) def writeToXMLvOneRepositoryID(self, o, indentation=''): writeXMLElement(o, 'repository-iD', body=self.repositoryID, indentation=indentation, close=True) def toUI_XML(group): return group.toXMLvOne()