[Zope3-checkins] CVS: Zope3/src/zope/schema - interfaces.py:1.20 vocabulary.py:1.6

Fred L. Drake, Jr. fred@zope.com
Fri, 30 May 2003 02:10:58 -0400


Update of /cvs-repository/Zope3/src/zope/schema
In directory cvs.zope.org:/tmp/cvs-serv32178/schema

Modified Files:
	interfaces.py vocabulary.py 
Log Message:
Refactoring of the multi-select vocabulary field:
- define 4 concrete types covering uniqueness constraints (whether a value
  may appear multiple times) and ordering support (whether theres

=== Zope3/src/zope/schema/interfaces.py 1.19 => 1.20 ===
--- Zope3/src/zope/schema/interfaces.py:1.19	Wed May 28 13:09:03 2003
+++ Zope3/src/zope/schema/interfaces.py	Fri May 30 02:10:58 2003
@@ -398,27 +398,6 @@
         )
 
 
-class IBaseVocabulary(Interface):
-    """Representation of a vocabulary.
-
-    At this most basic level, a vocabulary only need to support a test
-    for containment.  This can be implemented either by __contains__()
-    or by sequence __getitem__() (the later only being useful for
-    vocabularies which are intrinsically ordered).
-    """
-
-    def getQuery():
-        """Return an IVocabularyQuery object for this vocabulary.
-
-        Vocabularies which do not support query must return None.
-        """
-
-    def getTerm(value):
-        """Return the ITerm object for the term 'value'.
-
-        If 'value' is not a valid term, this method raises LookupError.
-        """
-
 class IVocabularyQuery(Interface):
     """Query object for a vocabulary.
 
@@ -459,18 +438,26 @@
         """)
 
 
-class IVocabularyTokenized(Interface):
-    """Vocabulary that provides support for tokenized representation.
-
-    This interface must be used as a mix-in with IBaseVocabulary.
+class IBaseVocabulary(Interface):
+    """Representation of a vocabulary.
 
-    Terms returned from getTerm() and provided by iteration must
-    conform to ITokenizedTerm.
+    At this most basic level, a vocabulary only need to support a test
+    for containment.  This can be implemented either by __contains__()
+    or by sequence __getitem__() (the later only being useful for
+    vocabularies which are intrinsically ordered).
     """
 
-    def getTermByToken(token):
-        """Return an ITokenizedTerm for the passed-in token."""
+    def getQuery():
+        """Return an IVocabularyQuery object for this vocabulary.
+
+        Vocabularies which do not support query must return None.
+        """
+
+    def getTerm(value):
+        """Return the ITerm object for the term 'value'.
 
+        If 'value' is not a valid term, this method raises LookupError.
+        """
 
 class IIterableVocabulary(Interface):
     """Vocabulary which supports iteration over allowed values.
@@ -497,6 +484,19 @@
     """Vocabulary which is iterable."""
 
 
+class IVocabularyTokenized(Interface):
+    """Vocabulary that provides support for tokenized representation.
+
+    This interface must be used as a mix-in with IBaseVocabulary.
+
+    Terms returned from getTerm() and provided by iteration must
+    conform to ITokenizedTerm.
+    """
+
+    def getTermByToken(token):
+        """Return an ITokenizedTerm for the passed-in token."""
+
+
 class IVocabularyFieldMixin(Interface):
     # Mix-in interface that defines interesting things common to all
     # vocabulary fields.
@@ -526,11 +526,45 @@
     """
 
 
-class IVocabularyMultiField(IVocabularyFieldMixin, IField):
+class IVocabularyMultiField(IVocabularyFieldMixin, IMinMaxLen, IField):
+    # XXX This is really a base class used in the more specific
+    # IVocabulary*Field interfaces.
     """Field with a value containing selections from a vocabulary..
 
     The value for fields of this type need to support at least
     containment checks using 'in' and iteration.
