[Zope-Checkins] CVS: Zope3/lib/python/Zope/App/Formulator/Validators - BooleanValidator.py:1.2 DateTimeValidator.py:1.2 EmailValidator.py:1.2 FileValidator.py:1.2 FloatValidator.py:1.2 IntegerValidator.py:1.2 LinesValidator.py:1.2 LinkValidator.py:1.2 ListLinesValidator.py:1.2 MultiSelectionValidator.py:1.2 PatternChecker.py:1.2 PatternValidator.py:1.2 SelectionValidator.py:1.2 StringBaseValidator.py:1.2 StringValidator.py:1.2 TextValidator.py:1.2 __init__.py:1.2 validators.zcml:1.2

Jim Fulton jim@zope.com
Mon, 10 Jun 2002 19:28:51 -0400


Update of /cvs-repository/Zope3/lib/python/Zope/App/Formulator/Validators
In directory cvs.zope.org:/tmp/cvs-serv17445/lib/python/Zope/App/Formulator/Validators

Added Files:
	BooleanValidator.py DateTimeValidator.py EmailValidator.py 
	FileValidator.py FloatValidator.py IntegerValidator.py 
	LinesValidator.py LinkValidator.py ListLinesValidator.py 
	MultiSelectionValidator.py PatternChecker.py 
	PatternValidator.py SelectionValidator.py 
	StringBaseValidator.py StringValidator.py TextValidator.py 
	__init__.py validators.zcml 
Log Message:
Merged Zope-3x-branch into newly forked Zope3 CVS Tree.


=== Zope3/lib/python/Zope/App/Formulator/Validators/BooleanValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from Zope.App.Formulator.Validator import Validator
+
+class BooleanValidator(Validator):
+
+    __implements__ = Validator.__implements__
+
+    
+    def validate(self, field, value):
+
+        if value in ['t', 1]:
+            return 1
+
+        if value in ['f', 0, '', None]:
+            return 0
+
+        # XXX Should raise some sort of error


=== Zope3/lib/python/Zope/App/Formulator/Validators/DateTimeValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from StringValidator import StringValidator
+from Zope.Misc.DateTimeParse import parse
+
+
+class DateTimeValidator(StringValidator):
+
+    propertyNames = StringValidator.propertyNames + \
+                    ['required', 'startDateTime', 'endDateTime']
+
+    requiredNotFound = 'Input is required but no input given.'
+    notDateTime = 'You did not enter a valid date and time.'
+    datetimeOutOfRange = 'The date and time you entered were out of range.'
+    
+    def validate(self, field, key, REQUEST):    
+        try:
+            year = field.validate_sub_field('year', REQUEST)
+            month = field.validate_sub_field('month', REQUEST)
+            day = field.validate_sub_field('day', REQUEST)
+            
+            if field.get_value('date_only'):
+                hour = 0
+                minute = 0
+            else:
+                hour = field.validate_sub_field('hour', REQUEST)
+                minute = field.validate_sub_field('minute', REQUEST)
+        except ValidationError:
+            self.raiseError('not_datetime', field)
+
+        # handling of completely empty sub fields
+        if ((year == '' and month == '' and day == '') and
+            (field.get_value('date_only') or (hour == '' and minute == ''))): 
+            if field.get_value('required'):
+                self.raise_error('required_not_found', field)
+            else:
+                # field is not required, return None for no entry
+                return None
+        # handling of partially empty sub fields; invalid datetime
+        if ((year == '' or month == '' or day == '') or
+            (not field.get_value('date_only') and
+             (hour == '' or minute == ''))):
+            self.raise_error('not_datetime', field)
+
+        try:
+            result = parse('%s/%s/%s %s:%s' %(year, month, day, hour, minute))
+        # ugh, a host of string based exceptions
+        except ('DateTimeError', 'Invalid Date Components', 'TimeError'):
+            self.raise_error('not_datetime', field)
+
+        # check if things are within range
+        start_datetime = field.get_value('start_datetime')
+        if (start_datetime is not None and
+            result < start_datetime):
+            self.raise_error('datetime_out_of_range', field)
+        end_datetime = field.get_value('end_datetime')
+        if (end_datetime is not None and
+            result >= end_datetime):
+            self.raise_error('datetime_out_of_range', field)
+
+        return result


