001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Alexey A. Ivanov
019: * @version $Revision$
020: */package javax.swing.text;
021:
022: import java.awt.Component;
023: import java.awt.Container;
024: import java.awt.Rectangle;
025: import java.awt.Shape;
026: import java.awt.geom.Rectangle2D;
027: import javax.swing.BasicSwingTestCase;
028: import javax.swing.JTextArea;
029: import javax.swing.event.DocumentEvent;
030: import javax.swing.event.DocumentListener;
031: import javax.swing.text.AbstractDocument.DefaultDocumentEvent;
032: import javax.swing.text.AbstractDocument.ElementEdit;
033:
034: /**
035: * Tests mostly methods which are used when processing change
036: * notifications.
037: *
038: * <p>This class uses simple initialization like in
039: * <code>SimpleTests</code> but it creates a specialized version of
040: * PlainView where tested methods are overridden.
041: *
042: */
043: public class PlainView_ChangesTest extends BasicSwingTestCase {
044: static final class LineRange {
045: public Component host;
046:
047: public int line0;
048:
049: public int line1;
050:
051: public Shape shape;
052:
053: public LineRange(final int line0, final int line1,
054: final Shape shape, final Component host) {
055: this .line0 = line0;
056: this .line1 = line1;
057: this .shape = shape;
058: this .host = host;
059: }
060:
061: /**
062: * Checks that fields have expected values.
063: */
064: public void check(final int s, final int e, final Shape a,
065: final Container container) {
066: assertEquals("Unexpected start line", s, line0);
067: assertEquals("Unexpected end line", e, line1);
068: assertSame("Unexpected shape", a, shape);
069: assertSame("Unexpected container/host", container, host);
070: }
071: }
072:
073: static final class PreferenceChange {
074: public boolean height;
075:
076: public boolean width;
077:
078: public PreferenceChange(final boolean width,
079: final boolean height) {
080: this .width = width;
081: this .height = height;
082: }
083:
084: public void check(final boolean width, final boolean height) {
085: assertEquals("Width has unexpected value", width,
086: this .width);
087: assertEquals("Height has unexpected value", height,
088: this .height);
089: }
090: }
091:
092: private boolean callSuperDamageRange;
093:
094: private boolean callSuperUpdateDamage;
095:
096: private Container container;
097:
098: private Document doc;
099:
100: private DocumentEvent event;
101:
102: private ViewFactory factory;
103:
104: private LineRange lineRange;
105:
106: private Rectangle paintRect;
107:
108: private PreferenceChange preferenceChange;
109:
110: private Element root;
111:
112: private Shape shape;
113:
114: private boolean updateDamageCalled;
115:
116: private PlainView view;
117:
118: public void testChangedUpdate() {
119: view.changedUpdate(event, shape, null);
120: assertTrue(updateDamageCalled);
121: }
122:
123: public void testDamageLineRange() {
124: view.updateMetrics();
125: final int height = view.metrics.getHeight();
126: final int y = 300;
127: callSuperDamageRange = true;
128: shape = new Rectangle(200, y, 300, 500);
129: view.damageLineRange(0, 0, shape, view.getContainer());
130: assertTrue(paintRect.equals(new Rectangle(200, y, 300, height)));
131: assertTrue(paintRect.equals(view.lineToRect(shape, 0)));
132: paintRect = null;
133: view.damageLineRange(1, 1, shape, view.getContainer());
134: assertTrue(paintRect.equals(new Rectangle(200, y + height, 300,
135: height)));
136: assertTrue(paintRect.equals(view.lineToRect(shape, 1)));
137: paintRect = null;
138: view.damageLineRange(2, 2, shape, view.getContainer());
139: assertTrue(paintRect.equals(new Rectangle(200, y + height * 2,
140: 300, height)));
141: assertTrue(paintRect.equals(view.lineToRect(shape, 2)));
142: paintRect = null;
143: view.damageLineRange(0, 2, shape, view.getContainer());
144: assertEquals(new Rectangle(200, 300, 300, 3 * height),
145: paintRect);
146: Rectangle r0 = view.lineToRect(shape, 0);
147: Rectangle r1 = view.lineToRect(shape, 1);
148: Rectangle r2 = view.lineToRect(shape, 2);
149: // Union all the rectangles
150: r0 = r0.union(r1).union(r2);
151: assertEquals(r0, paintRect);
152: }
153:
154: /**
155: * Tests that <code>insertUpdate</code> calls
156: * <code>updateDamage</code> to perform the actual updates.
157: */
158: public void testInsertUpdateDamage() {
159: view.insertUpdate(event, shape, null);
160: assertTrue(updateDamageCalled);
161: }
162:
163: public void testLineToRect() {
164: view.updateMetrics();
165: final int height = view.metrics.getHeight();
166: assertEquals(new Rectangle(0, 0, 500, height), view.lineToRect(
167: shape, 0));
168: assertEquals(new Rectangle(0, height, 500, height), view
169: .lineToRect(shape, 1));
170: assertEquals(new Rectangle(30, 50 + height, 70, height), view
171: .lineToRect(shape = new Rectangle2D.Float(30f, 50f,
172: 70f, 10f), 1));
173: }
174:
175: /**
176: * Tests nextTabStop method with TabSize set to 4.
177: */
178: public void testNextTabStop02() {
179: // Set tab size to 4
180: doc.putProperty(PlainDocument.tabSizeAttribute, new Integer(4));
181: assertEquals(4, view.getTabSize());
182: // Update metrics 'cause view state isn't fully initialized yet
183: view.updateMetrics();
184: float tabPos = view.getTabSize() * view.metrics.charWidth('m');
185: // Test tab stop positions
186: assertEquals(tabPos, view.nextTabStop(0.0f, 0), 0.00001f);
187: assertEquals(tabPos, view.nextTabStop(tabPos - 0.02f, 0),
188: 0.00001f);
189: assertEquals(tabPos * 2, view.nextTabStop(tabPos, 0), 0.00001f);
190: }
191:
192: /**
193: * Tests nextTabStop method with TabSize set to a negative value.
194: */
195: public void testNextTabStop03() {
196: // Set tab size to -4
197: doc
198: .putProperty(PlainDocument.tabSizeAttribute,
199: new Integer(-4));
200: assertEquals(-4, view.getTabSize());
201: // Update metrics 'cause view state isn't fully initialized yet
202: view.updateMetrics();
203: float tabPos = view.getTabSize() * view.metrics.charWidth('m');
204: // Test tab stop positions
205: assertEquals(tabPos, view.nextTabStop(0.0f, 0), 0.00001f);
206: assertEquals(tabPos, view.nextTabStop(-tabPos - 0.2f, 0),
207: 0.00001f);
208: assertEquals(0.0f, view.nextTabStop(-tabPos, 0), 0.00001f);
209: assertEquals(-tabPos, view.nextTabStop(-tabPos * 2, 0),
210: 0.00001f);
211: assertEquals(tabPos * 2, view.nextTabStop(tabPos, 0), 0.00001f);
212: }
213:
214: /**
215: * Tests nextTabStop method with TabSize set to zero.
216: */
217: public void testNextTabStop04() {
218: // Set tab size to 0
219: doc.putProperty(PlainDocument.tabSizeAttribute, new Integer(0));
220: assertEquals(0, view.getTabSize());
221: // Update metrics 'cause view state isn't fully initialized yet
222: view.updateMetrics();
223: float tabPos = view.getTabSize() * view.metrics.charWidth('m');
224: assertEquals(0.0f, tabPos, 0.00001f);
225: // Test tab stop positions
226: assertEquals(tabPos, view.nextTabStop(0.0f, 0), 0.00001f);
227: assertEquals(tabPos + 0.2f, view.nextTabStop(tabPos + 0.2f, 0),
228: 0.00001f);
229: assertEquals(4.75f, view.nextTabStop(4.75f, 0), 0.00001f);
230: }
231:
232: public void testRemoveUpdate() {
233: view.removeUpdate(event, shape, null);
234: assertTrue(updateDamageCalled);
235: }
236:
237: /**
238: * Tests updateDamage with insert event
239: */
240: public void testUpdateDamage01() throws BadLocationException {
241: createEvent();
242: doc.insertString(0, "1:0123\n2:\n3:abcdefg", null);
243: // 0123456 789 012345678
244: view.updateDamage(event, shape, factory);
245: assertNull(lineRange);
246: preferenceChange.check(true, true);
247: preferenceChange = null;
248: doc.insertString(14, "0123210", null);
249: view.updateDamage(event, shape, factory);
250: lineRange.check(2, 2, shape, container);
251: lineRange = null;
252: preferenceChange.check(true, false);
253: preferenceChange = null;
254: doc.insertString(14, "\n", null);
255: view.updateDamage(event, shape, factory);
256: if (!isHarmony()) {
257: assertNull(lineRange);
258: } else {
259: lineRange.check(2, 3, shape, container);
260: lineRange = null;
261: }
262: preferenceChange.check(true, true);
263: preferenceChange = null;
264: doc.insertString(15, "\n", null);
265: view.updateDamage(event, shape, factory);
266: if (!isHarmony()) {
267: assertNull(lineRange);
268: } else {
269: lineRange.check(2, 4, shape, container);
270: lineRange = null;
271: }
272: preferenceChange.check(isHarmony() ? false : true, true);
273: preferenceChange = null;
274: doc.insertString(15, "line1\nline2 change\nline3", null);
275: view.updateDamage(event, shape, factory);
276: if (!isHarmony()) {
277: assertNull(lineRange);
278: } else {
279: lineRange.check(2, 6, shape, container);
280: lineRange = null;
281: }
282: preferenceChange.check(isHarmony() ? false : true, true);
283: preferenceChange = null;
284: }
285:
286: /**
287: * Tests updateDamage with remove event
288: */
289: public void testUpdateDamage02() throws BadLocationException {
290: createEvent();
291: doc.insertString(0, "1:0123\n2:\n3:abcdefg", null);
292: // 0123456 789 012345678
293: // Update view
294: assertNull(preferenceChange);
295: view.updateDamage(event, shape, factory);
296: assertNull(lineRange);
297: preferenceChange.check(true, true);
298: preferenceChange = null;
299: // The widest line doesn't change, and line number neither
300: doc.remove(2, 2); // "01" => "1:23\n2:\n3:..."
301: view.updateDamage(event, shape, factory);
302: lineRange.check(0, 0, shape, container);
303: lineRange = null;
304: assertNull(preferenceChange);
305: // The widest line doesn't change, but line number changes
306: doc.remove(4, 1); // "\n" => "1:232:\n3:..."
307: view.updateDamage(event, shape, factory);
308: assertNull(lineRange);
309: preferenceChange.check(isHarmony() ? false : true, true);
310: preferenceChange = null;
311: // Again the widest line doesn't change, and line number stays the same
312: doc.remove(4, 2);
313: view.updateDamage(event, shape, factory);
314: lineRange.check(0, 0, shape, container);
315: assertNull(preferenceChange);
316: }
317:
318: /**
319: * Tests updateDamage with change event
320: */
321: public void testUpdateDamage03() throws BadLocationException {
322: createEvent();
323: doc.insertString(0, "1:0123\n2:\n3:abcdefg", null);
324: // 0123456 789 012345678
325: // Update view
326: assertNull(preferenceChange);
327: view.updateDamage(event, shape, factory);
328: assertNull(lineRange);
329: preferenceChange.check(true, true);
330: preferenceChange = null;
331: event = ((AbstractDocument) doc).new DefaultDocumentEvent(3,
332: 14, DocumentEvent.EventType.CHANGE);
333: view.updateDamage(event, shape, factory);
334: lineRange.check(0, 0, shape, container);
335: lineRange = null;
336: assertNull(preferenceChange);
337: event = ((AbstractDocument) doc).new DefaultDocumentEvent(7,
338: 12, DocumentEvent.EventType.CHANGE);
339: view.updateDamage(event, shape, factory);
340: lineRange.check(1, 1, shape, container);
341: lineRange = null;
342: assertNull(preferenceChange);
343: // We remove the first and second lines, but the widest one isn't
344: // changed, therefore the preferred width is not changed
345: ElementEdit ee = new ElementEdit(root, 0, null, new Element[] {
346: root.getElement(0), root.getElement(1) });
347: ((DefaultDocumentEvent) event).addEdit(ee);
348: view.updateDamage(event, shape, factory);
349: assertNull(lineRange);
350: preferenceChange.check(isHarmony() ? false : true, true);
351: }
352:
353: public void testUpdateMetrics() {
354: assertNull(view.metrics);
355: view.updateMetrics();
356: assertNotNull(view.metrics);
357: assertNull(preferenceChange);
358: }
359:
360: @Override
361: protected void setUp() throws Exception {
362: super .setUp();
363: doc = new PlainDocument();
364: root = doc.getDefaultRootElement();
365: view = new PlainView(root) {
366: @Override
367: public Container getContainer() {
368: if (container == null) {
369: container = new JTextArea() {
370: private static final long serialVersionUID = 1L;
371:
372: @Override
373: public void repaint(final int x, final int y,
374: final int w, final int h) {
375: if (paintRect == null) {
376: paintRect = new Rectangle(x, y, w, h);
377: } else {
378: paintRect
379: .add(new Rectangle(x, y, w, h));
380: }
381: }
382: };
383: }
384: return container;
385: }
386:
387: @Override
388: public void preferenceChanged(final View child,
389: final boolean width, final boolean height) {
390: preferenceChange = new PreferenceChange(width, height);
391: assertNull(child);
392: super .preferenceChanged(child, width, height);
393: }
394:
395: @Override
396: protected void damageLineRange(final int line0,
397: final int line1, final Shape shape,
398: final Component host) {
399: if (callSuperDamageRange) {
400: super .damageLineRange(line0, line1, shape, host);
401: } else {
402: lineRange = new LineRange(line0, line1, shape, host);
403: }
404: }
405:
406: @Override
407: protected void updateDamage(final DocumentEvent changes,
408: final Shape a, final ViewFactory f) {
409: if (callSuperUpdateDamage) {
410: super .updateDamage(changes, a, f);
411: } else {
412: assertSame(event, changes);
413: assertSame(shape, a);
414: assertNull(f);
415: updateDamageCalled = true;
416: }
417: }
418: };
419: shape = new Rectangle(500, 500);
420: event = ((AbstractDocument) doc).new DefaultDocumentEvent(0, 0,
421: DocumentEvent.EventType.CHANGE);
422: factory = new ViewFactory() {
423: public View create(final Element element) {
424: fail("factory.create() isn't supposed to be called");
425: return null;
426: }
427: };
428: }
429:
430: private void createEvent() {
431: doc.addDocumentListener(new DocumentListener() {
432: public void changedUpdate(final DocumentEvent changes) {
433: fail("changedUpdate isn't supposed to be called");
434: }
435:
436: public void insertUpdate(final DocumentEvent changes) {
437: event = changes;
438: }
439:
440: public void removeUpdate(final DocumentEvent changes) {
441: event = changes;
442: }
443: });
444: callSuperUpdateDamage = true;
445: }
446: }
|