]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/impl/RuleCharacterIterator.java
icu4jsrc
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / impl / RuleCharacterIterator.java
1 /*\r
2 **********************************************************************\r
3 * Copyright (c) 2003, International Business Machines\r
4 * Corporation and others.  All Rights Reserved.\r
5 **********************************************************************\r
6 * Author: Alan Liu\r
7 * Created: September 23 2003\r
8 * Since: ICU 2.8\r
9 **********************************************************************\r
10 */\r
11 package com.ibm.icu.impl;\r
12 \r
13 import java.text.ParsePosition;\r
14 import com.ibm.icu.text.SymbolTable;\r
15 import com.ibm.icu.text.UTF16;\r
16 \r
17 /**\r
18  * An iterator that returns 32-bit code points.  This class is deliberately\r
19  * <em>not</em> related to any of the JDK or ICU4J character iterator classes\r
20  * in order to minimize complexity.\r
21  * @author Alan Liu\r
22  * @since ICU 2.8\r
23  */\r
24 public class RuleCharacterIterator {\r
25 \r
26     // TODO: Ideas for later.  (Do not implement if not needed, lest the\r
27     // code coverage numbers go down due to unused methods.)\r
28     // 1. Add a copy constructor, equals() method, clone() method.\r
29     // 2. Rather than return DONE, throw an exception if the end\r
30     // is reached -- this is an alternate usage model, probably not useful.\r
31     // 3. Return isEscaped from next().  If this happens,\r
32     // don't keep an isEscaped member variable.\r
33 \r
34     /**\r
35      * Text being iterated.\r
36      */    \r
37     private String text;\r
38 \r
39     /**\r
40      * Position of iterator.\r
41      */\r
42     private ParsePosition pos;\r
43 \r
44     /**\r
45      * Symbol table used to parse and dereference variables.  May be null.\r
46      */\r
47     private SymbolTable sym;\r
48 \r
49     /**\r
50      * Current variable expansion, or null if none.\r
51      */\r
52     private char[] buf;\r
53 \r
54     /**\r
55      * Position within buf[].  Meaningless if buf == null.\r
56      */\r
57     private int bufPos;\r
58 \r
59     /**\r
60      * Flag indicating whether the last character was parsed from an escape.\r
61      */\r
62     private boolean isEscaped;\r
63 \r
64     /**\r
65      * Value returned when there are no more characters to iterate.\r
66      */\r
67     public static final int DONE = -1;\r
68 \r
69     /**\r
70      * Bitmask option to enable parsing of variable names.  If (options &\r
71      * PARSE_VARIABLES) != 0, then an embedded variable will be expanded to\r
72      * its value.  Variables are parsed using the SymbolTable API.\r
73      */\r
74     public static final int PARSE_VARIABLES = 1;\r
75 \r
76     /**\r
77      * Bitmask option to enable parsing of escape sequences.  If (options &\r
78      * PARSE_ESCAPES) != 0, then an embedded escape sequence will be expanded\r
79      * to its value.  Escapes are parsed using Utility.unescapeAt().\r
80      */\r
81     public static final int PARSE_ESCAPES   = 2;    \r
82 \r
83     /**\r
84      * Bitmask option to enable skipping of whitespace.  If (options &\r
85      * SKIP_WHITESPACE) != 0, then whitespace characters will be silently\r
86      * skipped, as if they were not present in the input.  Whitespace\r
87      * characters are defined by UCharacterProperty.isRuleWhiteSpace().\r
88      */\r
89     public static final int SKIP_WHITESPACE = 4;\r
90 \r
91     /**\r
92      * Constructs an iterator over the given text, starting at the given\r
93      * position.\r
94      * @param text the text to be iterated\r
95      * @param sym the symbol table, or null if there is none.  If sym is null,\r
96      * then variables will not be deferenced, even if the PARSE_VARIABLES\r
97      * option is set.\r
98      * @param pos upon input, the index of the next character to return.  If a\r
99      * variable has been dereferenced, then pos will <em>not</em> increment as\r
100      * characters of the variable value are iterated.\r
101      */\r
102     public RuleCharacterIterator(String text, SymbolTable sym,\r
103                                  ParsePosition pos) {\r
104         if (text == null || pos.getIndex() > text.length()) {\r
105             throw new IllegalArgumentException();\r
106         }\r
107         this.text = text;\r
108         this.sym = sym;\r
109         this.pos = pos;\r
110         buf = null;\r
111     }\r
112     \r
113     /**\r
114      * Returns true if this iterator has no more characters to return.\r
115      */\r
116     public boolean atEnd() {\r
117         return buf == null && pos.getIndex() == text.length();\r
118     }\r
119 \r
120     /**\r
121      * Returns the next character using the given options, or DONE if there\r
122      * are no more characters, and advance the position to the next\r
123      * character.\r
124      * @param options one or more of the following options, bitwise-OR-ed\r
125      * together: PARSE_VARIABLES, PARSE_ESCAPES, SKIP_WHITESPACE.\r
126      * @return the current 32-bit code point, or DONE\r
127      */\r
128     public int next(int options) {\r
129         int c = DONE;\r
130         isEscaped = false;\r
131 \r
132         for (;;) {\r
133             c = _current();\r
134             _advance(UTF16.getCharCount(c));\r
135 \r
136             if (c == SymbolTable.SYMBOL_REF && buf == null &&\r
137                 (options & PARSE_VARIABLES) != 0 && sym != null) {\r
138                 String name = sym.parseReference(text, pos, text.length());\r
139                 // If name == null there was an isolated SYMBOL_REF;\r
140                 // return it.  Caller must be prepared for this.\r
141                 if (name == null) {\r
142                     break;\r
143                 }\r
144                 bufPos = 0;\r
145                 buf = sym.lookup(name);\r
146                 if (buf == null) {\r
147                     throw new IllegalArgumentException(\r
148                                 "Undefined variable: " + name);\r
149                 }\r
150                 // Handle empty variable value\r
151                 if (buf.length == 0) {\r
152                     buf = null;\r
153                 }\r
154                 continue;\r
155             }\r
156 \r
157             if ((options & SKIP_WHITESPACE) != 0 &&\r
158                 UCharacterProperty.isRuleWhiteSpace(c)) {\r
159                 continue;\r
160             }\r
161 \r
162             if (c == '\\' && (options & PARSE_ESCAPES) != 0) {\r
163                 int offset[] = new int[] { 0 };\r
164                 c = Utility.unescapeAt(lookahead(), offset);\r
165                 jumpahead(offset[0]);\r
166                 isEscaped = true;\r
167                 if (c < 0) {\r
168                     throw new IllegalArgumentException("Invalid escape");\r
169                 }\r
170             }\r
171 \r
172             break;\r
173         }\r
174 \r
175         return c;\r
176     }\r
177 \r
178     /**\r
179      * Returns true if the last character returned by next() was\r
180      * escaped.  This will only be the case if the option passed in to\r
181      * next() included PARSE_ESCAPED and the next character was an\r
182      * escape sequence.\r
183      */\r
184     public boolean isEscaped() {\r
185         return isEscaped;\r
186     }\r
187 \r
188     /**\r
189      * Returns true if this iterator is currently within a variable expansion.\r
190      */\r
191     public boolean inVariable() {\r
192         return buf != null;\r
193     }\r
194 \r
195     /**\r
196      * Returns an object which, when later passed to setPos(), will\r
197      * restore this iterator's position.  Usage idiom:\r
198      *\r
199      * RuleCharacterIterator iterator = ...;\r
200      * Object pos = iterator.getPos(null); // allocate position object\r
201      * for (;;) {\r
202      *   pos = iterator.getPos(pos); // reuse position object\r
203      *   int c = iterator.next(...);\r
204      *   ...\r
205      * }\r
206      * iterator.setPos(pos);\r
207      *\r
208      * @param p a position object previously returned by getPos(),\r
209      * or null.  If not null, it will be updated and returned.  If\r
210      * null, a new position object will be allocated and returned.\r
211      * @return a position object which may be passed to setPos(),\r
212      * either `p,' or if `p' == null, a newly-allocated object\r
213      */\r
214     public Object getPos(Object p) {\r
215         if (p == null) {\r
216             return new Object[] {buf, new int[] {pos.getIndex(), bufPos}};\r
217         }\r
218         Object[] a = (Object[]) p;\r
219         a[0] = buf;\r
220         int[] v = (int[]) a[1];\r
221         v[0] = pos.getIndex();\r
222         v[1] = bufPos;\r
223         return p;\r
224     }\r
225 \r
226     /**\r
227      * Restores this iterator to the position it had when getPos()\r
228      * returned the given object.\r
229      * @param p a position object previously returned by getPos()\r
230      */\r
231     public void setPos(Object p) {\r
232         Object[] a = (Object[]) p;\r
233         buf = (char[]) a[0];\r
234         int[] v = (int[]) a[1];\r
235         pos.setIndex(v[0]);\r
236         bufPos = v[1];\r
237     }\r
238 \r
239     /**\r
240      * Skips ahead past any ignored characters, as indicated by the given\r
241      * options.  This is useful in conjunction with the lookahead() method.\r
242      *\r
243      * Currently, this only has an effect for SKIP_WHITESPACE.\r
244      * @param options one or more of the following options, bitwise-OR-ed\r
245      * together: PARSE_VARIABLES, PARSE_ESCAPES, SKIP_WHITESPACE.\r
246      */\r
247     public void skipIgnored(int options) {\r
248         if ((options & SKIP_WHITESPACE) != 0) {\r
249             for (;;) {\r
250                 int a = _current();\r
251                 if (!UCharacterProperty.isRuleWhiteSpace(a)) break;\r
252                 _advance(UTF16.getCharCount(a));\r
253             }\r
254         }\r
255     }\r
256 \r
257     /**\r
258      * Returns a string containing the remainder of the characters to be\r
259      * returned by this iterator, without any option processing.  If the\r
260      * iterator is currently within a variable expansion, this will only\r
261      * extend to the end of the variable expansion.  This method is provided\r
262      * so that iterators may interoperate with string-based APIs.  The typical\r
263      * sequence of calls is to call skipIgnored(), then call lookahead(), then\r
264      * parse the string returned by lookahead(), then call jumpahead() to\r
265      * resynchronize the iterator.\r
266      * @return a string containing the characters to be returned by future\r
267      * calls to next()\r
268      */\r
269     public String lookahead() {\r
270         if (buf != null) {\r
271             return new String(buf, bufPos, buf.length - bufPos);\r
272         } else {\r
273             return text.substring(pos.getIndex());\r
274         }\r
275     }\r
276 \r
277     /**\r
278      * Advances the position by the given number of 16-bit code units.\r
279      * This is useful in conjunction with the lookahead() method.\r
280      * @param count the number of 16-bit code units to jump over\r
281      */\r
282     public void jumpahead(int count) {\r
283         if (count < 0) {\r
284             throw new IllegalArgumentException();\r
285         }\r
286         if (buf != null) {\r
287             bufPos += count;\r
288             if (bufPos > buf.length) {\r
289                 throw new IllegalArgumentException();\r
290             }\r
291             if (bufPos == buf.length) {\r
292                 buf = null;\r
293             }\r
294         } else {\r
295             int i = pos.getIndex() + count;\r
296             pos.setIndex(i);\r
297             if (i > text.length()) {\r
298                 throw new IllegalArgumentException();\r
299             }\r
300         }\r
301     }\r
302 \r
303     /**\r
304      * Returns a string representation of this object, consisting of the\r
305      * characters being iterated, with a '|' marking the current position.\r
306      * Position within an expanded variable is <em>not</em> indicated.\r
307      * @return a string representation of this object\r
308      */\r
309     public String toString() {\r
310         int b = pos.getIndex();\r
311         return text.substring(0, b) + '|' + text.substring(b);\r
312     }\r
313 \r
314     /**\r
315      * Returns the current 32-bit code point without parsing escapes, parsing\r
316      * variables, or skipping whitespace.\r
317      * @return the current 32-bit code point\r
318      */\r
319     private int _current() {\r
320         if (buf != null) {\r
321             return UTF16.charAt(buf, 0, buf.length, bufPos);\r
322         } else {\r
323             int i = pos.getIndex();\r
324             return (i < text.length()) ? UTF16.charAt(text, i) : DONE;\r
325         }\r
326     }\r
327     \r
328     /**\r
329      * Advances the position by the given amount.\r
330      * @param count the number of 16-bit code units to advance past\r
331      */\r
332     private void _advance(int count) {\r
333         if (buf != null) {\r
334             bufPos += count;\r
335             if (bufPos == buf.length) {\r
336                 buf = null;\r
337             }\r
338         } else {\r
339             pos.setIndex(pos.getIndex() + count);\r
340             if (pos.getIndex() > text.length()) {\r
341                 pos.setIndex(text.length());\r
342             }\r
343         }\r
344     }\r
345 }