=== Zope3/lib/python/Zope/App/Formulator/Validators/EmailValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+import re
+from StringValidator import StringValidator
+
+class EmailValidator(StringValidator):
+
+    __implements__ = StringValidator.__implements__
+
+    messageNames = StringValidator.messageNames + ['notEmail']
+
+    notEmail = 'You did not enter an email address.'
+
+    # contributed, I don't pretend to understand this..
+    pattern = re.compile("^([0-9a-z_&.+-]+!)*[0-9a-z_&.+-]+"
+                         "@(([0-9a-z]([0-9a-z-]*[0-9a-z])?\.)+"
+                         "[a-z]{2,3}|([0-9]{1,3}\.){3}[0-9]{1,3})$")
+    
+    def validate(self, field, value):
+        value = StringValidator.validate(self, field, value)
+
+        if value == "" and not field.getValue('isRequired'):
+            return value
+
+        if self.pattern.search(value.lower()) == None:
+            self.raiseError('notEmail', field)
+        return value


=== Zope3/lib/python/Zope/App/Formulator/Validators/FileValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from Zope.App.Formulator.Validator import Validator
+
+
+class FileValidator(Validator):
+
+    __implements__ = Validator.__implements__
+
+    def validate(self, field, value):
+        return value
+


=== Zope3/lib/python/Zope/App/Formulator/Validators/FloatValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from StringBaseValidator import StringBaseValidator
+
+
+class FloatValidator(StringBaseValidator):
+
+    __implements__ = StringBaseValidator.__implements__
+
+    messageNames = StringBaseValidator.messageNames + ['notFloat']
+    notFloat = "You did not enter a floating point number."
+
+
+    def validate(self, field, value):
+        value = StringBaseValidator.validate(self, field, value)
+        if value == "" and not field.getValue('isRequired'):
+            return value
+
+        try:
+            value = float(value)
+        except ValueError:
+            self.raiseError('notFloat', field)
+        return value


=== Zope3/lib/python/Zope/App/Formulator/Validators/IntegerValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from StringBaseValidator import StringBaseValidator
+
+class IntegerValidator(StringBaseValidator):
+    """ """
+
+    __implements__ = StringBaseValidator.__implements__
+
+    propertyNames = StringBaseValidator.propertyNames +\
+                    ['start', 'end']
+
+    start = ""
+    end = ""
+    messageNames = StringBaseValidator.messageNames +\
+                   ['notInteger', 'integerOutOfRange']
+
+    notInteger = 'You did not enter an integer.'
+    integerOutOfRange = 'The integer you entered was out of range.'
+
+    def validate(self, field, value):
+        value = StringBaseValidator.validate(self, field, value)
+        
+        # we need to add this check again
+        if value == "" and not field.getValue('isRequired'):
+            return value
+
+        try:
+            value = int(value)
+        except ValueError:
+            self.raiseError('notInteger', field)
+
+        if self.start != "" and value < self.start:
+            self.raiseError('integerOutOfRange', field)
+        if self.end != "" and value >= self.end:
+            self.raiseError('integerOutOfRange', field)
+        return value


=== Zope3/lib/python/Zope/App/Formulator/Validators/LinesValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from StringBaseValidator import StringBaseValidator
+
+
+class LinesValidator(StringBaseValidator):
+
+    propertyNames = StringBaseValidator.propertyNames +\
+                     ['maxLines', 'maxLineLength', 'maxLength']
+
+    maxLines = ""
+    maxLineLength = ""
+    maxLength = ""
+    
+    messagenNames = StringBaseValidator.messageNames +\
+                   ['tooManylines', 'lineTooLong', 'tooLong']
+
+    tooManyLines = 'You entered too many lines.'
+    lineTooLong = 'A line was too long.'
+    tooLong = 'You entered too many characters.'
+
+    
+    def validate(self, field, value):
+        value = StringBaseValidator.validate(self, field, value)
+        # we need to add this check again
+        if value == "" and not field.get_value('required'):
+            return []
+        
+        # check whether the entire input is too long
+        maxLength = field.get_value('maxLength') or 0
+        if maxLength and len(value) > maxLength:
+            self.raise_error('tooLong', field)
+        # split input into separate lines
+        lines = string.split(value, "\n")
+
+        # check whether we have too many lines
+        maxLines = field.get_value('maxLines') or 0
+        if maxLines and len(lines) > maxLines:
+            self.raise_error('tooManyLines', field)
+
+        # strip extraneous data from lines and check whether each line is
+        # short enough
+        maxLineLength = field.get_value('maxLineLength') or 0
+        result = []
+        for line in lines:
+            line = string.strip(line)
+            if maxLineLength and len(line) > maxLineLength:
+                self.raise_error('lineTooLong', field)
+            result.append(line)
+            
+        return result


