Code

96a64ba31caab501ac6b39f53f09c759dd100064
[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.3 2004-02-11 23:55:09 richard Exp $'''
22 __docformat__ = 'restructuredtext'
23 __version__='$Revision: 1.3 $'[11:-2]
25 import string
27 class Iterator:
28     '''Simple Iterator class'''
30     __allow_access_to_unprotected_subobjects__ = 1
32     nextIndex = 0
33     def __init__(self, seq):
34         self.seq = seq
35         for inner in seqInner, iterInner:
36             if inner._supports(seq):
37                 self._inner = inner
38                 self._prep_next = inner.prep_next
39                 return
40         raise TypeError, "Iterator does not support %s" % `seq`
42     def __getattr__(self, name):
43         try:
44             inner = getattr(self._inner, 'it_' + name)
45         except AttributeError:
46             raise AttributeError, name
47         return inner(self)
49     def next(self):
50         if not (hasattr(self, '_next') or self._prep_next(self)):
51             return 0
52         self.index = i = self.nextIndex
53         self.nextIndex = i+1
54         self._advance(self)
55         return 1
57     def _advance(self, it):
58         self.item = self._next
59         del self._next
60         del self.end
61         self._advance = self._inner.advance
62         self.start = 1
63             
64     def number(self): return self.nextIndex
66     def even(self): return not self.index % 2
68     def odd(self): return self.index % 2
70     def letter(self, base=ord('a'), radix=26):
71         index = self.index
72         s = ''
73         while 1:
74             index, off = divmod(index, radix)
75             s = chr(base + off) + s
76             if not index: return s
78     def Letter(self):
79         return self.letter(base=ord('A'))
81     def Roman(self, rnvalues=(
82                     (1000,'M'),(900,'CM'),(500,'D'),(400,'CD'),
83                     (100,'C'),(90,'XC'),(50,'L'),(40,'XL'),
84                     (10,'X'),(9,'IX'),(5,'V'),(4,'IV'),(1,'I')) ):
85         n = self.index + 1
86         s = ''
87         for v, r in rnvalues:
88             rct, n = divmod(n, v)
89             s = s + r * rct
90         return s
92     def roman(self, lower=string.lower):
93         return lower(self.Roman())
95     def first(self, name=None):
96         if self.start: return 1
97         return not self.same_part(name, self._last, self.item)
99     def last(self, name=None):
100         if self.end: return 1
101         return not self.same_part(name, self.item, self._next)
103     def same_part(self, name, ob1, ob2):
104         if name is None:
105             return ob1 == ob2
106         no = []
107         return getattr(ob1, name, no) == getattr(ob2, name, no) is not no
109     def __iter__(self):
110         return IterIter(self)
112 class InnerBase:
113     '''Base Inner class for Iterators'''
114     # Prep sets up ._next and .end
115     def prep_next(self, it):
116         it.next = self.no_next
117         it.end = 1
118         return 0
120     # Advance knocks them down
121     def advance(self, it):
122         it._last = it.item
123         it.item = it._next
124         del it._next
125         del it.end
126         it.start = 0
127             
128     def no_next(self, it):
129         return 0
131     def it_end(self, it):
132         if hasattr(it, '_next'):
133             return 0
134         return not self.prep_next(it)
136 class SeqInner(InnerBase):
137     '''Inner class for sequence Iterators'''
139     def _supports(self, ob):
140         try: ob[0]
141         except (TypeError, AttributeError): return 0
142         except: pass
143         return 1
145     def prep_next(self, it):
146         i = it.nextIndex
147         try:
148             it._next = it.seq[i]
149         except IndexError:
150             it._prep_next = self.no_next
151             it.end = 1
152             return 0
153         it.end = 0
154         return 1
156     def it_length(self, it):
157         it.length = l = len(it.seq)
158         return l
160 try:
161     StopIteration=StopIteration
162 except NameError:
163     StopIteration="StopIteration"
165 class IterInner(InnerBase):
166     '''Iterator inner class for Python iterators'''
168     def _supports(self, ob):
169         try:
170             if hasattr(ob, 'next') and (ob is iter(ob)):
171                 return 1
172         except:
173             return 0
175     def prep_next(self, it):
176         try:
177             it._next = it.seq.next()
178         except StopIteration:
179             it._prep_next = self.no_next
180             it.end = 1
181             return 0
182         it.end = 0
183         return 1
185 class IterIter:
186     def __init__(self, it):
187         self.it = it
188         self.skip = it.nextIndex > 0 and not it.end
189     def next(self):
190         it = self.it
191         if self.skip:
192             self.skip = 0
193             return it.item
194         if it.next():
195             return it.item
196         raise StopIteration
198 seqInner = SeqInner()
199 iterInner = IterInner()