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