001: /*
002: * (C) Copyright IBM Corp. 1998-2004. All Rights Reserved.
003: *
004: * The program is provided "as is" without any warranty express or
005: * implied, including the warranty of non-infringement and the implied
006: * warranties of merchantibility and fitness for a particular purpose.
007: * IBM will not be liable for any damages suffered by you as a result
008: * of using the Program. In no event will IBM be liable for any
009: * special, indirect or consequential damages or lost profits even if
010: * IBM has been advised of the possibility of their occurrence. IBM
011: * will not be liable for any third party claims against you.
012: */
013: package com.ibm.richtext.test.unit;
014:
015: import com.ibm.icu.dev.test.TestFmwk;
016:
017: import com.ibm.richtext.styledtext.StyledText;
018: import com.ibm.richtext.styledtext.MConstText;
019: import com.ibm.richtext.styledtext.MText;
020: import com.ibm.richtext.textlayout.attributes.AttributeMap;
021: import com.ibm.richtext.styledtext.StyleModifier;
022: import java.util.Random;
023:
024: public final class TestParagraphStyles extends TestFmwk {
025:
026: static final String COPYRIGHT = "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
027:
028: public static void main(String[] args) throws Exception {
029:
030: new TestParagraphStyles().run(args);
031: }
032:
033: private static final int RAND_SEED = 1234;
034: private static final int NUM_TESTS = 2500;
035:
036: private static final boolean isParagraphBreak(char c) {
037:
038: return c == '\u2029' || c == '\n';
039: }
040:
041: private static final Object KEY = "KEY";
042: private static final AttributeMap PLAIN = AttributeMap.EMPTY_ATTRIBUTE_MAP;
043: private static final AttributeMap A_STYLE = new AttributeMap(KEY,
044: new Character('a'));
045: private static final StyleModifier A_MOD = StyleModifier
046: .createReplaceModifier(A_STYLE);
047: private static final AttributeMap B_STYLE = new AttributeMap(KEY,
048: new Character('b'));
049: private static final StyleModifier B_MOD = StyleModifier
050: .createReplaceModifier(B_STYLE);
051: private static final AttributeMap C_STYLE = new AttributeMap(KEY,
052: new Character('c'));
053: private static final StyleModifier C_MOD = StyleModifier
054: .createReplaceModifier(C_STYLE);
055: private static final AttributeMap D_STYLE = new AttributeMap(KEY,
056: new Character('d'));
057: private static final StyleModifier D_MOD = StyleModifier
058: .createReplaceModifier(D_STYLE);
059: private static final AttributeMap E_STYLE = new AttributeMap(KEY,
060: new Character('e'));
061: private static final StyleModifier E_MOD = StyleModifier
062: .createReplaceModifier(E_STYLE);
063:
064: public void test() {
065:
066: easyTests();
067: randomTest();
068: }
069:
070: private void easyTests() {
071:
072: MText text = new StyledText("a\nb\nc\nd\n", PLAIN);
073: text.modifyParagraphStyles(0, text.length(), A_MOD);
074: verifyParagraphCount(text);
075:
076: MText src = new StyledText("XXX\nYYY", PLAIN);
077: src.modifyParagraphStyles(0, src.length(), B_MOD);
078: verifyParagraphCount(src);
079:
080: MText temp = text.extractWritable(0, text.length());
081: temp.append(src);
082: verifyParagraphCount(temp);
083: for (int i = 0; i < text.length(); i++) {
084: if (!temp.paragraphStyleAt(i).equals(
085: text.paragraphStyleAt(i))) {
086: errln("Paragraph styles are wrong");
087: }
088: }
089: for (int i = 0; i < src.length(); i++) {
090: if (!temp.paragraphStyleAt(i + text.length()).equals(
091: src.paragraphStyleAt(i))) {
092: errln("Paragraph styles are wrong");
093: }
094: }
095:
096: temp = text.extractWritable(0, text.length());
097: temp.replace(0, 1, src, 0, src.length());
098: verifyParagraphCount(temp);
099: if (temp.paragraphLimit(0) != 4) {
100: errln("Paragraph limit is wrong");
101: }
102: if (!temp.paragraphStyleAt(0).equals(B_STYLE)) {
103: errln("First style is wrong");
104: }
105: if (!temp.paragraphStyleAt(4).equals(A_STYLE)) {
106: errln("Style after insert is wrong");
107: }
108:
109: // test append
110: MConstText newSrc = src.extract(4, 7);
111: MText initC = new StyledText("cccccc", PLAIN);
112: initC.modifyParagraphStyles(0, initC.length(), C_MOD);
113: initC.append(newSrc);
114: // now initC should be one paragraph with style B
115: if (initC.paragraphLimit(0) != initC.length()) {
116: errln("Should only be one paragraph");
117: }
118: if (initC.paragraphStyleAt(0) != initC.paragraphStyleAt(initC
119: .length())) {
120: errln("Two different paragraph styles");
121: }
122: if (!initC.paragraphStyleAt(initC.length() / 2).equals(B_STYLE)) {
123: errln("Incorrect paragraph style");
124: }
125:
126: text = new StyledText("aaa\n", PLAIN);
127: text.modifyParagraphStyles(0, text.length(), A_MOD);
128: text.modifyParagraphStyles(text.length(), text.length(), B_MOD);
129: if (text.paragraphStyleAt(text.length()) != B_STYLE) {
130: errln("0-length paragraph at end has incorrect style");
131: }
132: }
133:
134: private static int randInt(Random rand, int limit) {
135:
136: return randInt(rand, 0, limit);
137: }
138:
139: private static int randInt(Random rand, int start, int limit) {
140:
141: if (start > limit) {
142: throw new IllegalArgumentException("Range is 0-length.");
143: } else if (start == limit) {
144: return start;
145: }
146:
147: return start + (Math.abs(rand.nextInt()) % (limit - start));
148: }
149:
150: private void randomTest() {
151:
152: MText noParagraph = new StyledText("zzzz", PLAIN);
153: noParagraph.modifyParagraphStyles(0, noParagraph.length(),
154: A_MOD);
155: MText twoParagraphs = new StyledText("aaa\nbbb", PLAIN);
156: twoParagraphs.modifyParagraphStyles(0, twoParagraphs
157: .paragraphLimit(0), B_MOD);
158: MText threeParagraphs = new StyledText("cc\ndd\nee", PLAIN);
159: threeParagraphs.modifyParagraphStyles(0, 3, C_MOD);
160: threeParagraphs.modifyParagraphStyles(3, 6, D_MOD);
161: threeParagraphs.modifyParagraphStyles(6, 8, E_MOD);
162: MText trailingP1 = new StyledText("hhhh\n", PLAIN);
163: trailingP1.modifyParagraphStyles(0, trailingP1
164: .paragraphLimit(0), C_MOD);
165: MText trailingP2 = new StyledText("iii\n", PLAIN);
166: trailingP2.modifyParagraphStyles(0, 0, D_MOD);
167: trailingP2.modifyParagraphStyles(trailingP2.length(),
168: trailingP2.length(), B_MOD);
169:
170: if (!trailingP2.paragraphStyleAt(trailingP2.length() - 1)
171: .equals(D_STYLE)) {
172: errln("Style incorrect in trailingP2");
173: }
174: if (!trailingP2.paragraphStyleAt(trailingP2.length()).equals(
175: B_STYLE)) {
176: errln("Ending style incorrect in trailingP2");
177: }
178:
179: MConstText[] tests = { noParagraph, twoParagraphs,
180: threeParagraphs, trailingP1, trailingP2 };
181:
182: Random random = new Random(RAND_SEED);
183:
184: int stopAt = 465;
185: int i = 0;
186: try {
187: for (i = 0; i < NUM_TESTS; i++) {
188:
189: int srcIndex = randInt(random, tests.length);
190: int targetIndex = randInt(random, tests.length);
191: MText target = new StyledText(tests[targetIndex]);
192: MConstText src = tests[srcIndex];
193:
194: int srcStart = randInt(random, src.length());
195: int srcLimit = randInt(random, srcStart, src.length());
196: int start = randInt(random, target.length());
197: int limit = randInt(random, start, target.length());
198:
199: if (i == stopAt) {
200: stopAt = i;
201: }
202:
203: insertAndCheck(src, srcStart, srcLimit, target, start,
204: limit);
205: }
206: } finally {
207: if (i < NUM_TESTS) {
208: logln("iteration=" + i);
209: }
210: }
211: }
212:
213: private void insertAndCheck(MConstText src, int srcStart,
214: int srcLimit, MText target, int start, int limit) {
215:
216: // p-style after insertion
217: AttributeMap after;
218: if (limit == target.length() && srcLimit > srcStart) {
219: after = src.paragraphStyleAt(srcLimit);
220: } else {
221: after = target.paragraphStyleAt(limit);
222: }
223:
224: AttributeMap before;
225: boolean srcHasPBreak = false;
226: for (int i = srcStart; i < srcLimit; i++) {
227: if (isParagraphBreak(src.at(i))) {
228: srcHasPBreak = true;
229: break;
230: }
231: }
232:
233: if (start > 0 && isParagraphBreak(target.at(start - 1))) {
234: before = target.paragraphStyleAt(start - 1);
235: } else {
236: before = srcHasPBreak ? src.paragraphStyleAt(srcStart)
237: : after;
238: }
239: boolean stylePropogated = !before.equals(target
240: .paragraphStyleAt(Math.max(0, start - 1)));
241:
242: target.resetDamagedRange();
243: target.replace(start, limit, src, srcStart, srcLimit);
244: final int damageLimit = (start == limit && srcStart == srcLimit) ? Integer.MIN_VALUE
245: : start + (srcLimit - srcStart);
246:
247: if (target.damagedRangeLimit() != damageLimit) {
248: logln("limit: " + damageLimit + "; target.limit: "
249: + target.damagedRangeLimit());
250: errln("Damaged range limit is incorrect");
251: }
252:
253: final int damageStart = (damageLimit == Integer.MIN_VALUE) ? Integer.MAX_VALUE
254: : (stylePropogated ? target.paragraphStart(Math.max(0,
255: start - 1)) : start);
256: if (target.damagedRangeStart() > damageStart) {
257: logln("start: " + damageStart + "; target.start: "
258: + target.damagedRangeStart());
259: errln("Damaged range start is incorrect");
260: }
261:
262: verifyParagraphCount(target);
263:
264: // check endpoints
265: if (!before.equals(target.paragraphStyleAt(Math.max(start - 1,
266: 0)))) {
267: errln("Incorrect paragraph style before modified range");
268: }
269:
270: int lengthDelta = (srcLimit - srcStart) - (limit - start);
271: int indexAfterInsert = Math.min(target.length(), limit
272: + lengthDelta);
273: if (!after.equals(target.paragraphStyleAt(indexAfterInsert))) {
274: errln("Incorrect paragraph style after modified range");
275: }
276:
277: if (srcHasPBreak) {
278: int startP = target.paragraphLimit(start);
279: int limitOfTest = target.paragraphStart(indexAfterInsert);
280:
281: int offset = start - srcStart;
282:
283: while (startP < limitOfTest) {
284: int limitP = target.paragraphLimit(startP);
285: if (src.paragraphLimit(startP - offset) + offset != limitP) {
286: errln("paragraph limits are not consistent");
287: }
288: if (!src.paragraphStyleAt(startP - offset).equals(
289: target.paragraphStyleAt(startP))) {
290: errln("paragraph styles are not consistent");
291: }
292: startP = limitP;
293: }
294: } else {
295: for (int i = start; i < start + (srcLimit - srcStart); i++) {
296: if (!after.equals(target.paragraphStyleAt(i))) {
297: errln("paragraph style changed unexpectedly");
298: }
299: }
300: }
301: }
302:
303: private void verifyParagraphCount(MConstText text) {
304:
305: int pCount = 0;
306: int textLength = text.length();
307:
308: if (textLength == 0) {
309: pCount = 1;
310: } else {
311: for (int s = 0; s < textLength; s = text.paragraphLimit(s)) {
312: pCount++;
313: }
314: if (isParagraphBreak(text.at(textLength - 1))) {
315: pCount++;
316: }
317: }
318:
319: int sepCount = 0;
320: for (int i = 0; i < textLength; i++) {
321: if (isParagraphBreak(text.at(i))) {
322: sepCount++;
323: }
324: }
325:
326: if (sepCount + 1 != pCount) {
327: logln("sepCount=" + sepCount + "; pCount=" + pCount);
328: errln("Paragraph count is not consistent with characters");
329: }
330: }
331:
332: private void checkEndpoint(MConstText text) {
333:
334: boolean emptyFinalParagraph;
335: int length = text.length();
336:
337: if (length != 0) {
338: char ch = text.at(length - 1);
339: emptyFinalParagraph = isParagraphBreak(ch);
340: } else {
341: emptyFinalParagraph = true;
342: }
343:
344: if ((text.paragraphStart(length) == length) != emptyFinalParagraph) {
345: errln("Final paragraph length is incorrect");
346: }
347: }
348: }
|