=== Zope3/lib/python/Zope/App/Formulator/Validators/LinkValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from StringValidator import StringValidator
+
+
+class LinkHelper:
+    """A helper class to check if links are openable.
+    """
+    status = 0
+
+    def __init__(self, link):
+        self.link = link
+        
+    def open(self):
+        try:
+            urlopen(self.link)
+        except:
+            # all errors will definitely result in a failure
+            pass
+        else:
+            # FIXME: would like to check for 404 errors and such?
+            self.status = 1
+
+
+class LinkValidator(StringValidator):
+    """ """
+
+    propertyNames = StringValidator.propertyNames +\
+                     ['checkLink', 'checkTimeout', 'linkType']
+    
+    checkLink = 0
+    checkTimeout = 7.0
+    linkType = "external"
+    
+    messageNames = StringValidator.messageNames + ['notLink']
+    
+    notLink = 'The specified link is broken.'
+    
+    def validate(self, field, value):
+        value = StringValidator.validate(self, field, value)
+        if value == "" and not field.get_value('required'):
+            return value
+        
+        linkType = field.get_value('linkType')
+        if linkType == 'internal':
+            value = urljoin(REQUEST['BASE0'], value)
+        elif linkType == 'relative':
+            value = urljoin(REQUEST.URL[-1], value)
+        # otherwise must be external
+
+        # FIXME: should try regular expression to do some more checking here?
+        
+        # if we don't need to check the link, we're done now
+        if not field.get_value('checkLink'):
+            return value
+
+        # check whether we can open the link
+        link = LinkHelper(value)
+        thread = Thread(target=link.open)
+        thread.start()
+        thread.join(field.get_value('checkTimeout'))
+        del thread
+        if not link.status:
+            self.raise_error('notLink', field)
+            
+        return value


=== Zope3/lib/python/Zope/App/Formulator/Validators/ListLinesValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from LinesValidator import LinesValidator
+
+class ListLinesValidator(LinesValidator):
+    """A validator that can deal with lines that have a | separator
+    in them to split between text and value of list items.
+    """
+
+    __implements__ = LinesValidator.__implements__
+    
+    def validate(self, value):
+        
+        value = Validator.LinesValidator.validate(value)
+        result = []
+        for line in value:
+            elements = string.split(line, "|")
+            if len(elements) >= 2:
+                text, value = elements[:2]
+            else:
+                text = line
+                value = line
+            text = string.strip(text)
+            value = string.strip(value)
+            result.append((text, value))
+        return result


=== Zope3/lib/python/Zope/App/Formulator/Validators/MultiSelectionValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from Zope.App.Formulator.Validator import Validator
+from types import ListType
+
+class MultiSelectionValidator(Validator):
+    """ """
+    
+    __implements__ = Validator.__implements__
+
+    propertyNames = Validator.propertyNames + ['required']
+
+    required = 1
+
+    messageNames = Validator.messageNames + ['requiredNotFound',
+                                             'unknownSelection']
+    
+    requiredNotFound = 'Input is required but no input given.'
+    unknownSelection = 'You selected an item that was not in the list.'
+
+    
+    def validate(self, field, value):
+        values = value
+        # NOTE: a hack to deal with single item selections
+        if not isinstance(values, ListType):
+            # put whatever we got in a list
+            values = [values]
+
+        # if we selected nothing and entry is required, give error, otherwise
+        # give entry list
+        if len(values) == 0:
+            if field.get_value('isRequired'):
+                self.raise_error('requiredNotFound', field)
+            else:
+                return values
+            
+        # create a dictionary of possible values
+        value_dict = {}
+        for item in field.get_value('items'):
+            try:
+                item_text, item_value = item
+            except ValueError:
+                item_text = item
+                item_value = item
+            value_dict[item_value] = 0
+        # check whether all values are in dictionary
+        result = []
+        for value in values:
+            # FIXME: hack to accept int values as well
+            try:
+                int_value = int(value)
+            except ValueError:
+                int_value = None
+            if int_value is not None and (int_value in value_dict):
+                result.append(int_value)
+                continue
+            if value in value_dict:
+                result.append(value)
+                continue
+            self.raise_error('unknownSelection', field)
+        # everything checks out
+        return result


