"""
$RCSfile: DTDValidateMethod.py,v $

ZopeXMLMethods provides methods to apply to Zope objects for XML/XSLT
processing.  DTDValidateMethod tests an XML document for validity
against a schema, where the XML document is obtained from another Zope
object (the 'source' object) via acquisition.

Author: Craeg Strong <cstrong@arielpartners.com>
Release: 1.0

$Id: DTDValidateMethod.py,v 1.1 2003/06/16 05:47:53 arielpartners Exp $
"""

__cvstag__  = '$Name:  $'[6:-2]
__date__    = '$Date: 2003/06/16 05:47:53 $'[6:-2]
__version__ = '$Revision: 1.1 $'[10:-2]

# peer classes/modules
from interfaces import IDTDValidateMethod
from processors.interfaces import IDTDValidator
from processors.ProcessorRegistry import ProcessorRegistry
from GeneratorRegistry import GeneratorRegistry

# our base class
from XMLMethod import XMLMethod, getPublishedResult, \
     PERM_VIEW, PERM_EDIT, PERM_FTP, PERM_CONTENT, PERM_MANAGE

# Zope builtins
import Globals
from Globals import MessageDialog
from AccessControl import ClassSecurityInfo
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from zope.interface import implements

################################################################
# Module Scoped Convenience Methods
################################################################

def addInstance(folder,
                id, title='', description='', selected_processor='',
                content_type = "text/plain", behave_like='',
                debugLevel=0):
    """
    This is a convenience factory method for creating an instance of
    DTDValidateMethod.  It returns the object created, and may therefore be
    more convenient than the addDTDValidateMethod() method it calls.  It is
    used by the unit testing programs.
    """
    folder.manage_addProduct['ZopeXMLMethods'].addDTDValidateMethod(
        id, title, description, selected_processor,
        content_type, behave_like, debugLevel)
    return folder[id]

################################################################
# Contructors
################################################################

manage_addDTDValidateMethodForm = PageTemplateFile('www/createDTDValidator.pt', globals())
def manage_addDTDValidateMethod(self, id, title='', description='',
                                selected_processor='',
                                content_type='text/plain',
                                behave_like='', debugLevel=0,
                                REQUEST=None, RESPONSE=None):
    """
    Factory method to create an instance of DTDValidateMethod, called from
    a GUI in the Zope Management Interface.  It calls addDTDValidateMethod
    to actually do the work.
    """
    try:
        self.addDTDValidateMethod(id, title, description, selected_processor,
                                  content_type, behave_like, debugLevel)
        message = 'Successfully created ' + id + ' DTDValidateMethod object.'

        if REQUEST is None:
            return

        try:
            url = self.DestinationURL()
        except:
            url = REQUEST['URL1']
        REQUEST.RESPONSE.redirect(url + "/manage_main")
        
    except Exception, e:
        message = str(e)
        message.replace('\n','<br/>')
        return MessageDialog(title   = 'Error',
                             message = message,
                             action  = 'manage_main')

def addDTDValidateMethod(self, id, title='', description='',
                         selected_processor='',
                         content_type='text/html',
                         behave_like='', debugLevel=0):
    """
    Factory method to actually create an instance of DTDValidateMethod
    and return it.

    You should call this method directly if you are creating an
    instance of DTDValidateMethod programatically.
    """

    if not id:
        raise Exception('Required fields must not be blank')

    self._setObject(id, DTDValidateMethod(id, title, description,
                                          selected_processor,
                                          content_type, behave_like,
                                          debugLevel))
    self._getOb(id).reindex_object()


################################################################
# Main class
################################################################

class DTDValidateMethod(XMLMethod):
    """
    DTDValidateMethod tests an XML document for validity against a
    schema, where the XML document is obtained from another Zope
    object (the 'source' object) via acquisition.
    """

    meta_type = 'DTD Validate Method'

    implements(IDTDValidateMethod)

    _security = ClassSecurityInfo()

    # _properties inherited from XMLMethod
    # manage_options inherited from XMLMethod

    def __init__(self, id, title, description, selected_processor,
                 content_type="text/html", behave_like='',
                 debugLevel=0):

        XMLMethod.__init__(self, id, title, description,
                           selected_processor, content_type,
                           behave_like, debugLevel)

        # if selected_processor is blank, get the default
        if not self.selected_processor:
            self.selected_processor = ProcessorRegistry.defaultName(IDTDValidator)

    ################################################################
    # Methods implementing the IXPathMethod interface below
    ################################################################

    _security.declarePublic('availableProcessors')
    def availableProcessors(self):
        """
        Return names of currently available DTD validation processor libraries
        """
        return ProcessorRegistry.names(IDTDValidator)        

    _security.declarePublic('processor')
    def processor(self):
        """
        Obtain the object encapsulating the selected DTD processor.
        """
        return ProcessorRegistry.item(IDTDValidator,
                                      self.selected_processor)

    _security.declareProtected(PERM_VIEW, 'dtdValidationResultsString')
    def validationResultsString(self, REQUEST):
        "Return results of validation as a string"

        processor   = self.processor()
        xmlObject   = self.getXmlSourceObject()
        xmlContents = getPublishedResult("XML source", xmlObject, REQUEST)
        xmlURL      = xmlObject.absolute_url()
        
        processor.setDebugLevel(self.debugLevel)

        return processor.dtdValidationResultsString(xmlContents,
                                                    xmlURL)
    _security.declareProtected(PERM_VIEW, 'isValid')
    def isValid(self, REQUEST):
        """
        Return boolean indicating whether passed in document is valid
        with respect to this DTD
        """

        processor   = self.processor()
        xmlObject   = self.getXmlSourceObject()
        xmlContents = getPublishedResult("XML source", xmlObject, REQUEST)
        xmlURL      = xmlObject.absolute_url()
        
        processor.setDebugLevel(self.debugLevel)

        return processor.isValidForDTD(xmlContents, xmlURL)

    ################################################################
    # Standard Zope stuff
    ################################################################

    _security.declareProtected(PERM_VIEW, '__call__')
    def __call__(self, client=None, REQUEST=None, RESPONSE=None):
        """
        Render self by validating its content against its DTD
        """
        rawResult = self.validationResultsString(REQUEST)
            
        if self.behave_like == "":
            behave_like = self.getXmlSourceObject().meta_type
        else:
            behave_like = self.behave_like

        gen = GeneratorRegistry.getGenerator(behave_like)
        if gen is None:
            gen = GeneratorRegistry.getDefaultGenerator()

        # explicitly set the Content-Type here because calling the XML
        # source object or the Method might have changed it and the
        # call of gen.getResult() below doesn't guarantee that it will
        # be changed back.
        if RESPONSE is not None:
            RESPONSE.setHeader("Content-Type", self.content_type)

        obj = gen.createObject(self.id, self.title, rawResult,
                               content_type = self.content_type)

        if client is None:
            client = self

        return gen.getResult(obj, client, REQUEST, RESPONSE)

    ################################################################
    # Support for Schema Migration
    ################################################################

    def repair(self):
        """
        Repair this object.  This method is used for schema migration,
        when the class definition changes and existing instances of
        previous versions must be updated.
        """
        pass
        # nothing to repair at the moment

# register security information
Globals.InitializeClass(DTDValidateMethod)
