Code

allow multiple :remove and :add elements per property being edited
[roundup.git] / roundup / cgi / ZTUtils / Iterator.py
1 ##############################################################################
2 #
3 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
4
5 # This software is subject to the provisions of the Zope Public License,
6 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 # FOR A PARTICULAR PURPOSE
11
12 ##############################################################################
13 __doc__='''Iterator class
15 Unlike the builtin iterators of Python 2.2+, these classes are
16 designed to maintain information about the state of an iteration.
17 The Iterator() function accepts either a sequence or a Python
18 iterator.  The next() method fetches the next item, and returns
19 true if it succeeds.
21 $Id: Iterator.py,v 1.2 2002-09-26 21:54:17 richard Exp $'''
22 __version__='$Revision: 1.2 $'[11:-2]
24 import string
26 class Iterator:
27     '''Simple Iterator class'''
29     __allow_access_to_unprotected_subobjects__ = 1
31     nextIndex = 0
32     def __init__(self, seq):
33         self.seq = seq
34         for inner in seqInner, iterInner:
35             if inner._supports(seq):
36                 self._inner = inner
37                 self._prep_next = inner.prep_next
38                 return
39         raise TypeError, "Iterator does not support %s" % `seq`
41     def __getattr__(self, name):
42         try:
43             inner = getattr(self._inner, 'it_' + name)
44         except AttributeError:
45             raise AttributeError, name
46         return inner(self)
48     def next(self):
49         if not (hasattr(self, '_next') or self._prep_next(self)):
50             return 0
51         self.index = i = self.nextIndex
52         self.nextIndex = i+1
53         self._advance(self)
54         return 1
56     def _advance(self, it):
57         self.item = self._next
58         del self._next
59         del self.end
60         self._advance = self._inner.advance
61         self.start = 1
62             
63     def number(self): return self.nextIndex
65     def even(self): return not self.index % 2
67     def odd(self): return self.index % 2
69     def letter(self, base=ord('a'), radix=26):
70         index = self.index
71         s = ''
72         while 1:
73             index, off = divmod(index, radix)
74             s = chr(base + off) + s
75             if not index: return s
77     def Letter(self):
78         return self.letter(base=ord('A'))
80     def Roman(self, rnvalues=(
81                     (1000,'M'),(900,'CM'),(500,'D'),(400,'CD'),
82                     (100,'C'),(90,'XC'),(50,'L'),(40,'XL'),
83                     (10,'X'),(9,'IX'),(5,'V'),(4,'IV'),(1,'I')) ):
84         n = self.index + 1
85         s = ''
86         for v, r in rnvalues:
87             rct, n = divmod(n, v)
88             s = s + r * rct
89         return s
91     def roman(self, lower=string.lower):
92         return lower(self.Roman())
94     def first(self, name=None):
95         if self.start: return 1
96         return not self.same_part(name, self._last, self.item)
98     def last(self, name=None):
99         if self.end: return 1
100         return not self.same_part(name, self.item, self._next)
102     def same_part(self, name, ob1, ob2):
103         if name is None:
104             return ob1 == ob2
105         no = []
106         return getattr(ob1, name, no) == getattr(ob2, name, no) is not no
108     def __iter__(self):
109         return IterIter(self)
111 class InnerBase:
112     '''Base Inner class for Iterators'''
113     # Prep sets up ._next and .end
114     def prep_next(self, it):
115         it.next = self.no_next
116         it.end = 1
117         return 0
119     # Advance knocks them down
120     def advance(self, it):
121         it._last = it.item
122         it.item = it._next
123         del it._next
124         del it.end
125         it.start = 0
126             
127     def no_next(self, it):
128         return 0
130     def it_end(self, it):
131         if hasattr(it, '_next'):
132             return 0
133         return not self.prep_next(it)
135 class SeqInner(InnerBase):
136     '''Inner class for sequence Iterators'''
138     def _supports(self, ob):
139         try: ob[0]
140         except (TypeError, AttributeError): return 0
141         except: pass
142         return 1
144     def prep_next(self, it):
145         i = it.nextIndex
146         try:
147             it._next = it.seq[i]
148         except IndexError:
149             it._prep_next = self.no_next
150             it.end = 1
151             return 0
152         it.end = 0
153         return 1
155     def it_length(self, it):
156         it.length = l = len(it.seq)
157         return l
159 try:
160     StopIteration=StopIteration
161 except NameError:
162     StopIteration="StopIteration"
164 class IterInner(InnerBase):
165     '''Iterator inner class for Python iterators'''
167     def _supports(self, ob):
168         try:
169             if hasattr(ob, 'next') and (ob is iter(ob)):
170                 return 1
171         except:
172             return 0
174     def prep_next(self, it):
175         try:
176             it._next = it.seq.next()
177         except StopIteration:
178             it._prep_next = self.no_next
179             it.end = 1
180             return 0
181         it.end = 0
182         return 1
184 class IterIter:
185     def __init__(self, it):
186         self.it = it
187         self.skip = it.nextIndex > 0 and not it.end
188     def next(self):
189         it = self.it
190         if self.skip:
191             self.skip = 0
192             return it.item
193         if it.next():
194             return it.item
195         raise StopIteration
197 seqInner = SeqInner()
198 iterInner = IterInner()