1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html#License
3 /*
4 *******************************************************************************
5 *
6 *   Copyright (C) 2004-2010, International Business Machines
7 *   Corporation and others.  All Rights Reserved.
8 *
9 *******************************************************************************
10 *   file name:  ReplaceableContextIterator.java
11 *   encoding:   US-ASCII
12 *   tab size:   8 (not used)
13 *   indentation:4
14 *
15 *   created on: 2005feb04
16 *   created by: Markus W. Scherer
17 *
18 *   Implementation of UCaseProps.ContextIterator, iterates over a Replaceable.
19 *   Java port of casetrn.cpp/utrans_rep_caseContextIterator().
20 */
21 
22 package com.ibm.icu.text;
23 
24 import com.ibm.icu.impl.UCaseProps;
25 
26 /**
27  * Implementation of UCaseProps.ContextIterator, iterates over a Replaceable.
28  * See casetrn.cpp/utrans_rep_caseContextIterator().
29  * See also UCharacter.StringContextIterator.
30  */
31 class ReplaceableContextIterator implements UCaseProps.ContextIterator {
32     /**
33      * Constructor.
34      * @param rep Replaceable to iterate over.
35      */
ReplaceableContextIterator()36     ReplaceableContextIterator() {
37         this.rep=null;
38         limit=cpStart=cpLimit=index=contextStart=contextLimit=0;
39         dir=0;
40         reachedLimit=false;
41     }
42 
43     /**
44      * Set the text for iteration.
45      * @param rep Iteration text.
46      */
setText(Replaceable rep)47     public void setText(Replaceable rep) {
48         this.rep=rep;
49         limit=contextLimit=rep.length();
50         cpStart=cpLimit=index=contextStart=0;
51         dir=0;
52         reachedLimit=false;
53     }
54 
55     /**
56      * Set the index where nextCaseMapCP() is to start iterating.
57      * @param index Iteration start index for nextCaseMapCP().
58      */
setIndex(int index)59     public void setIndex(int index) {
60         cpStart=cpLimit=index;
61         this.index=0;
62         dir=0;
63         reachedLimit=false;
64     }
65 
66     /**
67      * Get the index of where the code point currently being case-mapped starts.
68      * @return The start index of the current code point.
69      */
getCaseMapCPStart()70     public int getCaseMapCPStart() {
71         return cpStart;
72     }
73 
74     /**
75      * Set the iteration limit for nextCaseMapCP() to an index within the string.
76      * If the limit parameter is negative or past the string, then the
77      * string length is restored as the iteration limit.
78      *
79      * @param lim The iteration limit.
80      */
setLimit(int lim)81     public void setLimit(int lim) {
82         if(0<=lim && lim<=rep.length()) {
83             limit=lim;
84         } else {
85             limit=rep.length();
86         }
87         reachedLimit=false;
88     }
89 
90     /**
91      * Set the start and limit indexes for context iteration with next().
92      * @param contextStart Start of context for next().
93      * @param contextLimit Limit of context for next().
94      */
setContextLimits(int contextStart, int contextLimit)95     public void setContextLimits(int contextStart, int contextLimit) {
96         if(contextStart<0) {
97             this.contextStart=0;
98         } else if(contextStart<=rep.length()) {
99             this.contextStart=contextStart;
100         } else {
101             this.contextStart=rep.length();
102         }
103         if(contextLimit<this.contextStart) {
104             this.contextLimit=this.contextStart;
105         } else if(contextLimit<=rep.length()) {
106             this.contextLimit=contextLimit;
107         } else {
108             this.contextLimit=rep.length();
109         }
110         reachedLimit=false;
111     }
112 
113     /**
114      * Iterate forward through the string to fetch the next code point
115      * to be case-mapped, and set the context indexes for it.
116      *
117      * @return The next code point to be case-mapped, or <0 when the iteration is done.
118      */
nextCaseMapCP()119     public int nextCaseMapCP() {
120         int c;
121         if(cpLimit<limit) {
122             cpStart=cpLimit;
123             c=rep.char32At(cpLimit);
124             cpLimit+=UTF16.getCharCount(c);
125             return c;
126         } else {
127             return -1;
128         }
129     }
130 
131     /**
132      * Replace the current code point by its case mapping,
133      * and update the indexes.
134      *
135      * @param text Replacement text.
136      * @return The delta for the change of the text length.
137      */
replace(String text)138     public int replace(String text) {
139         int delta=text.length()-(cpLimit-cpStart);
140         rep.replace(cpStart, cpLimit, text);
141         cpLimit+=delta;
142         limit+=delta;
143         contextLimit+=delta;
144         return delta;
145     }
146 
147     /**
148      * Did forward context iteration with next() reach the iteration limit?
149      * @return Boolean value.
150      */
didReachLimit()151     public boolean didReachLimit() {
152         return reachedLimit;
153     }
154 
155     // implement UCaseProps.ContextIterator
reset(int direction)156     public void reset(int direction) {
157         if(direction>0) {
158             /* reset for forward iteration */
159             this.dir=1;
160             index=cpLimit;
161         } else if(direction<0) {
162             /* reset for backward iteration */
163             this.dir=-1;
164             index=cpStart;
165         } else {
166             // not a valid direction
167             this.dir=0;
168             index=0;
169         }
170         reachedLimit=false;
171     }
172 
next()173     public int next() {
174         int c;
175 
176         if(dir>0) {
177             if(index<contextLimit) {
178                 c=rep.char32At(index);
179                 index+=UTF16.getCharCount(c);
180                 return c;
181             } else {
182                 // forward context iteration reached the limit
183                 reachedLimit=true;
184             }
185         } else if(dir<0 && index>contextStart) {
186             c=rep.char32At(index-1);
187             index-=UTF16.getCharCount(c);
188             return c;
189         }
190         return -1;
191     }
192 
193     // variables
194     protected Replaceable rep;
195     protected int index, limit, cpStart, cpLimit, contextStart, contextLimit;
196     protected int dir; // 0=initial state  >0=forward  <0=backward
197     protected boolean reachedLimit;
198 }
199