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