1 /*
2 **********************************************************************
3 * Copyright (c) 2002-2012, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 **********************************************************************
6 * Date Name Description
7 * 01/21/2002 aliu Creation.
8 **********************************************************************
9 */
10
11 #include "unicode/utypes.h"
12
13 #if !UCONFIG_NO_TRANSLITERATION
14
15 #include "unicode/uniset.h"
16 #include "unicode/utf16.h"
17 #include "strrepl.h"
18 #include "rbt_data.h"
19 #include "util.h"
20
21 U_NAMESPACE_BEGIN
22
~UnicodeReplacer()23 UnicodeReplacer::~UnicodeReplacer() {}
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringReplacer)24 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringReplacer)
25
26 /**
27 * Construct a StringReplacer that sets the emits the given output
28 * text and sets the cursor to the given position.
29 * @param theOutput text that will replace input text when the
30 * replace() method is called. May contain stand-in characters
31 * that represent nested replacers.
32 * @param theCursorPos cursor position that will be returned by
33 * the replace() method
34 * @param theData transliterator context object that translates
35 * stand-in characters to UnicodeReplacer objects
36 */
37 StringReplacer::StringReplacer(const UnicodeString& theOutput,
38 int32_t theCursorPos,
39 const TransliterationRuleData* theData) {
40 output = theOutput;
41 cursorPos = theCursorPos;
42 hasCursor = TRUE;
43 data = theData;
44 isComplex = TRUE;
45 }
46
47 /**
48 * Construct a StringReplacer that sets the emits the given output
49 * text and does not modify the cursor.
50 * @param theOutput text that will replace input text when the
51 * replace() method is called. May contain stand-in characters
52 * that represent nested replacers.
53 * @param theData transliterator context object that translates
54 * stand-in characters to UnicodeReplacer objects
55 */
StringReplacer(const UnicodeString & theOutput,const TransliterationRuleData * theData)56 StringReplacer::StringReplacer(const UnicodeString& theOutput,
57 const TransliterationRuleData* theData) {
58 output = theOutput;
59 cursorPos = 0;
60 hasCursor = FALSE;
61 data = theData;
62 isComplex = TRUE;
63 }
64
65 /**
66 * Copy constructor.
67 */
StringReplacer(const StringReplacer & other)68 StringReplacer::StringReplacer(const StringReplacer& other) :
69 UnicodeFunctor(other),
70 UnicodeReplacer(other)
71 {
72 output = other.output;
73 cursorPos = other.cursorPos;
74 hasCursor = other.hasCursor;
75 data = other.data;
76 isComplex = other.isComplex;
77 }
78
79 /**
80 * Destructor
81 */
~StringReplacer()82 StringReplacer::~StringReplacer() {
83 }
84
85 /**
86 * Implement UnicodeFunctor
87 */
clone() const88 UnicodeFunctor* StringReplacer::clone() const {
89 return new StringReplacer(*this);
90 }
91
92 /**
93 * Implement UnicodeFunctor
94 */
toReplacer() const95 UnicodeReplacer* StringReplacer::toReplacer() const {
96 return const_cast<StringReplacer *>(this);
97 }
98
99 /**
100 * UnicodeReplacer API
101 */
replace(Replaceable & text,int32_t start,int32_t limit,int32_t & cursor)102 int32_t StringReplacer::replace(Replaceable& text,
103 int32_t start,
104 int32_t limit,
105 int32_t& cursor) {
106 int32_t outLen;
107 int32_t newStart = 0;
108
109 // NOTE: It should be possible to _always_ run the complex
110 // processing code; just slower. If not, then there is a bug
111 // in the complex processing code.
112
113 // Simple (no nested replacers) Processing Code :
114 if (!isComplex) {
115 text.handleReplaceBetween(start, limit, output);
116 outLen = output.length();
117
118 // Setup default cursor position (for cursorPos within output)
119 newStart = cursorPos;
120 }
121
122 // Complex (nested replacers) Processing Code :
123 else {
124 /* When there are segments to be copied, use the Replaceable.copy()
125 * API in order to retain out-of-band data. Copy everything to the
126 * end of the string, then copy them back over the key. This preserves
127 * the integrity of indices into the key and surrounding context while
128 * generating the output text.
129 */
130 UnicodeString buf;
131 int32_t oOutput; // offset into 'output'
132 isComplex = FALSE;
133
134 // The temporary buffer starts at tempStart, and extends
135 // to destLimit. The start of the buffer has a single
136 // character from before the key. This provides style
137 // data when addition characters are filled into the
138 // temporary buffer. If there is nothing to the left, use
139 // the non-character U+FFFF, which Replaceable subclasses
140 // should treat specially as a "no-style character."
141 // destStart points to the point after the style context
142 // character, so it is tempStart+1 or tempStart+2.
143 int32_t tempStart = text.length(); // start of temp buffer
144 int32_t destStart = tempStart; // copy new text to here
145 if (start > 0) {
146 int32_t len = U16_LENGTH(text.char32At(start-1));
147 text.copy(start-len, start, tempStart);
148 destStart += len;
149 } else {
150 UnicodeString str((UChar) 0xFFFF);
151 text.handleReplaceBetween(tempStart, tempStart, str);
152 destStart++;
153 }
154 int32_t destLimit = destStart;
155
156 for (oOutput=0; oOutput<output.length(); ) {
157 if (oOutput == cursorPos) {
158 // Record the position of the cursor
159 newStart = destLimit - destStart; // relative to start
160 }
161 UChar32 c = output.char32At(oOutput);
162 UnicodeReplacer* r = data->lookupReplacer(c);
163 if (r == NULL) {
164 // Accumulate straight (non-segment) text.
165 buf.append(c);
166 } else {
167 isComplex = TRUE;
168
169 // Insert any accumulated straight text.
170 if (buf.length() > 0) {
171 text.handleReplaceBetween(destLimit, destLimit, buf);
172 destLimit += buf.length();
173 buf.truncate(0);
174 }
175
176 // Delegate output generation to replacer object
177 int32_t len = r->replace(text, destLimit, destLimit, cursor);
178 destLimit += len;
179 }
180 oOutput += U16_LENGTH(c);
181 }
182 // Insert any accumulated straight text.
183 if (buf.length() > 0) {
184 text.handleReplaceBetween(destLimit, destLimit, buf);
185 destLimit += buf.length();
186 }
187 if (oOutput == cursorPos) {
188 // Record the position of the cursor
189 newStart = destLimit - destStart; // relative to start
190 }
191
192 outLen = destLimit - destStart;
193
194 // Copy new text to start, and delete it
195 text.copy(destStart, destLimit, start);
196 text.handleReplaceBetween(tempStart + outLen, destLimit + outLen, UnicodeString());
197
198 // Delete the old text (the key)
199 text.handleReplaceBetween(start + outLen, limit + outLen, UnicodeString());
200 }
201
202 if (hasCursor) {
203 // Adjust the cursor for positions outside the key. These
204 // refer to code points rather than code units. If cursorPos
205 // is within the output string, then use newStart, which has
206 // already been set above.
207 if (cursorPos < 0) {
208 newStart = start;
209 int32_t n = cursorPos;
210 // Outside the output string, cursorPos counts code points
211 while (n < 0 && newStart > 0) {
212 newStart -= U16_LENGTH(text.char32At(newStart-1));
213 ++n;
214 }
215 newStart += n;
216 } else if (cursorPos > output.length()) {
217 newStart = start + outLen;
218 int32_t n = cursorPos - output.length();
219 // Outside the output string, cursorPos counts code points
220 while (n > 0 && newStart < text.length()) {
221 newStart += U16_LENGTH(text.char32At(newStart));
222 --n;
223 }
224 newStart += n;
225 } else {
226 // Cursor is within output string. It has been set up above
227 // to be relative to start.
228 newStart += start;
229 }
230
231 cursor = newStart;
232 }
233
234 return outLen;
235 }
236
237 /**
238 * UnicodeReplacer API
239 */
toReplacerPattern(UnicodeString & rule,UBool escapeUnprintable) const240 UnicodeString& StringReplacer::toReplacerPattern(UnicodeString& rule,
241 UBool escapeUnprintable) const {
242 rule.truncate(0);
243 UnicodeString quoteBuf;
244
245 int32_t cursor = cursorPos;
246
247 // Handle a cursor preceding the output
248 if (hasCursor && cursor < 0) {
249 while (cursor++ < 0) {
250 ICU_Utility::appendToRule(rule, (UChar)0x0040 /*@*/, TRUE, escapeUnprintable, quoteBuf);
251 }
252 // Fall through and append '|' below
253 }
254
255 for (int32_t i=0; i<output.length(); ++i) {
256 if (hasCursor && i == cursor) {
257 ICU_Utility::appendToRule(rule, (UChar)0x007C /*|*/, TRUE, escapeUnprintable, quoteBuf);
258 }
259 UChar c = output.charAt(i); // Ok to use 16-bits here
260
261 UnicodeReplacer* r = data->lookupReplacer(c);
262 if (r == NULL) {
263 ICU_Utility::appendToRule(rule, c, FALSE, escapeUnprintable, quoteBuf);
264 } else {
265 UnicodeString buf;
266 r->toReplacerPattern(buf, escapeUnprintable);
267 buf.insert(0, (UChar)0x20);
268 buf.append((UChar)0x20);
269 ICU_Utility::appendToRule(rule, buf,
270 TRUE, escapeUnprintable, quoteBuf);
271 }
272 }
273
274 // Handle a cursor after the output. Use > rather than >= because
275 // if cursor == output.length() it is at the end of the output,
276 // which is the default position, so we need not emit it.
277 if (hasCursor && cursor > output.length()) {
278 cursor -= output.length();
279 while (cursor-- > 0) {
280 ICU_Utility::appendToRule(rule, (UChar)0x0040 /*@*/, TRUE, escapeUnprintable, quoteBuf);
281 }
282 ICU_Utility::appendToRule(rule, (UChar)0x007C /*|*/, TRUE, escapeUnprintable, quoteBuf);
283 }
284 // Flush quoteBuf out to result
285 ICU_Utility::appendToRule(rule, -1,
286 TRUE, escapeUnprintable, quoteBuf);
287
288 return rule;
289 }
290
291 /**
292 * Implement UnicodeReplacer
293 */
addReplacementSetTo(UnicodeSet & toUnionTo) const294 void StringReplacer::addReplacementSetTo(UnicodeSet& toUnionTo) const {
295 UChar32 ch;
296 for (int32_t i=0; i<output.length(); i+=U16_LENGTH(ch)) {
297 ch = output.char32At(i);
298 UnicodeReplacer* r = data->lookupReplacer(ch);
299 if (r == NULL) {
300 toUnionTo.add(ch);
301 } else {
302 r->addReplacementSetTo(toUnionTo);
303 }
304 }
305 }
306
307 /**
308 * UnicodeFunctor API
309 */
setData(const TransliterationRuleData * d)310 void StringReplacer::setData(const TransliterationRuleData* d) {
311 data = d;
312 int32_t i = 0;
313 while (i<output.length()) {
314 UChar32 c = output.char32At(i);
315 UnicodeFunctor* f = data->lookup(c);
316 if (f != NULL) {
317 f->setData(data);
318 }
319 i += U16_LENGTH(c);
320 }
321 }
322
323 U_NAMESPACE_END
324
325 #endif /* #if !UCONFIG_NO_TRANSLITERATION */
326
327 //eof
328