source: TI12-security/trunk/python/ndg.security.common/ndg/security/common/authz/xacml/__init__.py @ 5162

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.common/ndg/security/common/authz/xacml/__init__.py@5162
Revision 5162, 30.4 KB checked in by pjkersha, 11 years ago (diff)

Moved function condition classes into cond module

Line 
1"""XACML Package
2
3NERC DataGrid Project
4"""
5__author__ = "P J Kershaw"
6__date__ = "13/02/09"
7__copyright__ = "(C) 2009 Science and Technology Facilities Council"
8__contact__ = "Philip.Kershaw@stfc.ac.uk"
9__license__ = "BSD - see LICENSE file in top-level directory"
10__contact__ = "Philip.Kershaw@stfc.ac.uk"
11__revision__ = "$Id$"
12
13import logging
14log = logging.getLogger(__name__)
15
16from ndg.security.common.authz.pdp import PDPInterface
17
18# For parsing: ElementTree helpers
19getNs = lambda elem: elem.tag.split('}')[0][1:]
20getLocalName = lambda elem: elem.tag.rsplit('}',1)[-1]
21
22class XacmlBase(object):
23    pass
24
25class Subject(XacmlBase):
26    '''XACML Subject designator'''
27    def __init__(self, attributes={}):
28        self.attributes = attributes
29
30class Resource(XacmlBase):
31    '''XACML Resource designator'''
32
33class Action(XacmlBase):
34    '''XACML Action designator'''
35
36class Environment(XacmlBase):
37    '''XACML Environment designator'''
38
39class PolicySet(XacmlBase):
40    def __init__(self):
41          self.policies = []
42          self.combiningAlg = None
43         
44class Policy(XacmlBase):
45
46    def __init__(self,
47                 id='',
48                 ruleCombiningAlg=None,
49                 description='',
50                 target=None,
51                 rules=[],
52                 obligations=[]):
53          self.id = id
54          self.description = description
55          self.rules = rules
56          self.ruleCombiningAlg = ruleCombiningAlg
57          self.obligations = obligations
58          self.target = target
59
60    def encode(self):
61        '''Encode the policy'''
62        raise NotImplemented()
63   
64    @classmethod
65    def getInstance(cls, elem):
66       
67        for elem in elem.getchildren():
68            localName = getLocalName(elem)
69            if localName == 'Description':
70                description = elem.text
71               
72            elif localName == 'Target':
73                target = Target.getInstance(elem)
74               
75            elif localName == 'Rule':
76                pass
77           
78        policy = cls(id=root.attrib['PolicyId'], 
79                     ruleCombiningAlg=root.attrib['RuleCombiningAlg'],
80                     description=description,
81                     target=target,
82                     rules=rules,
83                     obligations=obligations)
84        return policy
85
86class MatchResult(XacmlBase):
87    pass
88
89class Target(XacmlBase):
90    '''The target selects policies relevant to a request'''
91
92    def __init__(self, subjects=None, resources=None, actions=None):
93          self.subjects = subjects
94          self.resources = resources
95          self.actions = actions
96          self.rules = []
97
98    def Match(self, evaluationCtx):
99          return MatchResult()
100       
101    @classmethod
102    def getInstance(cls, root):
103        '''Parse a Target from a given XML ElementTree element
104        '''
105        subjects = None
106        resources = None
107        actions = None
108       
109        for elem in root.getchildren():
110            localName = getLocalName(elem)
111
112            if localName == "Subjects":
113                subjects = Target._getAttributes(elem, "Subject")
114               
115            elif name == "Resources":
116                resources = Target._getAttributes(elem, "Resource")
117               
118            elif name == "Actions":
119                actions = Target._getAttributes(elem, "Action")
120       
121        return cls()
122   
123    @staticmethod
124    def _getAttributes(root, prefix):
125        '''Helper method to get Target children elements'''
126        matches = []
127
128        for elem in root.getchildren():
129            localName = getLocalName(elem)
130
131            if localName == prefix:
132                matches += getMatches(elem, prefix)
133            elif name == "Any" + prefix:
134                return None
135
136        return matches
137   
138    @staticmethod
139    def _getMatches(root, prefix):
140
141        list = []
142
143        for elem in root.getchildren():
144            localName = getLocalName(elem)
145
146            if localName == prefix + "Match":
147                list += TargetMatch.getInstance(child, prefix)
148
149        return tuple(list)
150   
151   
152class AttributeDesignator(XacmlBase):
153    ACTION_TARGET, ENVIRONMENT_TARGET, RESOURCE_TARGET, SUBJECT_TARGET=range(4)
154
155    def __init__(self, target, type, id, mustBePresent=False, issuer=None):
156          self.target = target
157          self.type = type
158          self.id = id
159          self.mustBePresent = mustBePresent
160          self.issuer = issuer
161
162    def getInstance(self):
163        pass
164   
165class TargetMatch(XacmlBase):
166    '''Represents the SubjectMatch, ResourceMatch, or ActionMatch XML
167    types in XACML, depending on the value of the type field. This is the
168    part of the Target that actually evaluates whether the specified
169    attribute values in the Target match the corresponding attribute
170    values in the request context.
171    '''
172    types = range(3)
173    SUBJECT, RESOURCE, ACTION = types
174   
175    def __init__(self,
176                 type,
177                 function,
178                 eval,
179                 attributeValue):
180        '''Create a TargetMatch from components.
181         
182        @param type an integer indicating whether this class represents a
183        SubjectMatch, ResourceMatch, or ActionMatch
184        @param function the Function that represents the MatchId
185        @param eval the AttributeDesignator or AttributeSelector to be used to
186        select attributes from the request context
187        @param attrValue the AttributeValue to compare against
188        @raise TypeError if the input type isn't a valid value
189        '''
190        self.type = type
191        self.function = function
192        self.eval = eval
193        self.attrValue = attributeValue
194
195    def _getType(self):
196        return getattr(self, '_type', None)
197   
198    def _setType(self, type):
199        if type not in self.__class__.types:
200            raise TypeError('Type value "%d" not recognised, expecting one of '
201                            '%r types' % (type, self.__class__.types))
202        self._type = type
203       
204    type = property(fget=_getType, fset=_setType, 
205                    doc="the type of match for this target")
206   
207    @classmethod
208    def getInstance(cls, root, prefix, xpathVersion):
209        '''Creates a TargetMatch by parsing a node, using the
210        input prefix to determine whether this is a SubjectMatch,
211        ResourceMatch, or ActionMatch.
212     
213        @param root the node to parse for the TargetMatch
214        @param prefix a String indicating what type of TargetMatch
215        to instantiate (Subject, Resource, or Action)
216        @param xpathVersion the XPath version to use in any selectors, or
217        null if this is unspecified (ie, not supplied in
218        the defaults section of the policy)
219
220        @return a new TargetMatch constructed by parsing
221        '''
222
223        action = ["Subject", "Resource", "Action"].index(prefix)
224        if action not in self.__class__.types:
225            raise TypeError("Unknown TargetMatch type: %s" % prefix)
226
227        # function type
228        funcId = root.attrib["MatchId"]
229        factory = FunctionFactory.getTargetInstance()
230        try:
231            function = factory.createFunction(funcId)
232        except UnknownIdentifierException, e:
233            raise ParsingException("Unknown MatchId: %s" % e)
234       
235        except FunctionTypeException, e:
236            # try to create an abstract function
237            try:
238                function = factory.createAbstractFunction(funcId, root)
239            except Exception, e:
240                raise ParsingException("invalid abstract function: %s" % e)
241           
242        # Get the designator or selector being used, and the attribute
243        # value paired with it
244        for elem in root.getchildren():
245            localName = getLocalName(elem)
246
247            if name == prefix + "AttributeDesignator":
248                eval = AttributeDesignator.getInstance(node, type)
249               
250            elif name == "AttributeSelector":
251                eval = AttributeSelector.getInstance(node)
252               
253            elif name == "AttributeValue":
254                try:
255                    attrValue = attrFactory.createValue(node)
256                except UnknownIdentifierException, e:
257                    raise ParsingException("Unknown Attribute Type: %s" % e)
258
259        # finally, check that the inputs are valid for this function
260        inputs = [attrValue, eval]
261        function.checkInputsNoBag(inputs)
262       
263        return cls(type, function, eval, attributeValue)
264   
265
266    def match(self, context):
267        '''determines whether this <code>TargetMatch</code> matches
268        the input request (whether it is applicable)
269
270        @param context the representation of the request
271
272        @return the result of trying to match the TargetMatch and the request
273        '''
274       
275        result = eval.evaluate(context)
276       
277        if result.indeterminate():
278            # in this case, we don't ask the function for anything, and we
279            # simply return INDETERMINATE
280            return MatchResult(MatchResult.INDETERMINATE, result.getStatus())
281       
282
283        bag = result.getAttributeValue()
284
285        if not bag.isEmpty():
286           
287            # we got back a set of attributes, so we need to iterate through
288            # them, seeing if at least one matches
289            it = bag.iterator()
290            atLeastOneError = False
291            firstIndeterminateStatus = None
292
293            while it.hasNext():
294                inputs = []
295
296                inputs.add(attrValue)
297                inputs.add(it.next())
298
299                # do the evaluation
300                match = evaluateMatch(inputs, context)
301               
302                # we only need one match for this whole thing to match
303                if match.getResult() == MatchResult.MATCH:
304                    return match
305
306                # if it was INDETERMINATE, we want to remember for later
307                if match.getResult() == MatchResult.INDETERMINATE:
308                    atLeastOneError = True
309
310                    # there are no rules about exactly what status data
311                    # should be returned here, so like in the combining
312                    # also, we'll just track the first error
313                    if firstIndeterminateStatus == None:
314                        firstIndeterminateStatus = match.getStatus()
315
316            # if we got here, then nothing matched, so we'll either return
317            # INDETERMINATE or NO_MATCH
318            if atLeastOneError:
319                return MatchResult(MatchResult.INDETERMINATE,
320                                       firstIndeterminateStatus)
321            else:
322                return MatchResult(MatchResult.NO_MATCH)
323
324        else:
325            # this is just an optimization, since the loop above will
326            # actually handle this case, but this is just a little
327            # quicker way to handle an empty bag
328            return MatchResult(MatchResult.NO_MATCH)
329   
330    def evaluateMatch(self, inputs, context):
331        '''Private helper that evaluates an individual match'''
332       
333        # evaluate the function
334        result = function.evaluate(inputs, context)
335
336        # if it was indeterminate, then that's what we return immediately
337        if result.indeterminate():
338            return MatchResult(MatchResult.INDETERMINATE,
339                               result.getStatus())
340
341        # otherwise, we figure out if it was a match
342        bool = result.getAttributeValue()
343
344        if bool.getValue():
345            return MatchResult(MatchResult.MATCH)
346        else:
347            return MatchResult(MatchResult.NO_MATCH)
348
349    def encode(self, output, indenter=None):
350        '''Encodes this TargetMatch</code> into its XML representation
351        and writes this encoding to the given <code>OutputStream</code> with no
352        indentation.
353        @param output a stream into which the XML-encoded data is written'''
354        raise NotImplementedError()
355   
356   
357class Status(XacmlBase):
358    STATUS_MISSING_ATTRIBUTE = \
359          "urn:oasis:names:tc:xacml:1.0:status:missing-attribute"
360    STATUS_OK = "urn:oasis:names:tc:xacml:1.0:status:ok"
361    STATUS_PROCESSING_ERROR = \
362          "urn:oasis:names:tc:xacml:1.0:status:processing-error"
363    STATUS_SYNTAX_ERROR = \
364          "urn:oasis:names:tc:xacml:1.0:status:syntax-error" 
365     
366class EvaluationResult(XacmlBase):
367    def __init__(self, 
368                 attributeValue=None, 
369                 status=None, 
370                 indeterminate=False):
371        self.status = status
372        self.attributeValue = attributeValue
373        self.indeterminate = indeterminate
374
375
376class Evaluatable(XacmlBase):
377    '''Generic interface that is implemented by all objects that can appear in
378    an ApplyType. This lets the evaluation code of Apply and
379    functions iterate through their members and evaluate them, working only
380    on the returned values or errors.'''
381   
382    def evaluate(self, context):
383        '''Evaluates the object using the given context, and either returns an
384        error or a resulting value.
385   
386        @param context the representation of the request
387        @return the result of evaluation'''
388        raise NotImplementedError()
389
390    def getType(self):
391        '''Get the type of this object.  This may be the data type of an
392        Attribute or the return type of an
393        AttributeDesignator, etc.
394   
395        @return the type of data represented by this object'''
396        raise NotImplementedError()
397
398    def evaluatesToBag(self):
399        '''Tells whether evaluation will return a bag or a single value.
400   
401        @return true if evaluation will return a bag, false otherwise'''
402        raise NotImplementedError()
403
404    def getChildren(self):
405        '''Returns all children, in order, of this element in the Condition
406        tree, or en empty set if this element has no children. In XACML 1.x,
407        only the ApplyType ever has children.
408   
409        @return a list of Evaluatables'''
410        raise NotImplementedError()
411
412    def encode(self, output, indenter=None):
413        '''Encodes this Evaluatable into its XML representation and
414        writes this encoding to the given OutputStream with
415        indentation.
416   
417        @param output a stream into which the XML-encoded data is written
418        @param indenter an object that creates indentation strings'''
419        raise NotImplementedError()
420
421                   
422class Effect(XacmlBase):
423    def __str__(self):
424        raise NotImplementedError()
425         
426class DenyEffect(Effect):
427    def __str__(self):
428        return 'deny'
429         
430class PermitEffect(Effect):
431    def __str__(self):
432        return 'permit'
433
434class Rule(XacmlBase):
435    '''Consists of a condition, an effect, and a target.
436    '''
437    def __init__(self, conditions=[], effect=DenyEffect(), target=None):
438        # Conditions are statements about attributes that upon evaluation
439        # return either True, False, or Indeterminate.
440        self.conditions = conditions
441       
442        # Effect is the intended consequence of the satisfied rule. It can
443        # either take the value Permit or Deny.
444        self.effect = effect
445     
446        # Target, as in the case of a policy, helps in determining whether or
447        # not a rule is relevant for a request. The mechanism for achieving
448        # this is also similar to how it is done in the case of a target for a
449        # policy.
450        self.target = target
451       
452    @classmethod
453    def getInstance(cls, elem):
454        root = elem.getroot()
455        return Rule(conditions=conditions, effect=effect, target=target)
456         
457class Attribute(XacmlBase):
458    def __init__(self, id, type=None, issuer=None, issueInstant=None, value=None):
459        self.id = id
460        self.type = type or value.__class__
461        self.issuer = issuer
462        self.issueInstant = issueInstant
463        self.value = value
464
465       
466class Request(XacmlBase):
467    '''XACML Request XacmlBase
468   
469    TODO: refactor from this initial placeholder'''
470    def __init__(self, subject, resource, action=None, environment={}):
471          self.subject = subject
472          self.resource = resource
473          self.action = action
474          self.environment = environment
475
476class Response(XacmlBase):
477    pass
478
479
480class PDP(XacmlBase):
481    '''Modify PDPInterface to use the four XACML request designators: subject,
482    resource, action and environment
483   
484    This is an initial iteration toward a complete XACML implementation'''
485    def __init__(self, *arg, **kw):
486          pass
487   
488    def evaluate(self, request):
489          '''Make access control decision - override this in a derived class to
490          implement the decision logic but this method may be called within
491          the derived method to check input types
492         
493          @param request: request object containing the subject, resource,
494          action and environment
495          @type request: ndg.security.common.authz.xacml.Request
496          @return reponse object
497          @rtype: ndg.security.common.authz.xacml.Response
498          '''
499          raise NotImplementedError()
500
501
502class RuleCombiningAlg(XacmlBase):
503    id = None
504
505class DenyOverrides(RuleCombiningAlg):
506   '''Deny-overrides: If any rule evaluates to Deny, then the final
507   authorization decision is also Deny.'''
508   id = 'Deny-overrides'
509   
510class OrderedDenyOverrides(RuleCombiningAlg):
511    '''Ordered-deny-overrides: Same as deny-overrides, except the order in
512    which relevant rules are evaluated is the same as the order in which they
513    are added in the policy.'''
514    id = 'Ordered-deny-overrides'
515   
516class PermitOverrides(RuleCombiningAlg):
517    '''Permit-overrides: If any rule evaluates to Permit, then the final
518    authorization decision is also Permit.'''
519   
520class OrderedPermitOverrides(RuleCombiningAlg):
521    '''Ordered-permit-overrides: Same as permit-overrides, except the order in
522    which relevant rules are evaluated is the same as the order in which they
523    are added in the policy.'''
524    id = 'Ordered-permit-overrides'
525   
526class FirstApplicable(RuleCombiningAlg):
527    '''First-applicable: The result of the first relevant rule encountered is
528    the final authorization decision as well.'''
529    id = 'First-applicable'
530
531
532class EvaluationCtx(object):
533
534    # The standard URI for listing a resource's id
535    RESOURCE_ID ="urn:oasis:names:tc:xacml:1.0:resource:resource-id"
536
537    # The standard URI for listing a resource's scope
538    RESOURCE_SCOPE = "urn:oasis:names:tc:xacml:1.0:resource:scope"
539
540    # Resource scope of Immediate (only the given resource)
541    SCOPE_IMMEDIATE = 0
542
543    # Resource scope of Children (the given resource and its direct
544    # children)
545    SCOPE_CHILDREN = 1
546
547    # Resource scope of Descendants (the given resource and all descendants
548    # at any depth or distance)
549    SCOPE_DESCENDANTS = 2
550   
551    def getRequestRoot(self):
552        '''Returns the DOM root of the original RequestType XML document, if
553        this context is backed by an XACML Request. If this context is not
554        backed by an XML representation, then an exception is thrown.'''
555        raise NotImplementedError()
556
557    def getResourceId(self):
558        '''Returns the identifier for the resource being requested.'''
559        raise NotImplementedError()
560
561    def getScope(self):
562        '''Returns the resource scope, which will be one of the three fields
563        denoting Immediate, Children, or Descendants.'''
564        raise NotImplementedError()
565
566    def setResourceId(self, resourceId):
567        '''Changes the value of the resource-id attribute in this context. This
568        is useful when you have multiple resources (ie, a scope other than
569        IMMEDIATE), and you need to keep changing only the resource-id to
570        evaluate the different effective requests.'''
571        raise NotImplementedError()
572
573    def getCurrentTime(self):
574        '''Returns the cached value for the current time. If the value has
575        never been set by a call to <code>setCurrentTime</code>, or if caching
576        is not enabled in this instance, then this will return null.'''
577        raise NotImplementedError()
578
579    def setCurrentTime(self, currentTime):
580        '''Sets the current time for this evaluation. If caching is not enabled
581        for this instance then the value is ignored.
582     
583        @param currentTime the dynamically resolved current time'''
584        raise NotImplementedError()
585
586    def getCurrentDate(self):
587        '''Returns the cached value for the current date. If the value has
588        never been set by a call to <code>setCurrentDate</code>, or if caching
589        is not enabled in this instance, then this will return null.'''
590        raise NotImplementedError()
591
592    def setCurrentDate(self, currentDate):
593        '''Sets the current date for this evaluation. If caching is not enabled
594        for this instance then the value is ignored.'''
595        raise NotImplementedError()
596
597    def getCurrentDateTime(self):
598        '''Returns the cached value for the current dateTime. If the value has
599        never been set by a call to <code>setCurrentDateTime</code>, or if
600        caching is not enabled in this instance, then this will return null.
601        '''
602        raise NotImplementedError()
603
604    def setCurrentDateTime(self, currentDateTime):
605        '''Sets the current dateTime for this evaluation. If caching is not
606        enabled for this instance then the value is ignored.
607     
608        @param currentDateTime the dynamically resolved current dateTime'''
609        raise NotImplementedError()
610
611    def getSubjectAttribute(self, type, id, category):
612        '''Returns available subject attribute value(s) ignoring the issuer.
613     
614        @param type the type of the attribute value(s) to find
615        @param id the id of the attribute value(s) to find
616        @param category the category the attribute value(s) must be in
617     
618        @return a result containing a bag either empty because no values were
619        found or containing at least one value, or status associated with an
620        Indeterminate result'''
621        raise NotImplementedError()
622
623    def getSubjectAttribute(self, type, id, issuer, category):
624        '''Returns available subject attribute value(s).
625     
626        @param type the type of the attribute value(s) to find
627        @param id the id of the attribute value(s) to find
628        @param issuer the issuer of the attribute value(s) to find or null
629        @param category the category the attribute value(s) must be in
630     
631        @return a result containing a bag either empty because no values were
632        found or containing at least one value, or status associated with an
633        Indeterminate result'''
634        raise NotImplementedError()
635   
636    def getResourceAttribute(self, type, id, issuer):
637        '''Returns available resource attribute value(s).
638     
639        @param type the type of the attribute value(s) to find
640        @param id the id of the attribute value(s) to find
641        @param issuer the issuer of the attribute value(s) to find or null
642     
643        @return a result containing a bag either empty because no values were
644        found or containing at least one value, or status associated with an
645        Indeterminate result'''
646        raise NotImplementedError()
647
648    def getActionAttribute(self, type, id, issuer):
649        '''Returns available action attribute value(s).
650     
651        @param type the type of the attribute value(s) to find
652        @param id the id of the attribute value(s) to find
653        @param issuer the issuer of the attribute value(s) to find or null
654     
655        @return a result containing a bag either empty because no values were
656        found or containing at least one value, or status associated with an
657        Indeterminate result'''
658        raise NotImplementedError()
659
660    def getEnvironmentAttribute(self, type, id, issuer):
661        '''Returns available environment attribute value(s).
662     
663        @param type the type of the attribute value(s) to find
664        @param id the id of the attribute value(s) to find
665        @param issuer the issuer of the attribute value(s) to find or null
666     
667        @return a result containing a bag either empty because no values were
668        found or containing at least one value, or status associated with an
669        Indeterminate result'''
670        raise NotImplementedError()
671
672    def getAttribute(self, contextPath, namespaceNode, type, xpathVersion):
673        '''Returns the attribute value(s) retrieved using the given XPath
674        expression.
675     
676        @param contextPath the XPath expression to search
677        @param namespaceNode the DOM node defining namespace mappings to use,
678                            or null if mappings come from the context root
679        @param type the type of the attribute value(s) to find
680        @param xpathVersion the version of XPath to use
681     
682        @return a result containing a bag either empty because no values were
683       
684        found or containing at least one value, or status associated with an
685        Indeterminate result'''
686        raise NotImplementedError()
687
688
689class Apply(Evaluatable):
690    '''Represents the XACML ApplyType and ConditionType XML types.'''
691
692    def __init__(self, function, evals, bagFunction=None, isCondition=False):
693        '''Constructs an Apply object. Throws an
694        IllegalArgumentException if the given parameter list
695        isn't valid for the given function.
696       
697        @param function the Function to use in evaluating the elements in the
698        apply
699        @param evals the contents of the apply which will be the parameters
700        to the function, each of which is an Evaluatable
701        @param bagFunction the higher-order function to use
702        @param isCondition Rrue if this Apply is a Condition, False otherwise
703        '''
704   
705        # check that the given inputs work for the function
706        inputs = evals
707        if bagFunction is not None:
708            inputs = [bagFunction]
709            inputs += evals
710       
711        function.checkInputs(inputs)
712
713        # if everything checks out, then store the inputs
714        self.function = function
715        self.evals = tuple(evals)
716        self.bagFunction = bagFunction
717        self.isCondition = isCondition
718   
719   
720    @staticmethod
721    def getConditionInstance(root, xpathVersion):
722        '''Returns an instance of an Apply based on the given DOM
723        root node. This will actually return a special kind of
724        Apply, namely an XML ConditionType, which is the root
725        of the condition logic in a RuleType. A ConditionType is the same
726        as an ApplyType except that it must use a FunctionId that returns
727        a boolean value.
728       
729        @param root the DOM root of a ConditionType XML type
730        @param xpathVersion the XPath version to use in any selectors or XPath
731                            functions, or null if this is unspecified (ie, not
732                            supplied in the defaults section of the policy)
733       
734        '''
735        raise NotImplementedError()
736         
737    def getInstance(self, 
738                    root, 
739                    factory=None, 
740                    isCondition=False, 
741                    xpathVersion=None):
742        '''Returns an instance of Apply based on the given DOM root.
743       
744        @param root the DOM root of an ApplyType XML type
745        @param xpathVersion the XPath version to use in any selectors or XPath
746                            functions, or null if this is unspecified (ie, not
747                            supplied in the defaults section of the policy)'''
748       
749        raise NotImplementedError()
750   
751    @staticmethod
752    def getFunction(root, version, factory):
753        '''Helper method that tries to get a function instance'''
754        raise NotImplementedError()
755           
756    def getFunction(self):
757        '''Returns the Function used by this Apply.
758       
759        @return the Function'''
760        return function
761   
762    def getChildren(self):
763        '''Returns the List of children for this Apply.
764        The List contains Evaluatables. The list is
765        unmodifiable, and may be empty.
766       
767        @return a List of Evaluatables'''
768        return self.evals
769   
770    def getHigherOrderFunction(self):
771        '''Returns the higher order bag function used by this Apply
772        if it exists, or null if no higher order function is used.
773       
774        @return the higher order Function or null'''
775        return self.bagFunction
776   
777    def isCondition(self):
778        '''Returns whether or not this ApplyType is actually a ConditionType.
779       
780        @return whether or not this represents a ConditionType'''
781        return isCondition
782
783    def evaluate(self, context):
784        '''Evaluates the apply object using the given function. This will in
785        turn call evaluate on all the given parameters, some of which may be
786        other Apply objects.
787       
788        @param context the representation of the request
789       
790        @return the result of trying to evaluate this apply object'''
791        parameters = self.evals
792
793        # see if there is a higher-order function in here
794        if bagFunction != None:
795            # this is a special case, so we setup the parameters, starting
796            # with the function
797            parameters = [bagFunction]
798
799            # now we evaluate all the parameters, returning INDETERMINATE
800            # if that's what any of them return, and otherwise tracking
801            # all the AttributeValues that get returned
802            for eval in self.evals:
803                result = eval.evaluate(context)
804               
805                # in a higher-order case, if anything is INDETERMINATE, then
806                # we stop right away
807                if result.indeterminate():
808                    return result
809
810                parameters.add(result.getAttributeValue())
811           
812        # now we can call the base function
813        return function.evaluate(parameters, context)
814         
815    def getType(self):
816        '''Returns the type of attribute that this object will return on a call
817        to evaluate. In practice, this will always be the same as
818        the result of calling getReturnType on the function used
819        by this object.
820       
821        @return the type returned by evaluate'''
822        return self.function.getReturnType()
823     
824    def evaluatesToBag(self):
825        '''Returns whether or not the Function will return a bag
826        of values on evaluation.
827       
828        @return true if evaluation will return a bag of values, false otherwise
829        '''
830        return self.function.returnsBag()
831
832    def encode(self, output, indenter):
833        '''Encodes this Apply into its XML representation and
834        writes this encoding to the given OutputStream with
835        indentation.
836       
837        @param output a stream into which the XML-encoded data is written
838        @param indenter an object that creates indentation strings'''
839        raise NotImplementedError()
Note: See TracBrowser for help on using the repository browser.