+
+    The length constraint provided by IMinMaxLen constrains the number
+    of elements in the value.
+    """
+
+
+class IVocabularyBagField(IVocabularyMultiField):
+    """Field representing an unordered collection of values from a
+    vocabulary.
+
+    Specific values may be represented more than once.
+    """
+
+class IVocabularyListField(IVocabularyMultiField):
+    """Field representing an ordered collection of values from a
+    vocabulary.
+
+    Specific values may be represented more than once.
+    """
+
+class IVocabularySetField(IVocabularyMultiField):
+    """Field representing an unordered collection of values from a
+    vocabulary.
+
+    Specific values may be represented at most once.
+    """
+
+class IVocabularyUniqueListField(IVocabularyMultiField):
+    """Field representing an ordered collection of values from a
+    vocabulary.
+
+    Specific values may be represented at most once.
     """
 
 


=== Zope3/src/zope/schema/vocabulary.py 1.5 => 1.6 ===
--- Zope3/src/zope/schema/vocabulary.py:1.5	Wed May 28 17:23:20 2003
+++ Zope3/src/zope/schema/vocabulary.py	Fri May 30 02:10:58 2003
@@ -18,10 +18,14 @@
 from zope.schema import errornames
 from zope.schema.interfaces import ValidationError
 from zope.schema.interfaces import IVocabularyRegistry
-from zope.schema.interfaces import IVocabularyField, IVocabularyMultiField
-from interfaces import IVocabulary, IVocabularyTokenized, ITokenizedTerm
+from zope.schema.interfaces import IVocabularyField
+from zope.schema.interfaces import IVocabularyBagField, IVocabularyListField
+from zope.schema.interfaces import IVocabularySetField
+from zope.schema.interfaces import IVocabularyUniqueListField
+from zope.schema.interfaces import IVocabulary, IVocabularyTokenized
+from zope.schema.interfaces import ITokenizedTerm
 from zope.interface.declarations import directlyProvides
-from zope.schema import TextLine
+from zope.schema import MinMaxLen, TextLine
 
 try:
     basestring  # new in Python 2.3
@@ -68,12 +72,19 @@
         return clone
 
 
-class VocabularyMultiField(VocabularyField):
+class VocabularyMultiField(MinMaxLen, VocabularyField):
     """Field that adds support for use of an external vocabulary.
 
     The value is a collection of values from the vocabulary.
+
+    This class cannot be used directly; a subclass must be used to
+    specify concrete behavior.
     """
-    __implements__ = IVocabularyMultiField
+    def __init__(self, **kw):
+        if self.__class__ is VocabularyMultiField:
+            raise NotImplementedError(
+                "The VocabularyMultiField class cannot be used directly.")
+        super(VocabularyMultiField, self).__init__(**kw)
 
     def _validate(self, value):
         vocab = self.vocabulary
@@ -82,6 +93,34 @@
         for v in value:
             if v not in vocab:
                 raise ValidationError(errornames.ConstraintNotSatisfied, v)
+
+class UniqueElements(object):
+    """Mix-in class that checks that each contained element is unique."""
+
+    def _validate(self, value):
+        d = {}
+        for v in value:
+            if v in d:
+                raise ValidationError()
+            d[v] = v
+        super(UniqueElements, self)._validate(value)
+
+class VocabularyBagField(VocabularyMultiField):
+    __implements__ = IVocabularyBagField
+    __doc__ = IVocabularyBagField.__doc__
+
+class VocabularyListField(VocabularyMultiField):
+    __implements__ = IVocabularyListField
+    __doc__ = IVocabularyListField.__doc__
+
+class VocabularySetField(UniqueElements, VocabularyMultiField):
+    __implements__ = IVocabularySetField
+    __doc__ = IVocabularySetField.__doc__
+
+class VocabularyUniqueListField(UniqueElements, VocabularyMultiField):
+    __implements__ = IVocabularyUniqueListField
+    __doc__ = IVocabularyUniqueListField.__doc__
+
 
 # simple vocabularies performing enumerated-like tasks