=== Zope3/lib/python/Zope/App/Formulator/Validators/PatternChecker.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+import re
+
+# Symbols that are used to represent groups of characters
+
+NUMBERSYMBOL  = 'd'             # 0-9
+CHARSYMBOL    = 'e'             # a-zA-Z
+NUMCHARSYMBOL = 'f'             # a-zA-Z0-9
+
+# List of characters, that are special to Regex. Listing them here and
+# therefore escaping them will help making the Validator secure.
+# NOTE: Please do not add '*', since it is used to determine inifinite
+# long char symbol rows. (See examples at the of the file.)
+
+DANGEROUSCHARS = '\\()+?.$'
+
+class PatternChecker:
+    """
+    This class defines a basic user friendly checker and processor of
+    string values according to pattern.
+    It can verify whether a string value fits a certain pattern of
+    digits and letters and possible special characters.
+    """
+    # a dictionary that converts an array of symbols to regex expressions
+    symbol_regex_dict = {NUMBERSYMBOL  : '([0-9]{%i,%s})',
+                         CHARSYMBOL    : '([a-zA-Z]{%i,%s})',
+                         NUMCHARSYMBOL : '([0-9a-zA-Z]{%i,%s})'}
+    
+    def _escape(self, match_object):
+        """Escape a single character.
+        """ 
+        return '\\' + match_object.group(0)
+    
+    def _escape_special_characters(self, s):
+        """Escape the characters that have a special meaning in regex.
+        """
+        return re.sub('[' + DANGEROUSCHARS + ']', self._escape, s)
+
+    def _unescape_special_characters(self, s):
+        """Reverse the escaping, so that the final string is as close as
+        possible to the original one.
+        """
+        return re.sub('\\\\', '', s)
+
+    def _replace_symbol_by_regex(self, match_object):
+        """Replace the character symbol with their respective regex.
+        """
+        length = len(match_object.group(0))
+
+        # Yikes, what a hack! But I could not come up with something better.
+        if match_object.group(0)[-1] == '*':
+            min = length - 1
+            max = ''
+        else:
+            min = length
+            max = str(min)
+            
+        return self.symbol_regex_dict[match_object.group(0)[0]] %(min, max)
+
+    def make_regex_from_pattern(self, pattern):
+        """Replaces all symbol occurences and creates a complete regex
+        string.
+        """
+        regex = self._escape_special_characters(pattern)
+        for symbol in [NUMBERSYMBOL, CHARSYMBOL, NUMCHARSYMBOL]:
+            regex = re.sub(symbol+'{1,}\*?',
+                           self._replace_symbol_by_regex, regex)
+        return '^ *' + regex + ' *$'
+    
+    def construct_value_from_match(self, result, pattern):
+        """After we validated the string, we put it back together; this is
+        good, since we can easily clean up the data this way.
+        """
+        value = self._escape_special_characters(pattern)
+        _symbols = '['+NUMBERSYMBOL + CHARSYMBOL + NUMCHARSYMBOL + ']'
+        re_obj = re.compile(_symbols+'{1,}\*?')
+        for res in result.groups():
+            match = re_obj.search(value)
+            value = value[:match.start()] + res + value[match.end():]
+        return value
+
+    def clean_value(self, value):
+        """Clean up unnecessary white characters.
+        """
+        # same as string.strip, but since I am using re everywhere here,
+        # why not use it now too?
+        value = re.sub('^\s*', '', value)
+        value = re.sub('\s*$', '', value)
+        # make out of several white spaces, one whitespace...
+        value = re.sub('  *', ' ', value)
+        return value
+    
+    def validate_value(self, patterns, value):
+        """Validate method that manges the entire validation process.
+        
+        The validator goes through each pattern and
+        tries to get a match to the value (second parameter). At the end, the
+        first pattern of the list is taken to construct the value again; this
+        ensures data cleansing and a common data look.
+        """
+        value = self.clean_value(value)
+
+        result = None
+        for pattern in patterns:
+            regex = self.make_regex_from_pattern(pattern)
+            re_obj = re.compile(regex)
+            result = re_obj.search(value)
+            if result:
+                break
+
+        if not result:
+            return None
+
+        value = self.construct_value_from_match(result, patterns[0])
+        return self._unescape_special_characters(value)
+
+if __name__ == '__main__':
+
+    val = PatternChecker()
+
+    # American long ZIP
+    print val.validate_value(['ddddd-dddd'], '34567-1298')
+    print val.validate_value(['ddddd-dddd'], '  34567-1298  \t  ')
+
+    # American phone number
+    print val.validate_value(['(ddd) ddd-dddd', 'ddd-ddd-dddd',
+                              'ddd ddd-dddd'],
+                             '(345) 678-1298')
+    print val.validate_value(['(ddd) ddd-dddd', 'ddd-ddd-dddd',
+                              'ddd ddd-dddd'],
+                             '345-678-1298')
+
+    # American money
+    print val.validate_value(['$ d*.dd'], '$ 1345345.00')
+
+    # German money
+    print val.validate_value(['d*.dd DM'], '267.98 DM')
+
+    # German license plate 
+    print val.validate_value(['eee ee-ddd'], 'OSL HR-683')
+
+    # German phone number (international)
+    print val.validate_value(['+49 (d*) d*'], '+49 (3574) 7253')
+    print val.validate_value(['+49 (d*) d*'], '+49  (3574)  7253')
+
+
+
+
+
+
+
+
+


