appypod-rattail/gen/descriptors.py

199 lines
8.6 KiB
Python
Raw Normal View History

2009-06-29 07:06:01 -05:00
# ------------------------------------------------------------------------------
from appy.gen import State, Transition, Type
# ------------------------------------------------------------------------------
class Descriptor: # Abstract
def __init__(self, klass, orderedAttributes, generator):
self.klass = klass # The corresponding Python class
self.orderedAttributes = orderedAttributes # Names of the static appy-
# compliant attributes declared in self.klass
self.generator = generator # A reference to the code generator.
def __repr__(self): return '<Class %s>' % self.klass.__name__
class ClassDescriptor(Descriptor):
'''This class gives information about an Appy class.'''
def getOrderedAppyAttributes(self, condition=None):
2009-06-29 07:06:01 -05:00
'''Returns the appy types for all attributes of this class and parent
class(es). If a p_condition is specified, ony Appy types matching
the condition will be returned. p_condition must be a string
containing an expression that will be evaluated with, in its context,
"self" being this ClassDescriptor and "attrValue" being the current
Type instance.'''
2009-06-29 07:06:01 -05:00
res = []
# First, get the attributes for the current class
for attrName in self.orderedAttributes:
attrValue = getattr(self.klass, attrName)
if isinstance(attrValue, Type):
if not condition or eval(condition):
res.append( (attrName, attrValue) )
2009-06-29 07:06:01 -05:00
# Then, add attributes from parent classes
for baseClass in self.klass.__bases__:
# Find the classDescr that corresponds to baseClass
baseClassDescr = None
for classDescr in self.generator.classes:
if classDescr.klass == baseClass:
baseClassDescr = classDescr
break
if baseClassDescr:
res = baseClassDescr.getOrderedAppyAttributes() + res
return res
def getChildren(self):
'''Returns, among p_allClasses, the classes that inherit from p_self.'''
res = []
for classDescr in self.generator.classes:
if (classDescr.klass != self.klass) and \
issubclass(classDescr.klass, self.klass):
res.append(classDescr)
return res
def getPhases(self):
'''Gets the phases defined on fields of this class.'''
res = []
for fieldName, appyType in self.getOrderedAppyAttributes():
if appyType.phase not in res:
res.append(appyType.phase)
return res
class WorkflowDescriptor(Descriptor):
'''This class gives information about an Appy workflow.'''
def _getWorkflowElements(self, elemType):
res = []
for attrName in dir(self.klass):
attrValue = getattr(self.klass, attrName)
condition = False
if elemType == 'states':
condition = isinstance(attrValue, State)
elif elemType == 'transitions':
condition = isinstance(attrValue, Transition)
elif elemType == 'all':
condition = isinstance(attrValue, State) or \
isinstance(attrValue, Transition)
if condition:
res.append(attrValue)
return res
def getStates(self):
return self._getWorkflowElements('states')
def getTransitions(self):
return self._getWorkflowElements('transitions')
def getStateNames(self, ordered=False):
res = []
attrs = dir(self.klass)
allAttrs = attrs
if ordered:
attrs = self.orderedAttributes
allAttrs = dir(self.klass)
for attrName in attrs:
attrValue = getattr(self.klass, attrName)
if isinstance(attrValue, State):
res.append(attrName)
# Complete the list with inherited states. For the moment, we are unable
# to sort inherited states.
for attrName in allAttrs:
attrValue = getattr(self.klass, attrName)
if isinstance(attrValue, State) and (attrName not in attrs):
res.insert(0, attrName)
return res
def getInitialStateName(self):
res = None
for attrName in dir(self.klass):
attrValue = getattr(self.klass, attrName)
if isinstance(attrValue, State) and attrValue.initial:
res = attrName
break
return res
def getTransitionNamesOf(self, transitionName, transition,
limitToFromState=None):
'''Appy p_transition may correspond to several transitions of the
concrete workflow engine used. This method returns in a list the
name(s) of the "concrete" transition(s) corresponding to
p_transition.'''
res = []
if transition.isSingle():
res.append(transitionName)
else:
for fromState, toState in transition.states:
if not limitToFromState or \
(limitToFromState and (fromState == limitToFromState)):
fromStateName = self.getNameOf(fromState)
toStateName = self.getNameOf(toState)
res.append('%s%s%sTo%s%s' % (transitionName,
fromStateName[0].upper(), fromStateName[1:],
toStateName[0].upper(), toStateName[1:]))
return res
def getTransitionNames(self, limitToTransitions=None, limitToFromState=None,
withLabels=False):
'''Returns the name of all "concrete" transitions corresponding to the
Appy transitions of this worlflow. If p_limitToTransitions is not
None, it represents a list of Appy transitions and the result is a
list of the names of the "concrete" transitions that correspond to
those transitions only. If p_limitToFromState is not None, it
represents an Appy state; only transitions having this state as start
state will be taken into account. If p_withLabels is True, the method
returns a list of tuples (s_transitionName, s_transitionLabel); the
label being the name of the Appy transition.'''
res = []
for attrName in dir(self.klass):
attrValue = getattr(self.klass, attrName)
if isinstance(attrValue, Transition):
# We encountered a transition.
t = attrValue
tName = attrName
if not limitToTransitions or \
(limitToTransitions and t in limitToTransitions):
# We must take this transition into account according to
# param "limitToTransitions".
if (not limitToFromState) or \
(limitToFromState and \
t.hasState(limitToFromState, isFrom=True)):
# We must take this transition into account according
# to param "limitToFromState"
tNames = self.getTransitionNamesOf(
tName, t, limitToFromState)
if not withLabels:
res += tNames
else:
for tn in tNames:
res.append((tn, tName))
return res
def getEndStateName(self, transitionName):
'''Returns the name of the state where the "concrete" transition named
p_transitionName ends.'''
res = None
for attrName in dir(self.klass):
attrValue = getattr(self.klass, attrName)
if isinstance(attrValue, Transition):
# We got a transition.
t = attrValue
tName = attrName
if t.isSingle():
if transitionName == tName:
endState = t.states[1]
res = self.getNameOf(endState)
else:
transNames = self.getTransitionNamesOf(tName, t)
if transitionName in transNames:
endState = t.states[transNames.index(transitionName)][1]
res = self.getNameOf(endState)
return res
def getNameOf(self, stateOrTransition):
'''Gets the Appy name of a p_stateOrTransition.'''
res = None
for attrName in dir(self.klass):
attrValue = getattr(self.klass, attrName)
if attrValue == stateOrTransition:
res = attrName
break
return res
# ------------------------------------------------------------------------------