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

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

Moved function condition classes into cond module

Line 
1"""XACML cond module contains condition function classes and class factories
2
3NERC DataGrid Project
4"""
5__author__ = "P J Kershaw"
6__date__ = "02/04/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$"
12class FunctionBase(XacmlBase):
13    FUNCTION_NS = "urn:oasis:names:tc:xacml:1.0:function:"
14   
15    def __init__(self, 
16                 functionName, 
17                 functionId=None, 
18                 paramType=None,
19                 paramIsBag=False,
20                 numParams=0, 
21                 minParams=0,
22                 returnType='', 
23                 returnsBag=False):
24         
25        self.functionName = functionName
26        self.functionId = functionId
27        self.returnType = None
28        self.returnsBag = False
29   
30        self.singleType = True;
31   
32        self.paramType = paramType
33        self.paramIsBag = paramIsBag
34        self.numParams = numParams
35        self.minParams = minParams
36       
37 
38    def _setFunctionName(self, functionName):
39          if functionName not in self.__class__.supportedIdentifiers:
40              functionList = ', '.join(self.__class__.supportedIdentifiers)
41              raise TypeError("Function name [%s] is not on of the recognised "
42                              "types: %s" % (functionName, functionList))
43          self._functionName = functionName
44         
45    def _getFunctionName(self):
46          return getattr(self, '_functionName', None)
47   
48    functionName = property(fset=_setFunctionName,
49                                    fget=_getFunctionName)
50         
51    def checkInputs(self, inputs):
52        '''Checks that the given inputs are of the right types, in the right
53        order, and are the right number for this function to evaluate.'''
54        raise NotImplementedError()
55           
56    def checkInputsNoBag(self, inputs):
57        '''Checks that the given inputs are of the right types, in the right
58        order, and are the right number for this function to evaluate.'''
59        raise NotImplementedError()
60 
61    def evaluate(self, inputs, context):
62        '''Evaluates the Function using the given inputs.'''
63        raise NotImplementedError()
64     
65    def evalArgs(self, params, context, args):
66        '''Evaluates each of the parameters, in order, filling in the argument
67        array with the resulting values. If any error occurs, this method
68        returns the error, otherwise null is returned, signalling that
69        evaluation was successful for all inputs, and the resulting argument
70        list can be used.
71       
72        @param params a list of Evaluatable objects representing the parameters
73        to evaluate
74        @param context the representation of the request
75        @param args an array as long as the params list that will, on return,
76        contain the AttributeValues generated from evaluating all parameters
77
78        @return None if no errors were encountered, otherwise
79        an EvaluationResult representing the error
80        '''
81        index = 0
82
83        for eval in params:
84            # get and evaluate the next parameter
85            result = eval.evaluate(context)
86
87            # If there was an error, pass it back...
88            if result.indeterminate():
89                return result
90
91            # ...otherwise save it and keep going
92            args[index] = result.getAttributeValue()
93            index += 1
94           
95        return None
96
97# TODO: Condition classes - minimal implementation until opportunity to fully
98# implement   
99class Function(XacmlBase):
100    def __init__(self, *arg, **kw):
101        raise NotImplementedError()
102
103    @classmethod
104    def getInstance(cls, root):
105        raise NotImplementedError()
106
107class BagFunction(FunctionBase):
108    def __init__(self, *arg, **kw):
109        raise NotImplementedError()
110
111class SetFunction(FunctionBase):
112    def __init__(self, *arg, **kw):
113        raise NotImplementedError()
114       
115class ConditionBagFunction(BagFunction):
116    def __init__(self, *arg, **kw):
117        raise NotImplementedError()
118       
119class ConditionSetFunction(FunctionBase):
120    def __init__(self, *arg, **kw):
121        raise NotImplementedError()
122       
123class HigherOrderFunction(Function):
124    def __init__(self, *arg, **kw):
125        raise NotImplementedError()
126
127# TODO: Function classes - minimal implementation until opportunity to fully
128# implement                                   
129class LogicalFunction(FunctionBase):
130
131    def __init__(self, *arg, **kw):
132        raise NotImplementedError()
133
134class NOfFunction(FunctionBase):
135   
136    def __init__(self, *arg, **kw):
137        raise NotImplementedError()
138       
139class NotFunction(FunctionBase):
140   
141    def __init__(self, *arg, **kw):
142        raise NotImplementedError()
143       
144class ComparisonFunction(FunctionBase):
145   
146    def __init__(self, *arg, **kw):
147        raise NotImplementedError()
148
149class MatchFunction(FunctionBase):
150    NAME_REGEXP_STRING_MATCH = \
151          "urn:oasis:names:tc:xacml:1.0:function:regexp-string-match"
152    NAME_RFC822NAME_MATCH = \
153          "urn:oasis:names:tc:xacml:1.0:function:rfc822Name-match"
154    NAME_X500NAME_MATCH = \
155          "urn:oasis:names:tc:xacml:1.0:function:x500Name-match"     
156
157    supportedIdentifiers = (NAME_REGEXP_STRING_MATCH, 
158                            NAME_RFC822NAME_MATCH,
159                            NAME_X500NAME_MATCH)
160   
161    lut = {
162          NAME_REGEXP_STRING_MATCH: 'regexpStringMatch',
163          NAME_RFC822NAME_MATCH:    'rfc822NameMatch',
164          NAME_X500NAME_MATCH:      'x500NameMatch'
165    }
166   
167    def __init__(self, functionName, **kw):
168          super(MatchFunction, self).__init__(functionName, **kw)
169
170    def regexpStringMatch(self, regex, val):
171          return re.match(regex, val) is not None
172   
173    def rfc822NameMatch(self, *inputs):
174        raise NotImplementedError()
175   
176    def x500NameMatch(self, *inputs):
177        raise NotImplementedError()
178   
179    def evaluate(self, inputs, context):
180          matchFunction = getattr(self, MatchFunction.lut[self.functionName])
181          match = matchFunction(self, *inputs)
182          if match:
183                return EvaluationResult(status=Status.STATUS_OK)
184
185
186class EqualFunction(FunctionBase):
187    supportedIdentifiers = (
188          "urn:oasis:names:tc:xacml:1.0:function:anyURI-equal",
189          "urn:oasis:names:tc:xacml:1.0:function:base64Binary-equal",
190          "urn:oasis:names:tc:xacml:1.0:function:boolean-equal",
191          "urn:oasis:names:tc:xacml:1.0:function:date-equal",
192          "urn:oasis:names:tc:xacml:1.0:function:dateTime-equal",
193          "urn:oasis:names:tc:xacml:1.0:function:dayTimeDuration-equal",
194          "urn:oasis:names:tc:xacml:1.0:function:double-equal",
195          "urn:oasis:names:tc:xacml:1.0:function:hexBinary-equal",
196          "urn:oasis:names:tc:xacml:1.0:function:integer-equal",
197          "urn:oasis:names:tc:xacml:1.0:function:rfc822Name-equal",
198          "urn:oasis:names:tc:xacml:1.0:function:string-equal",
199          "urn:oasis:names:tc:xacml:1.0:function:time-equal",
200          "urn:oasis:names:tc:xacml:1.0:function:x500Name-equal",
201          "urn:oasis:names:tc:xacml:1.0:function:yearMonthDuration-equal"
202    )
203
204    (NAME_ANYURI_EQUAL,
205    NAME_BASE64BINARY_EQUAL,
206    NAME_BOOLEAN_EQUAL,
207    NAME_DATE_EQUAL,
208    NAME_DATETIME_EQUAL,
209    NAME_DAYTIME_DURATION_EQUAL,
210    NAME_DOUBLE_EQUAL,
211    NAME_HEXBINARY_EQUAL,
212    NAME_INTEGER_EQUAL,
213    NAME_RFC822NAME_EQUAL,
214    NAME_STRING_EQUAL,
215    NAME_TIME_EQUAL,
216    NAME_X500NAME_EQUAL,
217    NAME_YEARMONTH_DURATION_EQUAL) = supportedIdentifiers
218
219    lut = {
220          NAME_STRING_EQUAL: 'stringEqual'
221    }
222   
223    typeMap = {NAME_STRING_EQUAL: basestring}
224   
225    def __init__(self, functionName, **kw):
226          super(EqualFunction, self).__init__(functionName, **kw)
227
228    def evaluate(self, inputs, evaluationCtx):
229        function = EqualFunction.lut.get(self.functionName)
230        if function is None:
231            if self.functionName in supportedIdentifiers:
232                raise NotImplementedError("No implementation is available for "
233                                          "%s" % self.functionName)           
234            else:
235                raise AttributeError('function name "%s" not recognised '
236                                     'for %s' % (self.functionName,
237                                                 self.__class__.__name__))
238                                 
239        return getattr(self, function)(inputs, evaluationCtx)
240   
241    def stringEqual(self, inputs, evaluationCtx):
242        result = self.evalArgs(inputs, context, argValues)
243        if result is not None:
244            return result
245         
246        return EvaluationResult(argValues[0] == argValues[1])
247   
248    def getArgumentType(functionName):
249        datatype = EqualFunction.typeMap.get(functionName);
250        if datatype is None:
251            raise AttributeError("Not a standard function: %s" % functionName)
252         
253        return datatype
254
255class AddFunction(FunctionBase):
256   
257    def __init__(self, *arg, **kw):
258        raise NotImplementedError()
259           
260class SubtractFunction(FunctionBase):
261   
262    def __init__(self, *arg, **kw):
263        raise NotImplementedError()
264           
265class MultiplyFunction(FunctionBase):
266   
267    def __init__(self, *arg, **kw):
268        raise NotImplementedError()
269           
270class DivideFunction(FunctionBase):
271   
272    def __init__(self, *arg, **kw):
273        raise NotImplementedError()
274           
275class ModFunction(FunctionBase):
276   
277    def __init__(self, *arg, **kw):
278        raise NotImplementedError()
279
280class AbsFunction(FunctionBase):
281   
282    def __init__(self, *arg, **kw):
283        raise NotImplementedError()
284
285class RoundFunction(FunctionBase):
286   
287    def __init__(self, *arg, **kw):
288        raise NotImplementedError()
289
290class FloorFunction(FunctionBase):
291   
292    def __init__(self, *arg, **kw):
293        raise NotImplementedError()
294
295class DateMathFunction(FunctionBase):
296   
297    def __init__(self, *arg, **kw):
298        raise NotImplementedError()
299
300class GeneralBagFunction(BagFunction):
301   
302    def __init__(self, *arg, **kw):
303        raise NotImplementedError()
304
305class NumericConvertFunction(FunctionBase):
306   
307    def __init__(self, *arg, **kw):
308        raise NotImplementedError()
309
310class StringNormalizeFunction(FunctionBase):
311   
312    def __init__(self, *arg, **kw):
313        raise NotImplementedError()
314
315class GeneralSetFunction(SetFunction):
316   
317    def __init__(self, *arg, **kw):
318        raise NotImplementedError()
319   
320class MapFunction(Function):       
321    supportedIdentifiers = ()
322    NAME_MAP = FunctionBase.FUNCTION_NS + "map"
323   
324    def __init__(self, *arg, **kw):
325        raise NotImplementedError()
326
327    @classmethod
328    def getInstance(cls, root):
329        raise NotImplementedError()
330   
331class FunctionProxy():
332
333    def getInstance(self, root):
334        raise NotImplementedError()
335
336class MapFunctionProxy(FunctionProxy):
337
338    def getInstance(self, root):
339        return MapFunction.getInstance(root)
340
341
342class UnknownIdentifierException(Exception):
343    pass
344
345class FunctionTypeException(Exception):
346    pass
347
348class ParsingException(Exception):
349    pass
350
351class FunctionFactory(XacmlBase):
352    '''Factory used to create all functions. There are three kinds of factories:
353    general, condition, and target. These provide functions that can be used
354    anywhere, only in a condition's root and only in a target (respectively).
355   
356    Note that all functions, except for abstract functions, are singletons, so
357    any instance that is added to a factory will be the same one returned
358    from the create methods. This is done because most functions don't have
359    state, so there is no need to have more than one, or to spend the time
360    creating multiple instances that all do the same thing.'''
361
362    defaultFactoryProxy = StandardFunctionFactory()                 
363           
364    @classmethod
365    def getTargetInstance(cls):
366        '''Returns the default FunctionFactory that will only provide those
367        functions that are usable in Target matching.
368       
369        @return a FunctionFactory for target functions'''
370        return cls.defaultFactoryProxy.getTargetFactory()
371       
372    @classmethod 
373    def getConditionInstance(cls): 
374        '''Returns the default FunctionFactory that provides access to all the
375        functions. These Functions are a superset of the Condition functions.
376       
377        @return a FunctionFactory for all functions
378        '''
379        return cls.defaultFactoryProxy.getConditionFactory()
380   
381    @classmethod
382    def getGeneralInstance(cls): 
383        '''Sets the default factory. Note that this is just a place holder for
384        now, and will be replaced with a more useful mechanism soon.'''
385        return cls.defaultFactoryProxy.getGeneralFactory()
386   
387   
388    def addFunction(self, function):
389        '''Adds the function to the factory. Most functions have no state, so
390        the singleton model used here is typically desirable. The factory will
391        not enforce the requirement that a Target or Condition matching
392        function must be boolean.
393       
394        @param function the Function to add to the factory
395        '''
396        raise NotImplementedError()
397       
398    def addAbstractFunction(self, functionProxy, identity):
399        '''Adds the abstract function proxy to the factory. This is used for
400        those functions which have state, or change behaviour (for instance
401        the standard map function, which changes its return type based on
402        how it is used).
403       
404        @param proxy the FunctionProxy to add to the factory
405        @param identity the function's identifier
406        '''
407        raise NotImplementedError()       
408   
409    def getSupportedFunctions(self):
410        '''Returns the function identifiers supported by this factory.
411       
412        @return a Set of Strings'''
413        raise NotImplementedError()
414
415    def createFunction(self, identity):
416        '''Tries to get an instance of the specified function.
417       
418        @param identity the name of the function
419        '''       
420        raise NotImplementedError()
421   
422    def createAbstractFunction(self, identity, root):
423        '''Tries to get an instance of the specified abstract function.
424       
425        @param identity the name of the function
426        @param root the DOM root containing info used to create the function
427        '''
428        raise NotImplementedError()
429
430
431class FunctionFactoryProxy(XacmlBase):
432    '''A simple proxy interface used to install new FunctionFactorys.
433    The three kinds of factory (Target, Condition, and General) are tied
434    together in this interface because implementors writing new factories
435    should always implement all three types and provide them together'''
436    def getTargetFactory():
437        raise NotImplementedError()
438
439    def getConditionFactory():
440        raise NotImplementedError()
441
442    def getGeneralFactory():
443        raise NotImplementedError()
444
445
446class BasicFunctionFactoryProxy(FunctionFactoryProxy):
447    '''A simple utility class that manages triples of function factories.'''
448   
449    # the triple of factories
450    targetFactory = None
451    conditionFactory = None
452    generalFactory = None
453
454    def __init__(targetFactory, conditionFactory, generalFactory): 
455        '''Creates a new proxy.
456       
457        @param targetFactory the target factory provided by this proxy
458        @param conditionFactory the target condition provided by this proxy
459        @param generalFactory the general factory provided by this proxy
460        '''
461        self.targetFactory = targetFactory
462        self.conditionFactory = conditionFactory
463        self.generalFactory = generalFactory
464   
465    def getTargetFactory():
466        return targetFactory
467
468    def getConditionFactory():
469        return conditionFactory
470
471    def getGeneralFactory():
472        return generalFactory
473   
474
475class BaseFunctionFactory(FunctionFactory):
476    '''This is a basic implementation of <code>FunctionFactory</code>. It
477    implements the insertion and retrieval methods, but it doesn't actually
478    setup the factory with any functions. It also assumes a certain model
479    with regard to the different kinds of functions (Target, Condition, and
480    General). For this reason, you may want to re-use this class, or you
481    may want to extend FunctionFactory directly, if you're writing a new
482    factory implementation.
483   
484    Note that while this class is thread-safe on all creation methods, it
485    is not safe to add support for a new function while creating an instance
486    of a function. This follows from the assumption that most people will
487    initialize these factories up-front, and then start processing without
488    ever modifying the factories. If you need these mutual operations to
489    be thread-safe, then you should write a wrapper class that implements
490    the right synchronization.
491    '''
492   
493    def __init__(self, 
494                 superset=None, 
495                 supportedFunctions=[],
496                 supportedAbstractFunctions={}):
497        '''Sets a "superset factory". This is useful since
498        the different function factories (Target, Condition, and General)
499        have a superset relationship (Condition functions are a superset
500        of Target functions, etc.). Adding a function to this factory will
501        automatically add the same function to the superset factory.
502
503        Constructor that defines the initial functions supported by this
504        factory but doesn't use a superset factory.
505
506        Constructor that defines the initial functions supported by this
507        factory but doesn't use a superset factory.
508
509        Constructor that defines the initial functions supported by this
510        factory and uses a superset factory. Note that the functions
511        supplied here are not propagated up to the superset factory, so
512        you must either make sure the superset factory is correctly
513        initialized or use BaseFunctionFactory(FunctionFactory)
514        and then manually add each function.
515       
516        @param supportedFunctions a Set of Functions
517        @param supportedAbstractFunctions a mapping from URI to
518                                          FunctionProxy
519       
520        @param supportedFunctions a Set of Functions
521        @param supportedAbstractFunctions a mapping from URI to FunctionProxy
522       
523        @param superset the superset factory or None'''
524       
525        # the backing maps for the Function objects
526        self.functionMap = {}
527   
528        # the superset factory chained to this factory
529        self.superset = superset
530     
531        for function in supportedFunctions:
532            self.functionMap[function.functionId] = function
533       
534        for id in supportedAbstractFunctions.keys():
535            proxy = supportedAbstractFunctions.get(id)
536            self.functionMap[id] = proxy
537 
538    def addFunction(self, function):
539        '''Adds the function to the factory. Most functions have no state, so
540        the singleton model used here is typically desirable. The factory will
541        not enforce the requirement that a Target or Condition matching
542        function must be boolean.
543       
544        @param function the Function to add to the factory
545        @raise TypeError if the function's identifier is already used or if the
546        function is non-boolean (when this is a Target or Condition factory)
547        '''
548        id = function.functionId
549
550        # make sure this doesn't already exist
551        if id in self.functionMap:
552            raise TypeError("function %s already exists" % id)
553
554        # add to the superset factory
555        if self.superset != None:
556            self.superset.addFunction(function)
557
558        # Add to this factory
559        self.functionMap[id] = function
560   
561       
562    def addAbstractFunction(self, proxy, id):
563        '''Adds the abstract function proxy to the factory. This is used for
564        those functions which have state, or change behaviour (for instance
565        the standard map function, which changes its return type based on
566        how it is used).
567       
568        @param proxy: the FunctionProxy to add to the factory
569        @param id: the function's identifier
570       
571        @raise TypeError if the function's identifier is already used'''
572
573        # make sure this doesn't already exist
574        if id in self.functionMap:
575            raise TypeError("function already exists")
576
577        # add to the superset factory
578        if self.superset != None:
579            self.superset.addAbstractFunction(proxy, id)
580
581        # finally, add to this factory
582        functionMap[id] = proxy
583   
584
585    def getSupportedFunctions(self): 
586        '''Returns the function identifiers supported by this factory.
587       
588        @return a list of strings'''
589   
590        functions = self.functionMap.keys()
591
592        if self.superset != None:
593            functions += self.superset.getSupportedFunctions()
594
595        return functions
596   
597
598    def createFunction(self, identity):
599        '''Tries to get an instance of the specified function.
600       
601        @param identity the name of the function
602       
603        @throws UnknownIdentifierException if the name isn't known
604        @throws FunctionTypeException if the name is known to map to an
605                                      abstract function, and should therefore
606                                      be created through createAbstractFunction
607        '''
608        entry = self.functionMap.get(identity)
609        if entry is not None:
610            if isinstance(entry, Function): 
611                return entry
612            else:
613                # this is actually a proxy, which means the other create
614                # method should have been called
615                raise FunctionTypeException("function is abstract")   
616        else:
617            # we couldn't find a match
618            raise UnknownIdentifierException("functions of type %s are not "
619                                             "supported by this factory" % 
620                                             identity)       
621   
622   
623    def createAbstractFunction(identity, root):
624        '''Tries to get an instance of the specified abstract function.
625       
626        @param identity the name of the function
627        @param root the DOM root containing info used to create the function
628        @param xpathVersion the version specified in the containing policy, or
629                            None if no version was specified
630       
631        @throws UnknownIdentifierException if the name isn't known
632        @throws FunctionTypeException if the name is known to map to a
633                                      concrete function, and should therefore
634                                      be created through createFunction
635        @throws ParsingException if the function can't be created with the
636                                 given inputs'''
637   
638        entry = self.functionMap.get(identity)
639        if entry is not None:
640            if isinstance(entry, FunctionProxy): 
641                try: 
642                    return entry.getInstance(root)
643               
644                except Exception, e:
645                    raise ParsingException("Couldn't create abstract function "
646                                           "%s: %s" % identity, e)     
647            else:
648                # this is actually a concrete function, which means that
649                # the other create method should have been called
650                raise FunctionTypeException("function is concrete")
651           
652        else:
653            raise UnknownIdentifierException("Abstract functions of type %s "
654                                             "are not supported by this "
655                                             "factory" % identity)
656
657
658class StandardFunctionFactory(BaseFunctionFactory):
659    '''This factory supports the standard set of functions specified in XACML
660    1.0 and 1.1. It is the default factory used by the system, and imposes
661    a singleton pattern insuring that there is only ever one instance of
662    this class.
663    <p>
664    Note that because this supports only the standard functions, this
665    factory does not allow the addition of any other functions. If you call
666    addFunction on an instance of this class, an exception
667    will be thrown. If you need a standard factory that is modifiable,
668    you can either create a new BaseFunctionFactory (or some
669    other implementation of FunctionFactory) populated with
670    the standard functions from getStandardFunctions or
671    you can use getNewFactoryProxy to get a proxy containing
672    a new, modifiable set of factories.'''
673
674
675    # the three singleton instances
676    targetFactory = None
677    conditionFactory = None
678    generalFactory = None
679
680    # the three function sets/maps that we use internally
681    targetFunctions = None
682    conditionFunctions = None
683    generalFunctions = None
684
685    targetAbstractFunctions = None
686    conditionAbstractFunctions = None
687    generalAbstractFunctions = None
688
689    # the set/map used by each singleton factory instance
690    supportedFunctions = None
691    supportedAbstractFunctions = None
692
693   
694    def __init__(self, supportedFunctions, supportedAbstractFunctions): 
695        '''Creates a new StandardFunctionFactory, making sure that the default
696        maps are initialized correctly. Standard factories can't be modified,
697        so there is no notion of supersetting since that's only used for
698        correctly propagating new functions.'''
699        super(StandardFunctionFactory, self).__init__(supportedFunctions, 
700                                                    supportedAbstractFunctions)
701
702        self.supportedFunctions = supportedFunctions
703        self.supportedAbstractFunctions = supportedAbstractFunctions
704   
705
706   
707   
708    def _initTargetFunctions(self): 
709        '''Private initializer for the target functions. This is only ever
710        called once.'''
711        log.info("Initializing standard Target functions")
712
713        # Emulate a list with unique items using a dict with only the keys set
714        StandardFunctionFactory.targetFunctions = {}
715
716        # add EqualFunction
717        StandardFunctionFactory.targetFunctions.fromkeys(
718                                    EqualFunction.supportedIdentifiers)
719
720        # add LogicalFunction
721        StandardFunctionFactory.targetFunctions.fromkeys(
722                                    LogicalFunction.supportedIdentifiers)
723       
724        # add NOfFunction
725        StandardFunctionFactory.targetFunctions.fromkeys(
726                                    NOfFunction.supportedIdentifiers)
727       
728        # add NotFunction
729        StandardFunctionFactory.targetFunctions.fromkeys(
730                                    NotFunction.supportedIdentifiers)
731       
732        # add ComparisonFunction
733        StandardFunctionFactory.targetFunctions.fromkeys(
734                                    ComparisonFunction.supportedIdentifiers)
735
736        # add MatchFunction
737        StandardFunctionFactory.targetFunctions.fromkeys(
738                                    MatchFunction.supportedIdentifiers)
739
740        StandardFunctionFactory.targetAbstractFunctions = {}
741   
742   
743    def _initConditionFunctions(self): 
744        '''Private initializer for the condition functions. This is only ever
745        called once.'''
746        log.info("Initializing standard Condition functions")
747
748        if StandardFunctionFactory.targetFunctions is None:
749            self._initTargetFunctions()
750
751        StandardFunctionFactory.conditionFunctions = \
752            StandardFunctionFactory.targetFunctions.copy()
753
754        # add condition functions from BagFunction
755        conditionFunctions.fromkeys(ConditionBagFunction.supportedIdentifiers)
756       
757        # add condition functions from SetFunction
758        conditionFunctions.fromkeys(ConditionSetFunction.supportedIdentifiers)
759       
760        # add condition functions from HigherOrderFunction
761        conditionFunctions.fromkeys(HigherOrderFunction.supportedIdentifiers)
762
763        StandardFunctionFactory.conditionAbstractFunctions = \
764            StandardFunctionFactory.targetAbstractFunctions.copy()
765   
766
767    def _initGeneralFunctions(self):     
768        '''Private initializer for the general functions. This is only ever
769        called once.'''
770   
771        log.info("Initializing standard General functions")
772
773        if StandardFunctionFactory.conditionFunctions is None:
774            self._initConditionFunctions()
775
776        StandardFunctionFactory.generalFunctions = \
777            StandardFunctionFactory.conditionFunctions.copy()
778
779        # add AddFunction
780        StandardFunctionFactory.generalFunctions.fromkeys(
781            AddFunction.supportedIdentifiers)
782           
783        # add SubtractFunction
784        StandardFunctionFactory.generalFunctions.fromkeys(
785            SubtractFunction.supportedIdentifiers)
786           
787        # add MultiplyFunction
788        StandardFunctionFactory.generalFunctions.fromkeys(
789            MultiplyFunction.supportedIdentifiers)
790           
791        # add DivideFunction
792        StandardFunctionFactory.generalFunctions.fromkeys(
793            DivideFunction.supportedIdentifiers)
794           
795        # add ModFunction
796        StandardFunctionFactory.generalFunctions.fromkeys(
797            ModFunction.supportedIdentifiers)
798       
799        # add AbsFunction
800        StandardFunctionFactory.generalFunctions.fromkeys(
801            AbsFunction.supportedIdentifiers)
802           
803        # add RoundFunction
804        StandardFunctionFactory.generalFunctions.fromkeys(
805            RoundFunction.supportedIdentifiers)
806           
807        # add FloorFunction
808        StandardFunctionFactory.generalFunctions.fromkeys(
809            FloorFunction.supportedIdentifiers)
810       
811        # add DateMathFunction
812        StandardFunctionFactory.generalFunctions.fromkeys(
813            DateMathFunction.supportedIdentifiers)
814           
815        # add general functions from BagFunction
816        StandardFunctionFactory.generalFunctions.fromkeys(
817            GeneralBagFunction.supportedIdentifiers)
818           
819        # add NumericConvertFunction
820        StandardFunctionFactory.generalFunctions.fromkeys(
821            NumericConvertFunction.supportedIdentifiers)
822           
823        # add StringNormalizeFunction
824        StandardFunctionFactory.generalFunctions.fromkeys(
825            StringNormalizeFunction.supportedIdentifiers)
826       
827        # add general functions from SetFunction
828        StandardFunctionFactory.generalFunctions.fromkeys(
829            GeneralSetFunction.supportedIdentifiers)
830           
831        StandardFunctionFactory.generalAbstractFunctions = \
832            StandardFunctionFactory.conditionAbstractFunctions.copy()
833
834        # Add the map function's proxy
835        StandardFunctionFactory.generalAbstractFunctions[
836                                    MapFunction.NAME_MAP] = MapFunctionProxy()
837   
838    @classmethod 
839    def getTargetFactory(cls): 
840        '''Returns a FunctionFactory that will only provide those functions
841        that are usable in Target matching. This method enforces a singleton
842        model, meaning that this always returns the same instance, creating
843        the factory if it hasn't been requested before. This is the default
844        model used by the FunctionFactory, ensuring quick
845        access to this factory.
846       
847        @return a FunctionFactory for target functions'''
848        if StandardFunctionFactory.targetFactory is None: 
849            if StandardFunctionFactory.targetFunctions is None:
850                StandardFunctionFactory._initTargetFunctions()
851               
852            if StandardFunctionFactory.targetFactory is None:
853                StandardFunctionFactory.targetFactory=StandardFunctionFactory(
854                            StandardFunctionFactory.targetFunctions,
855                            StandardFunctionFactory.targetAbstractFunctions)
856       
857        return StandardFunctionFactory.targetFactory
858
859   
860    @classmethod
861    def getConditionFactory(cls): 
862        '''Returns a FuntionFactory that will only provide those functions that
863        are usable in the root of the Condition. These Functions are a
864        superset of the Target functions. This method enforces a singleton
865        model, meaning that this always returns the same instance, creating
866        the factory if it hasn't been requested before. This is the default
867        model used by the FunctionFactory, ensuring quick
868        access to this factory.
869   
870        @return a FunctionFactory for condition functions
871        '''
872        if StandardFunctionFactory.conditionFactory is None:
873            if StandardFunctionFactory.conditionFunctions is None:
874                StandardFunctionFactory._initConditionFunctions()
875               
876            if StandardFunctionFactory.conditionFactory is None:
877                StandardFunctionFactory.conditionFactory = \
878                    StandardFunctionFactory(
879                           StandardFunctionFactory.conditionFunctions,
880                           StandardFunctionFactory.conditionAbstractFunctions)       
881
882        return StandardFunctionFactory.conditionFactory
883   
884
885    @classmethod
886    def getGeneralFactory(cls): 
887        '''Returns a FunctionFactory that provides access to all the functions.
888        These Functions are a superset of the Condition functions. This method
889        enforces a singleton model, meaning that this always returns the same
890        instance, creating the factory if it hasn't been requested before.
891        This is the default model used by the FunctionFactory,
892        ensuring quick access to this factory.
893       
894        @return a FunctionFactory for all functions'''
895   
896        if StandardFunctionFactory.generalFactory is None:
897            if StandardFunctionFactory.generalFunctions is None:
898                StandardFunctionFactory._initGeneralFunctions()
899               
900                StandardFunctionFactory.generalFactory = \
901                    StandardFunctionFactory(
902                            StandardFunctionFactory.generalFunctions,
903                            StandardFunctionFactory.generalAbstractFunctions)
904               
905        return StandardFunctionFactory.generalFactory
906
907
908    def getStandardFunctions(self):
909        '''Returns the set of functions that this standard factory supports.
910       
911        @return a Set of Functions'''
912        return tuple(self.supportedFunctions.keys())
913       
914    def getStandardAbstractFunctions(self):
915        '''Returns the set of abstract functions that this standard factory
916        supports as a mapping of identifier to proxy.
917       
918        @return a Map mapping URIs to FunctionProxys'''
919        return tuple(self.supportedAbstractFunctions.keys())
920   
921   
922    @classmethod
923    def getNewFactoryProxy(cls): 
924        '''A convenience method that returns a proxy containing newly created
925        instances of BaseFunctionFactorys that are correctly
926        supersetted and contain the standard functions and abstract functions.
927        These factories allow adding support for new functions.
928       
929        @return a new proxy containing new factories supporting the standard
930        functions'''
931       
932        general = StandardFunctionFactory.getGeneralFactory()
933           
934        newGeneral=BaseFunctionFactory(general.getStandardFunctions(),
935                                       general.getStandardAbstractFunctions())
936
937        condition = StandardFunctionFactory.getConditionFactory()
938       
939        newCondition = BaseFunctionFactory(newGeneral,
940                                    condition.getStandardFunctions(),
941                                    condition.getStandardAbstractFunctions())
942
943        target = StandardFunctionFactory.getTargetFactory()
944        newTarget = BaseFunctionFactory(newCondition,
945                                    target.getStandardFunctions(),
946                                    target.getStandardAbstractFunctions())
947
948        return BasicFunctionFactoryProxy(newTarget, newCondition, newGeneral)
949   
950
951   
952   
953    def addFunction(self, function):
954        '''Always throws an exception, since support for new functions may not
955        be added to a standard factory.
956       
957        @param function the Function to add to the factory       
958        @raise NotImplementedError'''
959   
960        raise NotImplementedError("a standard factory cannot support new "
961                                  "functions")
962   
963   
964    def addAbstractFunction(self, proxy, identity):
965        '''Always throws an exception, since support for new functions may not
966        be added to a standard factory.
967       
968        @param proxy the FunctionProxy to add to the factory
969        @param identity the function's identifier
970       
971        @raise NotImplementedError always'''
972        raise NotImplementedError("a standard factory cannot support new "
973                                  "functions")
Note: See TracBrowser for help on using the repository browser.