=== Zope3/lib/python/Zope/App/Formulator/Validators/PatternValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from StringValidator import StringValidator
+import PatternChecker
+
+class PatternValidator(StringValidator):
+
+    __implements__ = StringValidator.__implements__
+
+    # does the real work
+    checker = PatternChecker.PatternChecker()
+    
+    propertyNames = StringValidator.propertyNames +\
+                     ['pattern']
+
+    pattern = ""
+
+    messageNames = StringValidator.messageNames +\
+                    ['patternNotMatched']
+
+    patternNotMatched = "The entered value did not match the pattern."
+
+    def validate(self, field, value):
+        value = StringValidator.validate(self, field, value)
+        
+        if value == "" and not field.get_value('required'):
+            return value
+
+        value = self.checker.validate_value([field.get_value('pattern')],
+                                            value)
+        if value is None:
+            self.raise_error('patternNotMatched', field)
+        return value


=== Zope3/lib/python/Zope/App/Formulator/Validators/SelectionValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from StringBaseValidator import StringBaseValidator
+
+
+class SelectionValidator(StringBaseValidator):
+
+    __implements__ = StringBaseValidator.__implements__
+
+    messageNames = StringBaseValidator.messageNames +\
+                   ['unknownSelection']
+
+    unknownSelection = 'You selected an item that was not in the list.'
+    
+    def validate(self, field, value):
+        value = StringBaseValidator.validate(self, field, value)
+
+        if value == "" and not field.get_value('required'):
+            return value
+
+        # get the text and the value from the list of items
+        for item in field.get_value('items'):
+            try:
+                item_text, item_value = item
+            except ValueError:
+                item_text = item
+                item_value = item
+            
+            # check if the value is equal to the *string* version of
+            # item_value; if that's the case, we can return the *original*
+            # value in the list (not the submitted value). This way, integers
+            # will remain integers.
+            if str(item_value) == value:
+                return item_value
+            
+        # if we didn't find the value, return error
+        self.raise_error('unknownSelection', field)


