# This file is part of ZTM.
#
# ZTM is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# ZTM is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with ZTM; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

""" Unit test for SearchableText implementation of ZTopic. """
#from ZTopic.ZTopic import SearchableText, _SearchableText

def SearchableText(self):
    """ Returns text that is used for searching """
    if self.portal_type == 'Topic':
        return self._SearchableText()

    textlist = []
    append = textlist.append
    try:
        configuredict = self.attributeslist(self.portal_type)
    except KeyError:
        LOG('ZTopic.SearchableText()', BLATHER, 'Could not find the configurationinformation for %s with portal_type: %s'%(self, self.portal_type))
        return ''

    for attribute, weight in configuredict['attributes']:
        attr = getattr(self, attribute)
        if callable(attr):
            attr = attr()
        append( ' '.join( [str(attr)] * weight ) )

    typeweight = configuredict['typeweight']
    for basename in self.getBaseNames():
        append( ' '.join( [str(basename.getData())] * typeweight) )
    
    if configuredict['listassocs']:
        assocroles = self.getAssociationRoles()
        for role in assocroles:
            assoc = role.getAssociation()
            weight = roleweight(role) + assocweight(assoc)
            if weight:
                otherrole = assoc.getOtherRole(role)
                title = otherrole.getPlayer().Title()
                append( ' '.join( [str(title)]*weight) )

    return ' '.join( textlist )

def _SearchableText(self):
    """ For indexing naked topics """
    text = []
    append = text.append
    for basename in self.getBaseNames():
        append( basename.getData() )

    for occurrence in self.getOccurrences():
        locator = occurrence.getLocator()
        if locator:
            append(locator.getAddress())
        else:
            append(occurrence.getData())

    return ' '.join(text)


import unittest

# Mockup code.

def LOG(*args): pass
def roleweight(role): return getattr(role, 'weight', 1)
def assocweight(assoc): return getattr(assoc, 'weight', 1)

class MockupAssociationRole:
    def getAssociation(self): return self._association
    def getPlayer(self): return self._player
    
class MockupAssociation:
    def getOtherRole(self, role): return self._role

class MockupBaseName:
    def __str__(self): return 'basename'
    def getData(self): return 'basename'

class MockupOccurrence:
    def __init__(self):
        self._islocator = 0
    def makeLocator(self): self._islocator = 1
    def makeOccurrence(self): self._islocator = 0
    def getData(self): 
        if not self._islocator: 
            return 'occurrence'
    def getLocator(self): 
        if self._islocator: 
            return MockupLocator()

class MockupLocator:
    def getAddress(self): return 'locator'
 
class MockupTopic:
    """ Simple mockupclass for testing the SearchableText functions. """
    portal_type = 'Topic'
    title = 'title'
    
    def __init__(self):
        self.listassocs = 0
        self.typeweight = 1
        self.filterassoc = []
        self.attributes = []
        self._roles = []
        self._occurrences = []
        self._basenames = []

    def doListAssociations(self): self.listassocs = 1
    def addFilterAssoc(self, assoc): 
        self.filter_assocs.append[assoc]
    def addAttributes(self, name, value):
        self.attributes.append((name, value,))
    
    
    searchableText = SearchableText
    _searchableText = _SearchableText

    def Title(self): return self.title
    
    def Description(self):
        self.Description_was_called = 1
        return "description"

    def getAssociationRoles(self):
        return self._roles

    def getOccurrences(self):
        return self._occurrences

    def getBaseNames(self):
        return self._basenames
        
    def attributeslist(self, type):
        """ Returns a dictionary of attributenames and their value. 
            This dictionary is used to compute the resulting text.
        """
        configuredict = {}
        configuredict['typeweight'] = self.typeweight
        configuredict['attributes'] = self.attributes
        configuredict['filterassocs'] = self.filterassoc
        configuredict['listassocs'] = self.listassocs
        return configuredict

    def SearchableText(self):
        """ Proxy method for SearchableText. """
        return self.searchableText()
    
    def _SearchableText(self):
        return self._searchableText()


def setup(withAssociation=0, withOccurrence=0, withBaseName=0):
    topic = MockupTopic()
    role = None
    assoc = None
    if withOccurrence:
        occurrence = MockupOccurrence()
        topic._occurrences = [occurrence]
    if withBaseName:
        basename = MockupBaseName()
        topic._basenames = [basename]
    if withAssociation:
        role = MockupAssociationRole()
        assoc = MockupAssociation()
        topic._roles = [role,]
        assoc._role = role
        role._player = topic
        role._association = assoc
    return topic, role, assoc

