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: *
021: */package javax.swing.text;
022:
023: import java.awt.Graphics;
024: import java.awt.Rectangle;
025: import java.awt.Shape;
026: import javax.swing.BasicSwingTestCase;
027: import javax.swing.text.AbstractDocument.BranchElement;
028: import javax.swing.text.CompositeViewTest.CompositeViewImpl;
029: import javax.swing.text.Position.Bias;
030: import junit.framework.TestCase;
031:
032: /**
033: * Tests CompositeView methods that deal with model/view conversions.
034: *
035: */
036: public class CompositeView_ModelViewTest extends TestCase {
037: static PlainDocument doc; // Document used in tests
038:
039: static Element root; // Default root element of the document
040:
041: static CompositeView view; // View object used in tests
042:
043: static ViewFactory factory; // View factory used to create new views
044:
045: static Shape shape; // View allocation (area to render into)
046:
047: static Bias[] bias; // Place for bias return
048:
049: /**
050: * View which passed as parameter to modelToView or viewToModel method.
051: */
052: private static View viewAsked;
053:
054: /**
055: * Shape which passed as parameter to modelToView or viewToModel method.
056: */
057: private static Shape shapeAsked;
058:
059: /**
060: * Width of one position in the view.
061: */
062: static final int POS_WIDTH = 4;
063:
064: /**
065: * Height of one child view, or line.
066: */
067: static final int LINE_HEIGHT = 16;
068:
069: /**
070: * Stores the same value as <code>shape</code> but with different
071: * type. Actually <code>bounds</code> and <code>shape</code>
072: * are the same object.
073: */
074: private static Rectangle bounds;
075:
076: /**
077: * Manages some children of type ChildView.
078: */
079: public static class WithChildrenView extends CompositeViewImpl {
080: public WithChildrenView(final Element element) {
081: super (element);
082: }
083:
084: @Override
085: protected void childAllocation(final int index,
086: final Rectangle rc) {
087: // The each view allocation is LINE_HEIGHT in height and
088: // represents a line-like rectangle
089: rc.y = ((Rectangle) shape).y + LINE_HEIGHT * index;
090: rc.height = LINE_HEIGHT;
091: }
092:
093: @Override
094: protected View getViewAtPoint(final int x, final int y,
095: final Rectangle rc) {
096: int index = getViewIndex(x, y, rc);
097: if (index != -1) {
098: childAllocation(index, rc);
099: return getView(index);
100: }
101: return null;
102: }
103: }
104:
105: /**
106: * Represents child view managed by WithChildrenView.
107: */
108: public static class ChildView extends View {
109: public ChildView(final Element element) {
110: super (element);
111: }
112:
113: @Override
114: public int viewToModel(float x, final float y,
115: final Shape shape, final Bias[] biasReturn) {
116: assertTrue(shape.contains(x, y));
117: viewAsked = this ;
118: shapeAsked = shape;
119: Rectangle rect = shape.getBounds();
120: x -= rect.x;
121: return getStartOffset() + (int) x / POS_WIDTH;
122: }
123:
124: @Override
125: public Shape modelToView(int pos, final Shape shape,
126: final Bias bias) throws BadLocationException {
127: Rectangle bounds = shape.getBounds();
128: viewAsked = this ;
129: shapeAsked = shape;
130: pos -= getStartOffset();
131: return new Rectangle(bounds.x + pos * POS_WIDTH, bounds.y,
132: 1, 16);
133: }
134:
135: @Override
136: public void paint(final Graphics g, final Shape shape) {
137: }
138:
139: @Override
140: public float getPreferredSpan(final int axis) {
141: return 0;
142: }
143: }
144:
145: /**
146: * View factory to create ChildView instances.
147: */
148: public static class ChildFactory implements ViewFactory {
149: public View create(final Element element) {
150: return new ChildView(element);
151: }
152: }
153:
154: @Override
155: protected void setUp() throws Exception {
156: super .setUp();
157: doc = new PlainDocument();
158: doc
159: .insertString(0, "line1\nline2\n\u05DC\u05DD\nline3\n",
160: null);
161: // positions: 012345 678901 2 3 4 567890
162: // 0 1 2
163: root = doc.getDefaultRootElement();
164: view = new WithChildrenView(root);
165: view.loadChildren(factory = new ChildFactory());
166: shape = new Rectangle(100, 200, 190, 560);
167: bounds = (Rectangle) shape;
168: bias = new Position.Bias[1];
169: }
170:
171: private Rectangle getChildRect(final int index)
172: throws BadLocationException {
173: View child = view.getView(index);
174: return (Rectangle) child.modelToView(child.getStartOffset(),
175: Bias.Forward, child.getEndOffset(), Bias.Forward, view
176: .getChildAllocation(index, shape));
177: }
178:
179: /*
180: * Class under test for Shape modelToView(int, Bias, int, Bias, Shape)
181: */
182: public void testModelToViewintBiasintBiasShape()
183: throws BadLocationException {
184: Shape res;
185: Rectangle rc1;
186: Rectangle rc2;
187: // Same positions
188: res = view.modelToView(0, Bias.Forward, 0, Bias.Forward, shape);
189: assertEquals(view.modelToView(0, shape, Bias.Forward), res);
190: // Both Forward
191: rc1 = (Rectangle) view.modelToView(0, shape, Bias.Forward);
192: rc2 = (Rectangle) view.modelToView(1, shape, Bias.Forward);
193: res = view.modelToView(0, Bias.Forward, 1, Bias.Forward, shape);
194: assertEquals(rc1.union(rc2), res);
195: rc1 = (Rectangle) view.modelToView(0, shape, Bias.Forward);
196: rc2 = (Rectangle) view.modelToView(2, shape, Bias.Forward);
197: res = view.modelToView(0, Bias.Forward, 2, Bias.Forward, shape);
198: assertEquals(rc1.union(rc2), res);
199: // Different biases
200: rc1 = (Rectangle) view.modelToView(0, shape, Bias.Forward);
201: rc2 = (Rectangle) view.modelToView(2, shape, Bias.Forward);
202: res = view
203: .modelToView(0, Bias.Forward, 2, Bias.Backward, shape);
204: assertEquals(rc1.union(rc2), res);
205: // On different lines
206: rc1 = (Rectangle) view.getView(0).modelToView(6, shape,
207: Bias.Forward);
208: rc2 = (Rectangle) view.modelToView(6, shape, Bias.Forward);
209: res = view.modelToView(0, Bias.Forward, 6, Bias.Forward, shape);
210: assertEquals(rc1.union(rc2), res);
211: rc1 = getChildRect(0);
212: rc2 = (Rectangle) view.modelToView(7, shape, Bias.Forward);
213: res = view.modelToView(0, Bias.Forward, 7, Bias.Forward, shape);
214: assertEquals(rc1.union(rc2), res);
215: // Assert both previous cases return the same value
216: assertEquals(view.modelToView(0, Bias.Forward, 6, Bias.Forward,
217: shape), view.modelToView(0, Bias.Forward, 7,
218: Bias.Forward, shape));
219: // When range doesn't include first line start
220: rc1 = getChildRect(0);
221: rc2 = (Rectangle) view.modelToView(9, shape, Bias.Forward);
222: res = view.modelToView(2, Bias.Forward, 7, Bias.Forward, shape);
223: assertEquals(rc1.union(rc2), res);
224: // Both on line 2
225: rc1 = (Rectangle) view.modelToView(7, shape, Bias.Forward);
226: rc2 = (Rectangle) view.modelToView(9, shape, Bias.Forward);
227: res = view.modelToView(7, Bias.Forward, 9, Bias.Forward, shape);
228: assertEquals(rc1.union(rc2), res);
229: // From line 1 to line 3
230: // TODO investigate more carefully modelToView(int, B, int, B, Shape)
231: // Can't use this code 'cause view.getChildAllocation(1, shape) and
232: // view.getChildAllocation(2, shape) return the same instance of
233: // rectangles, tho' the instances MUST be different acc. to the spec
234: //
235: // rc1 = (Rectangle)view.getChildAllocation(1, shape);
236: // rc1 = rc1.union((Rectangle)view.getChildAllocation(2, shape));
237: // rc1 = rc1.union((Rectangle)view.getChildAllocation(3, shape));
238: //
239: // The work around is to get child allocations from the inside but
240: // not the public interface
241: rc1 = shape.getBounds();
242: view.childAllocation(0, rc1);
243: rc2 = shape.getBounds();
244: view.childAllocation(2, rc2);
245: res = view
246: .modelToView(1, Bias.Forward, 13, Bias.Forward, shape);
247: assertEquals(rc1.union(rc2), res);
248: // From line 2 to line 3
249: rc1 = getChildRect(1);
250: rc2 = (Rectangle) view.modelToView(13, shape, Bias.Forward);
251: res = view
252: .modelToView(9, Bias.Forward, 13, Bias.Forward, shape);
253: assertEquals(rc1.union(rc2), res);
254: // From line 2 to line 4
255: rc1 = shape.getBounds();
256: view.childAllocation(1, rc1);
257: //System.out.println(rc1);
258: rc2 = shape.getBounds();
259: view.childAllocation(3, rc2);
260: res = view
261: .modelToView(9, Bias.Forward, 17, Bias.Forward, shape);
262: assertEquals(rc1.union(rc2), res);
263: }
264:
265: /*
266: * Class under test for Shape modelToView(int, Shape, Bias)
267: */
268: public void testModelToViewintShapeBias()
269: throws BadLocationException {
270: assertEquals(new Rectangle(bounds.x, bounds.y, 1, LINE_HEIGHT),
271: view.modelToView(0, shape, Bias.Forward));
272: assertSame(view.getView(0), viewAsked);
273: assertEquals(new Rectangle(bounds.x + 3 * POS_WIDTH, bounds.y,
274: 1, LINE_HEIGHT), view.modelToView(3, shape,
275: Bias.Forward));
276: assertSame(view.getView(0), viewAsked);
277: assertEquals(LINE_HEIGHT, ((Rectangle) shapeAsked).height);
278: assertEquals(bounds.y, ((Rectangle) shapeAsked).y);
279: assertEquals(new Rectangle(bounds.x + 1 * POS_WIDTH, bounds.y
280: + LINE_HEIGHT, 1, LINE_HEIGHT), view.modelToView(7,
281: shape, Bias.Forward));
282: assertSame(view.getView(1), viewAsked);
283: assertEquals(LINE_HEIGHT, ((Rectangle) shapeAsked).height);
284: assertEquals(bounds.y + LINE_HEIGHT, ((Rectangle) shapeAsked).y);
285: assertEquals(new Rectangle(bounds.x + 2 * POS_WIDTH, bounds.y
286: + 3 * LINE_HEIGHT, 1, LINE_HEIGHT), view.modelToView(
287: 17, shape, Bias.Forward));
288: assertSame(view.getView(3), viewAsked);
289: assertEquals(LINE_HEIGHT, ((Rectangle) shapeAsked).height);
290: assertEquals(bounds.y + 3 * LINE_HEIGHT,
291: ((Rectangle) shapeAsked).y);
292: }
293:
294: /**
295: * Tests viewToModel(float, float, Shape, Bias[]).
296: * For isBefore, isAfter the Y is used only. In this test either
297: * isBefore or isAfter is true.
298: */
299: public void testViewToModel01() {
300: CompositeViewTest.useBoth = false;
301: CompositeViewTest.useX = false;
302: int x, y;
303: // Before the allocation
304: x = bounds.x;
305: y = bounds.y - 1;
306: assertTrue(view.isBefore(x, y, bounds));
307: assertEquals(0, view.viewToModel(x, y, shape, bias));
308: // After the allocation
309: y = bounds.y + bounds.height + 1;
310: assertTrue(view.isAfter(x, y, bounds));
311: assertEquals(doc.getLength(), view.viewToModel(x, y, shape,
312: bias));
313: }
314:
315: /**
316: * Tests viewToModel(float, float, Shape, Bias[]).
317: * For isBefore, isAfter (always false in the test) the Y is used only.
318: */
319: public void testViewToModel02() {
320: CompositeViewTest.useBoth = false;
321: CompositeViewTest.useX = false;
322: int x, y;
323: // In the first box
324: x = bounds.x + 5;
325: y = bounds.y + 4;
326: assertEquals(5 / POS_WIDTH, view.viewToModel(x, y, shape, bias));
327: assertSame(view.getView(0), viewAsked);
328: assertEquals(view.getChildAllocation(0, shape), shapeAsked);
329: viewAsked = null;
330: shapeAsked = null;
331: // X coordinate is not in the shape
332: x = bounds.x - 1;
333: y = bounds.y + 4 + LINE_HEIGHT;
334: // Assert the condition used internally
335: assertFalse(view.isBefore(x, y, bounds));
336: assertFalse(view.isAfter(x, y, bounds));
337: assertEquals(-1, view.getViewIndex(x, y, shape));
338: assertNull(view.getViewAtPoint(x, y, shape.getBounds()));
339: // We will get -1, however it's invalid model position
340: assertEquals(-1, view.viewToModel(x, y, shape, bias));
341: // No view was asked
342: assertNull(viewAsked);
343: assertNull(shapeAsked);
344: // One may easily the assertions above are also true for this case
345: x = bounds.x + bounds.width;
346: y = bounds.y + 4 + LINE_HEIGHT;
347: assertEquals(-1, view.viewToModel(x, y, shape, bias));
348: assertNull(viewAsked);
349: assertNull(shapeAsked);
350: // In the second box
351: x = bounds.x;
352: y = bounds.y + 4 + LINE_HEIGHT;
353: assertEquals(1, view.getViewIndex(x, y, shape));
354: assertSame(view.getView(1), view.getViewAtPoint(x, y, shape
355: .getBounds()));
356: assertEquals(view.getView(1).getStartOffset(), view
357: .viewToModel(x, y, shape, bias));
358: assertSame(view.getView(1), viewAsked);
359: assertEquals(LINE_HEIGHT, ((Rectangle) shapeAsked).height);
360: assertEquals(bounds.y + LINE_HEIGHT, ((Rectangle) shapeAsked).y);
361: viewAsked = null;
362: shapeAsked = null;
363: }
364:
365: /**
366: * Tests viewToModel(float, float, Shape, Bias[]).
367: * The body is the same as in testViewToModel02
368: * (irrelevant asserts are removed) but:
369: * for isBefore and isAfter, the X is used only.
370: */
371: public void testViewToModel03() {
372: CompositeViewTest.useBoth = false;
373: CompositeViewTest.useX = true;
374: int x, y;
375: // In the first box
376: x = bounds.x + 5;
377: y = bounds.y + 4;
378: assertEquals(5 / POS_WIDTH, view.viewToModel(x, y, shape, bias));
379: assertSame(view.getView(0), viewAsked);
380: assertEquals(view.getChildAllocation(0, shape), shapeAsked);
381: viewAsked = null;
382: shapeAsked = null;
383: // X coordinate is not in the shape
384: x = bounds.x - 1;
385: y = bounds.y + 4 + LINE_HEIGHT;
386: // Assert the condition used internally
387: assertTrue(view.isBefore(x, y, bounds));
388: assertEquals(0, view.viewToModel(x, y, shape, bias));
389: // No view was asked
390: assertNull(viewAsked);
391: assertNull(shapeAsked);
392: x = bounds.x + bounds.width + 1;
393: y = bounds.y + 4 + LINE_HEIGHT;
394: assertTrue(view.isAfter(x, y, bounds));
395: assertEquals(doc.getLength(), view.viewToModel(x, y, shape,
396: bias));
397: assertNull(viewAsked);
398: assertNull(shapeAsked);
399: // In the second box
400: x = bounds.x;
401: y = bounds.y + 4 + LINE_HEIGHT;
402: assertEquals(1, view.getViewIndex(x, y, shape));
403: assertSame(view.getView(1), view.getViewAtPoint(x, y, shape
404: .getBounds()));
405: assertEquals(view.getView(1).getStartOffset(), view
406: .viewToModel(x, y, shape, bias));
407: assertSame(view.getView(1), viewAsked);
408: assertEquals(LINE_HEIGHT, ((Rectangle) shapeAsked).height);
409: assertEquals(bounds.y + LINE_HEIGHT, ((Rectangle) shapeAsked).y);
410: viewAsked = null;
411: shapeAsked = null;
412: }
413:
414: /**
415: * Tests viewToModel(float, float, Shape, Bias[]).
416: * For isBefore, isAfter the Y is used only. In this test either
417: * isBefore or isAfter is true.
418: * <p>In this test modified <code>root</code> is used so that
419: * <code>view.getStartOffset() != 0</code> and
420: * <code>view.getEndOffset() != doc.getLength()</code>.
421: */
422: public void testViewToModel04() {
423: // Modify the root
424: final int oldCount = root.getElementCount();
425: BranchElement docRoot = (BranchElement) root;
426: final Element[] empty = new Element[0];
427: docRoot.replace(0, 1, empty);
428: docRoot.replace(docRoot.getElementCount() - 1, 1, empty);
429: final int newCount = root.getElementCount();
430: assertEquals(oldCount - 2, newCount);
431: // Re-load children
432: view.removeAll();
433: view.loadChildren(factory);
434: CompositeViewTest.useBoth = false;
435: CompositeViewTest.useX = false;
436: int x, y;
437: // Before the allocation
438: x = bounds.x;
439: y = bounds.y - 1;
440: assertTrue(view.isBefore(x, y, bounds));
441: int offset = view.viewToModel(x, y, shape, bias);
442: assertEquals(view.getStartOffset(), offset);
443: assertEquals(root.getElement(0).getStartOffset(), offset);
444: // After the allocation
445: y = bounds.y + bounds.height + 1;
446: assertTrue(view.isAfter(x, y, bounds));
447: bias[0] = null;
448: offset = view.viewToModel(x, y, shape, bias);
449: assertEquals(view.getEndOffset() - 1, offset);
450: assertEquals(root.getElement(root.getElementCount() - 1)
451: .getEndOffset() - 1, offset);
452: if (BasicSwingTestCase.isHarmony()) {
453: assertSame(Bias.Backward, bias[0]);
454: }
455: }
456:
457: public void testViewToModel05() {
458: final boolean[] methodCalled = new boolean[] { false };
459: view = new WithChildrenView(root) {
460: @Override
461: protected View getViewAtPoint(int x, int y, Rectangle rc) {
462: methodCalled[0] = true;
463: return super .getViewAtPoint(x, y, rc);
464: }
465: };
466: view.loadChildren(factory);
467: assertFalse(methodCalled[0]);
468: view.viewToModel(bounds.x + bounds.width / 2, bounds.y
469: + bounds.height, shape, bias);
470: assertTrue(methodCalled[0]);
471: assertSame(view.getView(view.getViewIndex(bounds.x, bounds.y,
472: shape)), view.getViewAtPoint(bounds.x, bounds.y, view
473: .getInsideAllocation(shape)));
474: }
475: }
|