Code

svn repository setup
[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.4 2005-02-16 22:07:33 richard Exp $'''
22 __docformat__ = 'restructuredtext'
23 __version__='$Revision: 1.4 $'[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 = iter(seq)     # force seq to be an iterator
35         self._inner = iterInner
36         self._prep_next = iterInner.prep_next
38     def __getattr__(self, name):
39         try:
40             inner = getattr(self._inner, 'it_' + name)
41         except AttributeError:
42             raise AttributeError, name
43         return inner(self)
45     def next(self):
46         if not (hasattr(self, '_next') or self._prep_next(self)):
47             return 0
48         self.index = i = self.nextIndex
49         self.nextIndex = i+1
50         self._advance(self)
51         return 1
53     def _advance(self, it):
54         self.item = self._next
55         del self._next
56         del self.end
57         self._advance = self._inner.advance
58         self.start = 1
59             
60     def number(self): return self.nextIndex
62     def even(self): return not self.index % 2
64     def odd(self): return self.index % 2
66     def letter(self, base=ord('a'), radix=26):
67         index = self.index
68         s = ''
69         while 1:
70             index, off = divmod(index, radix)
71             s = chr(base + off) + s
72             if not index: return s
74     def Letter(self):
75         return self.letter(base=ord('A'))
77     def Roman(self, rnvalues=(
78                     (1000,'M'),(900,'CM'),(500,'D'),(400,'CD'),
79                     (100,'C'),(90,'XC'),(50,'L'),(40,'XL'),
80                     (10,'X'),(9,'IX'),(5,'V'),(4,'IV'),(1,'I')) ):
81         n = self.index + 1
82         s = ''
83         for v, r in rnvalues:
84             rct, n = divmod(n, v)
85             s = s + r * rct
86         return s
88     def roman(self, lower=string.lower):
89         return lower(self.Roman())
91     def first(self, name=None):
92         if self.start: return 1
93         return not self.same_part(name, self._last, self.item)
95     def last(self, name=None):
96         if self.end: return 1
97         return not self.same_part(name, self.item, self._next)
99     def same_part(self, name, ob1, ob2):
100         if name is None:
101             return ob1 == ob2
102         no = []
103         return getattr(ob1, name, no) == getattr(ob2, name, no) is not no
105     def __iter__(self):
106         return IterIter(self)
108 class InnerBase:
109     '''Base Inner class for Iterators'''
110     # Prep sets up ._next and .end
111     def prep_next(self, it):
112         it.next = self.no_next
113         it.end = 1
114         return 0
116     # Advance knocks them down
117     def advance(self, it):
118         it._last = it.item
119         it.item = it._next
120         del it._next
121         del it.end
122         it.start = 0
123             
124     def no_next(self, it):
125         return 0
127     def it_end(self, it):
128         if hasattr(it, '_next'):
129             return 0
130         return not self.prep_next(it)
132 class SeqInner(InnerBase):
133     '''Inner class for sequence Iterators'''
135     def _supports(self, ob):
136         try: ob[0]
137         except (TypeError, AttributeError): return 0
138         except: pass
139         return 1
141     def prep_next(self, it):
142         i = it.nextIndex
143         try:
144             it._next = it.seq[i]
145         except IndexError:
146             it._prep_next = self.no_next
147             it.end = 1
148             return 0
149         it.end = 0
150         return 1
152     def it_length(self, it):
153         it.length = l = len(it.seq)
154         return l
156 try:
157     StopIteration=StopIteration
158 except NameError:
159     StopIteration="StopIteration"
161 class IterInner(InnerBase):
162     '''Iterator inner class for Python iterators'''
164     def _supports(self, ob):
165         try:
166             if hasattr(ob, 'next') and (ob is iter(ob)):
167                 return 1
168         except:
169             return 0
171     def prep_next(self, it):
172         try:
173             it._next = it.seq.next()
174         except StopIteration:
175             it._prep_next = self.no_next
176             it.end = 1
177             return 0
178         it.end = 0
179         return 1
181 class IterIter:
182     def __init__(self, it):
183         self.it = it
184         self.skip = it.nextIndex > 0 and not it.end
185     def next(self):
186         it = self.it
187         if self.skip:
188             self.skip = 0
189             return it.item
190         if it.next():
191             return it.item
192         raise StopIteration
194 seqInner = SeqInner()
195 iterInner = IterInner()