# Start testcases.

class Attributes(unittest.TestCase):
    """ Test the handling of attributes. """

    def testCallable(self):
        """ SearchableText should handle callable attributes. """
        topic, role, assoc = setup()
        topic.portal_type = 'Callable'
        topic.addAttributes('Description', 1)
        self.assertEquals(topic.SearchableText(), 'description')

    def testNonCallable(self):
        """ SearchableText should handle non-callable attributes. """
        topic, role, assoc = setup()
        topic.portal_type = 'Callable'
        topic.addAttributes('nonCallable', 1)
        topic.nonCallable = 'title'
        self.assertEquals(topic.SearchableText(), 'title')

    def testAttributeWeighting(self):
        """ SearchableText should multiply attributevalues by their weight. """
        topic, role, assoc = setup()
        topic.portal_type = 'Callable'
        topic.addAttributes('Description', 3)
        self.assertEquals(topic.SearchableText(), 'description description description')

    def testNonTextualAttributes(self):
        """ SearchableText should deal with nontextual attributes. """
        topic, role, assoc = setup()
        topic.portal_type = 'Callable'
        topic.addAttributes('nonTextual', 3)
        topic.nonTextual = 1
        self.assertEquals(topic.SearchableText(), '1 1 1')

class Basenames(unittest.TestCase):
    def testBaseNames(self):
        """ SearchableText should include all basenames. """
        topic, role, assoc = setup(withBaseName=1)
        topic._basenames.append(MockupBaseName())
        self.assertEquals(topic.SearchableText(), 'basename basename')

    def testWeightedBaseNames(self):
        """ SearchableText should weigh basenames. """
        topic, role, assoc = setup(withBaseName=1)
        topic.portal_type = 'Callable'
        topic.typeweight = 2
        self.assertEquals(topic.SearchableText(), 'basename basename')


class Associations(unittest.TestCase):
    """ Test the handling of associations. """
    def testWeightedAssociation(self):
        """ SearchableText should handle weighted associations. """
        topic, role, assoc = setup(withAssociation=1)
        topic.portal_type = 'Callable'
        topic.doListAssociations()
        self.assertEquals(topic.SearchableText(), 'title title')

    def testMultipleAssociations(self):
        """ SearchableText should handle multiple associations. """
        topic, role, assoc = setup(withAssociation=1)
        topic._roles.append(role)
        topic.portal_type = 'Callable'
        topic.doListAssociations()
        self.assertEquals(topic.SearchableText(), 'title title title title')

    def testAssociationWeight(self):
        """ SearchableText should handle differently weighted associations. """
        topic, role, assoc = setup(withAssociation=1)
        topic.portal_type = 'Callable'
        topic.doListAssociations()
        role.weight = 0
        assoc.weight = 5
        self.assertEquals(topic.SearchableText(), 'title title title title title')

    def testRoleWeight(self):
        """ SearchableText should handle differently weighted associationroles. """
        topic, role, assoc = setup(withAssociation=1)
        topic.portal_type = 'Callable'
        topic.doListAssociations()
        role.weight = 5
        assoc.weight = 0
        self.assertEquals(topic.SearchableText(), 'title title title title title')

        

class DefaultTopic(unittest.TestCase):
    """ Test the handling of the default topics. """

    def testBaseNames(self):
        """ _SearchableText should include all basenames. """
        topic, role, assoc = setup(withBaseName=1)
        topic._basenames.append(MockupBaseName())
        self.assertEquals(topic.SearchableText(), 'basename basename')

    def testLocators(self):
        """ _SearchableText should include all occurrences. """
        topic, role, assoc = setup(withOccurrence=1)
        topic._occurrences.append(MockupOccurrence())
        for occ in topic.getOccurrences():
            occ.makeLocator()
        self.assertEquals(topic.SearchableText(), 'locator locator')

    def testLocators(self):
        """ _SearchableText should include all occurrences. """
        topic, role, assoc = setup(withOccurrence=1)
        topic._occurrences.append(MockupOccurrence())
        for occ in topic.getOccurrences():
            occ.makeOccurrence()
        self.assertEquals(topic.SearchableText(), 'occurrence occurrence')

        
if __name__ == "__main__":
    unittest.main()   
 