=== Zope3/lib/python/Zope/App/Formulator/Validators/StringBaseValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from Zope.App.Formulator.Validator import Validator
+from types import StringType
+
+  
+class StringBaseValidator(Validator):
+    """Simple string validator.
+    """
+
+    __implements__ = Validator.__implements__
+    
+    propertyNames = Validator.propertyNames + ['required']
+    messageNames = Validator.messageNames + ['requiredNotFound']
+    
+    requiredNotFound = 'Input is required but no input given.'
+    illegalValue = 'The value is not a string.'
+
+        
+    def validate(self, field, value):
+        """ """
+        if not isinstance(value, StringType):
+            self.raiseError('illegalValue', field)
+        value = value.strip()
+        if field.getValue('isRequired') and value == "":
+            self.raiseError('requiredNotFound', field)
+
+        return value


=== Zope3/lib/python/Zope/App/Formulator/Validators/StringValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from StringBaseValidator import StringBaseValidator
+
+
+class StringValidator(StringBaseValidator):
+    """ """
+
+    __implements__ = StringBaseValidator.__implements__
+    
+
+    propertyNames = StringBaseValidator.propertyNames + \
+                    ['maxLength', 'truncate']
+    maxLength = 0
+    truncate = 0
+    
+    messageNames = StringBaseValidator.messageNames + \
+                    ['tooLong']
+
+    tooLong = 'Too much input was given.'
+
+
+    def validate(self, field, value):
+        value = StringBaseValidator.validate(self, field, value)
+
+        maxLength = self.maxLength or 0
+        
+        if maxLength > 0 and len(value) > maxLength:
+            if self.truncate:
+                value = value[:maxLength]
+            else:
+                self.raiseError('tooLong', field)
+
+        return value
+


=== Zope3/lib/python/Zope/App/Formulator/Validators/TextValidator.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from LinesValidator import LinesValidator
+
+class TextValidator(LinesValidator):
+    """ """
+    
+    def validate(self, field, value):
+        value = LinesValidator.validate(self, field, value)
+        # we need to add this check again
+        if value == [] and not field.get_value('isRequired'):
+            return ""
+
+        # join everything into string again with \n and return
+        return "\n".join(value)
+


=== Zope3/lib/python/Zope/App/Formulator/Validators/__init__.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################


=== Zope3/lib/python/Zope/App/Formulator/Validators/validators.zcml 1.1 => 1.2 ===
+   xmlns='http://namespaces.zope.org/zope'
+   xmlns:formulator='http://namespaces.zope.org/formulator'
+>
+
+  <formulator:registerValidator name="BooleanValidator"
+    validator="Zope.App.Formulator.Validators.BooleanValidator." />
+
+  <formulator:registerValidator name="DateTimeValidator"
+    validator="Zope.App.Formulator.Validators.DateTimeValidator." />
+
+  <formulator:registerValidator name="EmailValidator"
+    validator="Zope.App.Formulator.Validators.EmailValidator." />
+
+  <formulator:registerValidator name="FileValidator"
+    validator="Zope.App.Formulator.Validators.FileValidator." />
+
+  <formulator:registerValidator name="FloatValidator"
+    validator="Zope.App.Formulator.Validators.FloatValidator." />
+
+  <formulator:registerValidator name="IntegerValidator"
+    validator="Zope.App.Formulator.Validators.IntegerValidator." />
+
+  <formulator:registerValidator name="LinesValidator"
+    validator="Zope.App.Formulator.Validators.LinesValidator." />
+
+  <formulator:registerValidator name="LinkValidator"
+    validator="Zope.App.Formulator.Validators.LinkValidator." />
+
+  <formulator:registerValidator name="ListLinesValidator"
+    validator="Zope.App.Formulator.Validators.ListLinesValidator." />
+
+  <formulator:registerValidator name="MultiSelectionValidator"
+    validator="Zope.App.Formulator.Validators.MultiSelectionValidator." />
+
+  <formulator:registerValidator name="PatternValidator"
+    validator="Zope.App.Formulator.Validators.PatternValidator." />
+
+  <formulator:registerValidator name="SelectionValidator"
+    validator="Zope.App.Formulator.Validators.SelectionValidator." />
+
+  <formulator:registerValidator name="StringBaseValidator"
+    validator="Zope.App.Formulator.Validators.StringBaseValidator." />
+
+  <formulator:registerValidator name="StringValidator"
+    validator="Zope.App.Formulator.Validators.StringValidator." />
+
+  <formulator:registerValidator name="TextValidator"
+    validator="Zope.App.Formulator.Validators.TextValidator." />
+
+</zopeConfigure>