"""
$RCSfile: ProcessorChooser.py,v $

ZopeXMLMethods provides filters to apply to Zope objects for XML/XSLT
processing.  XSLTMethod associates XSLT transformers with XML
documents.  XSLTMethod automatically transforms an XML document via
XSLT, where the XML document is obtained from another Zope object (the
'source' object) via acquisition.

ProcessorChooser automatically detects which of the supported
processor(s) are available in the environment, and makes it possible
to dynamically choose/switch processors at runtime

Author: <a href="mailto:cstrong@arielpartners.com">Craeg Strong</a>
Release: 1.0

$Id: ProcessorChooser.py,v 1.14 2003/03/30 20:51:59 cstrong Exp $
"""

__cvstag__  = '$Name:  $'[6:-2]
__date__    = '$Date: 2003/03/30 20:51:59 $'[6:-2]
__version__ = '$Revision: 1.14 $'[10:-2]

################################################################
# ProcessorChooser class
################################################################

default = None

class ProcessorChooser:
    """

    ProcessorChooser automatically detects which of the supported
    processor(s) are available in the environment, and makes it
    possible to dynamically choose/switch processors at runtime.

    I designed this to do lazy loading to enable ZSyncing from one
    Zope instance to another where the two Zope instances *do not*
    share the exact same set of XSLT libraries.  For a small
    performance price, we get lots more flexibility.  Besides, if you
    want performance, you should be using the CacheManager...

    CKS 10/31/2002

    """
    def __init__(self, preferred=None):
        self.preferred = preferred or default
        
    def tryFourSuite11(self, processorMap):
        try:
            from FourSuite11Processor import FourSuite11Processor as proc
            processorMap[proc.name] = 'loadFourSuite11'
        except:
            pass

    def loadFourSuite11(self):
        try:
            from FourSuite11Processor import FourSuite11Processor as proc
            return proc()
        except:
            return None

    def tryFourSuite(self, processorMap):
        try:
            from FourSuiteProcessor import FourSuiteProcessor as proc
            processorMap[proc.name] = 'loadFourSuite'
        except:
            pass
        
    def loadFourSuite(self):
        try:
            from FourSuiteProcessor import FourSuiteProcessor as proc
            return proc()
        except:
            return None

    def tryLibXslt(self, processorMap):
        try:
            from LibXsltProcessor import LibXsltProcessor as proc
            processorMap[proc.name] = 'loadLibXslt'
        except:
            pass

    def loadLibXslt(self):
        try:
            from LibXsltProcessor import LibXsltProcessor as proc
            return proc()
        except:
            return None

    def tryPyana(self, processorMap):
        try:
            from PyanaProcessor import PyanaProcessor as proc
            processorMap[proc.name] = 'loadPyana'
        except:
            pass

    def loadPyana(self):
        try:
            from PyanaProcessor import PyanaProcessor as proc
            return proc()
        except:
            return None

    def trySabPyth(self, processorMap):
        try:
            from SabPythProcessor import SabPythProcessor as proc
            processorMap[proc.name] = 'loadSabPyth'
        except:
            pass

    def loadSabPyth(self):
        try:
            from SabPythProcessor import SabPythProcessor as proc
            return proc()
        except:
            return None

    def processors(self):
        "Return list of currently available processors"
        processorMap = {}

        # Comment out a line if for some reason you don't want the
        # processor to appear in your list of options.  For example,
        # during testing  CKS 10/31/2002
        self.tryFourSuite11(processorMap)
        self.tryFourSuite(processorMap)
        self.tryLibXslt(processorMap)
        self.tryPyana(processorMap)
        self.trySabPyth(processorMap)

        return tuple(processorMap.keys())

    def processorObject(self, processorName):
        """

        Obtain the object encapsulating the named XSLT processor.
        The name must be in the list of names returned by processors()

        """
        processorMap = {}

        self.tryFourSuite11(processorMap)
        self.tryFourSuite(processorMap)
        self.tryLibXslt(processorMap)
        self.tryPyana(processorMap)
        self.trySabPyth(processorMap)

        attrName = processorMap.get(processorName, None)
        if attrName is None:
            procName = processorMap.keys()[0]
            attrName = processorMap[procName]
            print "Unknown XSLT processor", processorName, \
                  "substituting", procName, "instead."
        attr = getattr(ProcessorChooser, attrName)
        return apply(attr, (self,))

    def defaultProcessor(self):
        """
        Return the name of the default processor.  If preferred was
        indicated and it is available, return it.  Otherwise return
        the first one or None if there aren't any.
        """
        
        choices = self.processors()
        if self.preferred and self.preferred in choices:
            return self.preferred
        elif choices:
            return choices[0]
        else:
            return None

    def repair(self):
        """

        Update an instance created with a previous release of the
        software to work with the latest version

        """
        if hasattr(self, '_processors'):
            delattr(self, '_processors')
