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

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

XACML implementation: fixes to Apply class added separate exceptions module

Line 
1"""XACML cond module contains condition function classes
2
3NERC DataGrid Project
4
5This code is adapted from the Sun Java XACML implementation ...
6
7Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
8
9Redistribution and use in source and binary forms, with or without
10modification, are permitted provided that the following conditions are met:
11
12  1. Redistribution of source code must retain the above copyright notice,
13     this list of conditions and the following disclaimer.
14
15  2. Redistribution in binary form must reproduce the above copyright
16     notice, this list of conditions and the following disclaimer in the
17     documentation and/or other materials provided with the distribution.
18
19Neither the name of Sun Microsystems, Inc. or the names of contributors may
20be used to endorse or promote products derived from this software without
21specific prior written permission.
22
23This software is provided "AS IS," without a warranty of any kind. ALL
24EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
25ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
26OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
27AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
28AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
29DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
30REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
31INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
32OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
33EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34
35You acknowledge that this software is not designed or intended for use in
36the design, construction, operation or maintenance of any nuclear facility.
37"""
38__author__ = "P J Kershaw"
39__date__ = "02/04/09"
40__copyright__ = "(C) 2009 Science and Technology Facilities Council"
41__contact__ = "Philip.Kershaw@stfc.ac.uk"
42__license__ = "BSD - see LICENSE file in top-level directory"
43__contact__ = "Philip.Kershaw@stfc.ac.uk"
44__revision__ = "$Id$"
45import logging
46log = logging.getLogger(__name__)
47
48from ndg.security.common.utils import getLocalName
49from ndg.security.common.authz.xacml.exceptions import \
50    UnknownIdentifierException, ParsingException
51from ndg.security.common.authz.xacml.cond.eval import Evaluatable
52from ndg.security.common.authz.xacml.attr import AnyURIAttribute, \
53    Base64BinaryAttribute, BooleanAttribute, DateAttribute, DateTimeAttribute,\
54    DayTimeDurationAttribute, DoubleAttribute, HexBinaryAttribute, \
55    IntegerAttribute, RFC822NameAttribute, StringAttribute, TimeAttribute, \
56    X500NameAttribute, YearMonthDurationAttribute, AttributeFactory, \
57    AttributeDesignator
58
59
60class Apply(Evaluatable):
61    '''Represents the XACML ApplyType and ConditionType XML types.'''
62
63    def __init__(self, function, evals, bagFunction=None, isCondition=False):
64        '''Constructs an Apply object. Throws an
65        IllegalArgumentException if the given parameter list
66        isn't valid for the given function.
67       
68        @param function the Function to use in evaluating the elements in the
69        apply
70        @param evals the contents of the apply which will be the parameters
71        to the function, each of which is an Evaluatable
72        @param bagFunction the higher-order function to use
73        @param isCondition Rrue if this Apply is a Condition, False otherwise
74        '''
75   
76        # check that the given inputs work for the function
77        inputs = evals
78        if bagFunction is not None:
79            inputs = [bagFunction]
80            inputs += evals
81       
82        function.checkInputs(inputs)
83
84        # if everything checks out, then store the inputs
85        self._function = function
86        self._evals = tuple(evals)
87        self.bagFunction = bagFunction
88        self.isCondition = isCondition
89   
90   
91    @classmethod
92    def getConditionInstance(cls, root):
93        '''Returns an instance of an Apply based on the given DOM
94        root node. This will actually return a special kind of
95        Apply, namely an XML ConditionType, which is the root
96        of the condition logic in a RuleType. A ConditionType is the same
97        as an ApplyType except that it must use a FunctionId that returns
98        a boolean value.
99       
100        @param root the DOM root of a ConditionType XML type
101        '''
102        from ndg.security.common.authz.xacml.cond.factory import \
103            FunctionFactory
104        cls.__getInstance(root, FunctionFactory.getConditionInstance(), True)
105   
106   
107    @classmethod
108    def getInstance(cls, root):
109        '''Returns an instance of Apply based on the given root.
110         
111        @param root: the ElementTree.Element root of a ConditionType XML type
112        @raise ParsingException: if this is not a valid ApplyType
113        '''
114        from ndg.security.common.authz.xacml.cond.factory import \
115            FunctionFactory
116        cls.__getInstance(root, FunctionFactory.getGeneralInstance(), True)
117         
118       
119    @classmethod
120    def __getInstance(cls, root, factory, isCondition):
121        '''This is a helper method that is called by the two getInstance
122        methods. It takes a factory so we know that we're getting the right
123        kind of function.'''
124     
125        function = cls.__getFunction(root, factory)
126        bagFunction = None
127        evals = []
128       
129        attrFactory = AttributeFactory.getInstance()
130
131        for elem in root: 
132            name = getLocalName(elem)
133
134            if name == "Apply":
135                evals.append(Apply.getInstance(elem))
136               
137            elif name == "AttributeValue":
138                try: 
139                    evals.append(attrFactory.createValue(elem))
140                   
141                except UnknownIdentifierException, e:
142                    raise ParsingException("Unknown DataType: %s" % e)
143               
144            elif name == "SubjectAttributeDesignator":
145                evals.append(AttributeDesignator.getInstance(elem,
146                                      AttributeDesignator.SUBJECT_TARGET))
147               
148            elif name =="ResourceAttributeDesignator":
149                evals.append(AttributeDesignator.getInstance(elem,
150                                      AttributeDesignator.RESOURCE_TARGET))
151               
152            elif name == "ActionAttributeDesignator": 
153                evals.append(AttributeDesignator.getInstance(elem,
154                                      AttributeDesignator.ACTION_TARGET))
155               
156            elif name == "EnvironmentAttributeDesignator":
157                evals.append(AttributeDesignator.getInstance(elem,
158                                      AttributeDesignator.ENVIRONMENT_TARGET))
159               
160            elif name == "AttributeSelector":
161                evals.append(AttributeSelector.getInstance(elem))
162               
163            elif name == "Function": 
164                # while the schema doesn't enforce this, it's illegal to
165                # have more than one FunctionType in a given ApplyType
166                if bagFunction != None:
167                    raise ParsingException("Too many FunctionTypes")
168
169                from ndg.security.common.authz.xacml.cond.factory import \
170                    FunctionFactory
171                bagFunction = cls.__getFunction(elem, 
172                                        FunctionFactory.getGeneralInstance())
173           
174        return Apply(function, evals, bagFunction, isCondition)
175
176
177    @classmethod
178    def __getFunction(cls, root, factory):
179        '''Helper method that tries to get a function instance'''
180
181        functionName = root.attrib["FunctionId"]
182        try:
183            # try to get an instance of the given function
184            return factory.createFunction(functionName)
185       
186        except UnknownIdentifierException, e:
187            raise ParsingException("Unknown FunctionId in Apply: %s" % e)
188       
189        except FunctionTypeException, e:
190            # try creating as an abstract function, using a general factory
191            try:
192                from ndg.security.common.authz.xacml.cond.factory import \
193                    FunctionFactory
194                functionFactory = FunctionFactory.getGeneralInstance()
195                return functionFactory.createAbstractFunction(functionName, 
196                                                              root)
197            except Exception, e:
198                # any exception at this point is a failure
199                raise ParsingException("failed to create abstract function %s "
200                                       ": %s" % (functionName, e)) 
201           
202    def getFunction(self):
203        '''Returns the Function used by this Apply.
204       
205        @return the Function'''
206        return self._function
207   
208    def getChildren(self):
209        '''Returns the List of children for this Apply.
210        The List contains Evaluatables. The list is
211        unmodifiable, and may be empty.
212       
213        @return a List of Evaluatables'''
214        return self._evals
215   
216    def getHigherOrderFunction(self):
217        '''Returns the higher order bag function used by this Apply
218        if it exists, or null if no higher order function is used.
219       
220        @return the higher order Function or null'''
221        return self.bagFunction
222   
223    def isCondition(self):
224        '''Returns whether or not this ApplyType is actually a ConditionType.
225       
226        @return whether or not this represents a ConditionType'''
227        return isCondition
228
229    def evaluate(self, context):
230        '''Evaluates the apply object using the given function. This will in
231        turn call evaluate on all the given parameters, some of which may be
232        other Apply objects.
233       
234        @param context the representation of the request
235       
236        @return the result of trying to evaluate this apply object'''
237        parameters = self.evals
238
239        # see if there is a higher-order function in here
240        if bagFunction != None:
241            # this is a special case, so we setup the parameters, starting
242            # with the function
243            parameters = [bagFunction]
244
245            # now we evaluate all the parameters, returning INDETERMINATE
246            # if that's what any of them return, and otherwise tracking
247            # all the AttributeValues that get returned
248            for eval in self.evals:
249                result = eval.evaluate(context)
250               
251                # in a higher-order case, if anything is INDETERMINATE, then
252                # we stop right away
253                if result.indeterminate():
254                    return result
255
256                parameters.add(result.getAttributeValue())
257           
258        # now we can call the base function
259        return function.evaluate(parameters, context)
260         
261    def getType(self):
262        '''Returns the type of attribute that this object will return on a call
263        to evaluate. In practice, this will always be the same as
264        the result of calling getReturnType on the function used
265        by this object.
266       
267        @return the type returned by evaluate'''
268        return self.function.getReturnType()
269     
270    def evaluatesToBag(self):
271        '''Returns whether or not the Function will return a bag
272        of values on evaluation.
273       
274        @return true if evaluation will return a bag of values, false otherwise
275        '''
276        return self.function.returnsBag()
277
278    def encode(self, output, indenter):
279        '''Encodes this Apply into its XML representation and
280        writes this encoding to the given OutputStream with
281        indentation.
282       
283        @param output a stream into which the XML-encoded data is written
284        @param indenter an object that creates indentation strings'''
285        raise NotImplementedError()
286       
287class Function(object):
288    '''Interface that all functions in the system must implement.'''
289 
290    def evaluate(self, inputs, context):
291        '''Evaluates the Function using the given inputs.
292        The List contains Evaluatables which are all
293        of the correct type if the Function has been created as
294        part of an Apply or TargetMatch, but which
295        may otherwise be invalid. Each parameter should be evaluated by the
296        Function, unless this is a higher-order function (in
297        which case the Apply has already evaluated the inputs
298        to check for any INDETERMINATE conditions), or the Function
299        doesn't need to evaluate all inputs to determine a result (as in the
300        case of the or function). The order of the List is
301        significant, so a Function should have a very good reason
302        if it wants to evaluate the inputs in a different order.
303        <p>
304        Note that if this is a higher-order function, like any-of, then
305        the first argument in the List will actually be a Function
306        object representing the function to apply to some bag. In this case,
307        the second and any subsequent entries in the list are
308        AttributeValue objects (no INDETERMINATE values are
309        allowed, so the function is not given the option of dealing with
310        attributes that cannot be resolved). A function needs to know if it's
311        a higher-order function, and therefore whether or not to look for
312        this case. Also, a higher-order function is responsible for checking
313        that the inputs that it will pass to the Function
314        provided as the first parameter are valid, ie. it must do a
315        checkInputs on its sub-function when
316        checkInputs is called on the higher-order function.
317       
318        @param inputs the List of inputs for the function
319        @param context the representation of the request
320       
321        @return a result containing the AttributeValue computed
322                when evaluating the function, or Status
323                specifying some error condition'''
324        raise NotImplementedError()
325
326
327    def getIdentifier(self):
328        '''Returns the identifier of this function as known by the factories.
329        In the case of the standard XACML functions, this will be one of the
330        URIs defined in the standard namespace. This function must always
331        return the complete namespace and identifier of this function.
332       
333        @return the function's identifier'''
334        raise NotImplementedError()
335
336    def getReturnType(self):
337        '''Provides the type of AttributeValue that this function
338        returns from evaluate in a successful evaluation.
339       
340        @return the type returned by this function
341        '''
342        raise NotImplementedError()
343 
344    def returnsBag(self):
345        '''Tells whether this function will return a bag of values or just a
346        single value.
347       
348        @return true if evaluation will return a bag, false otherwise'''
349        raise NotImplementedError()
350
351    def checkInputs(self, inputs):
352        '''Checks that the given inputs are of the right types, in the right
353        order, and are the right number for this function to evaluate. If
354        the function cannot accept the inputs for evaluation, an
355        IllegalArgumentException is thrown.
356       
357        @param inputs a list of Evaluatables, with the first argument being a
358        Function if this is a higher-order function
359       
360        @throws TypeError if the inputs do match what the function accepts for
361        evaluation
362        '''
363        raise NotImplementedError()
364
365    def checkInputsNoBag(self, inputs):
366        '''Checks that the given inputs are of the right types, in the right
367        order, and are the right number for this function to evaluate. If
368        the function cannot accept the inputs for evaluation, an
369        IllegalArgumentException is thrown. Unlike the other
370        checkInput method in this interface, this assumes that
371        the parameters will never provide bags of values. This is useful if
372        you're considering a target function which has a designator or
373        selector in its input list, but which passes the values from the
374        derived bags one at a time to the function, so the function doesn't
375        have to deal with the bags that the selector or designator
376        generates.
377       
378        @param inputs a list of Evaluatables, with the first argument being a
379        Function if this is a higher-order function
380       
381        @throws TypeError if the inputs do match what the function accepts for
382        evaluation'''
383        raise NotImplementedError()
384
385
386class FunctionBase(Function):
387    FUNCTION_NS = "urn:oasis:names:tc:xacml:1.0:function:"
388    supportedIdentifiers = ()
389   
390    def __init__(self, 
391                 functionName, 
392                 functionId=None, 
393                 paramType=None,
394                 paramIsBag=False,
395                 numParams=-1, 
396                 minParams=0,
397                 returnType='', 
398                 returnsBag=False):
399        '''
400        @param functionName: the name of this function as used by the factory
401                            and any XACML policies
402        @param functionId: an optional identifier that can be used by your
403                          code for convenience
404        @param paramType: the type of each parameter, in order, required by
405                          this function, as used by the factory and any XACML
406                           documents
407        @param paramIsBag: whether or not each parameter is actually a bag
408                          of values
409        @param numParams: the number of parameters required by this function,
410        or -1 if any number are allowed
411        @param minParams: the minimum number of parameters required if
412        numParams is -1
413        @param returnType: the type returned by this function, as used by
414                          the factory and any XACML documents
415        @param returnsBag: whether or not this function returns a bag of values
416        '''         
417        self.functionName = functionName
418        self.functionId = functionId
419        self.returnType = returnType
420        self.returnsBag = returnsBag
421   
422        self.paramType = paramType
423               
424        if isinstance(self.paramType, (list, tuple)):
425            if not self.paramType:
426                raise TypeError('"paramType" is set to an empty list or tuple')
427            self.singleType = False
428           
429            # Keep this test within the paramType is-a-list if-block otherwise
430            # it may fail checking the length of a bool
431            if len(paramIsBag) != len(self.paramType):
432                raise TypeError('"paramIsBag" and "paramType" inputs must '
433                                'have the same length')
434        else:
435            self.singleType = True
436           
437            # These only apply if the input parameters are all of a single type
438            self.numParams = numParams
439            self.minParams = minParams
440           
441        self.paramIsBag = paramIsBag
442       
443 
444    def _setFunctionName(self, functionName):
445          if functionName not in self.__class__.supportedIdentifiers:
446              functionList = ', '.join(self.__class__.supportedIdentifiers)
447              raise TypeError("Function name [%s] is not on of the recognised "
448                              "types: %s" % (functionName, functionList))
449          self._functionName = functionName
450         
451    def _getFunctionName(self):
452          return getattr(self, '_functionName', None)
453   
454    functionName = property(fset=_setFunctionName,
455                                    fget=_getFunctionName)
456         
457    def checkInputs(self, inputs):
458        '''Checks that the given inputs are of the right types, in the right
459        order, and are the right number for this function to evaluate.'''
460        raise NotImplementedError()
461           
462    def checkInputsNoBag(self, inputs):
463        '''Default handling of input checking. This does some simple checking
464        based on the type of constructor used. If you need anything more
465        complex, or if you used the simple constructor, then you must
466        override this method.
467   
468        @param inputs: a list of Evaluatable instances
469       
470        @raise TypeError: if the inputs won't work
471        '''
472        numInputs = len(inputs)
473       
474        if self.singleType:
475            # first check to see if we need bags
476            if sum(self.paramIsBag):
477                raise TypeError('"%s" needs bags on input' % self.functionName)
478
479            # now check on the length
480            if self.numParams != -1: 
481                if numInputs != self.numParams:
482                    raise TypeError('wrong number of args to "%s"' % 
483                                    self.functionName)
484            else: 
485                if numInputs < self.minParams:
486                    raise TypeError("not enough args to " % self.functionName)
487           
488
489            # finally check param list
490            for eval in inputs: 
491                if eval.getType().toString() != self.paramType:
492                    raise TypeError("Illegal parameter: input type is %s but "
493                                    "%s type is %s" % 
494                                    (eval.getType().toString(),
495                                     self.__class__.__name__,
496                                     self.paramType))
497           
498        else: 
499            # first, check the length of the inputs
500            if len(self.paramType) != numInputs:
501                raise TypeError('Wrong number of args to "%s"' % 
502                                self.functionName)
503
504            # Ensure everything is of the same, correct type
505            it = zip(inputs, self.paramType, self.paramIsBag)
506            for eval, paramType, paramIsBag in it:
507                if eval.type != paramType or paramIsBag:
508                    raise TypeError("Illegal parameter: input type is %s but "
509                                    "%s type is %s" % 
510                                    (eval.type,
511                                     self.__class__.__name__,
512                                     paramType))
513
514 
515    def evaluate(self, inputs, context):
516        '''Evaluates the Function using the given inputs.'''
517        raise NotImplementedError()
518     
519    def evalArgs(self, params, context, args):
520        '''Evaluates each of the parameters, in order, filling in the argument
521        array with the resulting values. If any error occurs, this method
522        returns the error, otherwise null is returned, signalling that
523        evaluation was successful for all inputs, and the resulting argument
524        list can be used.
525       
526        @param params a list of Evaluatable objects representing the parameters
527        to evaluate
528        @param context the representation of the request
529        @param args an array as long as the params list that will, on return,
530        contain the AttributeValues generated from evaluating all parameters
531
532        @return None if no errors were encountered, otherwise
533        an EvaluationResult representing the error
534        '''
535        index = 0
536
537        for eval in params:
538            # get and evaluate the next parameter
539            result = eval.evaluate(context)
540
541            # If there was an error, pass it back...
542            if result.indeterminate():
543                return result
544
545            # ...otherwise save it and keep going
546            args[index] = result.getAttributeValue()
547            index += 1
548           
549        return None
550
551# TODO: Condition classes - minimal implementation until opportunity to fully
552# implement   
553class BagFunction(FunctionBase):
554    def __init__(self, *arg, **kw):
555        raise NotImplementedError()
556
557class SetFunction(FunctionBase):
558    def __init__(self, *arg, **kw):
559        raise NotImplementedError()
560       
561class ConditionBagFunction(BagFunction):
562    def __init__(self, *arg, **kw):
563        raise NotImplementedError()
564       
565class ConditionSetFunction(FunctionBase):
566    def __init__(self, *arg, **kw):
567        raise NotImplementedError()
568       
569class HigherOrderFunction(Function):
570    supportedIdentifiers = ()
571    def __init__(self, *arg, **kw):
572        raise NotImplementedError()
573
574# TODO: Function classes - minimal implementation until opportunity to fully
575# implement                                   
576class LogicalFunction(FunctionBase):
577
578    def __init__(self, *arg, **kw):
579        raise NotImplementedError()
580
581class NOfFunction(FunctionBase):
582   
583    def __init__(self, *arg, **kw):
584        raise NotImplementedError()
585       
586class NotFunction(FunctionBase):
587   
588    def __init__(self, *arg, **kw):
589        raise NotImplementedError()
590       
591class ComparisonFunction(FunctionBase):
592   
593    def __init__(self, *arg, **kw):
594        raise NotImplementedError()
595
596class MatchFunction(FunctionBase):
597    NAME_REGEXP_STRING_MATCH = FunctionBase.FUNCTION_NS + "regexp-string-match"
598    NAME_RFC822NAME_MATCH = FunctionBase.FUNCTION_NS + "rfc822Name-match"
599    NAME_X500NAME_MATCH = FunctionBase.FUNCTION_NS + "x500Name-match"     
600   
601    supportedIdentifiers = (
602        NAME_REGEXP_STRING_MATCH, 
603        NAME_RFC822NAME_MATCH,
604        NAME_X500NAME_MATCH)
605   
606    functionIds = range(3)
607    ID_REGEXP_STRING_MATCH, ID_X500NAME_MATCH, ID_RFC822NAME_MATCH=functionIds
608    getId = dict(zip(supportedIdentifiers, functionIds))
609    argParams = (
610        (StringAttribute.identifier,)*2,
611        (X500NameAttribute.identifier,)*2,
612        (StringAttribute.identifier,
613         RFC822NameAttribute.identifier)
614    )
615    getArgumentTypes = dict(zip(supportedIdentifiers, argParams))
616   
617    bagParams = (False, False)
618   
619    lut = {
620          NAME_REGEXP_STRING_MATCH: 'regexpStringMatch',
621          NAME_RFC822NAME_MATCH:    'rfc822NameMatch',
622          NAME_X500NAME_MATCH:      'x500NameMatch'
623    }
624   
625    def __init__(self, functionName, **kw):
626          super(MatchFunction, self).__init__(functionName, 
627                     functionId=MatchFunction.getId[functionName], 
628                     paramType=MatchFunction.getArgumentTypes[functionName],
629                     paramIsBag=MatchFunction.bagParams,
630                     returnType=BooleanAttribute.identifier, 
631                     returnsBag=False)
632
633
634    def regexpStringMatch(self, regex, val):
635          return re.match(regex, val) is not None
636   
637    def rfc822NameMatch(self, *inputs):
638        raise NotImplementedError()
639   
640    def x500NameMatch(self, *inputs):
641        raise NotImplementedError()
642   
643    def evaluate(self, inputs, context):
644          matchFunction = getattr(self, MatchFunction.lut[self.functionName])
645          match = matchFunction(self, *inputs)
646          if match:
647                return EvaluationResult(status=Status.STATUS_OK)
648
649
650class EqualFunction(FunctionBase):
651    supportedIdentifiers = (
652          FunctionBase.FUNCTION_NS + "anyURI-equal",
653          FunctionBase.FUNCTION_NS + "base64Binary-equal",
654          FunctionBase.FUNCTION_NS + "boolean-equal",
655          FunctionBase.FUNCTION_NS + "date-equal",
656          FunctionBase.FUNCTION_NS + "dateTime-equal",
657          FunctionBase.FUNCTION_NS + "dayTimeDuration-equal",
658          FunctionBase.FUNCTION_NS + "double-equal",
659          FunctionBase.FUNCTION_NS + "hexBinary-equal",
660          FunctionBase.FUNCTION_NS + "integer-equal",
661          FunctionBase.FUNCTION_NS + "rfc822Name-equal",
662          FunctionBase.FUNCTION_NS + "string-equal",
663          FunctionBase.FUNCTION_NS + "time-equal",
664          FunctionBase.FUNCTION_NS + "x500Name-equal",
665          FunctionBase.FUNCTION_NS + "yearMonthDuration-equal"
666    )
667
668    (NAME_ANYURI_EQUAL,
669    NAME_BASE64BINARY_EQUAL,
670    NAME_BOOLEAN_EQUAL,
671    NAME_DATE_EQUAL,
672    NAME_DATETIME_EQUAL,
673    NAME_DAYTIME_DURATION_EQUAL,
674    NAME_DOUBLE_EQUAL,
675    NAME_HEXBINARY_EQUAL,
676    NAME_INTEGER_EQUAL,
677    NAME_RFC822NAME_EQUAL,
678    NAME_STRING_EQUAL,
679    NAME_TIME_EQUAL,
680    NAME_X500NAME_EQUAL,
681    NAME_YEARMONTH_DURATION_EQUAL) = supportedIdentifiers
682
683    lut = {
684          NAME_STRING_EQUAL: 'stringEqual'
685    }
686   
687    _attrClasses = (
688        AnyURIAttribute,
689        Base64BinaryAttribute,
690        BooleanAttribute,
691        DateAttribute,
692        DateTimeAttribute,
693        DayTimeDurationAttribute,
694        DoubleAttribute,
695        HexBinaryAttribute,
696        IntegerAttribute,
697        RFC822NameAttribute,
698        StringAttribute,
699        TimeAttribute,
700        X500NameAttribute,
701        YearMonthDurationAttribute
702    )
703   
704    typeMap = dict([(i, j.identifier) for i,j in zip(supportedIdentifiers,
705                                                     _attrClasses)])
706   
707    def __init__(self, functionName, argumentType=None, **kw):
708        if kw.get('functionId') is None:
709            kw['functionId'] = functionName
710           
711        if kw.get('paramType') is None:
712            kw['paramType'] = EqualFunction._getArgumentType(functionName)
713           
714        super(EqualFunction, self).__init__(functionName, **kw)
715
716    def evaluate(self, inputs, evaluationCtx):
717        function = EqualFunction.lut.get(self.functionName)
718        if function is None:
719            if self.functionName in supportedIdentifiers:
720                raise NotImplementedError("No implementation is available for "
721                                          "%s" % self.functionName)           
722            else:
723                raise AttributeError('function name "%s" not recognised '
724                                     'for %s' % (self.functionName,
725                                                 self.__class__.__name__))
726                                 
727        return getattr(self, function)(inputs, evaluationCtx)
728   
729    def stringEqual(self, inputs, evaluationCtx):
730        result = self.evalArgs(inputs, context, argValues)
731        if result is not None:
732            return result
733         
734        return EvaluationResult(argValues[0] == argValues[1])
735   
736    @classmethod
737    def _getArgumentType(cls, functionName):
738        argumentType = cls.typeMap.get(functionName)
739        if argumentType is None:
740            if functionName in cls.supportedIdentifiers:
741                raise NotImplementedError('No implementation is currently '
742                                          'available for "%s"' % functionName)
743            else:
744                raise TypeError("Not a standard function: %s" % functionName)
745         
746        return argumentType
747
748class AddFunction(FunctionBase):
749   
750    def __init__(self, *arg, **kw):
751        raise NotImplementedError()
752           
753class SubtractFunction(FunctionBase):
754   
755    def __init__(self, *arg, **kw):
756        raise NotImplementedError()
757           
758class MultiplyFunction(FunctionBase):
759   
760    def __init__(self, *arg, **kw):
761        raise NotImplementedError()
762           
763class DivideFunction(FunctionBase):
764   
765    def __init__(self, *arg, **kw):
766        raise NotImplementedError()
767           
768class ModFunction(FunctionBase):
769   
770    def __init__(self, *arg, **kw):
771        raise NotImplementedError()
772
773class AbsFunction(FunctionBase):
774   
775    def __init__(self, *arg, **kw):
776        raise NotImplementedError()
777
778class RoundFunction(FunctionBase):
779   
780    def __init__(self, *arg, **kw):
781        raise NotImplementedError()
782
783class FloorFunction(FunctionBase):
784   
785    def __init__(self, *arg, **kw):
786        raise NotImplementedError()
787
788class DateMathFunction(FunctionBase):
789   
790    def __init__(self, *arg, **kw):
791        raise NotImplementedError()
792
793class GeneralBagFunction(BagFunction):
794   
795    def __init__(self, *arg, **kw):
796        raise NotImplementedError()
797
798class NumericConvertFunction(FunctionBase):
799   
800    def __init__(self, *arg, **kw):
801        raise NotImplementedError()
802
803class StringNormalizeFunction(FunctionBase):
804   
805    def __init__(self, *arg, **kw):
806        raise NotImplementedError()
807
808class GeneralSetFunction(SetFunction):
809   
810    def __init__(self, *arg, **kw):
811        raise NotImplementedError()
812   
813class MapFunction(Function):       
814    supportedIdentifiers = ()
815    NAME_MAP = FunctionBase.FUNCTION_NS + "map"
816   
817    def __init__(self, *arg, **kw):
818        raise NotImplementedError()
819
820    @classmethod
821    def getInstance(cls, root):
822        raise NotImplementedError()
823   
824class FunctionProxy():
825
826    def getInstance(self, root):
827        raise NotImplementedError()
828
829class MapFunctionProxy(FunctionProxy):
830
831    def getInstance(self, root):
832        return MapFunction.getInstance(root)
Note: See TracBrowser for help on using the repository browser.